From 121983ae6d71fd24c317ea501bc96532216d5635 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Mon, 21 Apr 2025 01:55:32 -0700 Subject: [PATCH] rebase part 1 --- .config/guardian/.gdnsuppress | 46 + .devcontainer/README.md | 2 +- .eslint-ignore | 3 +- .eslint-plugin-local/code-amd-node-module.ts | 2 +- .../code-declare-service-brand.ts | 2 +- ...code-ensure-no-disposables-leak-in-test.ts | 2 +- .eslint-plugin-local/code-import-patterns.ts | 2 +- .../code-limited-top-functions.ts | 10 +- .eslint-plugin-local/code-must-use-result.ts | 8 +- .../code-must-use-super-dispose.ts | 2 +- .../code-no-dangerous-type-assertions.ts | 2 +- .../code-no-global-document-listener.ts | 2 +- .../code-no-nls-in-standalone-editor.ts | 2 +- .../code-no-runtime-import.ts | 8 +- .../code-no-standalone-editor.ts | 2 +- .../code-no-static-self-ref.ts | 6 +- .../code-no-unused-expressions.ts | 8 +- .eslint-plugin-local/index.js | 4 + .../vscode-dts-string-type-literals.ts | 4 +- .gitignore | 5 - .npmrc | 2 +- .vscode-test.js | 25 +- .vscode/launch.json | 61 +- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/endgame.github-issues | 2 +- .vscode/notebooks/my-endgame.github-issues | 2 +- .vscode/notebooks/my-work.github-issues | 2 +- .vscode/settings.json | 2 + .vscode/tasks.json | 31 +- ThirdPartyNotices.txt | 53 +- build/.moduleignore | 1 + build/.npmrc | 1 + build/azure-pipelines/cli/cli-compile.yml | 23 +- .../cli/install-rust-posix.yml | 2 +- .../cli/install-rust-win32.yml | 2 +- build/azure-pipelines/common/publish.js | 12 +- build/azure-pipelines/common/publish.ts | 10 +- .../darwin/product-build-darwin-test.yml | 8 +- .../darwin/product-build-darwin.yml | 5 + .../product-build-linux-legacy-server.yml | 233 - build/azure-pipelines/linux/setup-env.sh | 82 +- build/azure-pipelines/product-build.yml | 81 +- build/azure-pipelines/product-compile.yml | 4 +- build/azure-pipelines/product-publish.yml | 1 - .../win32/product-build-win32-test.yml | 4 +- .../win32/product-build-win32.yml | 57 +- .../azure-pipelines/win32/sdl-scan-win32.yml | 31 +- build/buildfile.js | 29 +- build/checksums/electron.txt | 2 +- build/checksums/nodejs.txt | 14 +- build/checksums/vscode-sysroot.txt | 3 - build/darwin/create-universal-app.js | 1 + build/darwin/create-universal-app.ts | 1 + build/darwin/sign.js | 6 +- build/darwin/sign.ts | 6 +- build/filters.js | 8 +- build/gulpfile.compile.js | 16 +- build/gulpfile.editor.js | 237 +- build/gulpfile.extensions.js | 4 - build/gulpfile.reh.js | 32 +- build/gulpfile.vscode.js | 34 +- build/gulpfile.vscode.linux.js | 47 +- build/gulpfile.vscode.web.js | 7 +- build/gulpfile.vscode.win32.js | 4 +- build/lib/builtInExtensions.js | 5 +- build/lib/builtInExtensions.ts | 4 +- build/lib/bundle.js | 430 - build/lib/bundle.ts | 618 - build/lib/compilation.js | 1 + build/lib/compilation.ts | 2 +- build/lib/electron.js | 4 +- build/lib/electron.ts | 4 +- build/lib/i18n.js | 5 +- build/lib/i18n.ts | 5 +- build/lib/layersChecker.js | 5 +- build/lib/layersChecker.ts | 5 +- build/lib/optimize.js | 7 - build/lib/optimize.ts | 8 - build/lib/policies.js | 447 +- build/lib/policies.ts | 485 +- build/lib/propertyInitOrderChecker.js | 367 + build/lib/propertyInitOrderChecker.ts | 417 + build/lib/standalone.js | 100 +- build/lib/standalone.ts | 124 +- .../lib/stylelint/vscode-known-variables.json | 20 +- build/linux/dependencies-generator.js | 2 +- build/linux/dependencies-generator.ts | 2 +- build/linux/rpm/dep-lists.js | 3 + build/linux/rpm/dep-lists.ts | 3 + build/monaco/monaco.d.ts.recipe | 4 +- build/monaco/monaco.usage.recipe | 6 +- build/npm/postinstall.js | 2 +- build/package-lock.json | 231 +- build/package.json | 4 +- build/win32/Cargo.lock | 2 +- build/win32/Cargo.toml | 2 +- build/win32/inno_updater.exe | Bin 437760 -> 455168 bytes cgmanifest.json | 12 +- cli/ThirdPartyNotices.txt | 18 +- cli/src/auth.rs | 4 +- cli/src/log.rs | 2 +- cli/src/tunnels/singleton_server.rs | 2 +- cli/src/util/io.rs | 3 + cli/src/util/prereqs.rs | 36 +- eslint.config.js | 11 +- extensions/configuration-editing/package.json | 5 + extensions/csharp/language-configuration.json | 14 +- .../css-language-features/package-lock.json | 78 +- extensions/css-language-features/package.json | 2 +- .../server/package-lock.json | 8 +- .../css-language-features/server/package.json | 4 +- .../debug-server-ready/src/extension.ts | 11 +- extensions/git/package-lock.json | 6 - extensions/git/package.json | 15 +- extensions/git/package.nls.json | 1 + extensions/git/src/api/git.d.ts | 1 + extensions/git/src/blame.ts | 90 +- extensions/git/src/commands.ts | 224 +- extensions/git/src/decorators.ts | 2 +- extensions/git/src/git.ts | 197 +- extensions/git/src/historyProvider.ts | 2 +- extensions/git/src/repository.ts | 85 +- extensions/git/src/staging.ts | 9 +- extensions/git/src/timelineProvider.ts | 1 - extensions/git/tsconfig.json | 1 - .../html-language-features/package-lock.json | 82 +- .../html-language-features/package.json | 2 +- .../server/package-lock.json | 60 +- .../server/package.json | 8 +- extensions/ipynb/package.json | 5 +- extensions/ipynb/src/constants.ts | 2 +- extensions/ipynb/src/helper.ts | 18 +- extensions/ipynb/src/ipynbMain.ts | 5 +- .../src/test/notebookModelStoreSync.test.ts | 5 +- .../javascript-language-configuration.json | 2 +- extensions/javascript/syntaxes/Readme.md | 2 +- .../client/src/jsonClient.ts | 52 +- .../client/src/languageStatus.ts | 6 +- .../json-language-features/package-lock.json | 82 +- .../json-language-features/package.json | 5 +- .../server/package-lock.json | 52 +- .../server/package.json | 6 +- .../server/src/jsonServer.ts | 22 +- extensions/mangle-loader.js | 2 +- .../package-lock.json | 51 +- .../markdown-language-features/package.json | 4 +- .../preview-src/index.ts | 143 +- .../package-lock.json | 6 +- .../microsoft-authentication/package.json | 20 +- .../microsoft-authentication/package.nls.json | 3 - .../src/common/accountAccess.ts | 108 +- .../src/common/cachePlugin.ts | 2 +- .../src/common/publicClientCache.ts | 9 +- .../src/common/scopeData.ts | 52 +- .../src/common/test/scopeData.test.ts | 17 + .../src/extensionV2.ts | 6 +- .../src/node/authProvider.ts | 110 +- .../src/node/cachedPublicClientApplication.ts | 102 +- .../src/node/flows.ts | 7 +- .../src/node/publicClientCache.ts | 166 +- extensions/terminal-suggest/.vscodeignore | 3 + .../terminal-suggest/ThirdPartyNotices.txt | 2 - extensions/terminal-suggest/cgmanifest.json | 57 + extensions/terminal-suggest/package.json | 6 +- .../scripts/pullFishBuiltins.ts | 260 + .../scripts/pullZshBuiltins.ts | 19 +- .../scripts/terminalScriptHelpers.ts | 51 + .../terminal-suggest/scripts/update-specs.js | 31 +- .../src/completions/code-insiders.ts | 7 +- .../src/completions/code-tunnel-insiders.ts | 23 + .../src/completions/code-tunnel.ts | 94 + .../terminal-suggest/src/completions/code.ts | 680 +- .../terminal-suggest/src/completions/npx.ts | 145 + .../src/completions/upstream/apt.ts | 5 +- .../src/completions/upstream/brew.ts | 3292 ++-- .../src/completions/upstream/cat.ts | 78 +- .../src/completions/upstream/chmod.ts | 166 +- .../src/completions/upstream/chown.ts | 218 +- .../src/completions/upstream/cp.ts | 150 +- .../src/completions/upstream/curl.ts | 1820 +- .../src/completions/upstream/df.ts | 122 +- .../src/completions/upstream/du.ts | 180 +- .../src/completions/upstream/echo.ts | 70 +- .../src/completions/upstream/find.ts | 134 +- .../src/completions/upstream/git.ts | 185 +- .../src/completions/upstream/grep.ts | 680 +- .../src/completions/upstream/head.ts | 62 +- .../src/completions/upstream/killall.ts | 290 +- .../src/completions/upstream/less.ts | 560 +- .../src/completions/upstream/mkdir.ts | 66 +- .../src/completions/upstream/more.ts | 102 +- .../src/completions/upstream/mv.ts | 72 +- .../src/completions/upstream/nano.ts | 10 +- .../src/completions/upstream/node.ts | 833 +- .../src/completions/upstream/npm.ts | 142 +- .../src/completions/upstream/npx.ts | 317 - .../src/completions/upstream/nvm.ts | 4 - .../src/completions/upstream/pnpm.ts | 12 +- .../src/completions/upstream/ps.ts | 298 +- .../src/completions/upstream/pwd.ts | 24 +- .../src/completions/upstream/python.ts | 1 + .../src/completions/upstream/rm.ts | 76 +- .../src/completions/upstream/rmdir.ts | 26 +- .../src/completions/upstream/scp.ts | 424 +- .../src/completions/upstream/ssh.ts | 6 +- .../src/completions/upstream/tail.ts | 32 +- .../src/completions/upstream/top.ts | 90 +- .../src/completions/upstream/touch.ts | 110 +- .../src/completions/upstream/uname.ts | 64 +- .../src/completions/upstream/vim.ts | 480 +- .../src/completions/upstream/wget.ts | 790 +- .../src/completions/upstream/yarn.ts | 14 +- extensions/terminal-suggest/src/constants.ts | 1 - .../src/env/pathExecutableCache.ts | 55 +- .../fig/autocomplete-parser/parseArguments.ts | 17 +- .../generators/scriptSuggestionsGenerator.ts | 3 +- .../terminal-suggest/src/fig/execute.ts | 2 +- .../terminal-suggest/src/fig/figInterface.ts | 39 +- .../terminal-suggest/src/helpers/promise.ts | 8 + extensions/terminal-suggest/src/shell/bash.ts | 3 +- extensions/terminal-suggest/src/shell/fish.ts | 78 +- .../src/shell/fishBuiltinsCache.ts | 301 + extensions/terminal-suggest/src/shell/zsh.ts | 3 +- .../src/terminalSuggestMain.ts | 145 +- .../src/test/completions/code.test.ts | 66 +- .../src/test/completions/upstream/ls.test.ts | 2 +- .../test/completions/upstream/mkdir.test.ts | 8 +- .../test/completions/upstream/touch.test.ts | 6 +- .../src/test/env/pathExecutableCache.test.ts | 9 + .../terminal-suggest/src/test/fig.test.ts | 2 +- .../src/test/terminalSuggestMain.test.ts | 23 +- extensions/terminal-suggest/src/tokens.ts | 15 +- .../language-configuration.json | 2 +- .../typescript-basics/syntaxes/Readme.md | 2 +- .../typescript-language-features/package.json | 6 - .../package.nls.json | 1 - .../src/filesystems/autoInstallerFs.ts | 12 +- .../src/ui/intellisenseStatus.ts | 10 +- .../src/ui/versionStatus.ts | 2 +- .../src/singlefolder-tests/chat.test.ts | 2 +- .../src/singlefolder-tests/terminal.test.ts | 4 +- .../src/colorizer.test.ts | 8 +- .../colorize-fixtures/test-issue241715.ts | 47 + .../test/colorize-fixtures/test.css | 6 +- .../test/colorize-fixtures/test.regexp.ts | 6 + .../colorize-results/test-issue241715_ts.json | 6876 +++++++ .../test/colorize-results/test_css.json | 308 + .../test/colorize-results/test_regexp.ts.json | 1598 ++ .../test-241001_ts.json | 398 +- .../test-function-inv_ts.json | 36 +- .../test-issue11_ts.json | 452 +- .../test-issue241715_ts.json | 4692 +++++ .../test-issue5431_ts.json | 74 +- .../test-issue5465_ts.json | 66 +- .../test-issue5566_ts.json | 50 +- .../test-jsdoc-multiline-type_ts.json | 36 +- .../test-keywords_ts.json | 28 +- .../test-members_ts.json | 44 +- .../test-object-literals_ts.json | 34 +- .../test-strings_ts.json | 96 +- .../test-this_ts.json | 16 +- .../test-variables_css.json | 507 +- .../test_css.json | 7773 +++++++- .../test_regexp.ts.json | 2144 +++ .../colorize-tree-sitter-results/test_ts.json | 1458 +- package-lock.json | 14218 ++++----------- package.json | 44 +- remote/.npmrc | 4 +- remote/package-lock.json | 156 +- remote/package.json | 29 +- remote/web/package-lock.json | 96 +- remote/web/package.json | 20 +- resources/darwin/code.icns | Bin 815734 -> 189124 bytes resources/linux/code.desktop | 1 + resources/linux/code.png | Bin 832666 -> 2721 bytes resources/server/bin/code-server-linux.sh | 10 + .../check-requirements-linux-legacy.sh | 9 - .../bin/helpers/check-requirements-linux.sh | 15 +- resources/server/code-192.png | Bin 260284 -> 2721 bytes resources/server/code-512.png | Bin 260284 -> 2721 bytes resources/server/favicon.ico | Bin 29449 -> 34494 bytes resources/win32/code_150x150.png | Bin 6056 -> 395 bytes resources/win32/code_70x70.png | Bin 1877 -> 338 bytes resources/win32/inno-void.bmp | Bin 1441078 -> 0 bytes resources/win32/logo_cube_noshadow.png | Bin 813913 -> 0 bytes src/bootstrap-fork.ts | 3 - src/main.ts | 35 +- src/tsconfig.json | 1 - src/tsec.exemptions.json | 10 +- src/vs/amdX.ts | 2 +- src/vs/base/browser/contextmenu.ts | 4 + src/vs/base/browser/dnd.ts | 25 +- src/vs/base/browser/fonts.ts | 35 +- src/vs/base/browser/markdownRenderer.ts | 4 +- src/vs/base/browser/ui/button/button.ts | 36 +- .../browser/ui/codicons/codicon/codicon.ttf | Bin 84152 -> 84220 bytes .../browser/ui/contextview/contextview.ts | 4 +- src/vs/base/browser/ui/dialog/dialog.css | 38 +- src/vs/base/browser/ui/dialog/dialog.ts | 86 +- src/vs/base/browser/ui/dnd/dnd.css | 28 + src/vs/base/browser/ui/dnd/dnd.ts | 31 + src/vs/base/browser/ui/dropdown/dropdown.ts | 6 + src/vs/base/browser/ui/hover/hover.ts | 56 +- .../base/browser/ui/hover/hoverDelegate2.ts | 2 +- .../base/browser/ui/iconLabel/iconlabel.css | 5 - src/vs/base/browser/ui/inputbox/inputBox.ts | 2 +- src/vs/base/browser/ui/list/list.css | 10 - src/vs/base/browser/ui/list/listView.ts | 47 +- src/vs/base/browser/ui/list/listWidget.ts | 8 +- src/vs/base/browser/ui/menu/menu.ts | 1 + .../ui/severityIcon}/media/severityIcon.css | 0 .../browser/ui/severityIcon}/severityIcon.ts | 6 +- src/vs/base/browser/ui/splitview/paneview.ts | 9 +- ...ltWorkerFactory.ts => webWorkerFactory.ts} | 103 +- src/vs/base/common/arrays.ts | 30 + src/vs/base/common/codecs/baseDecoder.ts | 6 +- src/vs/base/common/diff/diff.ts | 176 + src/vs/base/common/envfile.ts | 100 + src/vs/base/common/hotReload.ts | 6 +- src/vs/base/common/htmlContent.ts | 15 +- src/vs/base/common/iterator.ts | 8 + src/vs/base/common/lifecycle.ts | 16 + src/vs/base/common/marshallingIds.ts | 1 + src/vs/base/common/mime.ts | 1 + src/vs/base/common/network.ts | 10 +- src/vs/base/common/numbers.ts | 2 +- src/vs/base/common/objects.ts | 35 - .../base/common/observableInternal/autorun.ts | 67 +- src/vs/base/common/observableInternal/base.ts | 52 +- .../base/common/observableInternal/derived.ts | 63 +- .../base/common/observableInternal/index.ts | 7 + .../logging/debugger/debuggerApi.d.ts | 7 + .../logging/debugger/devToolsLogger.ts | 229 +- .../base/common/observableInternal/utils.ts | 40 +- src/vs/base/common/policy.ts | 34 + src/vs/base/common/product.ts | 37 +- src/vs/base/common/strings.ts | 16 +- .../worker/{simpleWorker.ts => webWorker.ts} | 151 +- ...rkerBootstrap.ts => webWorkerBootstrap.ts} | 14 +- src/vs/base/node/powershell.ts | 12 + src/vs/base/node/processes.ts | 26 +- src/vs/base/parts/ipc/common/ipc.mp.ts | 14 +- src/vs/base/parts/ipc/node/ipc.mp.ts | 14 +- src/vs/base/parts/storage/node/storage.ts | 15 +- .../test/browser/markdownRenderer.test.ts | 2 +- src/vs/base/test/common/arrays.test.ts | 59 + src/vs/base/test/common/date.test.ts | 2 + src/vs/base/test/common/envfile.test.ts | 130 + src/vs/base/test/common/lifecycle.test.ts | 28 +- .../base/test/common/markdownString.test.ts | 8 +- src/vs/base/test/common/numbers.test.ts | 20 +- src/vs/base/test/common/strings.test.ts | 328 +- src/vs/base/worker/workerMain.ts | 50 - src/vs/code/browser/workbench/workbench.ts | 58 +- src/vs/code/electron-main/app.ts | 70 +- src/vs/code/electron-main/main.ts | 13 +- .../electron-sandbox/workbench/workbench.ts | 16 +- .../sharedProcess/contrib/codeCacheCleaner.ts | 16 +- .../contrib/languagePackCachedDataCleaner.ts | 18 +- .../sharedProcess/sharedProcessMain.ts | 14 +- src/vs/code/node/cli.ts | 1 + src/vs/code/node/cliProcessMain.ts | 24 +- .../editor/browser/config/migrateOptions.ts | 7 + .../editContext/native/nativeEditContext.css | 4 +- .../editContext/native/nativeEditContext.ts | 45 +- .../native/nativeEditContextUtils.ts | 37 +- .../editContext/native/screenReaderSupport.ts | 32 +- src/vs/editor/browser/rect.ts | 62 +- .../browser/services/editorWorkerService.ts | 32 +- .../services/hoverService/hoverService.ts | 45 +- src/vs/editor/browser/view.ts | 79 +- .../widget/codeEditor/codeEditorWidget.ts | 102 +- .../diffEditorViewZones/renderLines.ts | 17 + .../browser/widget/diffEditor/style.css | 3 +- .../codecs/markdownCodec/markdownDecoder.ts | 274 +- .../markdownCodec/parsers/markdownComment.ts | 177 + .../markdownCodec/parsers/markdownImage.ts | 99 + .../markdownCodec/parsers/markdownLink.ts | 213 + .../markdownCodec/tokens/markdownComment.ts | 56 + .../markdownCodec/tokens/markdownImage.ts | 141 + .../markdownCodec/tokens/markdownLink.ts | 8 +- .../common/codecs/simpleCodec/parserBase.ts | 59 +- .../codecs/simpleCodec/simpleDecoder.ts | 30 +- .../simpleCodec/tokens/angleBrackets.ts | 102 + .../codecs/simpleCodec/tokens/brackets.ts | 7 +- .../common/codecs/simpleCodec/tokens/colon.ts | 5 +- .../common/codecs/simpleCodec/tokens/dash.ts | 53 + .../simpleCodec/tokens/exclamationMark.ts | 53 + .../common/codecs/simpleCodec/tokens/hash.ts | 5 +- .../codecs/simpleCodec/tokens/parentheses.ts | 11 +- .../common/codecs/simpleCodec/tokens/tab.ts | 5 +- .../common/codecs/simpleCodec/tokens/word.ts | 2 +- .../config/editorConfigurationSchema.ts | 25 + src/vs/editor/common/config/editorOptions.ts | 57 +- src/vs/editor/common/languages.ts | 46 +- .../common/languages/highlights/css.scm | 90 + .../common/languages/highlights/regex.scm | 96 + .../languages/highlights/typescript.scm | 296 +- .../languages/injections/typescript.scm | 2 + src/vs/editor/common/model/tokenStore.ts | 21 +- .../model/treeSitterTokenStoreService.ts | 65 +- .../editor/common/model/treeSitterTokens.ts | 21 +- ...itorSimpleWorker.ts => editorWebWorker.ts} | 95 +- .../common/services/editorWebWorkerMain.ts | 9 + src/vs/editor/common/services/editorWorker.ts | 4 +- .../common/services/editorWorkerBootstrap.ts | 51 - .../common/services/editorWorkerHost.ts | 6 +- .../textModelSync/textModelSync.impl.ts | 8 +- .../services/textResourceConfiguration.ts | 2 +- .../textResourceConfigurationService.ts | 4 +- .../common/services/treeSitter/cursorUtils.ts | 84 + .../treeSitter/textModelTreeSitter.ts | 781 + .../treeSitter/treeSitterLanguages.ts | 126 + .../treeSitter/treeSitterParserService.ts | 599 +- .../services/treeSitterParserService.ts | 24 +- src/vs/editor/common/tokens/lineTokens.ts | 5 + src/vs/editor/common/viewModel.ts | 1 + .../editor/common/viewModel/viewModelImpl.ts | 10 +- .../editor/common/viewModelEventDispatcher.ts | 26 + .../contrib/clipboard/browser/clipboard.ts | 12 +- .../contrib/codelens/browser/codelens.ts | 7 + .../codelens/browser/codelensController.ts | 2 +- .../codelens/browser/codelensWidget.css | 3 +- .../colorPicker/browser/colorDetector.ts | 8 +- .../colorPickerParts/colorPickerHeader.ts | 4 +- .../contrib/find/browser/findController.ts | 4 +- .../folding/browser/syntaxRangeProvider.ts | 3 + .../gotoError/browser/gotoErrorWidget.ts | 10 +- .../browser/peek/referencesWidget.ts | 8 +- .../controller/inlineCompletionsController.ts | 12 +- .../inlineCompletionsAccessibleView.ts | 27 +- .../browser/model/changeRecorder.ts | 2 +- .../browser/model/ghostText.ts | 35 +- .../browser/model/inlineCompletionsModel.ts | 114 +- .../browser/model/inlineCompletionsSource.ts | 305 +- .../browser/model/inlineEdit.ts | 2 - .../browser/model/provideInlineCompletions.ts | 30 +- .../browser/structuredLogger.ts | 58 + .../browser/view/ghostText/ghostTextView.ts | 24 +- .../browser/view/inlineCompletionsView.ts | 2 +- .../components/gutterIndicatorMenu.ts | 105 +- .../components/gutterIndicatorView.ts | 415 +- .../view/inlineEdits/inlineEditWithChanges.ts | 2 - .../view/inlineEdits/inlineEditsModel.ts | 103 + .../view/inlineEdits/inlineEditsView.ts | 280 +- .../inlineEdits/inlineEditsViewInterface.ts | 26 +- ...Producer.ts => inlineEditsViewProducer.ts} | 56 +- .../inlineEditsCollapsedView.ts | 119 + .../inlineEditsDeletionView.ts | 145 +- .../inlineEditsInsertionView.ts | 193 +- .../inlineEditsLineReplacementView.ts | 107 +- .../inlineEditsSideBySideView.ts | 341 +- .../inlineEditsWordInsertView.ts | 7 +- .../inlineEditsWordReplacementView.ts | 171 +- .../originalEditorInlineDiffView.ts | 78 +- .../browser/view/inlineEdits/theme.ts | 134 +- .../browser/view/inlineEdits/utils/utils.ts | 23 +- .../browser/view/inlineEdits/view.css | 93 +- .../editor/contrib/links/browser/getLinks.ts | 5 +- src/vs/editor/contrib/links/browser/links.ts | 4 - .../multicursor/browser/multicursor.ts | 15 +- .../browser/gotoSymbolQuickAccess.ts | 10 +- .../contrib/rename/browser/renameWidget.ts | 2 +- .../sectionHeaders/browser/sectionHeaders.ts | 5 +- .../browser/stickyScrollController.ts | 29 +- .../browser/stickyScrollProvider.ts | 25 +- .../browser/stickyScrollWidget.ts | 58 +- .../test/browser/stickyScroll.test.ts | 6 +- .../contrib/suggest/browser/suggestModel.ts | 2 + .../contrib/suggest/browser/suggestWidget.ts | 16 +- .../suggest/test/browser/wordDistance.test.ts | 4 +- .../browser/unicodeHighlighter.ts | 56 +- .../contrib/zoneWidget/browser/zoneWidget.ts | 6 +- src/vs/editor/editor.worker.start.ts | 35 + .../standalone/browser/standalone-tokens.css | 20 +- .../standalone/browser/standaloneEditor.ts | 4 +- .../standalone/browser/standaloneServices.ts | 5 +- .../browser/standaloneThemeService.ts | 6 +- .../browser/standaloneTreeSitterService.ts | 4 +- .../standalone/browser/standaloneWebWorker.ts | 80 +- .../test/browser/standaloneLanguages.test.ts | 4 +- .../services/treeSitterParserService.test.ts | 7 +- .../common/codecs/markdownDecoder.test.ts | 1000 +- .../test/common/codecs/simpleDecoder.test.ts | 43 +- ...Worker.test.ts => editorWebWorker.test.ts} | 6 +- .../common/services/testTreeSitterService.ts | 4 +- .../editor/test/common/utils/testDecoder.ts | 34 +- src/vs/monaco.d.ts | 20 +- .../accessibility/browser/accessibleView.ts | 6 + .../browser/accessibilitySignalService.ts | 16 + .../browser/media/editsKept.mp3 | Bin 0 -> 49600 bytes .../browser/media/editsUndone.mp3 | Bin 0 -> 35200 bytes .../actionWidget/browser/actionList.ts | 7 +- .../dropdownWithPrimaryActionViewItem.ts | 6 + .../browser/menuEntryActionViewItem.ts | 4 +- src/vs/platform/actions/common/actions.ts | 1 + .../electron-main/auxiliaryWindow.ts | 4 +- .../backup/electron-main/backupMainService.ts | 5 +- .../configuration/common/configuration.ts | 24 + .../common/configurationModels.ts | 8 +- .../common/configurationRegistry.ts | 46 +- .../configuration/common/configurations.ts | 33 +- .../test/common/policyConfiguration.test.ts | 23 + .../platform/contextkey/common/contextkey.ts | 4 + .../contextview/browser/contextMenuHandler.ts | 2 +- .../contextview/browser/contextViewService.ts | 4 +- .../electron-main/diagnosticsMainService.ts | 4 +- .../diagnostics/node/diagnosticsService.ts | 3 +- src/vs/platform/dialogs/browser/dialog.ts | 41 + src/vs/platform/dialogs/common/dialogs.ts | 1 + src/vs/platform/environment/common/argv.ts | 4 +- src/vs/platform/environment/node/argv.ts | 14 +- .../abstractExtensionManagementService.ts | 41 +- .../common/extensionGalleryManifest.ts | 87 + .../common/extensionGalleryManifestService.ts | 231 + .../extensionGalleryManifestServiceIpc.ts | 50 + .../common/extensionGalleryService.ts | 696 +- .../common/extensionManagement.ts | 44 +- .../common/extensionManagementCLI.ts | 6 +- .../common/extensionManagementUtil.ts | 6 +- .../common/extensionsScannerService.ts | 2 + .../node/extensionManagementService.ts | 33 +- .../node/extensionsScannerService.test.ts | 5 +- .../browser/extensionResourceLoaderService.ts | 8 +- .../common/extensionResourceLoader.ts | 47 +- .../common/extensionResourceLoaderService.ts | 8 +- .../extensions/common/extensionValidator.ts | 1 - .../platform/extensions/common/extensions.ts | 6 + .../common/extensionsApiProposals.ts | 25 +- .../node/diskFileSystemProviderServer.ts | 6 +- .../node/watcher/nodejs/nodejsWatcherLib.ts | 17 +- .../node/watcher/parcel/parcelWatcher.ts | 9 +- src/vs/platform/hover/browser/hover.ts | 2 +- .../hover/test/browser/nullHoverService.ts | 2 +- .../common/jsonContributionRegistry.ts | 56 +- .../languagePacks/browser/languagePacks.ts | 2 +- .../electron-main/lifecycleMainService.ts | 3 +- src/vs/platform/log/common/log.ts | 11 +- .../platform/markers/common/markerService.ts | 8 +- .../markers/test/common/markerService.test.ts | 2 +- .../platform/mcp/common/mcpManagementCli.ts | 63 + .../platform/mcp/common/mcpPlatformTypes.ts | 27 + .../mcp/common/nativeMcpDiscoveryHelper.ts | 26 + .../node/nativeMcpDiscoveryHelperChannel.ts | 33 + .../node/nativeMcpDiscoveryHelperService.ts | 33 + .../electron-main/menubarMainService.ts | 4 +- src/vs/platform/native/common/native.ts | 3 + .../electron-main/nativeHostMainService.ts | 127 +- .../observable/common/observableMemento.ts | 95 + .../policy/common/filePolicyService.ts | 3 +- src/vs/platform/policy/common/policy.ts | 12 +- src/vs/platform/policy/common/policyIpc.ts | 5 +- src/vs/platform/product/common/product.ts | 5 +- .../common/profilingTelemetrySpec.ts | 12 +- .../electron-sandbox/profileAnalysisWorker.ts | 10 +- .../profileAnalysisWorkerMain.ts | 4 +- .../profileAnalysisWorkerService.ts | 7 +- src/vs/platform/prompts/common/config.ts | 27 +- src/vs/platform/prompts/common/constants.ts | 40 +- .../prompts/test/common/config.test.ts | 2 +- .../prompts/test/common/constants.test.ts | 11 +- .../electron-main/protocolMainService.ts | 14 +- .../quickinput/browser/media/quickInput.css | 11 + .../platform/quickinput/browser/quickInput.ts | 2 +- .../browser/quickInputController.ts | 30 +- .../quickinput/browser/quickInputTree.ts | 7 +- .../platform/quickinput/common/quickInput.ts | 3 + .../remote/common/remote.ts} | 5 +- .../test/common/testSecretStorageService.ts | 36 + src/vs/platform/state/node/stateService.ts | 6 +- src/vs/platform/storage/common/storage.ts | 6 +- .../platform/storage/common/storageService.ts | 25 +- .../electron-main/storageMainService.ts | 8 +- .../common/serverTelemetryService.ts | 24 +- .../telemetry/common/telemetryService.ts | 104 +- .../telemetry/common/telemetryUtils.ts | 2 +- .../telemetry/electron-main/errorTelemetry.ts | 38 + .../common/capabilities/capabilities.ts | 4 + .../commandDetection/promptInputModel.ts | 103 +- .../commandDetectionCapability.ts | 31 +- src/vs/platform/terminal/common/terminal.ts | 2 + .../terminal/common/terminalRecorder.ts | 1 + .../common/xterm/shellIntegrationAddon.ts | 42 +- src/vs/platform/terminal/node/ptyService.ts | 17 +- .../terminal/node/terminalEnvironment.ts | 48 +- .../platform/terminal/node/terminalProcess.ts | 2 +- .../commandDetection/promptInputModel.test.ts | 109 +- .../test/node/terminalEnvironment.test.ts | 113 +- src/vs/platform/theme/common/colorUtils.ts | 2 +- src/vs/platform/theme/common/themeService.ts | 6 +- .../theme/electron-main/themeMainService.ts | 3 +- .../theme/test/common/testThemeService.ts | 8 +- src/vs/platform/update/common/update.ts | 41 +- .../electron-main/abstractUpdateService.ts | 84 +- .../electron-main/updateService.darwin.ts | 46 +- .../electron-main/updateService.linux.ts | 5 +- .../electron-main/updateService.win32.ts | 72 +- .../userData/common/fileUserDataProvider.ts | 20 +- .../userDataSync/common/userDataSync.ts | 8 +- .../common/userDataSyncEnablementService.ts | 12 +- .../test/common/userDataSyncClient.ts | 5 + .../utilityProcessWorkerMainService.ts | 10 +- .../common/webContentExtractor.ts | 46 + .../electron-main/cdpAccessibilityDomain.ts | 199 + .../webContentExtractorService.ts | 73 + .../webContentExtractorService.ts | 10 + .../node/sharedWebContentExtractorService.ts | 38 + src/vs/platform/window/common/window.ts | 28 + .../platform/window/electron-main/window.ts | 7 +- .../windows/electron-main/windowImpl.ts | 143 +- .../platform/windows/electron-main/windows.ts | 37 +- .../electron-main/windowsMainService.ts | 6 +- .../electron-main/windowsStateHandler.ts | 4 +- src/vs/platform/workspace/common/workspace.ts | 28 +- .../electron-main/workspacesMainService.ts | 4 +- .../workspacesManagementMainService.ts | 4 +- .../server/node/remoteAgentEnvironmentImpl.ts | 2 +- .../node/remoteExtensionHostAgentCli.ts | 3 + .../node/remoteExtensionHostAgentServer.ts | 3 +- src/vs/server/node/serverServices.ts | 9 + .../api/browser/extensionHost.contribution.ts | 2 + .../api/browser/mainThreadChatAgents2.ts | 10 +- .../api/browser/mainThreadChatStatus.ts | 33 + .../api/browser/mainThreadComments.ts | 62 +- .../api/browser/mainThreadCustomEditors.ts | 4 +- .../api/browser/mainThreadLanguageModels.ts | 16 +- src/vs/workbench/api/browser/mainThreadMcp.ts | 197 + .../browser/mainThreadNotebookDocuments.ts | 3 +- .../workbench/api/browser/mainThreadTask.ts | 62 +- .../mainThreadTerminalShellIntegration.ts | 28 +- .../workbench/api/browser/mainThreadUrls.ts | 31 +- .../api/browser/mainThreadWebviewPanels.ts | 4 +- .../api/browser/mainThreadWorkspace.ts | 3 +- .../api/common/configurationExtensionPoint.ts | 21 +- .../workbench/api/common/extHost.api.impl.ts | 53 +- .../api/common/extHost.common.services.ts | 2 + .../workbench/api/common/extHost.protocol.ts | 45 +- .../api/common/extHostAuthentication.ts | 4 +- .../api/common/extHostChatAgents2.ts | 97 +- .../workbench/api/common/extHostChatStatus.ts | 99 + .../api/common/extHostConfiguration.ts | 4 +- .../api/common/extHostDebugService.ts | 2 +- .../common/extHostFileSystemEventService.ts | 8 +- .../api/common/extHostLanguageModelTools.ts | 50 +- .../api/common/extHostLanguageModels.ts | 19 +- src/vs/workbench/api/common/extHostMcp.ts | 242 + src/vs/workbench/api/common/extHostTask.ts | 37 + .../api/common/extHostTerminalService.ts | 28 +- .../common/extHostTerminalShellIntegration.ts | 194 +- .../api/common/extHostTypeConverters.ts | 140 +- src/vs/workbench/api/common/extHostTypes.ts | 161 +- src/vs/workbench/api/common/shared/tasks.ts | 60 + .../api/node/extHost.node.services.ts | 3 + .../api/node/extHostDiskFileSystemProvider.ts | 6 +- src/vs/workbench/api/node/extHostMpcNode.ts | 158 + .../api/node/extensionHostProcess.ts | 5 +- .../test/browser/extHostConfiguration.test.ts | 23 + .../extHostTerminalShellIntegration.test.ts | 307 + .../api/worker/extensionHostWorker.ts | 4 - .../browser/actions/layoutActions.ts | 2 +- src/vs/workbench/browser/labels.ts | 35 +- src/vs/workbench/browser/layout.ts | 35 +- src/vs/workbench/browser/media/code-icon.svg | 1 + .../workbench/browser/media/void-icon-sm.png | Bin 813913 -> 0 bytes .../parts/activitybar/activitybarPart.ts | 3 +- .../parts/auxiliarybar/auxiliaryBarActions.ts | 2 +- .../parts/auxiliarybar/auxiliaryBarPart.ts | 8 +- .../browser/parts/compositeBarActions.ts | 4 +- .../browser/parts/dialogs/dialogHandler.ts | 40 +- .../parts/editor/auxiliaryEditorPart.ts | 8 +- .../parts/editor/breadcrumbsControl.ts | 18 +- .../parts/editor/editor.contribution.ts | 5 + .../browser/parts/editor/editorActions.ts | 2 +- .../parts/editor/editorConfiguration.ts | 3 +- .../browser/parts/editor/editorDropTarget.ts | 8 +- .../browser/parts/editor/editorGroupView.ts | 23 +- .../parts/editor/editorGroupWatermark.ts | 196 + .../browser/parts/editor/editorPanes.ts | 29 +- .../browser/parts/editor/editorPart.ts | 10 +- .../browser/parts/editor/editorParts.ts | 7 +- .../browser/parts/editor/editorPlaceholder.ts | 8 +- .../browser/parts/editor/editorStatus.ts | 12 +- .../browser/parts/editor/editorTabsControl.ts | 10 +- .../parts/editor/editorTitleControl.ts | 5 +- .../parts/editor/media/editortabscontrol.css | 18 - .../editor/media/multieditortabscontrol.css | 25 +- .../parts/editor/media/slice_of_void.png | Bin 870931 -> 0 bytes .../parts/editor/media/void_cube_noshadow.png | Bin 813913 -> 0 bytes .../parts/editor/multiEditorTabsControl.ts | 48 +- .../browser/parts/editor/sideBySideEditor.ts | 4 +- .../parts/editor/singleEditorTabsControl.ts | 5 +- .../browser/parts/globalCompositeBar.ts | 2 +- .../notifications/notificationsCenter.ts | 18 +- .../parts/notifications/notificationsList.ts | 5 +- .../notifications/notificationsToasts.ts | 19 +- .../notifications/notificationsViewer.ts | 33 +- .../browser/parts/paneCompositePart.ts | 6 +- .../browser/parts/panel/panelActions.ts | 12 +- .../browser/parts/statusbar/statusbarItem.ts | 17 +- .../browser/parts/statusbar/statusbarPart.ts | 94 +- .../parts/titlebar/media/titlebarpart.css | 53 +- .../browser/parts/titlebar/titlebarPart.ts | 27 +- .../workbench/browser/parts/views/viewPane.ts | 28 +- .../browser/parts/views/viewPaneContainer.ts | 9 +- .../browser/workbench.contribution.ts | 2 +- .../common/editor/diffEditorInput.ts | 20 +- .../common/editor/resourceEditorInput.ts | 1 + .../common/editor/sideBySideEditorInput.ts | 4 +- src/vs/workbench/common/release.ts | 15 - src/vs/workbench/common/theme.ts | 4 +- .../browser/accessibilityConfiguration.ts | 29 + .../accessibility/browser/accessibleView.ts | 8 +- .../browser/accessibleViewActions.ts | 2 +- .../browser/callHierarchyPeek.ts | 2 +- .../browser/actions/chatAccessibilityHelp.ts | 50 +- .../chat/browser/actions/chatActions.ts | 313 +- .../chatAttachPromptAction.ts | 20 +- .../dialogs/askToSelectPrompt.ts | 331 - .../askToSelectPrompt/askToSelectPrompt.ts | 199 + .../dialogs/askToSelectPrompt/constants.ts | 57 + .../askToSelectPrompt/utils/attachPrompts.ts | 126 + .../utils/createPlaceholderText.ts | 52 + .../utils/createPromptPickItem.ts | 47 + .../utils/handleButtonClick.ts | 114 + .../chat/browser/actions/chatClearActions.ts | 172 +- .../browser/actions/chatCodeblockActions.ts | 2 +- .../browser/actions/chatContextActions.ts | 212 +- .../browser/actions/chatDeveloperActions.ts | 26 +- .../browser/actions/chatExecuteActions.ts | 288 +- .../browser/actions/chatGettingStarted.ts | 5 +- .../chat/browser/actions/chatMoveActions.ts | 30 +- .../browser/actions/chatQuickInputActions.ts | 1 + .../chat/browser/actions/chatTitleActions.ts | 52 +- .../chat/browser/actions/chatToolActions.ts | 325 +- .../chat/browser/actions/chatTransfer.ts | 19 + .../promptAttachmentWidget.ts | 16 +- .../contrib/chat/browser/chat.contribution.ts | 218 +- src/vs/workbench/contrib/chat/browser/chat.ts | 18 +- .../chat/browser/chatAccessibilityProvider.ts | 39 +- .../chat/browser/chatAttachmentModel.ts | 45 +- .../chatPromptAttachmentsCollection.ts | 7 +- .../chat/browser/chatAttachmentWidgets.ts | 398 + .../chatAttachmentsContentPart.ts | 8 +- .../chatCollapsibleContentPart.ts | 184 + .../chatConfirmationContentPart.ts | 4 +- .../chatConfirmationWidget.ts | 76 +- .../chatMarkdownContentPart.ts | 92 +- .../chatProgressContentPart.ts | 7 +- .../chatContentParts/chatQuotaExceededPart.ts | 9 +- .../chatReferencesContentPart.ts | 95 +- .../chatToolInvocationPart.ts | 171 +- .../media/chatConfirmationWidget.css | 12 + .../contrib/chat/browser/chatDragAndDrop.ts | 185 +- .../browser/chatEditing/chatEditingActions.ts | 145 +- .../chatEditingCodeEditorIntegration.ts | 29 +- .../chatEditing/chatEditingEditorActions.ts | 6 +- .../chatEditing/chatEditingEditorOverlay.ts | 4 +- .../chatEditingModifiedDocumentEntry.ts | 66 +- .../chatEditingModifiedFileEntry.ts | 25 +- .../chatEditingModifiedNotebookEntry.ts | 1302 +- .../chatEditingModifiedNotebookSnapshot.ts | 103 - .../chatEditingNotebookEditorIntegration.ts | 582 - .../chatEditing/chatEditingServiceImpl.ts | 72 +- .../browser/chatEditing/chatEditingSession.ts | 188 +- .../chatEditingModifiedNotebookDiff.ts | 69 + .../chatEditingModifiedNotebookSnapshot.ts | 182 + .../chatEditingNewNotebookContentEdits.ts | 86 + .../notebook/chatEditingNotebookCellEntry.ts | 345 + .../chatEditingNotebookEditorIntegration.ts | 674 + .../chatEditingNotebookFileSystemProvider.ts | 26 +- .../browser/chatEditing/notebook/helpers.ts | 418 + .../notebook/notebookCellChanges.ts | 122 + .../contrib/chat/browser/chatEditor.ts | 26 +- .../contrib/chat/browser/chatEditorInput.ts | 33 +- .../contrib/chat/browser/chatFollowups.ts | 3 +- .../contrib/chat/browser/chatInputPart.ts | 757 +- .../contrib/chat/browser/chatListRenderer.ts | 82 +- .../chatMarkdownDecorationsRenderer.ts | 6 +- .../browser/chatParticipant.contribution.ts | 201 +- .../contrib/chat/browser/chatQuick.ts | 2 +- .../contrib/chat/browser/chatSelectedTools.ts | 133 + .../contrib/chat/browser/chatSetup.ts | 1560 +- .../contrib/chat/browser/chatStatus.ts | 619 +- .../chat/browser/chatStatusItemService.ts | 62 + .../contrib/chat/browser/chatVariables.ts | 14 +- .../contrib/chat/browser/chatViewPane.ts | 120 +- .../contrib/chat/browser/chatWidget.ts | 155 +- .../contrib/chat/browser/codeBlockPart.ts | 17 +- .../browser/contrib/chatDynamicVariables.ts | 13 +- .../browser/contrib/chatImplicitContext.ts | 4 +- .../browser/contrib/chatInputCompletions.ts | 19 +- .../browser/contrib/chatInputEditorContrib.ts | 4 +- .../contrib/chatInputRelatedFilesContrib.ts | 74 +- .../chat/browser/languageModelToolsService.ts | 243 +- .../contrib/chat/browser/media/chat.css | 100 +- .../contrib/chat/browser/media/chatSetup.css | 76 + .../contrib/chat/browser/media/chatStatus.css | 117 +- .../chat/browser/media/chatViewWelcome.css | 8 - .../createPromptCommand.ts | 85 +- .../utils/createPromptFile.ts | 8 +- .../contributions/usePromptCommand.ts | 19 +- .../viewsWelcome/chatViewWelcomeController.ts | 15 +- .../contrib/chat/common/annotations.ts | 18 +- .../workbench/contrib/chat/common/chat.ts} | 17 +- .../contrib/chat/common/chatAgents.ts | 98 +- .../contrib/chat/common/chatContextKeys.ts | 66 +- .../contrib/chat/common/chatEditingService.ts | 15 +- .../chat/common/chatEntitlementService.ts | 886 + .../chat/common/chatEntitlementsService.ts | 46 - .../contrib/chat/common/chatModel.ts | 54 +- .../contrib/chat/common/chatParserTypes.ts | 3 +- .../common/chatParticipantContribTypes.ts | 4 +- .../chatProgressTypes/chatToolInvocation.ts | 9 +- .../contrib/chat/common/chatQuotasService.ts | 135 - .../contrib/chat/common/chatRequestParser.ts | 14 +- .../contrib/chat/common/chatService.ts | 38 +- .../contrib/chat/common/chatServiceImpl.ts | 282 +- .../contrib/chat/common/chatSessionStore.ts | 474 + .../contrib/chat/common/chatSlashCommands.ts | 11 +- .../chat/common/chatTransferService.ts | 36 + .../contrib/chat/common/chatVariables.ts | 2 +- .../contrib/chat/common/chatViewModel.ts | 5 +- .../chat/common/chatWidgetHistoryService.ts | 3 +- .../chat/common/codeBlockModelCollection.ts | 14 +- .../contrib/chat/common/constants.ts | 52 + .../chat/common/languageModelToolsService.ts | 68 +- .../contrib/chat/common/languageModels.ts | 40 +- .../chat/common/promptFileReferenceErrors.ts | 113 +- .../promptSyntax/codecs/chatPromptDecoder.ts | 215 +- .../codecs/parsers/promptVariableParser.ts | 251 + .../codecs/tokens/fileReference.ts | 94 +- .../promptSyntax/codecs/tokens/promptToken.ts | 11 + .../codecs/tokens/promptVariable.ts | 152 + .../chat/common/promptSyntax/constants.ts | 23 +- .../filePromptContentsProvider.ts | 16 +- .../promptContentsProviderBase.ts | 59 +- .../textModelContentsProvider.ts | 28 +- .../promptSyntax/contentProviders/types.d.ts | 18 +- .../promptLinkDiagnosticsProvider.ts | 224 + .../promptPathAutocompletion.ts | 16 +- .../promptSyntax/parsers/basePromptParser.ts | 374 +- .../promptSyntax/parsers/filePromptParser.ts | 2 + .../parsers/textModelPromptParser.ts | 5 +- .../common/promptSyntax/parsers/topError.ts | 102 + .../common/promptSyntax/parsers/types.d.ts | 87 +- .../promptSyntax/service/promptsService.ts | 8 +- .../promptSyntax/utils/promptFilesLocator.ts | 417 +- .../contrib/chat/common/tools/editFileTool.ts | 57 +- .../common/tools/insertNotebookCellsTool.ts | 219 + .../tools/languageModelToolsContribution.ts | 24 +- .../chat/common/tools/promptTsxTypes.ts | 74 + .../contrib/chat/common/tools/tools.ts | 2 + .../contrib/chat/common/voiceChatService.ts | 8 +- .../actions/chatDeveloperActions.ts | 36 + .../actions/voiceChatActions.ts | 32 +- .../electron-sandbox/chat.contribution.ts | 26 +- .../electron-sandbox/tools/fetchPageTool.ts | 185 + .../chatEditingModifiedNotebookEntry.test.ts | 2075 +++ .../test/browser/chatEditingService.test.ts | 19 +- .../browser/languageModelToolsService.test.ts | 21 +- .../chat/test/browser/mockChatWidget.ts | 6 +- ...atRequestParser_agent_but_edit_mode.0.snap | 19 + .../chat/test/common/chatAgents.test.ts | 3 +- .../chat/test/common/chatModel.test.ts | 8 +- .../test/common/chatRequestParser.test.ts | 21 +- .../chat/test/common/chatService.test.ts | 28 +- .../chat/test/common/mockChatService.ts | 29 +- .../chat/test/common/mockChatVariables.ts | 2 +- .../common/mockLanguageModelToolsService.ts | 8 + .../codecs/chatPromptCodec.test.ts | 8 +- .../codecs/chatPromptDecoder.test.ts | 7 +- .../codecs/tokens/fileReference.test.ts | 25 +- .../parsers/textModelPromptParser.test.ts | 10 +- .../promptSyntax/promptFileReference.test.ts | 4 +- .../testUtils/expectedReference.ts | 28 +- .../utils/promptFilesLocator.test.ts | 2679 ++- .../chat/test/common/voiceChatService.test.ts | 10 +- .../browser/dictation/editorDictation.ts | 13 +- .../emptyTextEditorHint.ts | 3 +- .../inspectEditorTokens.ts | 43 +- .../browser/workbenchEditorWorkerService.ts | 5 +- .../comments/browser/commentService.ts | 14 + .../browser/commentThreadZoneWidget.ts | 2 +- .../browser/commentsAccessibleView.ts | 32 +- .../comments/browser/commentsViewActions.ts | 9 +- .../configurationExportHelper.contribution.ts | 26 - .../configurationExportHelper.ts | 120 - .../customEditor/browser/customEditorInput.ts | 1 + .../contrib/debug/browser/breakpointsView.ts | 2 +- .../browser/callStackEditorContribution.ts | 5 +- .../debug/browser/debugEditorContribution.ts | 7 +- .../contrib/debug/browser/debugHover.ts | 4 +- .../contrib/debug/browser/debugService.ts | 2 +- .../contrib/debug/browser/debugSession.ts | 13 +- .../contrib/debug/browser/debugToolBar.ts | 10 +- .../contrib/debug/browser/disassemblyView.ts | 4 +- .../contrib/debug/browser/exceptionWidget.ts | 8 +- .../workbench/contrib/debug/common/debug.ts | 2 + .../contrib/debug/test/common/mockDebug.ts | 4 + .../browser/editSessionsStorageService.ts | 45 +- .../editSessions/common/workspaceStateSync.ts | 1 + .../extensions/browser/extensionEditor.ts | 123 +- .../browser/extensionFeaturesTab.ts | 4 +- .../browser/extensions.contribution.ts | 83 +- .../extensions/browser/extensionsActions.ts | 40 +- .../extensions/browser/extensionsIcons.ts | 1 + .../extensions/browser/extensionsList.ts | 21 +- .../extensions/browser/extensionsViewlet.ts | 87 +- .../extensions/browser/extensionsViews.ts | 2 +- .../extensions/browser/extensionsWidgets.ts | 228 +- .../browser/extensionsWorkbenchService.ts | 85 +- .../extensions/browser/media/extension.css | 23 +- .../browser/media/extensionEditor.css | 19 +- .../browser/media/extensionsWidgets.css | 4 +- .../extensions/common/extensionQuery.ts | 31 +- .../contrib/extensions/common/extensions.ts | 5 +- .../extensions.contribution.ts | 4 +- .../electron-sandbox/remoteExtensionsInit.ts | 46 +- .../test/common/extensionQuery.test.ts | 8 +- .../editors/textFileSaveErrorHandler.ts | 8 +- .../contrib/files/browser/explorerService.ts | 44 +- .../contrib/files/browser/fileCommands.ts | 9 +- .../files/browser/files.contribution.ts | 15 + .../browser/inlayHintsAccessibilty.ts | 2 +- .../inlineChat/browser/inlineChatActions.ts | 17 +- .../browser/inlineChatController.ts | 13 +- .../browser/inlineChatCurrentLine.ts | 9 +- .../browser/inlineChatSessionServiceImpl.ts | 57 +- .../browser/inlineChatZoneWidget.ts | 3 +- .../contrib/inlineChat/common/inlineChat.ts | 2 + .../test/browser/inlineChatController.test.ts | 3 +- .../test/browser/inlineChatSession.test.ts | 3 +- .../test/browser/testWorkerService.ts | 4 +- ...CompletionLanguageStatusBarContribution.ts | 2 +- .../issue/browser/baseIssueReporterService.ts | 29 +- .../contrib/issue/browser/issueFormService.ts | 2 + .../issue/browser/issueReporterPage.ts | 14 +- .../issue/browser/media/issueReporter.css | 13 +- .../issue/common/issue.contribution.ts | 19 +- .../electron-sandbox/issue.contribution.ts | 26 +- .../electron-sandbox/issueReporterService.ts | 19 +- .../electron-sandbox/media/issueReporter.css | 22 +- .../nativeIssueFormService.ts | 13 +- .../browser/limitIndicator.contribution.ts | 4 +- .../contrib/markers/browser/markersTable.ts | 2 +- .../markers/browser/markersTreeViewer.ts | 21 +- .../contrib/mcp/browser/mcp.contribution.ts | 60 + .../contrib/mcp/browser/mcpCommands.ts | 559 + .../browser/mcpCommandsAddConfiguration.ts | 498 + .../contrib/mcp/browser/mcpDiscovery.ts | 38 + .../mcp/browser/mcpLanguageFeatures.ts | 372 + .../contrib/mcp/browser/mcpUrlHandler.ts | 69 + .../common/discovery/configMcpDiscovery.ts | 166 + .../common/discovery/extensionMcpDiscovery.ts | 128 + .../mcp/common/discovery/mcpDiscovery.ts | 29 + .../discovery/nativeMcpDiscoveryAbstract.ts | 159 + .../discovery/nativeMcpDiscoveryAdapters.ts | 110 + .../discovery/nativeMcpRemoteDiscovery.ts | 51 + .../discovery/workspaceMcpDiscoveryAdapter.ts | 76 + .../contrib/mcp/common/mcpConfigFileUtils.ts | 41 + .../mcp/common/mcpConfigPathsService.ts | 152 + .../contrib/mcp/common/mcpConfiguration.ts | 169 + .../contrib/mcp/common/mcpContextKeys.ts | 63 + .../contrib/mcp/common/mcpRegistry.ts | 372 + .../mcp/common/mcpRegistryInputStorage.ts | 181 + .../contrib/mcp/common/mcpRegistryTypes.ts | 79 + .../workbench/contrib/mcp/common/mcpServer.ts | 512 + .../contrib/mcp/common/mcpServerConnection.ts | 128 + .../mcp/common/mcpServerRequestHandler.ts | 486 + .../contrib/mcp/common/mcpService.ts | 275 + .../workbench/contrib/mcp/common/mcpTypes.ts | 413 + .../mcp/common/modelContextProtocol.ts | 1138 ++ .../mcp/electron-sandbox/mcp.contribution.ts | 10 + .../electron-sandbox/nativeMpcDiscovery.ts | 42 + .../mcp/test/common/mcpRegistry.test.ts | 566 + .../common/mcpRegistryInputStorage.test.ts | 178 + .../mcp/test/common/mcpRegistryTypes.ts | 108 + .../test/common/mcpServerConnection.test.ts | 389 + .../common/mcpServerRequestHandler.test.ts | 398 + .../browser/mergeEditor.contribution.ts | 4 + .../browser/mergeEditorAccessibilityHelp.ts | 42 + .../browser/mergeEditorInputModel.ts | 29 +- .../contrib/mergeEditor/browser/utils.ts | 16 +- .../mergeEditor/browser/view/mergeEditor.ts | 37 +- .../cellDiagnosticEditorContrib.ts | 11 +- .../diagnosticCellStatusBarContrib.ts | 9 +- .../executionStatusBarItemController.ts | 2 +- .../browser/contrib/chatEdit/contribution.ts | 17 - .../chatEdit/notebookCellDecorators.ts | 361 - .../chatEdit/notebookChatActionsOverlay.ts | 323 - .../chatEdit/notebookChatEditContext.ts | 9 - .../chatEdit/notebookChatEditController.ts | 211 - .../contrib/chatEdit/notebookSynchronizer.ts | 500 - .../chatEdit/notebookSynchronizerService.ts | 94 - .../contrib/debug/notebookDebugDecorations.ts | 25 +- .../multicursor/notebookMulticursor.ts | 2 +- .../browser/controller/cellOutputActions.ts | 2 +- .../chat/notebook.chat.contribution.ts | 93 +- .../controller/chat/notebookChatController.ts | 2 +- .../browser/controller/executeActions.ts | 4 +- .../inlineDiff/notebookCellDiffDecorator.ts | 2 +- .../notebookDeletedCellDecorator.ts | 21 +- .../diff/inlineDiff/notebookInlineDiff.ts | 8 +- .../notebookInsertedCellDecorator.ts | 12 +- .../notebookModifiedCellDecorator.ts | 21 +- .../notebookOriginalCellModelFactory.ts | 0 .../notebookOriginalModelRefFactory.ts | 0 .../browser/diff/notebookDiffEditor.ts | 4 +- .../notebook/browser/diff/notebookDiffList.ts | 6 +- .../browser/diff/notebookDiffOverviewRuler.ts | 2 +- .../browser/diff/notebookDiffViewModel.ts | 94 +- .../notebook/browser/media/notebook.css | 4 - .../notebook/browser/notebook.contribution.ts | 6 - .../notebook/browser/notebookBrowser.ts | 42 +- .../notebook/browser/notebookEditor.ts | 1 + .../notebook/browser/notebookEditorWidget.ts | 49 +- .../browser/services/notebookServiceImpl.ts | 66 +- .../services/notebookWorkerServiceImpl.ts | 25 +- .../browser/view/cellParts/cellDecorations.ts | 4 +- .../browser/view/cellParts/cellOutput.ts | 2 +- .../browser/view/cellParts/cellStatusPart.ts | 2 +- .../view/cellParts/cellToolbarStickyScroll.ts | 10 +- .../browser/view/cellParts/codeCell.ts | 2 +- .../notebook/browser/view/notebookCellList.ts | 37 +- .../browser/view/notebookCellListView.ts | 15 +- .../browser/view/notebookRenderingCommon.ts | 4 +- .../view/renderers/backLayerWebView.ts | 17 +- .../browser/view/renderers/cellRenderer.ts | 16 +- .../browser/view/renderers/webviewMessages.ts | 2 +- .../browser/view/renderers/webviewPreloads.ts | 10 + .../viewModel/notebookViewModelImpl.ts | 32 +- .../browser/viewParts/notebookCellOverlays.ts | 204 + .../viewParts/notebookEditorStickyScroll.ts | 36 +- .../notebookEditorWidgetContextKeys.ts | 1 + .../notebookKernelQuickPickStrategy.ts | 35 +- .../browser/viewParts/notebookKernelView.ts | 4 +- .../viewParts/notebookOverviewRuler.ts | 39 + .../browser/viewParts/notebookViewZones.ts | 84 +- .../common/model/notebookCellTextModel.ts | 3 - .../common/model/notebookTextModel.ts | 50 +- .../contrib/notebook/common/notebookCommon.ts | 58 +- .../contrib/notebook/common/notebookDiff.ts | 133 + .../notebook/common/notebookEditorModel.ts | 7 +- .../notebookEditorModelResolverServiceImpl.ts | 11 +- .../common/notebookSynchronizerService.ts | 16 - .../common/services/notebookCellMatching.ts | 344 + .../common/services/notebookSimpleWorker.ts | 370 - .../common/services/notebookWebWorker.ts | 576 + ...WorkerMain.ts => notebookWebWorkerMain.ts} | 6 +- .../contrib/notebookCellDiagnostics.test.ts | 5 +- .../browser/diff/notebookDiffService.test.ts | 14750 ++++++++++++++++ .../test/browser/notebookTextModel.test.ts | 70 + .../output/browser/outputLinkProvider.ts | 9 +- .../output/common/outputLinkComputer.ts | 12 +- .../output/common/outputLinkComputerMain.ts | 4 +- .../browser/keybindingsEditorContribution.ts | 4 +- .../browser/preferences.contribution.ts | 27 +- .../browser/preferencesRenderers.ts | 3 +- .../preferences/browser/preferencesSearch.ts | 196 +- .../preferences/browser/preferencesWidgets.ts | 4 +- .../preferences/browser/settingsEditor2.ts | 84 +- .../settingsEditorSettingIndicators.ts | 12 +- .../preferences/browser/settingsLayout.ts | 10 +- .../preferences/browser/settingsTree.ts | 32 +- .../preferences/browser/settingsTreeModels.ts | 23 +- .../preferences/browser/settingsWidgets.ts | 52 +- .../contrib/preferences/browser/tocTree.ts | 7 +- .../contrib/preferences/common/preferences.ts | 2 +- .../common/preferencesContribution.ts | 7 - .../common/settingsFilesystemProvider.ts | 18 + .../browser/commandsQuickAccess.ts | 3 +- .../quickaccess/browser/viewQuickAccess.ts | 2 +- .../browser/relauncher.contribution.ts | 22 +- .../contrib/remote/browser/remoteIndicator.ts | 68 +- .../remote/common/remote.contribution.ts | 17 +- .../workbench/contrib/scm/browser/activity.ts | 119 +- .../contrib/scm/browser/media/scm.css | 4 + .../contrib/scm/browser/quickDiffDecorator.ts | 9 +- .../contrib/scm/browser/quickDiffWidget.ts | 2 +- .../contrib/scm/browser/scm.contribution.ts | 19 +- .../scm/browser/scmAccessibilityHelp.ts | 18 +- .../contrib/scm/browser/scmHistoryViewPane.ts | 64 +- .../scm/browser/scmRepositoriesViewPane.ts | 7 + .../contrib/scm/browser/scmViewPane.ts | 7 +- .../contrib/scm/browser/scmViewService.ts | 135 +- .../contrib/scm/browser/workingSet.ts | 6 +- .../workbench/contrib/scm/common/quickDiff.ts | 11 +- src/vs/workbench/contrib/scm/common/scm.ts | 1 + .../search/browser/AISearch/aiSearchModel.ts | 18 + .../search/browser/searchActionsCopy.ts | 29 + .../search/browser/searchActionsTopBar.ts | 32 +- .../contrib/search/browser/searchMessage.ts | 2 +- .../search/browser/searchResultsView.ts | 8 +- .../browser/searchTreeModel/searchModel.ts | 5 + .../browser/searchTreeModel/searchResult.ts | 4 +- .../searchTreeModel/searchTreeCommon.ts | 10 +- .../searchTreeModel/textSearchHeading.ts | 4 +- .../contrib/search/browser/searchView.ts | 78 +- .../contrib/search/common/constants.ts | 3 + .../searchEditor/browser/searchEditor.ts | 2 +- .../contrib/speech/browser/speechService.ts | 14 +- .../surveys/browser/nps.contribution.ts | 6 +- .../tasks/browser/abstractTaskService.ts | 3 +- .../tasks/browser/task.contribution.ts | 2 +- .../tasks/browser/taskTerminalStatus.ts | 4 +- .../tasks/browser/terminalTaskSystem.ts | 21 +- .../contrib/tasks/common/problemCollectors.ts | 6 + .../contrib/tasks/common/taskService.ts | 5 + .../workbench/contrib/tasks/common/tasks.ts | 67 +- .../browser/telemetry.contribution.ts | 19 +- .../browser/environmentVariableInfo.ts | 3 +- .../terminal/browser/terminal.contribution.ts | 5 +- .../terminal/browser/terminalActions.ts | 12 +- .../terminal/browser/terminalIconPicker.ts | 2 +- .../terminal/browser/terminalInstance.ts | 26 +- .../terminal/browser/terminalService.ts | 5 +- .../terminal/browser/terminalStatusList.ts | 1 + .../terminal/browser/terminalTabbedView.ts | 4 +- .../terminal/browser/terminalTabsList.ts | 13 +- .../terminal/browser/terminalTelemetry.ts | 143 + .../terminal/browser/terminalTooltip.ts | 109 +- .../contrib/terminal/browser/terminalView.ts | 8 +- .../browser/widgets/terminalHoverWidget.ts | 2 +- .../terminal/browser/xterm/decorationAddon.ts | 23 +- .../terminal/browser/xterm/xtermTerminal.ts | 2 +- .../common/remote/remoteTerminalChannel.ts | 16 +- .../common/scripts/shellIntegration-bash.sh | 63 +- .../common/scripts/shellIntegration-rc.zsh | 18 +- .../common/scripts/shellIntegration.fish | 13 + .../common/scripts/shellIntegration.ps1 | 27 +- .../contrib/terminal/common/terminal.ts | 4 + .../terminal/common/terminalConfiguration.ts | 33 +- .../terminal/common/terminalStorageKeys.ts | 1 + .../terminal/common/terminalStrings.ts | 6 +- .../terminal.initialHint.contribution.ts | 3 +- .../chat/browser/terminalChatActions.ts | 15 +- .../chat/browser/terminalChatEnabler.ts | 7 +- .../chat/browser/terminalChatWidget.ts | 31 +- .../test/browser/terminalInitialHint.test.ts | 3 +- .../browser/media/terminalSymbolIcons.css | 30 +- .../browser/pwshCompletionProviderAddon.ts | 91 +- .../browser/terminal.suggest.contribution.ts | 62 +- .../browser/terminalCompletionModel.ts | 48 +- .../browser/terminalCompletionService.ts | 6 +- .../suggest/browser/terminalSuggestAddon.ts | 126 +- .../browser/terminalSuggestTelemetry.ts | 112 + .../suggest/browser/terminalSymbolIcons.ts | 22 +- .../suggest/common/terminal.suggest.ts | 3 +- .../common/terminalSuggestConfiguration.ts | 12 +- .../themes/browser/themes.contribution.ts | 6 +- .../browser/themes.test.contribution.ts | 112 +- .../timeline/common/timelineService.ts | 9 +- .../browser/typeHierarchyPeek.ts | 2 +- .../update/browser/releaseNotesEditor.ts | 3 +- .../contrib/update/browser/update.ts | 8 +- .../url/browser/trustedDomainService.ts | 60 +- .../url/browser/trustedDomainsValidator.ts | 3 +- .../contrib/url/common/trustedDomains.ts | 59 + .../test/browser/mockTrustedDomainService.ts | 6 +- .../url/test/browser/trustedDomains.test.ts | 2 +- .../browser/userDataProfilesEditor.ts | 33 +- .../browser/userDataProfilesEditorModel.ts | 32 +- .../browser/userDataSync.contribution.ts | 20 +- .../userDataSync/browser/userDataSync.ts | 79 +- .../userDataSync/browser/userDataSyncViews.ts | 8 +- .../contrib/void/common/prompt/prompts.ts | 1 - .../contrib/webview/browser/overlayWebview.ts | 2 +- .../webview/browser/pre/index-no-csp.html | 2 +- .../contrib/webview/browser/pre/index.html | 4 +- .../webview/browser/pre/service-worker.js | 37 +- .../browser/gettingStarted.contribution.ts | 13 +- .../browser/gettingStarted.ts | 17 +- .../browser/gettingStartedDetailsRenderer.ts | 4 +- .../browser/gettingStartedService.ts | 10 +- .../browser/startupPage.ts | 4 - .../common/gettingStartedContent.ts | 6 +- .../actions/developerActions.ts | 2 +- .../electron-sandbox/desktop.contribution.ts | 14 + .../electron-sandbox/desktop.main.ts | 24 +- .../parts/dialogs/dialogHandler.ts | 8 +- .../parts/titlebar/titlebarPart.ts | 92 +- src/vs/workbench/electron-sandbox/window.ts | 27 +- .../accounts/common/defaultAccount.ts | 306 + .../actions/common/menusExtensionPoint.ts | 7 + .../authenticationExtensionsService.ts | 16 +- .../browser/auxiliaryWindowService.ts | 5 +- .../configuration/browser/configuration.ts | 4 +- .../configuration/common/configuration.ts | 4 + .../common/configurationEditing.ts | 41 +- .../common/jsonEditingService.ts | 31 +- .../baseConfigurationResolverService.ts | 291 +- .../common/configurationResolver.ts | 31 +- .../common/configurationResolverExpression.ts | 234 + .../common/variableResolver.ts | 231 +- .../configurationResolverService.test.ts | 157 +- .../editor/browser/editorResolverService.ts | 17 - .../browser/extensionEnablementService.ts | 57 +- .../extensionGalleryManifestService.ts | 31 + .../browser/webExtensionsScannerService.ts | 6 +- .../common/extensionGalleryService.ts | 38 + .../common/extensionManagement.ts | 1 + .../common/extensionManagementService.ts | 21 +- .../common/webExtensionManagementService.ts | 6 + .../extensionGalleryManifestService.ts | 186 + .../extensionEnablementService.test.ts | 43 +- .../services/extensions/common/extensions.ts | 1 + .../extensions/common/extensionsRegistry.ts | 5 + .../diskFileSystemProvider.ts | 6 +- .../common/filesConfigurationService.ts | 39 +- .../history/browser/historyService.ts | 77 +- .../electron-sandbox/nativeHostService.ts | 20 +- .../electron-sandbox/integrityService.ts | 17 +- ...orker.ts => languageDetectionWebWorker.ts} | 30 +- ...n.ts => languageDetectionWebWorkerMain.ts} | 6 +- .../languageDetectionWorker.protocol.ts | 6 +- .../languageDetectionWorkerServiceImpl.ts | 10 +- .../services/layout/browser/layoutService.ts | 6 + .../lifecycle/browser/lifecycleService.ts | 1 + .../services/lifecycle/common/lifecycle.ts | 5 + .../lifecycle/common/lifecycleService.ts | 3 + .../electron-sandbox/lifecycleService.ts | 2 + .../electron-sandbox/lifecycleService.test.ts | 17 + .../common/notebookDocumentService.ts | 39 + .../common/notificationService.ts | 4 +- .../policies/common/accountPolicyService.ts | 60 + .../policies/common/multiplexPolicyService.ts | 61 + .../preferences/browser/preferencesService.ts | 19 +- .../preferences/common/preferences.ts | 4 +- .../preferences/common/preferencesModels.ts | 15 - .../progress/browser/progressService.ts | 40 +- .../services/search/browser/searchService.ts | 18 +- .../common/localFileSearchWorkerTypes.ts | 14 +- .../services/search/worker/localFileSearch.ts | 20 +- .../search/worker/localFileSearchMain.ts | 4 +- .../storage/browser/storageService.ts | 4 +- .../suggest/browser/media/suggest.css | 7 + .../suggest/browser/simpleCompletionItem.ts | 5 + .../suggest/browser/simpleSuggestWidget.ts | 41 +- .../browser/simpleSuggestWidgetDetails.ts | 7 + .../browser/simpleSuggestWidgetRenderer.ts | 57 +- .../textMateWorkerTokenizerController.ts | 2 +- .../threadedBackgroundTokenizerFactory.ts | 8 +- .../textMateTokenizationWorker.worker.ts | 12 +- .../textMateTokenizationWorker.workerMain.ts | 4 +- .../worker/textMateWorkerHost.ts | 6 +- .../textfile/browser/textFileService.ts | 9 +- .../textfile/common/textFileEditorModel.ts | 7 +- .../common/textFileEditorModelManager.ts | 4 +- .../themes/browser/workbenchThemeService.ts | 20 +- .../services/themes/common/colorThemeData.ts | 8 +- .../themes/common/workbenchThemeService.ts | 8 +- .../test/node/tokenStyleResolving.test.ts | 3 +- .../services/timer/browser/timerService.ts | 2 +- .../browser/treeSitterCodeEditors.ts | 60 +- .../browser/treeSitterTokenizationFeature.ts | 438 +- .../common/untitledTextEditorModel.ts | 4 +- .../common/untitledFileWorkingCopy.ts | 7 +- .../common/workingCopyBackupTracker.ts | 4 +- .../common/workingCopyFileService.ts | 9 +- .../workspaces/common/workspaceUtils.ts | 30 + .../parts/editor/editorGroupModel.test.ts | 4 +- .../editor/filteredEditorGroupModel.test.ts | 3 +- .../test/browser/workbenchTestServices.ts | 27 +- .../test/common/workbenchTestServices.ts | 4 +- .../treeSitterTokenizationFeature.test.ts | 149 +- .../electron-sandbox/workbenchTestServices.ts | 3 + src/vs/workbench/workbench.common.main.ts | 9 +- src/vs/workbench/workbench.desktop.main.ts | 8 +- .../workbench/workbench.web.main.internal.ts | 6 + src/vscode-dts/vscode.d.ts | 27 +- ...ode.proposed.chatParticipantAdditions.d.ts | 23 +- ...scode.proposed.chatParticipantPrivate.d.ts | 10 +- .../vscode.proposed.chatProvider.d.ts | 8 +- .../vscode.proposed.chatStatusItem.d.ts | 61 + ...scode.proposed.defaultChatParticipant.d.ts | 10 +- ...e.proposed.inlineCompletionsAdditions.d.ts | 2 +- ...vscode.proposed.languageModelDataPart.d.ts | 98 + ....proposed.languageModelToolsForAgent.d.ts} | 4 +- ...ode.proposed.mcpConfigurationProvider.d.ts | 45 + ...ode.proposed.taskProblemMatcherStatus.d.ts | 42 + .../vscode.proposed.terminalShellEnv.d.ts | 2 +- .../vscode.proposed.terminalShellType.d.ts | 40 - test/automation/package-lock.json | 327 +- test/automation/package.json | 6 +- test/automation/src/code.ts | 4 +- test/automation/src/debug.ts | 21 +- test/automation/src/editor.ts | 2 +- test/automation/src/editors.ts | 14 +- test/automation/src/explorer.ts | 4 +- test/automation/src/keybindings.ts | 14 +- test/automation/src/notebook.ts | 4 +- test/automation/src/peek.ts | 3 +- test/automation/src/playwrightDriver.ts | 12 +- test/automation/src/profiler.ts | 21 +- test/automation/src/quickaccess.ts | 31 +- test/automation/src/quickinput.ts | 14 +- test/automation/src/scm.ts | 3 +- test/automation/src/search.ts | 15 +- test/automation/src/settings.ts | 22 +- test/automation/src/task.ts | 13 +- test/automation/src/terminal.ts | 17 +- test/monaco/core.js | 2 +- test/monaco/webpack.config.js | 3 +- test/smoke/package-lock.json | 40 +- test/smoke/package.json | 3 +- .../src/areas/preferences/preferences.test.ts | 8 +- test/smoke/src/areas/search/search.test.ts | 10 +- .../terminal-shellIntegration.test.ts | 7 +- 1308 files changed, 102127 insertions(+), 38374 deletions(-) create mode 100644 .config/guardian/.gdnsuppress delete mode 100644 build/azure-pipelines/linux/product-build-linux-legacy-server.yml create mode 100644 build/lib/propertyInitOrderChecker.js create mode 100644 build/lib/propertyInitOrderChecker.ts create mode 100644 extensions/terminal-suggest/scripts/pullFishBuiltins.ts create mode 100644 extensions/terminal-suggest/scripts/terminalScriptHelpers.ts create mode 100644 extensions/terminal-suggest/src/completions/code-tunnel-insiders.ts create mode 100644 extensions/terminal-suggest/src/completions/code-tunnel.ts create mode 100644 extensions/terminal-suggest/src/completions/npx.ts delete mode 100644 extensions/terminal-suggest/src/completions/upstream/npx.ts create mode 100644 extensions/terminal-suggest/src/helpers/promise.ts create mode 100644 extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts create mode 100644 extensions/vscode-colorize-tests/test/colorize-fixtures/test-issue241715.ts create mode 100644 extensions/vscode-colorize-tests/test/colorize-fixtures/test.regexp.ts create mode 100644 extensions/vscode-colorize-tests/test/colorize-results/test-issue241715_ts.json create mode 100644 extensions/vscode-colorize-tests/test/colorize-results/test_regexp.ts.json create mode 100644 extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue241715_ts.json create mode 100644 extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test_regexp.ts.json delete mode 100755 resources/server/bin/helpers/check-requirements-linux-legacy.sh delete mode 100644 resources/win32/inno-void.bmp delete mode 100644 resources/win32/logo_cube_noshadow.png create mode 100644 src/vs/base/browser/ui/dnd/dnd.css create mode 100644 src/vs/base/browser/ui/dnd/dnd.ts rename src/vs/{platform/severityIcon/browser => base/browser/ui/severityIcon}/media/severityIcon.css (100%) rename src/vs/{platform/severityIcon/browser => base/browser/ui/severityIcon}/severityIcon.ts (82%) rename src/vs/base/browser/{defaultWorkerFactory.ts => webWorkerFactory.ts} (70%) create mode 100644 src/vs/base/common/envfile.ts create mode 100644 src/vs/base/common/policy.ts rename src/vs/base/common/worker/{simpleWorker.ts => webWorker.ts} (77%) rename src/vs/base/common/worker/{simpleWorkerBootstrap.ts => webWorkerBootstrap.ts} (64%) create mode 100644 src/vs/base/test/common/envfile.test.ts delete mode 100644 src/vs/base/worker/workerMain.ts create mode 100644 src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts create mode 100644 src/vs/editor/common/codecs/markdownCodec/parsers/markdownImage.ts create mode 100644 src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts create mode 100644 src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts create mode 100644 src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts create mode 100644 src/vs/editor/common/languages/highlights/css.scm create mode 100644 src/vs/editor/common/languages/highlights/regex.scm create mode 100644 src/vs/editor/common/languages/injections/typescript.scm rename src/vs/editor/common/services/{editorSimpleWorker.ts => editorWebWorker.ts} (87%) create mode 100644 src/vs/editor/common/services/editorWebWorkerMain.ts delete mode 100644 src/vs/editor/common/services/editorWorkerBootstrap.ts create mode 100644 src/vs/editor/common/services/treeSitter/cursorUtils.ts create mode 100644 src/vs/editor/common/services/treeSitter/textModelTreeSitter.ts create mode 100644 src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/structuredLogger.ts create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts rename src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/{viewAndDiffProducer.ts => inlineEditsViewProducer.ts} (53%) create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts create mode 100644 src/vs/editor/editor.worker.start.ts rename src/vs/editor/test/common/services/{editorSimpleWorker.test.ts => editorWebWorker.test.ts} (98%) create mode 100644 src/vs/platform/accessibilitySignal/browser/media/editsKept.mp3 create mode 100644 src/vs/platform/accessibilitySignal/browser/media/editsUndone.mp3 create mode 100644 src/vs/platform/dialogs/browser/dialog.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionGalleryManifest.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionGalleryManifestService.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionGalleryManifestServiceIpc.ts create mode 100644 src/vs/platform/mcp/common/mcpManagementCli.ts create mode 100644 src/vs/platform/mcp/common/mcpPlatformTypes.ts create mode 100644 src/vs/platform/mcp/common/nativeMcpDiscoveryHelper.ts create mode 100644 src/vs/platform/mcp/node/nativeMcpDiscoveryHelperChannel.ts create mode 100644 src/vs/platform/mcp/node/nativeMcpDiscoveryHelperService.ts create mode 100644 src/vs/platform/observable/common/observableMemento.ts rename src/vs/{editor/common/services/editorSimpleWorkerMain.ts => platform/remote/common/remote.ts} (68%) create mode 100644 src/vs/platform/secrets/test/common/testSecretStorageService.ts create mode 100644 src/vs/platform/telemetry/electron-main/errorTelemetry.ts create mode 100644 src/vs/platform/webContentExtractor/common/webContentExtractor.ts create mode 100644 src/vs/platform/webContentExtractor/electron-main/cdpAccessibilityDomain.ts create mode 100644 src/vs/platform/webContentExtractor/electron-main/webContentExtractorService.ts create mode 100644 src/vs/platform/webContentExtractor/electron-sandbox/webContentExtractorService.ts create mode 100644 src/vs/platform/webContentExtractor/node/sharedWebContentExtractorService.ts create mode 100644 src/vs/workbench/api/browser/mainThreadChatStatus.ts create mode 100644 src/vs/workbench/api/browser/mainThreadMcp.ts create mode 100644 src/vs/workbench/api/common/extHostChatStatus.ts create mode 100644 src/vs/workbench/api/common/extHostMcp.ts create mode 100644 src/vs/workbench/api/node/extHostMpcNode.ts create mode 100644 src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts create mode 100644 src/vs/workbench/browser/media/code-icon.svg delete mode 100644 src/vs/workbench/browser/media/void-icon-sm.png delete mode 100644 src/vs/workbench/browser/parts/editor/media/slice_of_void.png delete mode 100644 src/vs/workbench/browser/parts/editor/media/void_cube_noshadow.png delete mode 100644 src/vs/workbench/common/release.ts delete mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/askToSelectPrompt.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/constants.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/attachPrompts.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPlaceholderText.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPromptPickItem.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/handleButtonClick.ts create mode 100644 src/vs/workbench/contrib/chat/browser/actions/chatTransfer.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts delete mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookSnapshot.ts delete mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookEditorIntegration.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookDiff.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookSnapshot.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts rename src/vs/workbench/contrib/chat/browser/chatEditing/{ => notebook}/chatEditingNotebookFileSystemProvider.ts (83%) create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/helpers.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatSelectedTools.ts create mode 100644 src/vs/workbench/contrib/chat/browser/chatStatusItemService.ts create mode 100644 src/vs/workbench/contrib/chat/browser/media/chatSetup.css rename src/{vscode-dts/vscode.proposed.chatReadonlyPromptReference.d.ts => vs/workbench/contrib/chat/common/chat.ts} (54%) create mode 100644 src/vs/workbench/contrib/chat/common/chatEntitlementService.ts delete mode 100644 src/vs/workbench/contrib/chat/common/chatEntitlementsService.ts delete mode 100644 src/vs/workbench/contrib/chat/common/chatQuotasService.ts create mode 100644 src/vs/workbench/contrib/chat/common/chatSessionStore.ts create mode 100644 src/vs/workbench/contrib/chat/common/chatTransferService.ts create mode 100644 src/vs/workbench/contrib/chat/common/constants.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptSyntax/codecs/parsers/promptVariableParser.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptToken.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptSyntax/parsers/topError.ts create mode 100644 src/vs/workbench/contrib/chat/common/tools/insertNotebookCellsTool.ts create mode 100644 src/vs/workbench/contrib/chat/common/tools/promptTsxTypes.ts create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/actions/chatDeveloperActions.ts create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/tools/fetchPageTool.ts create mode 100644 src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts create mode 100644 src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_but_edit_mode.0.snap delete mode 100644 src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts delete mode 100644 src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts create mode 100644 src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts create mode 100644 src/vs/workbench/contrib/mcp/browser/mcpCommands.ts create mode 100644 src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts create mode 100644 src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts create mode 100644 src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts create mode 100644 src/vs/workbench/contrib/mcp/browser/mcpUrlHandler.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.ts create mode 100644 src/vs/workbench/contrib/mcp/common/discovery/workspaceMcpDiscoveryAdapter.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpConfigPathsService.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpRegistry.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpServer.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpService.ts create mode 100644 src/vs/workbench/contrib/mcp/common/mcpTypes.ts create mode 100644 src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts create mode 100644 src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts create mode 100644 src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts create mode 100644 src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts create mode 100644 src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts create mode 100644 src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts create mode 100644 src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts create mode 100644 src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditorAccessibilityHelp.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/contribution.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizer.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts rename src/vs/workbench/contrib/notebook/browser/{contrib/chatEdit => diff/inlineDiff}/notebookOriginalCellModelFactory.ts (100%) rename src/vs/workbench/contrib/notebook/browser/{contrib/chatEdit => diff/inlineDiff}/notebookOriginalModelRefFactory.ts (100%) create mode 100644 src/vs/workbench/contrib/notebook/browser/viewParts/notebookCellOverlays.ts create mode 100644 src/vs/workbench/contrib/notebook/common/notebookDiff.ts delete mode 100644 src/vs/workbench/contrib/notebook/common/notebookSynchronizerService.ts create mode 100644 src/vs/workbench/contrib/notebook/common/services/notebookCellMatching.ts delete mode 100644 src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts create mode 100644 src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts rename src/vs/workbench/contrib/notebook/common/services/{notebookSimpleWorkerMain.ts => notebookWebWorkerMain.ts} (65%) create mode 100644 src/vs/workbench/contrib/notebook/test/browser/diff/notebookDiffService.test.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalTelemetry.ts create mode 100644 src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestTelemetry.ts create mode 100644 src/vs/workbench/contrib/url/common/trustedDomains.ts create mode 100644 src/vs/workbench/services/accounts/common/defaultAccount.ts create mode 100644 src/vs/workbench/services/configurationResolver/common/configurationResolverExpression.ts create mode 100644 src/vs/workbench/services/extensionManagement/browser/extensionGalleryManifestService.ts create mode 100644 src/vs/workbench/services/extensionManagement/common/extensionGalleryService.ts create mode 100644 src/vs/workbench/services/extensionManagement/electron-sandbox/extensionGalleryManifestService.ts rename src/vs/workbench/services/languageDetection/browser/{languageDetectionSimpleWorker.ts => languageDetectionWebWorker.ts} (88%) rename src/vs/workbench/services/languageDetection/browser/{languageDetectionSimpleWorkerMain.ts => languageDetectionWebWorkerMain.ts} (65%) create mode 100644 src/vs/workbench/services/policies/common/accountPolicyService.ts create mode 100644 src/vs/workbench/services/policies/common/multiplexPolicyService.ts create mode 100644 src/vs/workbench/services/workspaces/common/workspaceUtils.ts create mode 100644 src/vscode-dts/vscode.proposed.chatStatusItem.d.ts create mode 100644 src/vscode-dts/vscode.proposed.languageModelDataPart.d.ts rename src/{vs/editor/editor.worker.ts => vscode-dts/vscode.proposed.languageModelToolsForAgent.d.ts} (78%) create mode 100644 src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts create mode 100644 src/vscode-dts/vscode.proposed.taskProblemMatcherStatus.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.terminalShellType.d.ts diff --git a/.config/guardian/.gdnsuppress b/.config/guardian/.gdnsuppress new file mode 100644 index 00000000..d1d93c2a --- /dev/null +++ b/.config/guardian/.gdnsuppress @@ -0,0 +1,46 @@ +{ + "hydrated": false, + "properties": { + "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/suppressions" + }, + "version": "1.0.0", + "suppressionSets": { + "default": { + "name": "default", + "createdDate": "2025-03-17 11:52:32Z", + "lastUpdatedDate": "2025-03-17 11:52:32Z" + } + }, + "results": { + "216e2ac9cb596796224b47799f656570a01fa0d9b5f935608b47d15ab613c8e8": { + "signature": "216e2ac9cb596796224b47799f656570a01fa0d9b5f935608b47d15ab613c8e8", + "alternativeSignatures": [ + "07746898f43afab7cc50931b33154c2d9e1a35f82a649dbe8aecf785b3d5a813" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-03-17 11:52:32Z" + }, + "77797a3e44634bb2994bd13ccc95ff4575bba474585dbd2cf3068a1c16bc0624": { + "signature": "77797a3e44634bb2994bd13ccc95ff4575bba474585dbd2cf3068a1c16bc0624", + "alternativeSignatures": [ + "4a6cb67bd4b401e9669c13a2162660aaefc0a94a4122e5b50c198414db545672" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-03-17 11:52:32Z" + }, + "30418bcc5269eaeb2832a2404465784431d4e72a2af332320c2b1db4768902ad": { + "signature": "30418bcc5269eaeb2832a2404465784431d4e72a2af332320c2b1db4768902ad", + "alternativeSignatures": [ + "b7b9eb974d7d3a4ae14df8695ca5a62592c8c9d20b7eda70a6535d50cbda3e7f" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-03-17 11:52:32Z" + } + } +} diff --git a/.devcontainer/README.md b/.devcontainer/README.md index dd59adb6..3d1e15e5 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,6 +1,6 @@ # Code - OSS Development Container -[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) +[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) This repository includes configuration for a development container for working with Code - OSS in a local container or using [GitHub Codespaces](https://github.com/features/codespaces). diff --git a/.eslint-ignore b/.eslint-ignore index 969f7a5b..e4931981 100644 --- a/.eslint-ignore +++ b/.eslint-ignore @@ -11,10 +11,10 @@ **/extensions/markdown-language-features/notebook-out/** **/extensions/markdown-math/notebook-out/** **/extensions/notebook-renderers/renderer-out/index.js -**/extensions/open-remote-ssh/out/extension.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 @@ -37,4 +37,5 @@ **/test/unit/assert.js **/test/automation/out/** **/typings/** +**/.build/** !.vscode diff --git a/.eslint-plugin-local/code-amd-node-module.ts b/.eslint-plugin-local/code-amd-node-module.ts index ff7ef6ab..b622c98a 100644 --- a/.eslint-plugin-local/code-amd-node-module.ts +++ b/.eslint-plugin-local/code-amd-node-module.ts @@ -51,7 +51,7 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { node, messageId: 'amdX' }); - } + }; return { ['ImportExpression Literal']: checkImport, diff --git a/.eslint-plugin-local/code-declare-service-brand.ts b/.eslint-plugin-local/code-declare-service-brand.ts index f2d28bbf..85cf0671 100644 --- a/.eslint-plugin-local/code-declare-service-brand.ts +++ b/.eslint-plugin-local/code-declare-service-brand.ts @@ -19,7 +19,7 @@ export = new class DeclareServiceBrand implements eslint.Rule.RuleModule { node, message: `The '_serviceBrand'-property should not have a value`, fix: (fixer) => { - return fixer.replaceText(node, 'declare _serviceBrand: undefined;') + return fixer.replaceText(node, 'declare _serviceBrand: undefined;'); } }); } diff --git a/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts b/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts index 56a1d4a7..c657df9b 100644 --- a/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts +++ b/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts @@ -27,7 +27,7 @@ export = new class EnsureNoDisposablesAreLeakedInTestSuite implements eslint.Rul return { [`Program > ExpressionStatement > CallExpression[callee.name='suite']`]: (node: Node) => { - const src = context.getSourceCode().getText(node) + const src = context.getSourceCode().getText(node); if (!src.includes('ensureNoDisposablesAreLeakedInTestSuite(')) { context.report({ node, diff --git a/.eslint-plugin-local/code-import-patterns.ts b/.eslint-plugin-local/code-import-patterns.ts index e4fe5241..ed14fa35 100644 --- a/.eslint-plugin-local/code-import-patterns.ts +++ b/.eslint-plugin-local/code-import-patterns.ts @@ -44,7 +44,7 @@ export = new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization', - badFilename: 'Missing definition in `code-import-patterns` for this file. Define rules at https://github.com/microsoft/vscode/blob/main/.eslintrc.json', + badFilename: 'Missing definition in `code-import-patterns` for this file. Define rules at https://github.com/microsoft/vscode/blob/main/eslint.config.js', badAbsolute: 'Imports have to be relative to support ESM', badExtension: 'Imports have to end with `.js` or `.css` to support ESM', }, diff --git a/.eslint-plugin-local/code-limited-top-functions.ts b/.eslint-plugin-local/code-limited-top-functions.ts index 97eef9a6..7b48d02a 100644 --- a/.eslint-plugin-local/code-limited-top-functions.ts +++ b/.eslint-plugin-local/code-limited-top-functions.ts @@ -14,13 +14,13 @@ export = new class implements eslint.Rule.RuleModule { layerbreaker: 'You are only allowed to define limited top level functions.' }, schema: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', additionalProperties: { - type: "array", + type: 'array', items: { - type: "string" + type: 'string' } } } @@ -65,6 +65,6 @@ export = new class implements eslint.Rule.RuleModule { } } } - } + }; } }; diff --git a/.eslint-plugin-local/code-must-use-result.ts b/.eslint-plugin-local/code-must-use-result.ts index e59b1920..e249f36d 100644 --- a/.eslint-plugin-local/code-must-use-result.ts +++ b/.eslint-plugin-local/code-must-use-result.ts @@ -14,22 +14,22 @@ const VALID_USES = new Set([ export = new class MustUseResults implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { schema: false - } + }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - const config = <{ message: string, functions: string[] }[]>context.options[0]; + const config = <{ message: string; functions: string[] }[]>context.options[0]; const listener: eslint.Rule.RuleListener = {}; for (const { message, functions } of config) { for (const fn of functions) { - const query = `CallExpression[callee.property.name='${fn}'], CallExpression[callee.name='${fn}']` + const query = `CallExpression[callee.property.name='${fn}'], CallExpression[callee.name='${fn}']`; listener[query] = (node: any) => { const cast: TSESTree.CallExpression = node; if (!VALID_USES.has(cast.parent?.type)) { context.report({ node, message }); } - } + }; } } diff --git a/.eslint-plugin-local/code-must-use-super-dispose.ts b/.eslint-plugin-local/code-must-use-super-dispose.ts index 4f7f9646..ca776d8a 100644 --- a/.eslint-plugin-local/code-must-use-super-dispose.ts +++ b/.eslint-plugin-local/code-must-use-super-dispose.ts @@ -14,7 +14,7 @@ export = new class NoAsyncSuite implements eslint.Rule.RuleModule { return; } - const body = context.getSourceCode().getText(node) + const body = context.getSourceCode().getText(node); if (body.includes('super.dispose')) { return; diff --git a/.eslint-plugin-local/code-no-dangerous-type-assertions.ts b/.eslint-plugin-local/code-no-dangerous-type-assertions.ts index 233fae02..f900d778 100644 --- a/.eslint-plugin-local/code-no-dangerous-type-assertions.ts +++ b/.eslint-plugin-local/code-no-dangerous-type-assertions.ts @@ -32,7 +32,7 @@ export = new class NoDangerousTypeAssertions implements eslint.Rule.RuleModule { context.report({ node, - message: "Don't use type assertions for creating objects as this can hide type errors." + message: `Don't use type assertions for creating objects as this can hide type errors.` }); }, }; diff --git a/.eslint-plugin-local/code-no-global-document-listener.ts b/.eslint-plugin-local/code-no-global-document-listener.ts index 6b3e83fe..049426a5 100644 --- a/.eslint-plugin-local/code-no-global-document-listener.ts +++ b/.eslint-plugin-local/code-no-global-document-listener.ts @@ -25,6 +25,6 @@ export = new class NoGlobalDocumentListener implements eslint.Rule.RuleModule { }); } }, - } + }; } }; diff --git a/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts b/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts index 19ad65ee..c0d60985 100644 --- a/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts +++ b/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts @@ -24,7 +24,7 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName) || /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName) || /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName) - || /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName) + || /vs(\/|\\)editor(\/|\\)editor.worker.start/.test(fileName) ) { return createImportRuleListener((node, path) => { // resolve relative paths diff --git a/.eslint-plugin-local/code-no-runtime-import.ts b/.eslint-plugin-local/code-no-runtime-import.ts index 61597236..afebe0b0 100644 --- a/.eslint-plugin-local/code-no-runtime-import.ts +++ b/.eslint-plugin-local/code-no-runtime-import.ts @@ -16,13 +16,13 @@ export = new class implements eslint.Rule.RuleModule { layerbreaker: 'You are only allowed to import {{import}} from here using `import type ...`.' }, schema: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', additionalProperties: { - type: "array", + type: 'array', items: { - type: "string" + type: 'string' } } } diff --git a/.eslint-plugin-local/code-no-standalone-editor.ts b/.eslint-plugin-local/code-no-standalone-editor.ts index 3fad6719..36bf48b1 100644 --- a/.eslint-plugin-local/code-no-standalone-editor.ts +++ b/.eslint-plugin-local/code-no-standalone-editor.ts @@ -38,7 +38,7 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path) || /vs(\/|\\)editor(\/|\\)editor.api/.test(path) || /vs(\/|\\)editor(\/|\\)editor.main/.test(path) - || /vs(\/|\\)editor(\/|\\)editor.worker/.test(path) + || /vs(\/|\\)editor(\/|\\)editor.worker.start/.test(path) ) { context.report({ loc: node.loc, diff --git a/.eslint-plugin-local/code-no-static-self-ref.ts b/.eslint-plugin-local/code-no-static-self-ref.ts index 52edfb25..f6206455 100644 --- a/.eslint-plugin-local/code-no-static-self-ref.ts +++ b/.eslint-plugin-local/code-no-static-self-ref.ts @@ -26,19 +26,19 @@ export = new class implements eslint.Rule.RuleModule { return; } - const classCtor = classDeclaration.body.body.find(node => node.type === 'MethodDefinition' && node.kind === 'constructor') + const classCtor = classDeclaration.body.body.find(node => node.type === 'MethodDefinition' && node.kind === 'constructor'); if (!classCtor) { return; } const name = classDeclaration.id.name; - const valueText = context.sourceCode.getText(propertyDefinition.value) + const valueText = context.sourceCode.getText(propertyDefinition.value); if (valueText.includes(name + '.')) { if (classCtor.value?.type === 'FunctionExpression' && !classCtor.value.params.find((param: any) => param.type === 'TSParameterProperty' && param.decorators?.length > 0)) { - return + return; } context.report({ diff --git a/.eslint-plugin-local/code-no-unused-expressions.ts b/.eslint-plugin-local/code-no-unused-expressions.ts index 14f2f53d..bd632884 100644 --- a/.eslint-plugin-local/code-no-unused-expressions.ts +++ b/.eslint-plugin-local/code-no-unused-expressions.ts @@ -58,7 +58,7 @@ module.exports = { allowTernary = config.allowTernary || false, allowTaggedTemplates = config.allowTaggedTemplates || false; - // eslint-disable-next-line jsdoc/require-description + /** * @param node any node * @returns whether the given node structurally represents a directive @@ -68,7 +68,7 @@ module.exports = { node.expression.type === 'Literal' && typeof node.expression.value === 'string'; } - // eslint-disable-next-line jsdoc/require-description + /** * @param predicate ([a] -> Boolean) the function used to make the determination * @param list the input list @@ -83,7 +83,7 @@ module.exports = { return list.slice(); } - // eslint-disable-next-line jsdoc/require-description + /** * @param node a Program or BlockStatement node * @returns the leading sequence of directive nodes in the given node's body @@ -92,7 +92,7 @@ module.exports = { return takeWhile(looksLikeDirective, node.body); } - // eslint-disable-next-line jsdoc/require-description + /** * @param node any node * @param ancestors the given node's ancestors diff --git a/.eslint-plugin-local/index.js b/.eslint-plugin-local/index.js index 9f453168..198cb836 100644 --- a/.eslint-plugin-local/index.js +++ b/.eslint-plugin-local/index.js @@ -1,3 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ const glob = require('glob'); const path = require('path'); diff --git a/.eslint-plugin-local/vscode-dts-string-type-literals.ts b/.eslint-plugin-local/vscode-dts-string-type-literals.ts index bca084c4..0f6d711a 100644 --- a/.eslint-plugin-local/vscode-dts-string-type-literals.ts +++ b/.eslint-plugin-local/vscode-dts-string-type-literals.ts @@ -19,7 +19,7 @@ export = new class ApiTypeDiscrimination implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { ['TSPropertySignature[optional=false] TSTypeAnnotation TSLiteralType Literal']: (node: any) => { - const raw = String((node).raw) + const raw = String((node).raw); if (/^('|").*\1$/.test(raw)) { @@ -29,6 +29,6 @@ export = new class ApiTypeDiscrimination implements eslint.Rule.RuleModule { }); } } - } + }; } }; diff --git a/.gitignore b/.gitignore index a40dd0d1..b73ce578 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,3 @@ vscode.db product.overrides.json *.snap.actual .vscode-test -.tmp/ -.tmp2/ -.tool-versions -src/vs/workbench/contrib/void/browser/react/out/** -src/vs/workbench/contrib/void/browser/react/src2/** diff --git a/.npmrc b/.npmrc index 74a42c31..71d98e6c 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ disturl="https://electronjs.org/headers" target="34.3.2" -ms_build_id="11044223" +ms_build_id="11161073" runtime="electron" build_from_source="true" legacy-peer-deps="true" diff --git a/.vscode-test.js b/.vscode-test.js index 917413ed..823ef615 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/launch.json b/.vscode/launch.json index afd57934..f922b5d9 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 09d6480e..3a0eaec9 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"February 2025\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"March 2025\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index ca93d503..d778179a 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"February 2025\"" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"March 2025\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 0fd05ece..c9890631 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"February 2025\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"March 2025\"\n\n$MINE=assignee:@me" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index c7674cef..401fde7e 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"February 2025\"\n" + "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"March 2025\"\n" }, { "kind": 1, diff --git a/.vscode/settings.json b/.vscode/settings.json index 59a78245..80c37853 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 @@ -156,6 +157,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/.vscode/tasks.json b/.vscode/tasks.json index bfce54d4..3bcb5d5c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -29,34 +29,6 @@ } } }, - { - "type": "npm", - "script": "watchreactd", - "label": "React - Build", - "isBackground": true, - "presentation": { - "reveal": "never", - "group": "buildWatchers", - "close": false - }, - "problemMatcher": { - "owner": "typescript", - "applyTo": "closedDocuments", - "fileLocation": [ - "absolute" - ], - "pattern": { - "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$", - "file": 1, - "location": 2, - "message": 3 - }, - "background": { - "beginsPattern": "Starting compilation", - "endsPattern": "Finished compilation" - } - } - }, { "type": "npm", "script": "watch-extensionsd", @@ -89,8 +61,7 @@ "label": "VS Code - Build", "dependsOn": [ "Core - Build", - "Ext - Build", - "React - Build" + "Ext - Build" ], "group": { "kind": "build", diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 0f17c5ed..6be514a4 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -225,7 +225,7 @@ OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -atom/language-sass 0.62.1 - MIT +atom/language-sass 0.61.4 - MIT https://github.com/atom/language-sass The MIT License (MIT) @@ -1203,6 +1203,33 @@ to the base-name name of the original file, and an extension of txt, html, or si --------------------------------------------------------- +fish-shell 3.7.1 +https://github.com/fish-shell/fish-shell + +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. +--------------------------------------------------------- + +--------------------------------------------------------- + go-syntax 0.7.9 - MIT https://github.com/worlpaker/go-syntax @@ -1519,7 +1546,7 @@ SOFTWARE. --------------------------------------------------------- -jlelong/vscode-latex-basics 1.10.0 - MIT +jlelong/vscode-latex-basics 1.9.0 - MIT https://github.com/jlelong/vscode-latex-basics Copyright (c) vscode-latex-basics authors @@ -2101,7 +2128,7 @@ SOFTWARE. --------------------------------------------------------- -microsoft/vscode-mssql 1.23.0 - MIT +microsoft/vscode-mssql 1.29.0 - MIT https://github.com/microsoft/vscode-mssql ------------------------------------------ START OF LICENSE ----------------------------------------- @@ -3485,4 +3512,24 @@ Apache License WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +--------------------------------------------------------- + +--------------------------------------------------------- + +zsh 5.9 +https://github.com/zsh-users/zsh + +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. --------------------------------------------------------- \ No newline at end of file diff --git a/build/.moduleignore b/build/.moduleignore index 6b7f3657..3e654cfe 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -59,6 +59,7 @@ fsevents/test/** !@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm !@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm !@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm +!@vscode/tree-sitter-wasm/wasm/tree-sitter-css.wasm native-keymap/binding.gyp native-keymap/build/** diff --git a/build/.npmrc b/build/.npmrc index 1b073e71..551822f7 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 71cd3f71..a5d8bdc1 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 fee56e02..0607cde3 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 45a1cfd1..bff114fc 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 444e005d..8e052881 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 f061d043..37bf5ccc 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -694,7 +694,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 +739,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}`); } @@ -896,8 +896,7 @@ async function processArtifact( } 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.statSync(filePath).size; const stream = fs.createReadStream(filePath); @@ -956,7 +955,6 @@ 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'); } diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 9e054574..1eb387ae 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -23,10 +23,10 @@ steps: - script: npm run test-node displayName: Run unit tests (node.js) timeoutInMinutes: 15 - - script: npm run test-browser-no-install -- --sequential --browser chromium --browser webkit --tfs "Browser Unit Tests" + - script: npm run test-browser-no-install -- --browser webkit --tfs "Browser Unit Tests" env: DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium & Webkit) + displayName: Run unit tests (Browser, Webkit) timeoutInMinutes: 30 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: @@ -36,10 +36,10 @@ steps: - script: npm run test-node -- --build displayName: Run unit tests (node.js) timeoutInMinutes: 15 - - script: npm run test-browser-no-install -- --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" + - script: npm run test-browser-no-install -- --build --browser webkit --tfs "Browser Unit Tests" env: DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium & Webkit) + displayName: Run unit tests (Browser, Webkit) timeoutInMinutes: 30 - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index e77000d4..bd37d675 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 4c26baf2..00000000 --- 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 1f198441..94105642 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 0e5faf24..bf25633b 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 @@ -118,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 @@ -183,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: @@ -279,6 +267,10 @@ extends: 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: @@ -292,6 +284,10 @@ extends: 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: @@ -322,13 +318,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: @@ -394,6 +390,10 @@ extends: timeoutInMinutes: 120 variables: VSCODE_ARCH: x64 + templateContext: + sdl: + suppression: + suppressionFile: $(Build.SourcesDirectory)\.config\guardian\.gdnsuppress steps: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: @@ -418,6 +418,10 @@ extends: timeoutInMinutes: 90 variables: VSCODE_ARCH: arm64 + templateContext: + sdl: + suppression: + suppressionFile: $(Build.SourcesDirectory)\.config\guardian\.gdnsuppress steps: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: @@ -547,51 +551,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: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index dcc1a622..6096157a 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -104,12 +104,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 8ecf5e62..1127e521 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/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index 09db30d1..ebc59303 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -28,7 +28,7 @@ steps: - powershell: npm run test-node displayName: Run unit tests (node.js) timeoutInMinutes: 15 - - powershell: node test/unit/browser/index.js --sequential --browser chromium --tfs "Browser Unit Tests" + - powershell: node test/unit/browser/index.js --browser chromium --tfs "Browser Unit Tests" displayName: Run unit tests (Browser, Chromium) timeoutInMinutes: 20 @@ -39,7 +39,7 @@ steps: - powershell: npm run test-node -- --build displayName: Run unit tests (node.js) timeoutInMinutes: 15 - - powershell: npm run test-browser-no-install -- --sequential --build --browser chromium --tfs "Browser Unit Tests" + - powershell: npm run test-browser-no-install -- --build --browser chromium --tfs "Browser Unit Tests" displayName: Run unit tests (Browser, Chromium) timeoutInMinutes: 20 diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ed7f1004..b10de41d 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 @@ -231,6 +231,11 @@ steps: SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign executables and shared libraries + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows-appx $(CodeSigningFolderPath) '*.ps1' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + displayName: Codesign Powershell scripts + - ${{ if eq(parameters.VSCODE_QUALITY, 'insider') }}: - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows-appx $(CodeSigningFolderPath) '*.appx' env: @@ -247,13 +252,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 +270,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,32 +283,30 @@ 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) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { npm run -- gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" --sign } - $SetupPath = ".build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" - mv .build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe $SetupPath - echo "##vso[task.setvariable variable=SYSTEM_SETUP_PATH]$SetupPath" - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - displayName: Build system setup + exec { npm exec -- npm-run-all -lp "gulp vscode-win32-$(VSCODE_ARCH)-system-setup -- --sign" "gulp vscode-win32-$(VSCODE_ARCH)-user-setup -- --sign" } - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { npm run -- gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } - $SetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeUserSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" - mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $SetupPath - echo "##vso[task.setvariable variable=USER_SETUP_PATH]$SetupPath" + $SystemSetupPath = ".build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + $UserSetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeUserSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + + mv .build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe $SystemSetupPath + mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $UserSetupPath + + echo "##vso[task.setvariable variable=SYSTEM_SETUP_PATH]$SystemSetupPath" + echo "##vso[task.setvariable variable=USER_SETUP_PATH]$UserSetupPath" env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) - displayName: Build user setup + displayName: Build setup packages (system, user) - powershell: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) diff --git a/build/azure-pipelines/win32/sdl-scan-win32.yml b/build/azure-pipelines/win32/sdl-scan-win32.yml index def3cb53..bf6819a4 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/buildfile.js b/build/buildfile.js index 683e20fc..9430fb3d 100644 --- a/build/buildfile.js +++ b/build/buildfile.js @@ -5,31 +5,22 @@ /** * @param {string} name - * @param {string[]=} exclude * @returns {import('./lib/bundle').IEntryPoint} */ -function createModuleDescription(name, exclude) { +function createModuleDescription(name) { return { - name, - exclude + name }; } -/** - * @param {string} name - */ -function createEditorWorkerModuleDescription(name) { - return createModuleDescription(name, ['vs/base/common/worker/simpleWorker', 'vs/editor/common/services/editorSimpleWorker']); -} - -exports.workerEditor = createEditorWorkerModuleDescription('vs/editor/common/services/editorSimpleWorkerMain'); -exports.workerExtensionHost = createEditorWorkerModuleDescription('vs/workbench/api/worker/extensionHostWorkerMain'); -exports.workerNotebook = createEditorWorkerModuleDescription('vs/workbench/contrib/notebook/common/services/notebookSimpleWorkerMain'); -exports.workerLanguageDetection = createEditorWorkerModuleDescription('vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorkerMain'); -exports.workerLocalFileSearch = createEditorWorkerModuleDescription('vs/workbench/services/search/worker/localFileSearchMain'); -exports.workerProfileAnalysis = createEditorWorkerModuleDescription('vs/platform/profiling/electron-sandbox/profileAnalysisWorkerMain'); -exports.workerOutputLinks = createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputerMain'); -exports.workerBackgroundTokenization = createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.workerMain'); +exports.workerEditor = createModuleDescription('vs/editor/common/services/editorWebWorkerMain'); +exports.workerExtensionHost = createModuleDescription('vs/workbench/api/worker/extensionHostWorkerMain'); +exports.workerNotebook = createModuleDescription('vs/workbench/contrib/notebook/common/services/notebookWebWorkerMain'); +exports.workerLanguageDetection = createModuleDescription('vs/workbench/services/languageDetection/browser/languageDetectionWebWorkerMain'); +exports.workerLocalFileSearch = createModuleDescription('vs/workbench/services/search/worker/localFileSearchMain'); +exports.workerProfileAnalysis = createModuleDescription('vs/platform/profiling/electron-sandbox/profileAnalysisWorkerMain'); +exports.workerOutputLinks = createModuleDescription('vs/workbench/contrib/output/common/outputLinkComputerMain'); +exports.workerBackgroundTokenization = createModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.workerMain'); exports.workbenchDesktop = [ createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index d58fe966..d390d7a2 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -72,4 +72,4 @@ f7db8ebe91a1cc8d24ef6aad12949a18d8e4975ac296e3e5e9ecd88c9bccb143 *mksnapshot-v34 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 \ No newline at end of file +8168bfbf61882cfac80aed1e71e364e1c7f2fccd11eac298e6abade8b46894ea *mksnapshot-v34.3.2-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index d00d52bc..d394605d 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 0b5f38c6..67182b07 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 535d46eb..7d3f9164 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 9e013cdb..7872eccc 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/darwin/sign.js b/build/darwin/sign.js index 2dbc5702..dff30fd0 100644 --- a/build/darwin/sign.js +++ b/build/darwin/sign.js @@ -54,11 +54,9 @@ async function main(buildDir) { ...defaultOpts, // TODO(deepak1556): Incorrectly declared type in electron-osx-sign ignore: (filePath) => { - const ext = path_1.default.extname(filePath); return filePath.includes(gpuHelperAppName) || filePath.includes(rendererHelperAppName) || - filePath.includes(pluginHelperAppName) || - ext === '.asar' || ext === '.dat' || ext === '.gif' || ext === '.icns' || ext === '.ico' || ext === '.json' || ext === '.mp3' || ext === '.nib' || ext === '.pak' || ext === '.png' || ext === '.scpt' || ext === '.ttf' || ext === '.wasm' || ext === '.woff' || ext === '.woff2'; + filePath.includes(pluginHelperAppName); } }; const gpuHelperOpts = { @@ -83,7 +81,7 @@ async function main(buildDir) { // universal will get its copy from the x64 build. if (arch !== 'universal') { await (0, cross_spawn_promise_1.spawn)('plutil', [ - '-replace', // Void changed this to replace + '-insert', 'NSAppleEventsUsageDescription', '-string', 'An application in Visual Studio Code wants to use AppleScript.', diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts index cff07c75..ecf16274 100644 --- a/build/darwin/sign.ts +++ b/build/darwin/sign.ts @@ -58,11 +58,9 @@ async function main(buildDir?: string): Promise { ...defaultOpts, // TODO(deepak1556): Incorrectly declared type in electron-osx-sign ignore: (filePath: string) => { - const ext = path.extname(filePath); return filePath.includes(gpuHelperAppName) || filePath.includes(rendererHelperAppName) || - filePath.includes(pluginHelperAppName) || - ext === '.asar' || ext === '.dat' || ext === '.gif' || ext === '.icns' || ext === '.ico' || ext === '.json' || ext === '.mp3' || ext === '.nib' || ext === '.pak' || ext === '.png' || ext === '.scpt' || ext === '.ttf' || ext === '.wasm' || ext === '.woff' || ext === '.woff2'; + filePath.includes(pluginHelperAppName); } }; @@ -91,7 +89,7 @@ async function main(buildDir?: string): Promise { // universal will get its copy from the x64 build. if (arch !== 'universal') { await spawn('plutil', [ - '-replace', // Void changed this to replace + '-insert', 'NSAppleEventsUsageDescription', '-string', 'An application in Visual Studio Code wants to use AppleScript.', diff --git a/build/filters.js b/build/filters.js index 4d19dcb1..4f18bf66 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/**', @@ -79,6 +80,7 @@ module.exports.indentationFilter = [ '!src/vs/base/node/terminateProcess.sh', '!src/vs/base/node/cpuUsage.sh', '!src/vs/editor/common/languages/highlights/*.scm', + '!src/vs/editor/common/languages/injections/*.scm', '!test/unit/assert.js', '!resources/linux/snap/electron-launch', '!build/ext.js', @@ -90,6 +92,7 @@ module.exports.indentationFilter = [ '!test/monaco/out/**', '!test/smoke/out/**', '!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/**', @@ -137,9 +140,6 @@ module.exports.indentationFilter = [ '!extensions/ipynb/notebook-out/**', '!extensions/notebook-renderers/renderer-out/*.js', '!extensions/simple-browser/media/*.js', - - '!extensions/open-remote-ssh/out/*.js', // Void added this - '!extensions/open-remote-wsl/out/*.js', // Void added this ]; module.exports.copyrightFilter = [ @@ -198,6 +198,7 @@ module.exports.tsFormattingFilter = [ '!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 = [ @@ -205,6 +206,7 @@ module.exports.eslintFilter = [ '**/*.cjs', '**/*.mjs', '**/*.ts', + '.eslint-plugin-local/**/*.ts', ...readFileSync(join(__dirname, '..', '.eslint-ignore')) .toString() .split(/\r\n|\n/) diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index e40b05f8..0c0a024c 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -24,12 +24,12 @@ function makeCompileBuildTask(disableMangle) { ); } -// Full compile, including nls and inline sources in sourcemaps, mangling, minification, for build -const compileBuildTask = task.define('compile-build', makeCompileBuildTask(false)); -gulp.task(compileBuildTask); -exports.compileBuildTask = compileBuildTask; +// Local/PR compile, including nls and inline sources in sourcemaps, minification, no mangling +const compileBuildWithoutManglingTask = task.define('compile-build-without-mangling', makeCompileBuildTask(true)); +gulp.task(compileBuildWithoutManglingTask); +exports.compileBuildWithoutManglingTask = compileBuildWithoutManglingTask; -// Full compile for PR ci, e.g no mangling -const compileBuildTaskPullRequest = task.define('compile-build-pr', makeCompileBuildTask(true)); -gulp.task(compileBuildTaskPullRequest); -exports.compileBuildTaskPullRequest = compileBuildTaskPullRequest; +// CI compile, including nls and inline sources in sourcemaps, mangling, minification, for build +const compileBuildWithManglingTask = task.define('compile-build-with-mangling', makeCompileBuildTask(false)); +gulp.task(compileBuildWithManglingTask); +exports.compileBuildWithManglingTask = compileBuildWithManglingTask; diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 9f7ea574..4787605d 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +//@ts-check + const gulp = require('gulp'); const path = require('path'); const util = require('./lib/util'); const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); -const optimize = require('./lib/optimize'); const es = require('event-stream'); const File = require('vinyl'); const i18n = require('./lib/i18n'); @@ -17,39 +18,13 @@ const cp = require('child_process'); const compilation = require('./lib/compilation'); const monacoapi = require('./lib/monaco-api'); const fs = require('fs'); +const filter = require('gulp-filter'); const root = path.dirname(__dirname); const sha1 = getVersion(root); const semver = require('./monaco/package.json').version; const headerVersion = semver + '(' + sha1 + ')'; -// Build - -const editorEntryPoints = [ - { - name: 'vs/editor/editor.main', - include: [], - exclude: ['vs/css'], - prepend: [ - { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' } - ], - }, - { - name: 'vs/base/common/worker/simpleWorker', - include: ['vs/editor/common/services/editorSimpleWorker'], - exclude: [], - prepend: [ - { path: 'vs/loader.js' }, - { path: 'vs/base/worker/workerMain.js' } - ], - dest: 'vs/base/worker/workerMain.js' - } -]; - -const editorResources = [ - 'out-editor-build/vs/base/browser/ui/codicons/**/*.ttf' -]; - const BUNDLED_FILE_HEADER = [ '/*!-----------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -60,8 +35,6 @@ const BUNDLED_FILE_HEADER = [ '' ].join('\n'); -const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); - const extractEditorSrcTask = task.define('extract-editor-src', () => { const apiusages = monacoapi.execute().usageContent; const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); @@ -69,128 +42,43 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { sourcesRoot: path.join(root, 'src'), entryPoints: [ 'vs/editor/editor.main', - 'vs/editor/editor.worker', - 'vs/base/worker/workerMain', + 'vs/editor/editor.worker.start', + 'vs/editor/common/services/editorWebWorkerMain', ], inlineEntryPoints: [ apiusages, extrausages ], + typings: [], shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers importIgnorePattern: /\.css$/, destRoot: path.join(root, 'out-editor-src'), + tsOutDir: '../out-monaco-editor-core/esm/vs', redirects: { '@vscode/tree-sitter-wasm': '../node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter', } }); }); -// Disable mangling for the editor, as it complicates debugging & quite a few users rely on private/protected fields. -// Disable NLS task to remove english strings to preserve backwards compatibility when we removed the `vs/nls!` AMD plugin. -const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true, preserveEnglish: true })); - -const bundleEditorAMDTask = task.define('bundle-editor-amd', optimize.bundleTask( - { - out: 'out-editor', - esm: { - src: 'out-editor-build', - entryPoints: editorEntryPoints, - resources: editorResources - } - } -)); - -const minifyEditorAMDTask = task.define('minify-editor-amd', optimize.minifyTask('out-editor')); - -const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => { - standalone.createESMSourcesAndResources2({ - srcFolder: './out-editor-src', - outFolder: './out-editor-esm', - outResourcesFolder: './out-monaco-editor-core/esm', - ignores: [ - 'inlineEntryPoint:0.ts', - 'inlineEntryPoint:1.ts', - 'vs/loader.js', - 'vs/base/worker/workerMain.ts', - ], - renames: { - } - }); -}); - const compileEditorESMTask = task.define('compile-editor-esm', () => { - const KEEP_PREV_ANALYSIS = false; - const FAIL_ON_PURPOSE = false; - console.log(`Launching the TS compiler at ${path.join(__dirname, '../out-editor-esm')}...`); - let result; - if (process.platform === 'win32') { - result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, { - cwd: path.join(__dirname, '../out-editor-esm'), - shell: true - }); - } else { - result = cp.spawnSync(`node`, [`../node_modules/.bin/tsc`], { - cwd: path.join(__dirname, '../out-editor-esm') - }); - } - console.log(result.stdout.toString()); - console.log(result.stderr.toString()); + const src = 'out-editor-src'; + const out = 'out-monaco-editor-core/esm'; - if (FAIL_ON_PURPOSE || result.status !== 0) { - console.log(`The TS Compilation failed, preparing analysis folder...`); - const destPath = path.join(__dirname, '../../vscode-monaco-editor-esm-analysis'); - const keepPrevAnalysis = (KEEP_PREV_ANALYSIS && fs.existsSync(destPath)); - const cleanDestPath = (keepPrevAnalysis ? Promise.resolve() : util.rimraf(destPath)()); - return cleanDestPath.then(() => { - // build a list of files to copy - const files = util.rreddir(path.join(__dirname, '../out-editor-esm')); + const compile = compilation.createCompile(src, { build: true, emitError: true, transpileOnly: false, preserveEnglish: true }); + const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); - if (!keepPrevAnalysis) { - fs.mkdirSync(destPath); - - // initialize a new repository - cp.spawnSync(`git`, [`init`], { - cwd: destPath - }); - - // copy files from src - for (const file of files) { - const srcFilePath = path.join(__dirname, '../src', file); - const dstFilePath = path.join(destPath, file); - if (fs.existsSync(srcFilePath)) { - util.ensureDir(path.dirname(dstFilePath)); - const contents = fs.readFileSync(srcFilePath).toString().replace(/\r\n|\r|\n/g, '\n'); - fs.writeFileSync(dstFilePath, contents); - } - } - - // create an initial commit to diff against - cp.spawnSync(`git`, [`add`, `.`], { - cwd: destPath - }); - - // create the commit - cp.spawnSync(`git`, [`commit`, `-m`, `"original sources"`, `--no-gpg-sign`], { - cwd: destPath - }); - } - - // copy files from tree shaken src - for (const file of files) { - const srcFilePath = path.join(__dirname, '../out-editor-src', file); - const dstFilePath = path.join(destPath, file); - if (fs.existsSync(srcFilePath)) { - util.ensureDir(path.dirname(dstFilePath)); - const contents = fs.readFileSync(srcFilePath).toString().replace(/\r\n|\r|\n/g, '\n'); - fs.writeFileSync(dstFilePath, contents); - } - } - - console.log(`Open in VS Code the folder at '${destPath}' and you can analyze the compilation error`); - throw new Error('Standalone Editor compilation failed. If this is the build machine, simply launch `npm run gulp editor-distro` on your machine to further analyze the compilation problem.'); - }); - } + return ( + srcPipe + .pipe(compile()) + .pipe(i18n.processNlsFiles({ + out, + fileHeader: BUNDLED_FILE_HEADER, + languages: i18n.defaultLanguages, + })) + .pipe(filter(['**', '!**/inlineEntryPoint*', '!**/tsconfig.json', '!**/loader.js'])) + .pipe(gulp.dest(out)) + ); }); /** @@ -239,18 +127,6 @@ function toExternalDTS(contents) { return lines.join('\n').replace(/\n\n\n+/g, '\n\n'); } -/** - * @param {{ (path: string): boolean }} testFunc - */ -function filterStream(testFunc) { - return es.through(function (data) { - if (!testFunc(data.relative)) { - return; - } - this.emit('data', data); - }); -} - const finalEditorResourcesTask = task.define('final-editor-resources', () => { return es.merge( // other assets @@ -299,42 +175,6 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { })); })) .pipe(gulp.dest('out-monaco-editor-core')), - - // dev folder - es.merge( - gulp.src('out-editor/**/*') - ).pipe(gulp.dest('out-monaco-editor-core/dev')), - - // min folder - es.merge( - gulp.src('out-editor-min/**/*') - ).pipe(filterStream(function (path) { - // no map files - return !/(\.js\.map$)|(nls\.metadata\.json$)|(bundleInfo\.json$)/.test(path); - })).pipe(es.through(function (data) { - // tweak the sourceMappingURL - if (!/\.js$/.test(data.path)) { - this.emit('data', data); - return; - } - - const relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map')); - - let strContents = data.contents.toString(); - const newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); - strContents = strContents.replace(/\/\/# sourceMappingURL=[^ ]+$/, newStr); - - data.contents = Buffer.from(strContents); - this.emit('data', data); - })).pipe(gulp.dest('out-monaco-editor-core/min')), - - // min-maps folder - es.merge( - gulp.src('out-editor-min/**/*') - ).pipe(filterStream(function (path) { - // no map files - return /\.js\.map$/.test(path); - })).pipe(gulp.dest('out-monaco-editor-core/min-maps')) ); }); @@ -349,38 +189,11 @@ gulp.task('editor-distro', task.series( task.parallel( util.rimraf('out-editor-src'), - util.rimraf('out-editor-build'), - util.rimraf('out-editor-esm'), - util.rimraf('out-monaco-editor-core'), - util.rimraf('out-editor'), - util.rimraf('out-editor-min') - ), - extractEditorSrcTask, - task.parallel( - task.series( - compileEditorAMDTask, - bundleEditorAMDTask, - minifyEditorAMDTask - ), - task.series( - createESMSourcesAndResourcesTask, - compileEditorESMTask, - ) - ), - finalEditorResourcesTask - ) -); - -gulp.task('editor-esm', - task.series( - task.parallel( - util.rimraf('out-editor-src'), - util.rimraf('out-editor-esm'), util.rimraf('out-monaco-editor-core'), ), extractEditorSrcTask, - createESMSourcesAndResourcesTask, compileEditorESMTask, + finalEditorResourcesTask ) ); @@ -393,6 +206,9 @@ gulp.task('monacodts', task.define('monacodts', () => { //#region monaco type checking +/** + * @param {boolean} watch + */ function createTscCompileTask(watch) { return () => { const createReporter = require('./lib/reporter').createReporter; @@ -421,6 +237,7 @@ function createTscCompileTask(watch) { report = reporter.end(false); } else if (str.indexOf('Compilation complete') >= 0) { + // @ts-ignore report.end(); } else if (str) { diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 0a553366..f05738fa 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -71,10 +71,6 @@ const compilations = [ '.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json', '.vscode/extensions/vscode-selfhost-import-aid/tsconfig.json', - - 'extensions/open-remote-ssh/tsconfig.json', // Void added this - 'extensions/open-remote-wsl/tsconfig.json', // Void added this - ]; const getBaseUrl = out => `https://main.vscode-cdn.net/sourcemaps/${commit}/${out}`; diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index b4b7f49e..c1d64c00 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -26,7 +26,7 @@ const gunzip = require('gulp-gunzip'); const File = require('vinyl'); const fs = require('fs'); const glob = require('glob'); -const { compileBuildTask } = require('./gulpfile.compile'); +const { compileBuildWithManglingTask } = require('./gulpfile.compile'); const { cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions'); const { vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web'); const cp = require('child_process'); @@ -154,20 +154,9 @@ function getNodeChecksum(expectedName) { } function extractAlpinefromDocker(nodeVersion, platform, arch) { - let imageName = 'node'; - let dockerPlatform = ''; - - if (arch === 'arm64') { - imageName = 'arm64v8/node'; - - const architecture = cp.execSync(`docker info --format '{{json .Architecture}}'`, { encoding: 'utf8' }).trim(); - if (architecture !== '"aarch64"') { - dockerPlatform = '--platform=linux/arm64'; - } - } - + const imageName = arch === 'arm64' ? 'arm64v8/node' : 'node'; log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from docker image ${imageName}`); - const contents = cp.execSync(`docker run --rm ${dockerPlatform} ${imageName}:${nodeVersion}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' }); + const contents = cp.execSync(`docker run --rm ${imageName}:${nodeVersion}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' }); return es.readArray([new File({ path: 'node', contents, stat: { mode: parseInt('755', 8) } })]); } @@ -319,11 +308,10 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa } const name = product.nameShort; - const release = packageJson.release; let packageJsonContents; const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' }) - .pipe(json({ name, version, release, dependencies: undefined, optionalDependencies: undefined, type: 'module' })) + .pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined, type: 'module' })) .pipe(es.through(function (file) { packageJsonContents = file.contents.toString(); this.emit('data', file); @@ -331,7 +319,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json({ commit, date: readISODate('out-build'), version, release })) + .pipe(json({ commit, date: readISODate('out-build'), version })) .pipe(es.through(function (file) { productJsonContents = file.contents.toString(); this.emit('data', file); @@ -414,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`)) @@ -491,7 +473,7 @@ function tweakProductForServerWeb(product) { gulp.task(serverTaskCI); const serverTask = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( - compileBuildTask, + compileBuildWithManglingTask, cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask, diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index f34954fa..7046ee00 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -30,7 +30,7 @@ const { getProductionDependencies } = require('./lib/dependencies'); const { config } = require('./lib/electron'); const createAsar = require('./lib/asar').createAsar; const minimist = require('minimist'); -const { compileBuildTask } = require('./gulpfile.compile'); +const { compileBuildWithoutManglingTask, compileBuildWithManglingTask } = require('./gulpfile.compile'); const { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask } = require('./gulpfile.extensions'); const { promisify } = require('util'); const glob = promisify(require('glob')); @@ -101,6 +101,9 @@ const vscodeResourceIncludes = [ // Tree Sitter highlights 'out-build/vs/editor/common/languages/highlights/*.scm', + + // Tree Sitter injection queries + 'out-build/vs/editor/common/languages/injections/*.scm', ]; const vscodeResources = [ @@ -166,25 +169,25 @@ const minifyVSCodeTask = task.define('minify-vscode', task.series( )); gulp.task(minifyVSCodeTask); -const core = task.define('core-ci', task.series( - gulp.task('compile-build'), +const coreCI = task.define('core-ci', task.series( + gulp.task('compile-build-with-mangling'), task.parallel( gulp.task('minify-vscode'), gulp.task('minify-vscode-reh'), gulp.task('minify-vscode-reh-web'), ) )); -gulp.task(core); +gulp.task(coreCI); -const corePr = task.define('core-ci-pr', task.series( - gulp.task('compile-build-pr'), +const coreCIPR = task.define('core-ci-pr', task.series( + gulp.task('compile-build-without-mangling'), task.parallel( gulp.task('minify-vscode'), gulp.task('minify-vscode-reh'), gulp.task('minify-vscode-reh-web'), ) )); -gulp.task(corePr); +gulp.task(coreCIPR); /** * Compute checksums for some files. @@ -267,8 +270,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op } const name = product.nameShort; - const release = packageJson.release; - const packageJsonUpdates = { name, version, release }; + const packageJsonUpdates = { name, version }; if (platform === 'linux') { packageJsonUpdates.desktopName = `${product.applicationName}.desktop`; @@ -282,10 +284,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op this.emit('data', file); })); - // Void - this is important, creates the product.json in .app let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json({ commit, date: readISODate('out-build'), checksums, version, release })) + .pipe(json({ commit, date: readISODate('out-build'), checksums, version })) .pipe(es.through(function (file) { productJsonContents = file.contents.toString(); this.emit('data', file); @@ -373,9 +374,10 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op } else if (platform === 'darwin') { const shortcut = gulp.src('resources/darwin/bin/code.sh') .pipe(replace('@@APPNAME@@', product.applicationName)) - .pipe(rename('bin/' + product.applicationName)); - - all = es.merge(all, shortcut); + .pipe(rename('bin/code')); + 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 @@ -502,7 +504,7 @@ BUILD_TARGETS.forEach(buildTarget => { gulp.task(vscodeTaskCI); const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( - compileBuildTask, + minified ? compileBuildWithManglingTask : compileBuildWithoutManglingTask, cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask, @@ -540,7 +542,7 @@ const innoSetupConfig = { gulp.task(task.define( 'vscode-translations-export', task.series( - core, + coreCI, compileAllExtensionsBuildTask, function () { const pathToMetadata = './out-build/nls.metadata.json'; diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 620548ec..cd8610da 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -25,6 +25,7 @@ const exec = util.promisify(cp.exec); const root = path.dirname(__dirname); const commit = getVersion(root); +const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); /** * @param {string} arch @@ -38,7 +39,9 @@ function prepareDebPackage(arch) { const debArch = getDebPackageArch(arch); const destination = '.build/linux/deb/' + debArch + '/' + product.applicationName + '-' + debArch; - return function () { + return async function () { + const dependencies = await dependenciesGenerator.getDependencies('deb', binaryDir, product.applicationName, debArch); + const desktop = gulp.src('resources/linux/code.desktop', { base: '.' }) .pipe(rename('usr/share/applications/' + product.applicationName + '.desktop')); @@ -81,12 +84,11 @@ function prepareDebPackage(arch) { let size = 0; const control = code.pipe(es.through( function (f) { size += f.isDirectory() ? 4096 : f.contents.length; }, - async function () { + function () { const that = this; - const dependencies = await dependenciesGenerator.getDependencies('deb', binaryDir, product.applicationName, debArch); gulp.src('resources/linux/debian/control.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`)) + .pipe(replace('@@VERSION@@', packageJson.version + '-' + linuxPackageRevision)) .pipe(replace('@@ARCHITECTURE@@', debArch)) .pipe(replace('@@DEPENDS@@', dependencies.join(', '))) .pipe(replace('@@RECOMMENDS@@', debianRecommendedDependencies.join(', '))) @@ -153,7 +155,9 @@ function prepareRpmPackage(arch) { const rpmArch = getRpmPackageArch(arch); const stripBinary = process.env['STRIP'] ?? '/usr/bin/strip'; - return function () { + return async function () { + const dependencies = await dependenciesGenerator.getDependencies('rpm', binaryDir, product.applicationName, rpmArch); + const desktop = gulp.src('resources/linux/code.desktop', { base: '.' }) .pipe(rename('BUILD/usr/share/applications/' + product.applicationName + '.desktop')); @@ -193,24 +197,19 @@ function prepareRpmPackage(arch) { const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'BUILD/usr/share/' + product.applicationName + '/' + p.dirname; })); - const spec = code.pipe(es.through( - async function () { - const that = this; - const dependencies = await dependenciesGenerator.getDependencies('rpm', binaryDir, product.applicationName, rpmArch); - gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) - .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@NAME_LONG@@', product.nameLong)) - .pipe(replace('@@ICON@@', product.linuxIconName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`)) - .pipe(replace('@@ARCHITECTURE@@', rpmArch)) - .pipe(replace('@@LICENSE@@', product.licenseName)) - .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) - .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) - .pipe(replace('@@DEPENDENCIES@@', dependencies.join(', '))) - .pipe(replace('@@STRIP@@', stripBinary)) - .pipe(rename('SPECS/' + product.applicationName + '.spec')) - .pipe(es.through(function (f) { that.emit('data', f); }, function () { that.emit('end'); })); - })); + const spec = gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@ICON@@', product.linuxIconName)) + .pipe(replace('@@VERSION@@', packageJson.version)) + .pipe(replace('@@RELEASE@@', linuxPackageRevision)) + .pipe(replace('@@ARCHITECTURE@@', rpmArch)) + .pipe(replace('@@LICENSE@@', product.licenseName)) + .pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@')) + .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) + .pipe(replace('@@DEPENDENCIES@@', dependencies.join(', '))) + .pipe(replace('@@STRIP@@', stripBinary)) + .pipe(rename('SPECS/' + product.applicationName + '.spec')); const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) .pipe(rename('SOURCES/' + product.applicationName + '.xpm')); @@ -277,7 +276,7 @@ function prepareSnapPackage(arch) { const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}.${packageJson.release}`)) + .pipe(replace('@@VERSION@@', commit.substr(0, 8))) // Possible run-on values https://snapcraft.io/docs/architectures .pipe(replace('@@ARCHITECTURE@@', arch === 'x64' ? 'amd64' : arch)) .pipe(rename('snap/snapcraft.yaml')); diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 02b17022..2828f507 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -19,7 +19,7 @@ const filter = require('gulp-filter'); const { getProductionDependencies } = require('./lib/dependencies'); const vfs = require('vinyl-fs'); const packageJson = require('../package.json'); -const { compileBuildTask } = require('./gulpfile.compile'); +const { compileBuildWithManglingTask } = require('./gulpfile.compile'); const extensions = require('./lib/extensions'); const VinylFile = require('vinyl'); @@ -52,6 +52,9 @@ const vscodeWebResourceIncludes = [ // Tree Sitter highlights 'out-build/vs/editor/common/languages/highlights/*.scm', + // Tree Sitter injections + 'out-build/vs/editor/common/languages/injections/*.scm', + // Extension Host Worker 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html', ]; @@ -223,7 +226,7 @@ const dashed = (/** @type {string} */ str) => (str ? `-${str}` : ``); gulp.task(vscodeWebTaskCI); const vscodeWebTask = task.define(`vscode-web${dashed(minified)}`, task.series( - compileBuildTask, + compileBuildWithManglingTask, vscodeWebTaskCI )); gulp.task(vscodeWebTask); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index afacbe34..98175f53 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -87,8 +87,8 @@ function buildWin32Setup(arch, target) { NameLong: product.nameLong, NameShort: product.nameShort, DirName: product.win32DirName, - Version: `${pkg.version}.${pkg.release}`, - RawVersion: `${pkg.version.replace(/-\w+$/, '')}.${pkg.release}`, + Version: pkg.version, + RawVersion: pkg.version.replace(/-\w+$/, ''), NameVersion: product.win32NameVersion + (target === 'user' ? ' (User)' : ''), ExeBasename: product.nameShort, RegValueName: product.win32RegValueName, diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index ba9c134a..249777c4 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -85,7 +85,10 @@ function getExtensionDownloadStream(extension) { if (extension.vsix) { input = ext.fromVsix(path_1.default.join(root, extension.vsix), extension); } - else { // Void - ext-from-gh.patch + else if (productjson.extensionsGallery?.serviceUrl) { + input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension); + } + else { input = ext.fromGithub(extension); } return input.pipe((0, gulp_rename_1.default)(p => p.dirname = `${extension.name}/${p.dirname}`)); diff --git a/build/lib/builtInExtensions.ts b/build/lib/builtInExtensions.ts index 6d210d0f..e9a1180c 100644 --- a/build/lib/builtInExtensions.ts +++ b/build/lib/builtInExtensions.ts @@ -73,7 +73,9 @@ function getExtensionDownloadStream(extension: IExtensionDefinition) { if (extension.vsix) { input = ext.fromVsix(path.join(root, extension.vsix), extension); - } else { // Void - ext-from-gh.patch + } else if (productjson.extensionsGallery?.serviceUrl) { + input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension); + } else { input = ext.fromGithub(extension); } diff --git a/build/lib/bundle.js b/build/lib/bundle.js index f1490f4a..382b648d 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -3,229 +3,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); -exports.bundle = bundle; exports.removeAllTSBoilerplate = removeAllTSBoilerplate; -const fs_1 = __importDefault(require("fs")); -const path_1 = __importDefault(require("path")); -const vm_1 = __importDefault(require("vm")); -/** - * Bundle `entryPoints` given config `config`. - */ -function bundle(entryPoints, config, callback) { - const entryPointsMap = {}; - entryPoints.forEach((module) => { - if (entryPointsMap[module.name]) { - throw new Error(`Cannot have two entry points with the same name '${module.name}'`); - } - entryPointsMap[module.name] = module; - }); - const allMentionedModulesMap = {}; - entryPoints.forEach((module) => { - allMentionedModulesMap[module.name] = true; - module.include?.forEach(function (includedModule) { - allMentionedModulesMap[includedModule] = true; - }); - module.exclude?.forEach(function (excludedModule) { - allMentionedModulesMap[excludedModule] = true; - }); - }); - const code = require('fs').readFileSync(path_1.default.join(__dirname, '../../src/vs/loader.js')); - const r = vm_1.default.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); - const loaderModule = { exports: {} }; - r.call({}, require, loaderModule, loaderModule.exports); - const loader = loaderModule.exports; - config.isBuild = true; - config.paths = config.paths || {}; - if (!config.paths['vs/css']) { - config.paths['vs/css'] = 'out-build/vs/css.build'; - } - config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; - config.buildForceInvokeFactory['vs/css'] = true; - loader.config(config); - loader(['require'], (localRequire) => { - const resolvePath = (entry) => { - let r = localRequire.toUrl(entry.path); - if (!r.endsWith('.js')) { - r += '.js'; - } - // avoid packaging the build version of plugins: - r = r.replace('vs/css.build.js', 'vs/css.js'); - return { path: r, amdModuleId: entry.amdModuleId }; - }; - for (const moduleId in entryPointsMap) { - const entryPoint = entryPointsMap[moduleId]; - if (entryPoint.prepend) { - entryPoint.prepend = entryPoint.prepend.map(resolvePath); - } - } - }); - loader(Object.keys(allMentionedModulesMap), () => { - const modules = loader.getBuildInfo(); - const partialResult = emitEntryPoints(modules, entryPointsMap); - const cssInlinedResources = loader('vs/css').getInlinedResources(); - callback(null, { - files: partialResult.files, - cssInlinedResources: cssInlinedResources, - bundleData: partialResult.bundleData - }); - }, (err) => callback(err, null)); -} -function emitEntryPoints(modules, entryPoints) { - const modulesMap = {}; - modules.forEach((m) => { - modulesMap[m.id] = m; - }); - const modulesGraph = {}; - modules.forEach((m) => { - modulesGraph[m.id] = m.dependencies; - }); - const sortedModules = topologicalSort(modulesGraph); - let result = []; - const usedPlugins = {}; - const bundleData = { - graph: modulesGraph, - bundles: {} - }; - Object.keys(entryPoints).forEach((moduleToBundle) => { - const info = entryPoints[moduleToBundle]; - const rootNodes = [moduleToBundle].concat(info.include || []); - const allDependencies = visit(rootNodes, modulesGraph); - const excludes = ['require', 'exports', 'module'].concat(info.exclude || []); - excludes.forEach((excludeRoot) => { - const allExcludes = visit([excludeRoot], modulesGraph); - Object.keys(allExcludes).forEach((exclude) => { - delete allDependencies[exclude]; - }); - }); - const includedModules = sortedModules.filter((module) => { - return allDependencies[module]; - }); - bundleData.bundles[moduleToBundle] = includedModules; - const res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend || [], info.dest); - result = result.concat(res.files); - for (const pluginName in res.usedPlugins) { - usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; - } - }); - Object.keys(usedPlugins).forEach((pluginName) => { - const plugin = usedPlugins[pluginName]; - if (typeof plugin.finishBuild === 'function') { - const write = (filename, contents) => { - result.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.finishBuild(write); - } - }); - return { - // TODO@TS 2.1.2 - files: extractStrings(removeAllDuplicateTSBoilerplate(result)), - bundleData: bundleData - }; -} -function extractStrings(destFiles) { - const parseDefineCall = (moduleMatch, depsMatch) => { - const module = moduleMatch.replace(/^"|"$/g, ''); - let deps = depsMatch.split(','); - deps = deps.map((dep) => { - dep = dep.trim(); - dep = dep.replace(/^"|"$/g, ''); - dep = dep.replace(/^'|'$/g, ''); - let prefix = null; - let _path = null; - const pieces = dep.split('!'); - if (pieces.length > 1) { - prefix = pieces[0] + '!'; - _path = pieces[1]; - } - else { - prefix = ''; - _path = pieces[0]; - } - if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { - const res = path_1.default.join(path_1.default.dirname(module), _path).replace(/\\/g, '/'); - return prefix + res; - } - return prefix + _path; - }); - return { - module: module, - deps: deps - }; - }; - destFiles.forEach((destFile) => { - if (!/\.js$/.test(destFile.dest)) { - return; - } - if (/\.nls\.js$/.test(destFile.dest)) { - return; - } - // Do one pass to record the usage counts for each module id - const useCounts = {}; - destFile.sources.forEach((source) => { - const matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); - if (!matches) { - return; - } - const defineCall = parseDefineCall(matches[1], matches[2]); - useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; - defineCall.deps.forEach((dep) => { - useCounts[dep] = (useCounts[dep] || 0) + 1; - }); - }); - const sortedByUseModules = Object.keys(useCounts); - sortedByUseModules.sort((a, b) => { - return useCounts[b] - useCounts[a]; - }); - const replacementMap = {}; - sortedByUseModules.forEach((module, index) => { - replacementMap[module] = index; - }); - destFile.sources.forEach((source) => { - source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, (_, moduleMatch, depsMatch) => { - const defineCall = parseDefineCall(moduleMatch, depsMatch); - return `define(__m[${replacementMap[defineCall.module]}/*${defineCall.module}*/], __M([${defineCall.deps.map(dep => replacementMap[dep] + '/*' + dep + '*/').join(',')}])`; - }); - }); - destFile.sources.unshift({ - path: null, - contents: [ - '(function() {', - `var __m = ${JSON.stringify(sortedByUseModules)};`, - `var __M = function(deps) {`, - ` var result = [];`, - ` for (var i = 0, len = deps.length; i < len; i++) {`, - ` result[i] = __m[deps[i]];`, - ` }`, - ` return result;`, - `};` - ].join('\n') - }); - destFile.sources.push({ - path: null, - contents: '}).call(this);' - }); - }); - return destFiles; -} -function removeAllDuplicateTSBoilerplate(destFiles) { - destFiles.forEach((destFile) => { - const SEEN_BOILERPLATE = []; - destFile.sources.forEach((source) => { - source.contents = removeDuplicateTSBoilerplate(source.contents, SEEN_BOILERPLATE); - }); - }); - return destFiles; -} function removeAllTSBoilerplate(source) { const seen = new Array(BOILERPLATE.length).fill(true, 0, BOILERPLATE.length); return removeDuplicateTSBoilerplate(source, seen); @@ -280,213 +59,4 @@ function removeDuplicateTSBoilerplate(source, SEEN_BOILERPLATE = []) { } return newLines.join('\n'); } -function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, dest) { - if (!dest) { - dest = entryPoint + '.js'; - } - const mainResult = { - sources: [], - dest: dest - }, results = [mainResult]; - const usedPlugins = {}; - const getLoaderPlugin = (pluginName) => { - if (!usedPlugins[pluginName]) { - usedPlugins[pluginName] = modulesMap[pluginName].exports; - } - return usedPlugins[pluginName]; - }; - includedModules.forEach((c) => { - const bangIndex = c.indexOf('!'); - if (bangIndex >= 0) { - const pluginName = c.substr(0, bangIndex); - const plugin = getLoaderPlugin(pluginName); - mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); - return; - } - const module = modulesMap[c]; - if (module.path === 'empty:') { - return; - } - const contents = readFileAndRemoveBOM(module.path); - if (module.shim) { - mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } - else if (module.defineLocation) { - mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); - } - else { - const moduleCopy = { - id: module.id, - path: module.path, - defineLocation: module.defineLocation, - dependencies: module.dependencies - }; - throw new Error(`Cannot bundle module '${module.id}' for entry point '${entryPoint}' because it has no shim and it lacks a defineLocation: ${JSON.stringify(moduleCopy)}`); - } - }); - Object.keys(usedPlugins).forEach((pluginName) => { - const plugin = usedPlugins[pluginName]; - if (typeof plugin.writeFile === 'function') { - const req = (() => { - throw new Error('no-no!'); - }); - req.toUrl = something => something; - const write = (filename, contents) => { - results.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.writeFile(pluginName, entryPoint, req, write, {}); - } - }); - const toIFile = (entry) => { - let contents = readFileAndRemoveBOM(entry.path); - if (entry.amdModuleId) { - contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); - } - return { - path: entry.path, - contents: contents - }; - }; - const toPrepend = (prepend || []).map(toIFile); - mainResult.sources = toPrepend.concat(mainResult.sources); - return { - files: results, - usedPlugins: usedPlugins - }; -} -function readFileAndRemoveBOM(path) { - const BOM_CHAR_CODE = 65279; - let contents = fs_1.default.readFileSync(path, 'utf8'); - // Remove BOM - if (contents.charCodeAt(0) === BOM_CHAR_CODE) { - contents = contents.substring(1); - } - return contents; -} -function emitPlugin(entryPoint, plugin, pluginName, moduleName) { - let result = ''; - if (typeof plugin.write === 'function') { - const write = ((what) => { - result += what; - }); - write.getEntryPoint = () => { - return entryPoint; - }; - write.asModule = (moduleId, code) => { - code = code.replace(/^define\(/, 'define("' + moduleId + '",'); - result += code; - }; - plugin.write(pluginName, moduleName, write); - } - return { - path: null, - contents: result - }; -} -function emitNamedModule(moduleId, defineCallPosition, path, contents) { - // `defineCallPosition` is the position in code: |define() - const defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); - // `parensOffset` is the position in code: define|() - const parensOffset = contents.indexOf('(', defineCallOffset); - const insertStr = '"' + moduleId + '", '; - return { - path: path, - contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) - }; -} -function emitShimmedModule(moduleId, myDeps, factory, path, contents) { - const strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); - const strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; - return { - path: path, - contents: contents + '\n;\n' + strDefine - }; -} -/** - * Convert a position (line:col) to (offset) in string `str` - */ -function positionToOffset(str, desiredLine, desiredCol) { - if (desiredLine === 1) { - return desiredCol - 1; - } - let line = 1; - let lastNewLineOffset = -1; - do { - if (desiredLine === line) { - return lastNewLineOffset + 1 + desiredCol - 1; - } - lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); - line++; - } while (lastNewLineOffset >= 0); - return -1; -} -/** - * Return a set of reachable nodes in `graph` starting from `rootNodes` - */ -function visit(rootNodes, graph) { - const result = {}; - const queue = rootNodes; - rootNodes.forEach((node) => { - result[node] = true; - }); - while (queue.length > 0) { - const el = queue.shift(); - const myEdges = graph[el] || []; - myEdges.forEach((toNode) => { - if (!result[toNode]) { - result[toNode] = true; - queue.push(toNode); - } - }); - } - return result; -} -/** - * Perform a topological sort on `graph` - */ -function topologicalSort(graph) { - const allNodes = {}, outgoingEdgeCount = {}, inverseEdges = {}; - Object.keys(graph).forEach((fromNode) => { - allNodes[fromNode] = true; - outgoingEdgeCount[fromNode] = graph[fromNode].length; - graph[fromNode].forEach((toNode) => { - allNodes[toNode] = true; - outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; - inverseEdges[toNode] = inverseEdges[toNode] || []; - inverseEdges[toNode].push(fromNode); - }); - }); - // https://en.wikipedia.org/wiki/Topological_sorting - const S = [], L = []; - Object.keys(allNodes).forEach((node) => { - if (outgoingEdgeCount[node] === 0) { - delete outgoingEdgeCount[node]; - S.push(node); - } - }); - while (S.length > 0) { - // Ensure the exact same order all the time with the same inputs - S.sort(); - const n = S.shift(); - L.push(n); - const myInverseEdges = inverseEdges[n] || []; - myInverseEdges.forEach((m) => { - outgoingEdgeCount[m]--; - if (outgoingEdgeCount[m] === 0) { - delete outgoingEdgeCount[m]; - S.push(m); - } - }); - } - if (Object.keys(outgoingEdgeCount).length > 0) { - throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); - } - return L; -} //# sourceMappingURL=bundle.js.map \ No newline at end of file diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 68182e6b..708bb5e9 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -3,360 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import fs from 'fs'; -import path from 'path'; -import vm from 'vm'; - -interface IPosition { - line: number; - col: number; -} - -interface IBuildModuleInfo { - id: string; - path: string; - defineLocation: IPosition | null; - dependencies: string[]; - shim: string; - exports: any; -} - -interface IBuildModuleInfoMap { - [moduleId: string]: IBuildModuleInfo; -} - -interface ILoaderPlugin { - write(pluginName: string, moduleName: string, write: ILoaderPluginWriteFunc): void; - writeFile(pluginName: string, entryPoint: string, req: ILoaderPluginReqFunc, write: (filename: string, contents: string) => void, config: any): void; - finishBuild(write: (filename: string, contents: string) => void): void; -} - -interface ILoaderPluginWriteFunc { - (something: string): void; - getEntryPoint(): string; - asModule(moduleId: string, code: string): void; -} - -interface ILoaderPluginReqFunc { - (something: string): void; - toUrl(something: string): string; -} - -export interface IExtraFile { - path: string; - amdModuleId?: string; -} - export interface IEntryPoint { name: string; include?: string[]; - exclude?: string[]; - /** @deprecated unsupported by ESM */ - prepend?: IExtraFile[]; dest?: string; } -interface IEntryPointMap { - [moduleId: string]: IEntryPoint; -} - -export interface IGraph { - [node: string]: string[]; -} - -interface INodeSet { - [node: string]: boolean; -} - -export interface IFile { - path: string | null; - contents: string; -} - -export interface IConcatFile { - dest: string; - sources: IFile[]; -} - -export interface IBundleData { - graph: IGraph; - bundles: { [moduleId: string]: string[] }; -} - -export interface IBundleResult { - files: IConcatFile[]; - cssInlinedResources: string[]; - bundleData: IBundleData; -} - -interface IPartialBundleResult { - files: IConcatFile[]; - bundleData: IBundleData; -} - -export interface ILoaderConfig { - isBuild?: boolean; - paths?: { [path: string]: any }; - /* - * Normally, during a build, no module factories are invoked. This can be used - * to forcefully execute a module's factory. - */ - buildForceInvokeFactory: { - [moduleId: string]: boolean; - }; -} - -/** - * Bundle `entryPoints` given config `config`. - */ -export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callback: (err: any, result: IBundleResult | null) => void): void { - const entryPointsMap: IEntryPointMap = {}; - entryPoints.forEach((module: IEntryPoint) => { - if (entryPointsMap[module.name]) { - throw new Error(`Cannot have two entry points with the same name '${module.name}'`); - } - entryPointsMap[module.name] = module; - }); - - const allMentionedModulesMap: { [modules: string]: boolean } = {}; - entryPoints.forEach((module: IEntryPoint) => { - allMentionedModulesMap[module.name] = true; - module.include?.forEach(function (includedModule) { - allMentionedModulesMap[includedModule] = true; - }); - module.exclude?.forEach(function (excludedModule) { - allMentionedModulesMap[excludedModule] = true; - }); - }); - - - const code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); - const r: Function = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); - const loaderModule = { exports: {} }; - r.call({}, require, loaderModule, loaderModule.exports); - - const loader: any = loaderModule.exports; - config.isBuild = true; - config.paths = config.paths || {}; - if (!config.paths['vs/css']) { - config.paths['vs/css'] = 'out-build/vs/css.build'; - } - config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; - config.buildForceInvokeFactory['vs/css'] = true; - loader.config(config); - - loader(['require'], (localRequire: any) => { - const resolvePath = (entry: IExtraFile) => { - let r = localRequire.toUrl(entry.path); - if (!r.endsWith('.js')) { - r += '.js'; - } - // avoid packaging the build version of plugins: - r = r.replace('vs/css.build.js', 'vs/css.js'); - return { path: r, amdModuleId: entry.amdModuleId }; - }; - for (const moduleId in entryPointsMap) { - const entryPoint = entryPointsMap[moduleId]; - if (entryPoint.prepend) { - entryPoint.prepend = entryPoint.prepend.map(resolvePath); - } - } - }); - - loader(Object.keys(allMentionedModulesMap), () => { - const modules = loader.getBuildInfo(); - const partialResult = emitEntryPoints(modules, entryPointsMap); - const cssInlinedResources = loader('vs/css').getInlinedResources(); - callback(null, { - files: partialResult.files, - cssInlinedResources: cssInlinedResources, - bundleData: partialResult.bundleData - }); - }, (err: any) => callback(err, null)); -} - -function emitEntryPoints(modules: IBuildModuleInfo[], entryPoints: IEntryPointMap): IPartialBundleResult { - const modulesMap: IBuildModuleInfoMap = {}; - modules.forEach((m: IBuildModuleInfo) => { - modulesMap[m.id] = m; - }); - - const modulesGraph: IGraph = {}; - modules.forEach((m: IBuildModuleInfo) => { - modulesGraph[m.id] = m.dependencies; - }); - - const sortedModules = topologicalSort(modulesGraph); - - let result: IConcatFile[] = []; - const usedPlugins: IPluginMap = {}; - const bundleData: IBundleData = { - graph: modulesGraph, - bundles: {} - }; - - Object.keys(entryPoints).forEach((moduleToBundle: string) => { - const info = entryPoints[moduleToBundle]; - const rootNodes = [moduleToBundle].concat(info.include || []); - const allDependencies = visit(rootNodes, modulesGraph); - const excludes: string[] = ['require', 'exports', 'module'].concat(info.exclude || []); - - excludes.forEach((excludeRoot: string) => { - const allExcludes = visit([excludeRoot], modulesGraph); - Object.keys(allExcludes).forEach((exclude: string) => { - delete allDependencies[exclude]; - }); - }); - - const includedModules = sortedModules.filter((module: string) => { - return allDependencies[module]; - }); - - bundleData.bundles[moduleToBundle] = includedModules; - - const res = emitEntryPoint( - modulesMap, - modulesGraph, - moduleToBundle, - includedModules, - info.prepend || [], - info.dest - ); - - result = result.concat(res.files); - for (const pluginName in res.usedPlugins) { - usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; - } - }); - - Object.keys(usedPlugins).forEach((pluginName: string) => { - const plugin = usedPlugins[pluginName]; - if (typeof plugin.finishBuild === 'function') { - const write = (filename: string, contents: string) => { - result.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.finishBuild(write); - } - }); - - return { - // TODO@TS 2.1.2 - files: extractStrings(removeAllDuplicateTSBoilerplate(result)), - bundleData: bundleData - }; -} - -function extractStrings(destFiles: IConcatFile[]): IConcatFile[] { - const parseDefineCall = (moduleMatch: string, depsMatch: string) => { - const module = moduleMatch.replace(/^"|"$/g, ''); - let deps = depsMatch.split(','); - deps = deps.map((dep) => { - dep = dep.trim(); - dep = dep.replace(/^"|"$/g, ''); - dep = dep.replace(/^'|'$/g, ''); - let prefix: string | null = null; - let _path: string | null = null; - const pieces = dep.split('!'); - if (pieces.length > 1) { - prefix = pieces[0] + '!'; - _path = pieces[1]; - } else { - prefix = ''; - _path = pieces[0]; - } - - if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { - const res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); - return prefix + res; - } - return prefix + _path; - }); - return { - module: module, - deps: deps - }; - }; - - destFiles.forEach((destFile) => { - if (!/\.js$/.test(destFile.dest)) { - return; - } - if (/\.nls\.js$/.test(destFile.dest)) { - return; - } - - // Do one pass to record the usage counts for each module id - const useCounts: { [moduleId: string]: number } = {}; - destFile.sources.forEach((source) => { - const matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/); - if (!matches) { - return; - } - - const defineCall = parseDefineCall(matches[1], matches[2]); - useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1; - defineCall.deps.forEach((dep) => { - useCounts[dep] = (useCounts[dep] || 0) + 1; - }); - }); - - const sortedByUseModules = Object.keys(useCounts); - sortedByUseModules.sort((a, b) => { - return useCounts[b] - useCounts[a]; - }); - - const replacementMap: { [moduleId: string]: number } = {}; - sortedByUseModules.forEach((module, index) => { - replacementMap[module] = index; - }); - - destFile.sources.forEach((source) => { - source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, (_, moduleMatch, depsMatch) => { - const defineCall = parseDefineCall(moduleMatch, depsMatch); - return `define(__m[${replacementMap[defineCall.module]}/*${defineCall.module}*/], __M([${defineCall.deps.map(dep => replacementMap[dep] + '/*' + dep + '*/').join(',')}])`; - }); - }); - - destFile.sources.unshift({ - path: null, - contents: [ - '(function() {', - `var __m = ${JSON.stringify(sortedByUseModules)};`, - `var __M = function(deps) {`, - ` var result = [];`, - ` for (var i = 0, len = deps.length; i < len; i++) {`, - ` result[i] = __m[deps[i]];`, - ` }`, - ` return result;`, - `};` - ].join('\n') - }); - - destFile.sources.push({ - path: null, - contents: '}).call(this);' - }); - }); - return destFiles; -} - -function removeAllDuplicateTSBoilerplate(destFiles: IConcatFile[]): IConcatFile[] { - destFiles.forEach((destFile) => { - const SEEN_BOILERPLATE: boolean[] = []; - destFile.sources.forEach((source) => { - source.contents = removeDuplicateTSBoilerplate(source.contents, SEEN_BOILERPLATE); - }); - }); - - return destFiles; -} - export function removeAllTSBoilerplate(source: string) { const seen = new Array(BOILERPLATE.length).fill(true, 0, BOILERPLATE.length); return removeDuplicateTSBoilerplate(source, seen); @@ -411,273 +63,3 @@ function removeDuplicateTSBoilerplate(source: string, SEEN_BOILERPLATE: boolean[ } return newLines.join('\n'); } - -interface IPluginMap { - [moduleId: string]: ILoaderPlugin; -} - -interface IEmitEntryPointResult { - files: IConcatFile[]; - usedPlugins: IPluginMap; -} - -function emitEntryPoint( - modulesMap: IBuildModuleInfoMap, - deps: IGraph, - entryPoint: string, - includedModules: string[], - prepend: IExtraFile[], - dest: string | undefined -): IEmitEntryPointResult { - if (!dest) { - dest = entryPoint + '.js'; - } - const mainResult: IConcatFile = { - sources: [], - dest: dest - }, - results: IConcatFile[] = [mainResult]; - - const usedPlugins: IPluginMap = {}; - const getLoaderPlugin = (pluginName: string): ILoaderPlugin => { - if (!usedPlugins[pluginName]) { - usedPlugins[pluginName] = modulesMap[pluginName].exports; - } - return usedPlugins[pluginName]; - }; - - includedModules.forEach((c: string) => { - const bangIndex = c.indexOf('!'); - - if (bangIndex >= 0) { - const pluginName = c.substr(0, bangIndex); - const plugin = getLoaderPlugin(pluginName); - mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1))); - return; - } - - const module = modulesMap[c]; - - if (module.path === 'empty:') { - return; - } - - const contents = readFileAndRemoveBOM(module.path); - - if (module.shim) { - mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } else if (module.defineLocation) { - mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); - } else { - const moduleCopy = { - id: module.id, - path: module.path, - defineLocation: module.defineLocation, - dependencies: module.dependencies - }; - throw new Error(`Cannot bundle module '${module.id}' for entry point '${entryPoint}' because it has no shim and it lacks a defineLocation: ${JSON.stringify(moduleCopy)}`); - } - }); - - Object.keys(usedPlugins).forEach((pluginName: string) => { - const plugin = usedPlugins[pluginName]; - if (typeof plugin.writeFile === 'function') { - const req: ILoaderPluginReqFunc = (() => { - throw new Error('no-no!'); - }); - req.toUrl = something => something; - - const write = (filename: string, contents: string) => { - results.push({ - dest: filename, - sources: [{ - path: null, - contents: contents - }] - }); - }; - plugin.writeFile(pluginName, entryPoint, req, write, {}); - } - }); - - const toIFile = (entry: IExtraFile): IFile => { - let contents = readFileAndRemoveBOM(entry.path); - if (entry.amdModuleId) { - contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); - } - return { - path: entry.path, - contents: contents - }; - }; - - const toPrepend = (prepend || []).map(toIFile); - - mainResult.sources = toPrepend.concat(mainResult.sources); - - return { - files: results, - usedPlugins: usedPlugins - }; -} - -function readFileAndRemoveBOM(path: string): string { - const BOM_CHAR_CODE = 65279; - let contents = fs.readFileSync(path, 'utf8'); - // Remove BOM - if (contents.charCodeAt(0) === BOM_CHAR_CODE) { - contents = contents.substring(1); - } - return contents; -} - -function emitPlugin(entryPoint: string, plugin: ILoaderPlugin, pluginName: string, moduleName: string): IFile { - let result = ''; - if (typeof plugin.write === 'function') { - const write: ILoaderPluginWriteFunc = ((what: string) => { - result += what; - }); - write.getEntryPoint = () => { - return entryPoint; - }; - write.asModule = (moduleId: string, code: string) => { - code = code.replace(/^define\(/, 'define("' + moduleId + '",'); - result += code; - }; - plugin.write(pluginName, moduleName, write); - } - return { - path: null, - contents: result - }; -} - -function emitNamedModule(moduleId: string, defineCallPosition: IPosition, path: string, contents: string): IFile { - - // `defineCallPosition` is the position in code: |define() - const defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col); - - // `parensOffset` is the position in code: define|() - const parensOffset = contents.indexOf('(', defineCallOffset); - - const insertStr = '"' + moduleId + '", '; - - return { - path: path, - contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1) - }; -} - -function emitShimmedModule(moduleId: string, myDeps: string[], factory: string, path: string, contents: string): IFile { - const strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : ''); - const strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');'; - return { - path: path, - contents: contents + '\n;\n' + strDefine - }; -} - -/** - * Convert a position (line:col) to (offset) in string `str` - */ -function positionToOffset(str: string, desiredLine: number, desiredCol: number): number { - if (desiredLine === 1) { - return desiredCol - 1; - } - - let line = 1; - let lastNewLineOffset = -1; - - do { - if (desiredLine === line) { - return lastNewLineOffset + 1 + desiredCol - 1; - } - lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1); - line++; - } while (lastNewLineOffset >= 0); - - return -1; -} - - -/** - * Return a set of reachable nodes in `graph` starting from `rootNodes` - */ -function visit(rootNodes: string[], graph: IGraph): INodeSet { - const result: INodeSet = {}; - const queue = rootNodes; - - rootNodes.forEach((node) => { - result[node] = true; - }); - - while (queue.length > 0) { - const el = queue.shift(); - const myEdges = graph[el!] || []; - myEdges.forEach((toNode) => { - if (!result[toNode]) { - result[toNode] = true; - queue.push(toNode); - } - }); - } - - return result; -} - -/** - * Perform a topological sort on `graph` - */ -function topologicalSort(graph: IGraph): string[] { - - const allNodes: INodeSet = {}, - outgoingEdgeCount: { [node: string]: number } = {}, - inverseEdges: IGraph = {}; - - Object.keys(graph).forEach((fromNode: string) => { - allNodes[fromNode] = true; - outgoingEdgeCount[fromNode] = graph[fromNode].length; - - graph[fromNode].forEach((toNode) => { - allNodes[toNode] = true; - outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0; - - inverseEdges[toNode] = inverseEdges[toNode] || []; - inverseEdges[toNode].push(fromNode); - }); - }); - - // https://en.wikipedia.org/wiki/Topological_sorting - const S: string[] = [], - L: string[] = []; - - Object.keys(allNodes).forEach((node: string) => { - if (outgoingEdgeCount[node] === 0) { - delete outgoingEdgeCount[node]; - S.push(node); - } - }); - - while (S.length > 0) { - // Ensure the exact same order all the time with the same inputs - S.sort(); - - const n: string = S.shift()!; - L.push(n); - - const myInverseEdges = inverseEdges[n] || []; - myInverseEdges.forEach((m: string) => { - outgoingEdgeCount[m]--; - if (outgoingEdgeCount[m] === 0) { - delete outgoingEdgeCount[m]; - S.push(m); - } - }); - } - - if (Object.keys(outgoingEdgeCount).length > 0) { - throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount)); - } - - return L; -} diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 841dbe13..55fb1480 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -41,6 +41,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = void 0; +exports.createCompile = createCompile; exports.transpileTask = transpileTask; exports.compileTask = compileTask; exports.watchTask = watchTask; diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 6e1fcab5..625fc430 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -49,7 +49,7 @@ interface ICompileTaskOptions { readonly preserveEnglish: boolean; } -function createCompile(src: string, { build, emitError, transpileOnly, preserveEnglish }: ICompileTaskOptions) { +export function createCompile(src: string, { build, emitError, transpileOnly, preserveEnglish }: ICompileTaskOptions) { const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); diff --git a/build/lib/electron.js b/build/lib/electron.js index f7814d5d..56992d8a 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -90,7 +90,7 @@ function darwinBundleDocumentType(extensions, icon, nameOrSuffix, utis) { role: 'Editor', ostypes: ['TEXT', 'utxt', 'TUTX', '****'], extensions, - iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns', // <-- Void icon code.icns + iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns', utis }; } @@ -215,7 +215,7 @@ exports.config = { darwinForceDarkModeSupport: true, darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined, linuxExecutableName: product.applicationName, - winIcon: 'resources/win32/code.ico', // <-- Void icon + winIcon: 'resources/win32/code.ico', token: process.env['GITHUB_TOKEN'], repo: product.electronRepository || undefined, validateChecksum: true, diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 9cdf58dd..3bb047df 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -68,7 +68,7 @@ function darwinBundleDocumentType(extensions: string[], icon: string, nameOrSuff role: 'Editor', ostypes: ['TEXT', 'utxt', 'TUTX', '****'], extensions, - iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns', // <-- Void icon code.icns + iconFile: 'resources/darwin/' + icon.toLowerCase() + '.icns', utis }; } @@ -196,7 +196,7 @@ export const config = { darwinForceDarkModeSupport: true, darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined, linuxExecutableName: product.applicationName, - winIcon: 'resources/win32/code.ico', // <-- Void icon + winIcon: 'resources/win32/code.ico', token: process.env['GITHUB_TOKEN'], repo: product.electronRepository || undefined, validateChecksum: true, diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 9483d319..1d3bfb90 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -319,9 +319,10 @@ globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), function processNlsFiles(opts) { return (0, event_stream_1.through)(function (file) { const fileName = path_1.default.basename(file.path); - if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles (TODO@esm this file is not created anymore, pick another) + if (fileName === 'nls.keys.json') { try { - const json = JSON.parse(fs_1.default.readFileSync(path_1.default.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); + const contents = file.contents.toString('utf8'); + const json = JSON.parse(contents); if (NLSKeysFormat.is(json)) { processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this); } diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index d2904ccf..96468033 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -387,9 +387,10 @@ globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), export function processNlsFiles(opts: { out: string; fileHeader: string; languages: Language[] }): ThroughStream { return through(function (this: ThroughStream, file: File) { const fileName = path.basename(file.path); - if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles (TODO@esm this file is not created anymore, pick another) + if (fileName === 'nls.keys.json') { try { - const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); + const contents = file.contents.toString('utf8'); + const json = JSON.parse(contents); if (NLSKeysFormat.is(json)) { processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this); } diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index 5cf5c584..9785bb8d 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -79,9 +79,12 @@ const CORE_TYPES = [ 'PerformanceMark', 'PerformanceObserver', 'ImportMeta', + 'structuredClone', // 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 63377328..f8aa401a 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -77,10 +77,13 @@ const CORE_TYPES = [ 'PerformanceMark', 'PerformanceObserver', 'ImportMeta', + 'structuredClone', // 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/optimize.js b/build/lib/optimize.js index d75a2978..105dc02a 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -70,12 +70,6 @@ function bundleESMTask(opts) { } return entryPoint; }); - const allMentionedModules = new Set(); - for (const entryPoint of entryPoints) { - allMentionedModules.add(entryPoint.name); - entryPoint.include?.forEach(allMentionedModules.add, allMentionedModules); - entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules); - } const bundleAsync = async () => { const files = []; const tasks = []; @@ -127,7 +121,6 @@ function bundleESMTask(opts) { }; const task = esbuild_1.default.build({ bundle: true, - external: entryPoint.exclude, packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages platform: 'neutral', // makes esm format: 'esm', diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index e12c85c3..88a2a25d 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -62,13 +62,6 @@ function bundleESMTask(opts: IBundleESMTaskOpts): NodeJS.ReadWriteStream { return entryPoint; }); - const allMentionedModules = new Set(); - for (const entryPoint of entryPoints) { - allMentionedModules.add(entryPoint.name); - entryPoint.include?.forEach(allMentionedModules.add, allMentionedModules); - entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules); - } - const bundleAsync = async () => { const files: VinylFile[] = []; const tasks: Promise[] = []; @@ -129,7 +122,6 @@ function bundleESMTask(opts: IBundleESMTaskOpts): NodeJS.ReadWriteStream { const task = esbuild.build({ bundle: true, - external: entryPoint.exclude, packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages platform: 'neutral', // makes esm format: 'esm', diff --git a/build/lib/policies.js b/build/lib/policies.js index b76d9ffe..ac697629 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 IntPolicy extends BasePolicy { +class ParseError extends Error { + constructor(message, moduleName, node) { + super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`); + } +} +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]; @@ -229,7 +339,16 @@ const IntQ = { const StringQ = { Q: `[ (string (string_fragment) @value) - (call_expression function: (identifier) @localizeFn arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value)) (#eq? @localizeFn localize)) + (call_expression + function: [ + (identifier) @localizeFn (#eq? @localizeFn localize) + (member_expression + object: (identifier) @nlsObj (#eq? @nlsObj nls) + property: (property_identifier) @localizeFn (#eq? @localizeFn localize) + ) + ] + arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value)) + ) ]`, value(matches) { const match = matches[0]; @@ -260,47 +379,53 @@ 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 { + const matches = query.matches(node).filter(m => m.captures[0].node.parent?.parent === node); + return qtype.value(matches); + } + 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 ParseError(`Missing required 'name' property`, moduleName, policyNode); } else if (isNlsString(name)) { - throw new Error(`Property 'name' should be a literal string.`); + throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode); } - const categoryName = getStringProperty(configurationNode, 'title'); + const categoryName = getStringProperty(moduleName, configurationNode, 'title'); if (!categoryName) { - throw new Error(`Missing required 'title' property.`); + throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode); } else if (!isNlsString(categoryName)) { - throw new Error(`Property 'title' should be localized.`); + throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode); } const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; let category = categories.get(categoryKey); @@ -308,19 +433,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 ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode); } else if (isNlsString(minimumVersion)) { - throw new Error(`Property 'minimumVersion' should be a literal string.`); + throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode); } - 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 ParseError(`Missing required 'description' property.`, moduleName, settingNode); } if (!isNlsString(description)) { - throw new Error(`Property 'description' should be localized.`); + throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode); } let result; for (const policyType of PolicyTypes) { @@ -329,7 +454,7 @@ function getPolicy(moduleName, configurationNode, settingNode, policyNode, categ } } if (!result) { - throw new Error(`Failed to parse policy '${name}'.`); + throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode); } return result; } @@ -339,11 +464,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 )) @@ -400,8 +525,8 @@ function renderADML(appName, versions, categories, policies, translations) { ${appName} - ${versions.map(v => `${appName} >= ${v}`)} - ${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations))} + ${versions.map(v => `${appName} >= ${v}`).join(`\n `)} + ${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations)).join(`\n `)} ${policies.map(p => p.renderADMLStrings(translations)).flat().join(`\n `)} @@ -411,6 +536,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 +831,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 +843,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 2488920c..34d20e93 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 { @@ -367,7 +503,16 @@ const IntQ: QType = { const StringQ: QType = { Q: `[ (string (string_fragment) @value) - (call_expression function: (identifier) @localizeFn arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value)) (#eq? @localizeFn localize)) + (call_expression + function: [ + (identifier) @localizeFn (#eq? @localizeFn localize) + (member_expression + object: (identifier) @nlsObj (#eq? @nlsObj nls) + property: (property_identifier) @localizeFn (#eq? @localizeFn localize) + ) + ] + arguments: (arguments (string (string_fragment) @nlsKey) (string (string_fragment) @value)) + ) ]`, value(matches: Parser.QueryMatch[]): string | NlsString | undefined { @@ -407,7 +552,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 +560,34 @@ 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 { + const matches = query.matches(node).filter(m => m.captures[0].node.parent?.parent === node); + return qtype.value(matches); + } 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 +600,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 ParseError(`Missing required 'name' property`, moduleName, policyNode); } else if (isNlsString(name)) { - throw new Error(`Property 'name' should be a literal string.`); + throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode); } - const categoryName = getStringProperty(configurationNode, 'title'); + const categoryName = getStringProperty(moduleName, configurationNode, 'title'); if (!categoryName) { - throw new Error(`Missing required 'title' property.`); + throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode); } else if (!isNlsString(categoryName)) { - throw new Error(`Property 'title' should be localized.`); + throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode); } const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; @@ -474,20 +624,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 ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode); } else if (isNlsString(minimumVersion)) { - throw new Error(`Property 'minimumVersion' should be a literal string.`); + throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode); } - 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 ParseError(`Missing required 'description' property.`, moduleName, settingNode); } if (!isNlsString(description)) { - throw new Error(`Property 'description' should be localized.`); + throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode); } let result: Policy | undefined; @@ -499,7 +649,7 @@ function getPolicy( } if (!result) { - throw new Error(`Failed to parse policy '${name}'.`); + throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode); } return result; @@ -511,11 +661,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 )) @@ -578,8 +728,8 @@ function renderADML(appName: string, versions: string[], categories: Category[], ${appName} - ${versions.map(v => `${appName} >= ${v}`)} - ${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations))} + ${versions.map(v => `${appName} >= ${v}`).join(`\n `)} + ${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations)).join(`\n `)} ${policies.map(p => p.renderADMLStrings(translations)).flat().join(`\n `)} @@ -590,6 +740,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 +1076,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 +1092,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 00000000..dbca887b --- /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 00000000..dc182135 --- /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/standalone.js b/build/lib/standalone.js index 0e7a9ecc..fb6c4fcf 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -41,7 +41,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractEditor = extractEditor; -exports.createESMSourcesAndResources2 = createESMSourcesAndResources2; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const tss = __importStar(require("./treeshaking")); @@ -75,6 +74,9 @@ function extractEditor(options) { compilerOptions = tsConfig.compilerOptions; } tsConfig.compilerOptions = compilerOptions; + tsConfig.compilerOptions.sourceMap = true; + tsConfig.compilerOptions.module = 'es2022'; + tsConfig.compilerOptions.outDir = options.tsOutDir; compilerOptions.noEmit = false; compilerOptions.noUnusedLocals = false; compilerOptions.preserveConstEnums = false; @@ -142,102 +144,6 @@ function extractEditor(options) { 'vs/loader.js' ].forEach(copyFile); } -function createESMSourcesAndResources2(options) { - const SRC_FOLDER = path_1.default.join(REPO_ROOT, options.srcFolder); - const OUT_FOLDER = path_1.default.join(REPO_ROOT, options.outFolder); - const OUT_RESOURCES_FOLDER = path_1.default.join(REPO_ROOT, options.outResourcesFolder); - const getDestAbsoluteFilePath = (file) => { - const dest = options.renames[file.replace(/\\/g, '/')] || file; - if (dest === 'tsconfig.json') { - return path_1.default.join(OUT_FOLDER, `tsconfig.json`); - } - if (/\.ts$/.test(dest)) { - return path_1.default.join(OUT_FOLDER, dest); - } - return path_1.default.join(OUT_RESOURCES_FOLDER, dest); - }; - const allFiles = walkDirRecursive(SRC_FOLDER); - for (const file of allFiles) { - if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { - continue; - } - if (file === 'tsconfig.json') { - const tsConfig = JSON.parse(fs_1.default.readFileSync(path_1.default.join(SRC_FOLDER, file)).toString()); - tsConfig.compilerOptions.module = 'es2022'; - tsConfig.compilerOptions.outDir = path_1.default.join(path_1.default.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); - write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); - continue; - } - if (/\.ts$/.test(file) || /\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file) || /\.ttf$/.test(file)) { - // Transport the files directly - write(getDestAbsoluteFilePath(file), fs_1.default.readFileSync(path_1.default.join(SRC_FOLDER, file))); - continue; - } - console.log(`UNKNOWN FILE: ${file}`); - } - function walkDirRecursive(dir) { - if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { - dir += '/'; - } - const result = []; - _walkDirRecursive(dir, result, dir.length); - return result; - } - function _walkDirRecursive(dir, result, trimPos) { - const files = fs_1.default.readdirSync(dir); - for (let i = 0; i < files.length; i++) { - const file = path_1.default.join(dir, files[i]); - if (fs_1.default.statSync(file).isDirectory()) { - _walkDirRecursive(file, result, trimPos); - } - else { - result.push(file.substr(trimPos)); - } - } - } - function write(absoluteFilePath, contents) { - if (/(\.ts$)|(\.js$)/.test(absoluteFilePath)) { - contents = toggleComments(contents.toString()); - } - writeFile(absoluteFilePath, contents); - function toggleComments(fileContents) { - const lines = fileContents.split(/\r\n|\r|\n/); - let mode = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (mode === 0) { - if (/\/\/ ESM-comment-begin/.test(line)) { - mode = 1; - continue; - } - if (/\/\/ ESM-uncomment-begin/.test(line)) { - mode = 2; - continue; - } - continue; - } - if (mode === 1) { - if (/\/\/ ESM-comment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = '// ' + line; - continue; - } - if (mode === 2) { - if (/\/\/ ESM-uncomment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { - return indent; - }); - } - } - return lines.join('\n'); - } - } -} function transportCSS(module, enqueue, write) { if (!/\.css/.test(module)) { return false; diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index b2ae02f1..be7d07f7 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -29,7 +29,7 @@ function writeFile(filePath: string, contents: Buffer | string): void { fs.writeFileSync(filePath, contents); } -export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void { +export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string; tsOutDir: string }): void { const ts = require('typescript') as typeof import('typescript'); const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); @@ -41,6 +41,9 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str compilerOptions = tsConfig.compilerOptions; } tsConfig.compilerOptions = compilerOptions; + tsConfig.compilerOptions.sourceMap = true; + tsConfig.compilerOptions.module = 'es2022'; + tsConfig.compilerOptions.outDir = options.tsOutDir; compilerOptions.noEmit = false; compilerOptions.noUnusedLocals = false; @@ -119,125 +122,6 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str ].forEach(copyFile); } -export interface IOptions2 { - srcFolder: string; - outFolder: string; - outResourcesFolder: string; - ignores: string[]; - renames: { [filename: string]: string }; -} - -export function createESMSourcesAndResources2(options: IOptions2): void { - - const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder); - const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); - const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); - - const getDestAbsoluteFilePath = (file: string): string => { - const dest = options.renames[file.replace(/\\/g, '/')] || file; - if (dest === 'tsconfig.json') { - return path.join(OUT_FOLDER, `tsconfig.json`); - } - if (/\.ts$/.test(dest)) { - return path.join(OUT_FOLDER, dest); - } - return path.join(OUT_RESOURCES_FOLDER, dest); - }; - - const allFiles = walkDirRecursive(SRC_FOLDER); - for (const file of allFiles) { - - if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { - continue; - } - - if (file === 'tsconfig.json') { - const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); - tsConfig.compilerOptions.module = 'es2022'; - tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); - write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); - continue; - } - - if (/\.ts$/.test(file) || /\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file) || /\.ttf$/.test(file)) { - // Transport the files directly - write(getDestAbsoluteFilePath(file), fs.readFileSync(path.join(SRC_FOLDER, file))); - continue; - } - - console.log(`UNKNOWN FILE: ${file}`); - } - - - function walkDirRecursive(dir: string): string[] { - if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { - dir += '/'; - } - const result: string[] = []; - _walkDirRecursive(dir, result, dir.length); - return result; - } - - function _walkDirRecursive(dir: string, result: string[], trimPos: number): void { - const files = fs.readdirSync(dir); - for (let i = 0; i < files.length; i++) { - const file = path.join(dir, files[i]); - if (fs.statSync(file).isDirectory()) { - _walkDirRecursive(file, result, trimPos); - } else { - result.push(file.substr(trimPos)); - } - } - } - - function write(absoluteFilePath: string, contents: string | Buffer): void { - if (/(\.ts$)|(\.js$)/.test(absoluteFilePath)) { - contents = toggleComments(contents.toString()); - } - writeFile(absoluteFilePath, contents); - - function toggleComments(fileContents: string): string { - const lines = fileContents.split(/\r\n|\r|\n/); - let mode = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (mode === 0) { - if (/\/\/ ESM-comment-begin/.test(line)) { - mode = 1; - continue; - } - if (/\/\/ ESM-uncomment-begin/.test(line)) { - mode = 2; - continue; - } - continue; - } - - if (mode === 1) { - if (/\/\/ ESM-comment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = '// ' + line; - continue; - } - - if (mode === 2) { - if (/\/\/ ESM-uncomment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { - return indent; - }); - } - } - - return lines.join('\n'); - } - } -} - function transportCSS(module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { if (!/\.css/.test(module)) { diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 9a5b0802..352b5361 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -234,6 +234,8 @@ "--vscode-editorGutter-commentUnresolvedGlyphForeground", "--vscode-editorGutter-deletedBackground", "--vscode-editorGutter-foldingControlForeground", + "--vscode-editorGutter-itemBackground", + "--vscode-editorGutter-itemGlyphForeground", "--vscode-editorGutter-modifiedBackground", "--vscode-editorHint-border", "--vscode-editorHint-foreground", @@ -345,6 +347,7 @@ "--vscode-extensionButton-prominentHoverBackground", "--vscode-extensionButton-separator", "--vscode-extensionIcon-preReleaseForeground", + "--vscode-extensionIcon-privateForeground", "--vscode-extensionIcon-sponsorForeground", "--vscode-extensionIcon-starForeground", "--vscode-extensionIcon-verifiedForeground", @@ -370,14 +373,14 @@ "--vscode-inlineChatInput-placeholderForeground", "--vscode-inlineEdit-gutterIndicator-background", "--vscode-inlineEdit-gutterIndicator-primaryBackground", + "--vscode-inlineEdit-gutterIndicator-primaryBorder", "--vscode-inlineEdit-gutterIndicator-primaryForeground", "--vscode-inlineEdit-gutterIndicator-secondaryBackground", + "--vscode-inlineEdit-gutterIndicator-secondaryBorder", "--vscode-inlineEdit-gutterIndicator-secondaryForeground", "--vscode-inlineEdit-gutterIndicator-successfulBackground", + "--vscode-inlineEdit-gutterIndicator-successfulBorder", "--vscode-inlineEdit-gutterIndicator-successfulForeground", - "--vscode-inlineEdit-indicator-background", - "--vscode-inlineEdit-indicator-border", - "--vscode-inlineEdit-indicator-foreground", "--vscode-inlineEdit-modifiedBackground", "--vscode-inlineEdit-modifiedBorder", "--vscode-inlineEdit-modifiedChangedLineBackground", @@ -386,8 +389,8 @@ "--vscode-inlineEdit-originalBorder", "--vscode-inlineEdit-originalChangedLineBackground", "--vscode-inlineEdit-originalChangedTextBackground", - "--vscode-inlineEdit-tabWillAcceptBorder", - "--vscode-inlineEdit-wordReplacementView-background", + "--vscode-inlineEdit-tabWillAcceptModifiedBorder", + "--vscode-inlineEdit-tabWillAcceptOriginalBorder", "--vscode-input-background", "--vscode-input-border", "--vscode-input-foreground", @@ -783,7 +786,14 @@ "--vscode-terminalStickyScroll-border", "--vscode-terminalStickyScrollHover-background", "--vscode-terminalSymbolIcon-aliasForeground", + "--vscode-terminalSymbolIcon-argumentForeground", + "--vscode-terminalSymbolIcon-fileForeground", "--vscode-terminalSymbolIcon-flagForeground", + "--vscode-terminalSymbolIcon-folderForeground", + "--vscode-terminalSymbolIcon-inlineSuggestionForeground", + "--vscode-terminalSymbolIcon-methodForeground", + "--vscode-terminalSymbolIcon-optionForeground", + "--vscode-terminalSymbolIcon-optionValueForeground", "--vscode-testing-coverCountBadgeBackground", "--vscode-testing-coverCountBadgeForeground", "--vscode-testing-coveredBackground", diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index 39e2b4e3..448ab38c 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 83d2ec78..6c1f7b75 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 13f32376..f45b6f34 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 63b47522..d277ca7e 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/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 6192ad5b..cb4c9082 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -92,7 +92,7 @@ declare namespace monaco.editor { #includeAll(vs/editor/standalone/browser/standaloneEditor;languages.Token=>Token): #include(vs/editor/standalone/common/standaloneTheme): BuiltinTheme, IStandaloneThemeData, IColors #include(vs/editor/common/languages/supports/tokenization): ITokenThemeRule -#include(vs/editor/standalone/browser/standaloneWebWorker): MonacoWebWorker, IWebWorkerOptions +#include(vs/editor/standalone/browser/standaloneWebWorker): MonacoWebWorker, IInternalWebWorkerOptions #include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IGlobalEditorOptions, IStandaloneEditorConstructionOptions, IStandaloneDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor export interface ICommandHandler { (...args: any[]): void; @@ -145,7 +145,7 @@ declare namespace monaco.languages { declare namespace monaco.worker { #include(vs/editor/common/model/mirrorTextModel): IMirrorTextModel -#includeAll(vs/editor/common/services/editorSimpleWorker;): +#includeAll(vs/editor/common/services/editorWebWorker;): } diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index a3369eb2..9e96a685 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -4,8 +4,7 @@ import { IObservable } from './vs/base/common/observable'; import { ServiceIdentifier } from './vs/platform/instantiation/common/instantiation'; -import { create as create1 } from './vs/base/common/worker/simpleWorker'; -import { create as create2 } from './vs/editor/common/services/editorSimpleWorker'; +import { start } from './vs/editor/editor.worker.start'; import { SyncDescriptor0 } from './vs/platform/instantiation/common/descriptors'; import * as editorAPI from './vs/editor/editor.api'; @@ -13,8 +12,7 @@ import * as editorAPI from './vs/editor/editor.api'; var a: any; var b: any; a = (>b).type; - a = create1; - a = create2; + a = start; // injection madness a = (>b).ctor; diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index c1f22aa5..458847af 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 aa939e40..445e842c 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -57,13 +57,13 @@ "source-map": "0.6.1", "ternary-stream": "^3.0.0", "through2": "^4.0.2", - "tree-sitter": "^0.20.5", + "tree-sitter": "^0.22.4", "vscode-universal-bundler": "^0.1.3", "workerpool": "^6.4.0", "yauzl": "^2.10.0" }, "optionalDependencies": { - "tree-sitter-typescript": "^0.20.5", + "tree-sitter-typescript": "^0.23.2", "vscode-gulp-watch": "^5.0.3" } }, @@ -1551,7 +1551,7 @@ "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", @@ -1580,7 +1580,8 @@ "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", @@ -1632,7 +1633,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 +1648,7 @@ "url": "https://feross.org/support" } ], + "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -1821,7 +1823,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", @@ -1991,7 +1994,7 @@ "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, "dependencies": { "mimic-response": "^3.1.0" }, @@ -2006,7 +2009,7 @@ "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, "engines": { "node": ">=10" }, @@ -2018,7 +2021,8 @@ "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" } @@ -2075,7 +2079,8 @@ "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" } @@ -2214,7 +2219,7 @@ "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" } @@ -2331,7 +2336,8 @@ "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" } @@ -2471,7 +2477,8 @@ "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", @@ -2553,7 +2560,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", @@ -2867,7 +2875,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 +2889,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "optional": true }, "node_modules/inflight": { "version": "1.0.6", @@ -2904,7 +2913,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", @@ -3222,7 +3232,7 @@ "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" }, @@ -3330,13 +3340,14 @@ "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/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", @@ -3350,23 +3361,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" }, @@ -3378,7 +3385,8 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -3402,6 +3410,18 @@ "dev": true, "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/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3470,7 +3490,7 @@ "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" } @@ -3646,7 +3666,8 @@ "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", @@ -3700,7 +3721,7 @@ "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" @@ -3737,7 +3758,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", @@ -3950,27 +3972,7 @@ "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, - "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/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,6 +3987,28 @@ "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==", + "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" + } + ], + "optional": true, "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", @@ -4079,7 +4103,8 @@ "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" } @@ -4119,7 +4144,8 @@ "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, + "dev": true, + "optional": true, "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -4131,7 +4157,8 @@ "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", @@ -4211,25 +4238,86 @@ } }, "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.22.4", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz", + "integrity": "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==", + "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "nan": "^2.18.0", - "prebuild-install": "^7.1.1" + "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": { + "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": { @@ -4251,7 +4339,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" }, @@ -4500,7 +4589,7 @@ "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/xml2js": { "version": "0.5.0", @@ -4546,7 +4635,7 @@ "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", diff --git a/build/package.json b/build/package.json index 3a435a83..73d4f42e 100644 --- a/build/package.json +++ b/build/package.json @@ -51,7 +51,7 @@ "source-map": "0.6.1", "ternary-stream": "^3.0.0", "through2": "^4.0.2", - "tree-sitter": "^0.20.5", + "tree-sitter": "^0.22.4", "vscode-universal-bundler": "^0.1.3", "workerpool": "^6.4.0", "yauzl": "^2.10.0" @@ -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 4c169ba0..0bbdba1b 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -95,7 +95,7 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "inno_updater" -version = "0.12.1" +version = "0.14.2" dependencies = [ "byteorder", "crc", diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index e2130dd2..02f8d0d7 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "inno_updater" -version = "0.12.1" +version = "0.14.2" authors = ["Microsoft "] build = "build.rs" diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index ef7cb934eb637a39ef4246012216e64dc6d5cc68..e70d5b64ee349919c67c73dfe7c6e333ce7107ad 100644 GIT binary patch literal 455168 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%0DLPq@531B`>mDp}Csi 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

nxbT&mQ*IF>eyvV^Idw8Q2*%!CY!neP@Soup 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

FGhHUij1-L}|C3MVDF*2`MZ1nfQ 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

MNZ)38XeUjCbt51|mSCKHZ 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 M`Tzg@U*Eug0=z;>82|tP diff --git a/cgmanifest.json b/cgmanifest.json index 24c00b37..eb5b37d3 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/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index cc255c04..3d6131c6 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -2949,7 +2949,7 @@ getrandom 0.1.16 - MIT OR Apache-2.0 getrandom 0.2.15 - MIT OR Apache-2.0 https://github.com/rust-random/getrandom -Copyright (c) 2018-2024 The rust-random Project Developers +Copyright (c) 2018-2025 The rust-random Project Developers Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any @@ -5513,7 +5513,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- opentelemetry 0.19.0 - Apache-2.0 -https://github.com/open-telemetry/opentelemetry-rust +https://github.com/open-telemetry/opentelemetry-rust/tree/main/opentelemetry Apache License Version 2.0, January 2004 @@ -5929,7 +5929,7 @@ Apache License --------------------------------------------------------- opentelemetry_sdk 0.19.0 - Apache-2.0 -https://github.com/open-telemetry/opentelemetry-rust +https://github.com/open-telemetry/opentelemetry-rust/tree/main/opentelemetry-sdk Apache License Version 2.0, January 2004 @@ -8584,7 +8584,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes -[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -8596,6 +8595,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`groestl`]: ./groestl [`jh`]: ./jh [`k12`]: ./k12 +[`kupyna`]: ./kupyna [`md2`]: ./md2 [`md4`]: ./md4 [`md5`]: ./md5 @@ -8638,6 +8638,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [Grøstl]: https://en.wikipedia.org/wiki/Grøstl [JH]: https://www3.ntu.edu.sg/home/wuhj/research/jh [KangarooTwelve]: https://keccak.team/kangarootwelve.html +[Kupyna]: https://eprint.iacr.org/2015/885.pdf [MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography) [MD4]: https://en.wikipedia.org/wiki/MD4 [MD5]: https://en.wikipedia.org/wiki/MD5 @@ -8677,7 +8678,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes -[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -8689,6 +8689,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`groestl`]: ./groestl [`jh`]: ./jh [`k12`]: ./k12 +[`kupyna`]: ./kupyna [`md2`]: ./md2 [`md4`]: ./md4 [`md5`]: ./md5 @@ -8731,6 +8732,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [Grøstl]: https://en.wikipedia.org/wiki/Grøstl [JH]: https://www3.ntu.edu.sg/home/wuhj/research/jh [KangarooTwelve]: https://keccak.team/kangarootwelve.html +[Kupyna]: https://eprint.iacr.org/2015/885.pdf [MD2]: https://en.wikipedia.org/wiki/MD2_(cryptography) [MD4]: https://en.wikipedia.org/wiki/MD4 [MD5]: https://en.wikipedia.org/wiki/MD5 @@ -9395,7 +9397,7 @@ DEALINGS IN THE SOFTWARE. tar 0.4.40 - MIT/Apache-2.0 https://github.com/alexcrichton/tar-rs -Copyright (c) 2014 Alex Crichton +Copyright (c) The tar-rs Project Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -9519,7 +9521,7 @@ DEALINGS IN THE SOFTWARE. time 0.3.36 - MIT OR Apache-2.0 https://github.com/time-rs/time -Copyright (c) 2024 Jacob Pratt et al. +Copyright (c) Jacob Pratt et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9545,7 +9547,7 @@ SOFTWARE. time-core 0.1.2 - MIT OR Apache-2.0 https://github.com/time-rs/time -Copyright (c) 2024 Jacob Pratt et al. +Copyright (c) Jacob Pratt et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 9116e483..d4d62a8b 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 538827ed..f58f49b2 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 7fbd00d0..f183f935 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 c5816ae5..4b8118a2 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 f8eff34e..44c85977 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 be583f8b..7acc50f2 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -11,7 +11,6 @@ import { fileURLToPath } from 'url'; import stylisticTs from '@stylistic/eslint-plugin-ts'; import pluginLocal from 'eslint-plugin-local'; import pluginJsdoc from 'eslint-plugin-jsdoc'; -// import pluginReact from 'eslint-plugin-react'; // Void import pluginHeader from 'eslint-plugin-header'; pluginHeader.rules.header.meta.schema = false; @@ -25,7 +24,10 @@ const ignores = fs.readFileSync(path.join(__dirname, '.eslint-ignore'), 'utf8') export default tseslint.config( // Global ignores { - ignores, + ignores: [ + ...ignores, + '!**/.eslint-plugin-local/**/*' + ], }, // All files (JS and TS) { @@ -81,7 +83,7 @@ export default tseslint.config( 'local/code-parameter-properties-must-have-explicit-accessibility': 'warn', 'local/code-no-nls-in-standalone-editor': 'warn', 'local/code-no-potentially-unsafe-disposables': 'warn', - 'local/code-no-dangerous-type-assertions': 'off', + 'local/code-no-dangerous-type-assertions': 'off', // Void warn -> off 'local/code-no-standalone-editor': 'warn', 'local/code-no-unexternalized-strings': 'warn', 'local/code-must-use-super-dispose': 'warn', @@ -958,7 +960,7 @@ export default tseslint.config( ] }, { - 'target': 'src/vs/editor/editor.worker.ts', + 'target': 'src/vs/editor/editor.worker.start.ts', 'layer': 'worker', 'restrictions': [ 'vs/base/~', @@ -999,6 +1001,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.json b/extensions/configuration-editing/package.json index 224ea33c..531ba21f 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -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" diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index 60814ae0..7db6640f 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/css-language-features/package-lock.json b/extensions/css-language-features/package-lock.json index a645c2ab..c843143c 100644 --- a/extensions/css-language-features/package-lock.json +++ b/extensions/css-language-features/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "vscode-languageclient": "^10.0.0-next.13", + "vscode-languageclient": "^10.0.0-next.14", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -31,48 +31,38 @@ "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/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", "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.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -87,50 +77,48 @@ "dev": true }, "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.7", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.7.tgz", + "integrity": "sha512-7SgnbbbJfYr3off0T2KV/RCMYhVsuLeFPw8l3bkxSiavtoTLsOdu1jyxK3yWbdQuO8QOJC7+no0TXmYjRWSC+g==", + "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.14", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.14.tgz", + "integrity": "sha512-4m/cpNocRgrAkWc8IH4wd3zllAs16NvMmeGcQxFa6xt+mGXJASIeqp0NAFWKZERKg6ClVgBph+SDSZSVvNZ2oA==", "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.1", + "semver": "^7.6.3", + "vscode-languageserver-protocol": "3.17.6-next.12" }, "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.12.tgz", + "integrity": "sha512-EqrbwF0glTWD2HiDpFc32pJOr6/bJvyKSfCpRQrKy3XsfdloH4p3o/rNJYcpujM0OVLmPZgl1i9g57z9g2YRJA==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.7", + "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==" } } } diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 05338813..a24a5352 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -994,7 +994,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^10.0.0-next.13", + "vscode-languageclient": "^10.0.0-next.14", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/package-lock.json b/extensions/css-language-features/server/package-lock.json index b9c936d8..9e560d6e 100644 --- a/extensions/css-language-features/server/package-lock.json +++ b/extensions/css-language-features/server/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.2", + "vscode-css-languageservice": "^6.3.3", "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, @@ -49,9 +49,9 @@ "dev": true }, "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.3", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.3.tgz", + "integrity": "sha512-xXa+ftMPv6JxRgzkvPwZuDCafIdwDW3kyijGcfij1a2qBVScr2qli6MfgJzYm/AMYdbHq9I/4hdpKV0Thim2EA==", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 9b6a8b47..6898d432 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -11,7 +11,7 @@ "browser": "./dist/browser/cssServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.2", + "vscode-css-languageservice": "^6.3.3", "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, @@ -22,7 +22,7 @@ "scripts": { "compile": "gulp compile-extension:css-language-features-server", "watch": "gulp watch-extension:css-language-features-server", - "install-service-next": "npm install vscode-css-languageservice@next", + "install-service-next": "npm install vscode-css-languageservice", "install-service-local": "npm link vscode-css-languageservice", "install-server-next": "npm install vscode-languageserver@next", "install-server-local": "npm install vscode-languageserver", diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 22a8ff83..40155f30 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/git/package-lock.json b/extensions/git/package-lock.json index 7a9cb2ad..2629a940 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.8", - "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", "picomatch": "2.3.1", @@ -217,11 +216,6 @@ "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==" - }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", diff --git a/extensions/git/package.json b/extensions/git/package.json index 23feced6..6761f02c 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -21,7 +21,6 @@ "contribSourceControlInputBoxMenu", "contribSourceControlTitleMenu", "contribViewsWelcome", - "diffCommand", "editSessionIdentityProvider", "quickDiffProvider", "quickInputButtonLocation", @@ -3327,6 +3326,11 @@ "type": "boolean", "default": true, "markdownDescription": "%config.discardUntrackedChangesToTrash%" + }, + "git.showReferenceDetails": { + "type": "boolean", + "default": true, + "markdownDescription": "%config.showReferenceDetails%" } } }, @@ -3435,10 +3439,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" } } ], @@ -3564,7 +3568,6 @@ "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.8", - "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", "picomatch": "2.3.1", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 101618a8..52ba3819 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -289,6 +289,7 @@ "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.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 5bbb4b03..3e8c9234 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; } diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index f29212c5..e4dda68c 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(); } @@ -204,7 +248,6 @@ export class GitBlameController { const markdownString = new MarkdownString(); markdownString.isTrusted = true; - markdownString.supportHtml = true; markdownString.supportThemeIcons = true; // Author, date @@ -318,7 +361,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 +420,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 +444,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 +469,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 +486,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 +534,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 +560,10 @@ export class GitBlameController { } } - this.textEditorBlameInformation = lineBlameInformation; + this.textEditorBlameInformation = { + resource: textEditor.document.uri, + blameInformation: lineBlameInformation + }; } dispose() { @@ -555,7 +613,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 +659,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 +738,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 fa566adb..176a95b5 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 { ForcePushMode, GitErrorCodes, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref } 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, isLinuxSnap, 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?.commitDate) { + return fromNow(this.ref.commitDetails.commitDate, 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}$(circle-small-filled)${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); } @@ -96,8 +108,26 @@ class RefItem implements QuickPickItem { constructor(protected readonly ref: Ref) { } } -class CheckoutItem extends RefItem { +class BranchItem extends RefItem { + override get description(): string { + const description: string[] = []; + if (typeof this.ref.behind === 'number' && typeof this.ref.ahead === 'number') { + description.push(`${this.ref.behind}↓ ${this.ref.ahead}↑`); + } + if (this.ref.commitDetails?.commitDate) { + description.push(fromNow(this.ref.commitDetails.commitDate, true, true)); + } + + return description.length > 0 ? description.join('$(circle-small-filled)') : this.shortCommit; + } + + constructor(override readonly ref: Branch) { + super(ref); + } +} + +class CheckoutItem extends BranchItem { async run(repository: Repository, opts?: { detached?: boolean }): Promise { if (!this.ref.name) { return; @@ -116,7 +146,6 @@ class CheckoutProtectedItem extends CheckoutItem { override get label(): string { return `$(lock) ${this.ref.name ?? this.shortCommit}`; } - } class CheckoutRemoteHeadItem extends RefItem { @@ -152,7 +181,7 @@ class CheckoutTagItem extends RefItem { } } -class BranchDeleteItem extends RefItem { +class BranchDeleteItem extends BranchItem { async run(repository: Repository, force?: boolean): Promise { if (this.ref.type === RefType.Head && this.refName) { @@ -186,7 +215,7 @@ class RemoteTagDeleteItem extends RefItem { } } -class MergeItem extends RefItem { +class MergeItem extends BranchItem { async run(repository: Repository): Promise { if (this.ref.name || this.ref.commit) { @@ -195,7 +224,7 @@ class MergeItem extends RefItem { } } -class RebaseItem extends RefItem { +class RebaseItem extends BranchItem { async run(repository: Repository): Promise { if (this.ref?.name) { @@ -275,7 +304,6 @@ class StashItem implements QuickPickItem { interface ScmCommandOptions { repository?: boolean; - diff?: boolean; } interface ScmCommand { @@ -327,6 +355,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,12 +372,12 @@ 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[]; const buttons = await getRemoteRefItemButtons(repository); - const itemsProcessor = new CheckoutItemsProcessor(refProcessors, repository, buttons, detached); + const itemsProcessor = new CheckoutItemsProcessor(repository, refProcessors, buttons, detached); return itemsProcessor.processRefs(refs); } @@ -405,10 +435,22 @@ class RefProcessor { class RefItemsProcessor { - constructor(protected readonly processors: RefProcessor[]) { } + constructor( + protected readonly repository: Repository, + protected readonly processors: RefProcessor[], + protected readonly options: { + skipCurrentBranch?: boolean; + skipCurrentBranchRemote?: boolean; + } = {} + ) { } processRefs(refs: Ref[]): QuickPickItem[] { + const refsToSkip = this.getRefsToSkip(); + for (const ref of refs) { + if (ref.name && refsToSkip.includes(ref.name)) { + continue; + } for (const processor of this.processors) { if (processor.processRef(ref)) { break; @@ -423,48 +465,19 @@ class RefItemsProcessor { return result; } -} -class RebaseItemsProcessors extends RefItemsProcessor { + protected getRefsToSkip(): string[] { + const refsToSkip = ['origin/HEAD']; - private upstreamName: string | undefined; - - constructor(private readonly repository: Repository) { - super([ - new RefProcessor(RefType.Head, RebaseItem), - new RefProcessor(RefType.RemoteHead, RebaseItem) - ]); - - if (this.repository.HEAD?.upstream) { - this.upstreamName = `${this.repository.HEAD?.upstream.remote}/${this.repository.HEAD?.upstream.name}`; - } - } - - override processRefs(refs: Ref[]): QuickPickItem[] { - const result: QuickPickItem[] = []; - - for (const ref of refs) { - if (ref.name === this.repository.HEAD?.name) { - continue; - } - - if (ref.name === this.upstreamName) { - result.push(new RebaseUpstreamItem(ref)); - continue; - } - - for (const processor of this.processors) { - if (processor.processRef(ref)) { - break; - } - } + if (this.options.skipCurrentBranch && this.repository.HEAD?.name) { + refsToSkip.push(this.repository.HEAD.name); } - for (const processor of this.processors) { - result.push(...processor.items); + if (this.options.skipCurrentBranchRemote && this.repository.HEAD?.upstream) { + refsToSkip.push(`${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`); } - return result; + return refsToSkip; } } @@ -490,11 +503,11 @@ class CheckoutItemsProcessor extends RefItemsProcessor { private defaultButtons: RemoteSourceActionButton[] | undefined; constructor( + repository: Repository, processors: RefProcessor[], - private readonly repository: Repository, private readonly buttons: Map, private readonly detached = false) { - super(processors); + super(repository, processors); // Default button(s) const remote = repository.remotes.find(r => r.pushUrl === repository.HEAD?.remote || r.fetchUrl === repository.HEAD?.remote) ?? repository.remotes[0]; @@ -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)); @@ -1635,7 +1643,6 @@ export class CommandCenter { if (!textEditor) { return; } - modifiedDocument = textEditor.document; modifiedUri = modifiedDocument.uri; } @@ -1653,8 +1660,8 @@ export class CommandCenter { await repository.stage(resource, result, modifiedDocument.encoding)); } - @command('git.stageSelectedRanges', { diff: true }) - async stageSelectedChanges(changes: LineChange[]): Promise { + @command('git.stageSelectedRanges') + async stageSelectedChanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { @@ -1668,7 +1675,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)}`); @@ -1849,8 +1855,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 +1870,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 +1944,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 +1983,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)}`); @@ -2716,6 +2720,7 @@ export class CommandCenter { const quickPick = window.createQuickPick(); quickPick.busy = true; quickPick.sortByLabel = false; + quickPick.matchOnDetail = false; quickPick.placeholder = opts?.detached ? l10n.t('Select a branch to checkout in detached mode') : l10n.t('Select a branch or tag to checkout'); @@ -2936,10 +2941,13 @@ 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 refProcessors = new RefItemsProcessor([ + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); + const refProcessors = new RefItemsProcessor(repository, [ new RefProcessor(RefType.Head), new RefProcessor(RefType.RemoteHead), new RefProcessor(RefType.Tag) @@ -3041,6 +3049,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,34 +3061,26 @@ 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 processors = options.remote + ? [new RefProcessor(RefType.RemoteHead, BranchDeleteItem)] + : [new RefProcessor(RefType.Head, BranchDeleteItem)]; - const refsToExclude: string[] = []; - if (options.remote) { - refsToExclude.push('origin/HEAD'); + const itemsProcessor = new RefItemsProcessor(repository, processors, { + skipCurrentBranch: true, + skipCurrentBranchRemote: true + }); - if (repository.HEAD?.upstream) { - // Current branch's upstream - refsToExclude.push(`${repository.HEAD.upstream.remote}/${repository.HEAD.upstream.name}`); - } - } else { - if (repository.HEAD?.name) { - // Current branch - refsToExclude.push(repository.HEAD.name); - } - } - - return refs.filter(ref => ref.name && !refsToExclude.includes(ref.name)) - .map(ref => new BranchDeleteItem(ref)); + return itemsProcessor.processRefs(refs); }; const placeHolder = !options.remote ? l10n.t('Select a branch to delete') : l10n.t('Select a remote branch to delete'); - const choice = await this.pickRef(getBranchPicks(), placeHolder); + const choice = await this.pickRef(getBranchPicks(), placeHolder); - if (!choice || !choice.refName) { + if (!(choice instanceof BranchDeleteItem) || !choice.refName) { return; } name = choice.refName; @@ -3128,13 +3131,19 @@ 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 itemsProcessor = new RefItemsProcessor([ + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); + const itemsProcessor = new RefItemsProcessor(repository, [ new RefProcessor(RefType.Head, MergeItem), new RefProcessor(RefType.RemoteHead, MergeItem), new RefProcessor(RefType.Tag, MergeItem) - ]); + ], { + skipCurrentBranch: true, + skipCurrentBranchRemote: true + }); return itemsProcessor.processRefs(refs); }; @@ -3154,11 +3163,31 @@ export class CommandCenter { @command('git.rebase', { repository: true }) async rebase(repository: Repository): Promise { - const getQuickPickItems = async (): Promise => { - const refs = await repository.getRefs(); - const itemsProcessor = new RebaseItemsProcessors(repository); + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; - return itemsProcessor.processRefs(refs); + const getQuickPickItems = async (): Promise => { + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); + const itemsProcessor = new RefItemsProcessor(repository, [ + new RefProcessor(RefType.Head, RebaseItem), + new RefProcessor(RefType.RemoteHead, RebaseItem) + ], { + skipCurrentBranch: true, + skipCurrentBranchRemote: true + }); + + const quickPickItems = itemsProcessor.processRefs(refs); + + if (repository.HEAD?.upstream) { + const upstreamRef = refs.find(ref => ref.type === RefType.RemoteHead && + ref.name === `${repository.HEAD!.upstream!.remote}/${repository.HEAD!.upstream!.name}`); + + if (upstreamRef) { + quickPickItems.splice(0, 0, new RebaseUpstreamItem(upstreamRef)); + } + } + + return quickPickItems; }; const placeHolder = l10n.t('Select a branch to rebase onto'); @@ -3193,8 +3222,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 b1a25d4f..f89ff232 100644 --- a/extensions/git/src/decorators.ts +++ b/extensions/git/src/decorators.ts @@ -98,4 +98,4 @@ export function debounce(delay: number): Function { this[timerKey] = setTimeout(() => fn.apply(this, args), delay); }; }); -} \ No newline at end of file +} diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 29fc38ab..e42ca1e0 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -10,11 +10,10 @@ import * as cp from 'child_process'; import { fileURLToPath } from 'url'; 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 { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery as ApiRefQuery, InitOptions } from './api/git'; import * as byline from 'byline'; import { StringDecoder } from 'string_decoder'; @@ -193,7 +192,6 @@ function cpErrorHandler(cb: (reason?: any) => void): (reason?: any) => void { export interface SpawnOptions extends cp.SpawnOptions { input?: string; - encoding?: string; log?: boolean; cancellationToken?: CancellationToken; onSpawn?: (childProcess: cp.ChildProcess) => void; @@ -354,6 +352,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'; @@ -616,12 +618,9 @@ export class Git { } } - let encoding = options.encoding || 'utf8'; - encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; - const result: IExecutionResult = { exitCode: bufferResult.exitCode, - stdout: iconv.decode(bufferResult.stdout, encoding), + stdout: bufferResult.stdout.toString('utf8'), stderr: bufferResult.stderr }; @@ -730,6 +729,10 @@ export interface Commit { shortStat?: CommitShortStat; } +export interface RefQuery extends ApiRefQuery { + readonly includeCommitDetails?: boolean; +} + interface GitConfigSection { name: string; subSectionName?: string; @@ -1125,6 +1128,75 @@ 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%(committerdate:unix)%00%(*committerdate:unix)%00%(subject)%00%(*subject)`; + +function parseRefs(data: string): (Ref | Branch)[] { + const refRegex = /^(refs\/[^\0]+)\0([0-9a-f]{40})\0([0-9a-f]{40})?(?:\0(.*))?$/gm; + + const headRegex = /^refs\/heads\/([^ ]+)$/; + const remoteHeadRegex = /^refs\/remotes\/([^/]+)\/([^ ]+)$/; + const tagRegex = /^refs\/tags\/([^ ]+)$/; + const statusRegex = /\[(?:ahead ([0-9]+))?[,\s]*(?:behind ([0-9]+))?]|\[gone]/; + + let ref: string | undefined; + let commitHash: string | undefined; + let tagCommitHash: string | undefined; + let details: 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 committerDate: string | undefined; + let tagCommitterDate: string | undefined; + let status: string | undefined; + + const refs: (Ref | Branch)[] = []; + + let match: RegExpExecArray | null; + let refMatch: RegExpExecArray | null; + + do { + match = refRegex.exec(data); + if (match === null) { + break; + } + + [, ref, commitHash, tagCommitHash, details] = match; + [commitParents, tagCommitParents, authorName, tagAuthorName, committerDate, tagCommitterDate, commitSubject, tagCommitSubject, status] = details?.split('\0') ?? []; + + const parents = tagCommitParents || commitParents; + const subject = tagCommitSubject || commitSubject; + const author = tagAuthorName || authorName; + const date = tagCommitterDate || committerDate; + + const commitDetails = parents && subject && author && date + ? { + hash: commitHash, + message: subject, + parents: parents.split(' '), + authorName: author, + commitDate: date ? new Date(Number(date) * 1000) : undefined, + } satisfies ApiCommit : undefined; + + if (refMatch = headRegex.exec(ref)) { + const [, aheadCount, behindCount] = statusRegex.exec(status) ?? []; + const ahead = status ? aheadCount ? Number(aheadCount) : 0 : undefined; + const behind = status ? behindCount ? Number(behindCount) : 0 : undefined; + refs.push({ name: refMatch[1], commit: commitHash, commitDetails, ahead, behind, type: RefType.Head }); + } else if (refMatch = remoteHeadRegex.exec(ref)) { + const name = `${refMatch[1]}/${refMatch[2]}`; + refs.push({ name, 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 +1401,9 @@ export class Repository { .filter(entry => !!entry); } - async buffer(object: string): Promise { - const child = this.stream(['show', '--textconv', object]); + async buffer(ref: string, filePath: string): Promise { + const relativePath = sanitizeRelativePath(this.repositoryRoot, filePath); + const child = this.stream(['show', '--textconv', `${ref}:${relativePath}`]); if (!child.stdout) { return Promise.reject('Can\'t open file from git'); @@ -1382,10 +1455,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 +1473,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 +1498,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 +1578,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 +1591,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 +1604,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 +1617,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 +1637,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 +1729,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,14 +1744,15 @@ 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] }); - child.stdin!.end(iconv.encode(data, encoding)); + async stage(path: string, data: Uint8Array): Promise { + const relativePath = sanitizeRelativePath(this.repositoryRoot, path); + const child = this.stream(['hash-object', '--stdin', '-w', '--path', relativePath], { stdio: [null, null, null] }); + child.stdin!.end(data); const { exitCode, stdout } = await exec(child); const hash = stdout.toString('utf8'); @@ -1690,7 +1780,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 +1800,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 +2014,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 +2054,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 +2299,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 +2319,7 @@ export class Repository { args.push(ref); } - args.push('--', sanitizePath(path)); + args.push('--', sanitizeRelativePath(this.repositoryRoot, path)); const result = await this.exec(args); @@ -2535,7 +2625,7 @@ export class Repository { .map(([ref]): Branch => ({ name: ref, type: RefType.Head })); } - async getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise { + async getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise<(Ref | Branch)[]> { if (cancellationToken && cancellationToken.isCancellationRequested) { throw new CancellationError(); } @@ -2550,7 +2640,14 @@ export class Repository { args.push('--sort', `-${query.sort}`); } - args.push('--format', '%(refname) %(objectname) %(*objectname)'); + if (query.includeCommitDetails) { + const format = this._git.compareGitVersionTo('1.9.0') !== -1 + ? `${REFS_WITH_DETAILS_FORMAT}%00%(upstream:track)` + : REFS_WITH_DETAILS_FORMAT; + args.push('--format', format); + } else { + args.push('--format', REFS_FORMAT); + } if (query.pattern) { const patterns = Array.isArray(query.pattern) ? query.pattern : [query.pattern]; @@ -2564,25 +2661,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); } async getRemoteRefs(remote: string, opts?: { heads?: boolean; tags?: boolean; cancellationToken?: CancellationToken }): Promise { @@ -2840,7 +2919,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 +2958,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/historyProvider.ts b/extensions/git/src/historyProvider.ts index db316c81..8e5a8f9d 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 25445d26..910256ff 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -10,11 +10,11 @@ import picomatch from 'picomatch'; 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'; -import { Branch, BranchQuery, Change, CommitOptions, FetchOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefQuery, RefType, Remote, Status } from './api/git'; +import { Branch, BranchQuery, Change, CommitOptions, FetchOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } from './api/git'; import { AutoFetcher } from './autofetch'; import { GitBranchProtectionProvider, IBranchProtectionProviderRegistry } from './branchProtection'; import { debounce, memoize, throttle } from './decorators'; -import { Repository as BaseRepository, BlameInformation, Commit, GitError, LogFileOptions, LsTreeElement, PullOptions, Stash, Submodule } from './git'; +import { Repository as BaseRepository, BlameInformation, Commit, GitError, LogFileOptions, LsTreeElement, PullOptions, RefQuery, Stash, Submodule } from './git'; import { GitHistoryProvider } from './historyProvider'; import { Operation, OperationKind, OperationManager, OperationResult } from './operation'; import { CommitCommandsCenter, IPostCommitCommandsProviderRegistry } from './postCommitCommands'; @@ -1222,8 +1222,8 @@ export class Repository implements Disposable { async stage(resource: Uri, contents: string, encoding: 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 data = await workspace.encode(contents, resource, { encoding }); + await this.repository.stage(resource.fsPath, data); this._onDidChangeOriginalResource.fire(resource); this.closeDiffEditors([], [...resource.fsPath]); @@ -1388,22 +1388,29 @@ export class Repository implements Disposable { } }); - 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 })))); - } else { - await this.repository.clean(toClean); - } - - try { - await this.repository.checkout('', toCheckout); - } catch (err) { - if (err.gitErrorCode !== GitErrorCodes.BranchNotYetBorn) { - throw err; + if (toClean.length > 0) { + 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 })))); + } else { + await this.repository.clean(toClean); } } - await this.repository.updateSubmodules(submodulesToUpdate); + + if (toCheckout.length > 0) { + try { + await this.repository.checkout('', toCheckout); + } catch (err) { + if (err.gitErrorCode !== GitErrorCodes.BranchNotYetBorn) { + throw err; + } + } + } + + if (submodulesToUpdate.length > 0) { + await this.repository.updateSubmodules(submodulesToUpdate); + } this.closeDiffEditors([], [...toClean, ...toCheckout]); }, @@ -1521,7 +1528,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) { } @@ -1613,7 +1624,7 @@ export class Repository implements Disposable { } } - async getRefs(query: RefQuery = {}, cancellationToken?: CancellationToken): Promise { + async getRefs(query: RefQuery = {}, cancellationToken?: CancellationToken): Promise<(Ref | Branch)[]> { const config = workspace.getConfiguration('git'); let defaultSort = config.get<'alphabetically' | 'committerdate'>('branchSortOrder'); if (defaultSort !== 'alphabetically' && defaultSort !== 'committerdate') { @@ -1840,18 +1851,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,15 +1972,13 @@ 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, '/'); - try { - const content = await this.repository.buffer(`${ref}:${path}`); + const content = await this.repository.buffer(ref, filePath); return await workspace.decode(content, Uri.file(filePath)); } catch (err) { if (err.gitErrorCode === GitErrorCodes.WrongCase) { - const gitRelativePath = await this.repository.getGitRelativePath(ref, path); - const content = await this.repository.buffer(`${ref}:${gitRelativePath}`); + const gitFilePath = await this.repository.getGitFilePath(ref, filePath); + const content = await this.repository.buffer(ref, gitFilePath); return await workspace.decode(content, Uri.file(filePath)); } @@ -1985,21 +1988,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 14e4e379..ec7232be 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/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 90d8d28c..ae55d427 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -57,7 +57,6 @@ export class GitTimelineItem extends TimelineItem { setItemDetails(uri: Uri, hash: string | undefined, avatar: string | undefined, author: string, email: string | undefined, date: string, message: string, shortStat?: CommitShortStat, remoteSourceCommands: Command[] = []): void { this.tooltip = new MarkdownString('', true); this.tooltip.isTrusted = true; - this.tooltip.supportHtml = true; const avatarMarkdown = avatar ? `![${author}](${avatar}|width=${AVATAR_SIZE},height=${AVATAR_SIZE})` diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 1d330ea2..fc72bd70 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -11,7 +11,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts", - "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", "../../src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts", "../../src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts", "../../src/vscode-dts/vscode.proposed.quickInputButtonLocation.d.ts", diff --git a/extensions/html-language-features/package-lock.json b/extensions/html-language-features/package-lock.json index 20b14561..562d6751 100644 --- a/extensions/html-language-features/package-lock.json +++ b/extensions/html-language-features/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@vscode/extension-telemetry": "^0.9.8", - "vscode-languageclient": "^10.0.0-next.13", + "vscode-languageclient": "^10.0.0-next.14", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -168,50 +168,40 @@ } }, "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==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "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==", + "license": "MIT", "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.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -226,50 +216,48 @@ "dev": true }, "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.7", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.7.tgz", + "integrity": "sha512-7SgnbbbJfYr3off0T2KV/RCMYhVsuLeFPw8l3bkxSiavtoTLsOdu1jyxK3yWbdQuO8QOJC7+no0TXmYjRWSC+g==", + "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.14", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.14.tgz", + "integrity": "sha512-4m/cpNocRgrAkWc8IH4wd3zllAs16NvMmeGcQxFa6xt+mGXJASIeqp0NAFWKZERKg6ClVgBph+SDSZSVvNZ2oA==", "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.1", + "semver": "^7.6.3", + "vscode-languageserver-protocol": "3.17.6-next.12" }, "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.12.tgz", + "integrity": "sha512-EqrbwF0glTWD2HiDpFc32pJOr6/bJvyKSfCpRQrKy3XsfdloH4p3o/rNJYcpujM0OVLmPZgl1i9g57z9g2YRJA==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.7", + "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==" } } } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index be411fe6..d4356de4 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -259,7 +259,7 @@ }, "dependencies": { "@vscode/extension-telemetry": "^0.9.8", - "vscode-languageclient": "^10.0.0-next.13", + "vscode-languageclient": "^10.0.0-next.14", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package-lock.json b/extensions/html-language-features/server/package-lock.json index 5795436c..d8af9583 100644 --- a/extensions/html-language-features/server/package-lock.json +++ b/extensions/html-language-features/server/package-lock.json @@ -10,9 +10,9 @@ "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.3", + "vscode-html-languageservice": "^5.3.3", + "vscode-languageserver": "^10.0.0-next.12", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, @@ -51,9 +51,9 @@ "dev": true }, "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.3", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.3.tgz", + "integrity": "sha512-xXa+ftMPv6JxRgzkvPwZuDCafIdwDW3kyijGcfij1a2qBVScr2qli6MfgJzYm/AMYdbHq9I/4hdpKV0Thim2EA==", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", @@ -63,50 +63,53 @@ } }, "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.3.3", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.3.tgz", + "integrity": "sha512-AK/jJM0VIWRrlfqkDBMZxNMnxYT5I2uoMVRoNJ5ePSplnSaT9mbYjqJlxxeLvUrOW7MEH0vVIDzU48u44QZE0w==", "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.7", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.7.tgz", + "integrity": "sha512-7SgnbbbJfYr3off0T2KV/RCMYhVsuLeFPw8l3bkxSiavtoTLsOdu1jyxK3yWbdQuO8QOJC7+no0TXmYjRWSC+g==", + "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.12.tgz", + "integrity": "sha512-6lT2CJhH93YFmdDrFTwWvuG0/yzEN2Zbw/DfPaRF91sylZ3TSD0NkJU5jug6t/3NLoDh9VjfJZkgkKr6e3UmRw==", "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.12" }, "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.12.tgz", + "integrity": "sha512-EqrbwF0glTWD2HiDpFc32pJOr6/bJvyKSfCpRQrKy3XsfdloH4p3o/rNJYcpujM0OVLmPZgl1i9g57z9g2YRJA==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.7", + "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 +122,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 7017ee85..7eabd342 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,9 +10,9 @@ "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.3", + "vscode-html-languageservice": "^5.3.3", + "vscode-languageserver": "^10.0.0-next.12", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, @@ -23,7 +23,7 @@ "scripts": { "compile": "npx gulp compile-extension:html-language-features-server", "watch": "npx gulp watch-extension:html-language-features-server", - "install-service-next": "npm install vscode-css-languageservice@next && npm install vscode-html-languageservice@next", + "install-service-next": "npm install vscode-css-languageservice && npm install vscode-html-languageservice", "install-service-local": "npm install vscode-css-languageservice && npm install vscode-html-languageservice", "install-server-next": "npm install vscode-languageserver@next", "install-server-local": "npm install vscode-languageserver", diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index f7ee3c34..beb65118 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", @@ -144,7 +145,7 @@ }, { "command": "notebook.cellOutput.addToChat", - "when": "webviewId == 'notebook.output' && webviewSection == 'image'", + "when": "webviewId == 'notebook.output' && (webviewSection == 'text' || webviewSection == 'image')", "group": "context@2" }, { diff --git a/extensions/ipynb/src/constants.ts b/extensions/ipynb/src/constants.ts index 9a82ccfa..b7218596 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 beab091f..6d67b7d5 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 e4fe302d..cc55d39e 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/test/notebookModelStoreSync.test.ts b/extensions/ipynb/src/test/notebookModelStoreSync.test.ts index eab92167..7174678a 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/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index c2444c45..20827982 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": "(? = new RequestType('json/languageStatus'); } +namespace ValidateContentRequest { + export const type: RequestType<{ schemaUri: string; content: string }, LSPDiagnostic[], any> = new RequestType('json/validateContent'); +} interface SortOptions extends LSPFormattingOptions { } @@ -182,7 +186,7 @@ export async function startClient(context: ExtensionContext, newLanguageClient: }; } -async function startClientWithParticipants(context: ExtensionContext, languageParticipants: LanguageParticipants, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise { +async function startClientWithParticipants(_context: ExtensionContext, languageParticipants: LanguageParticipants, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise { const toDispose: Disposable[] = []; @@ -211,6 +215,10 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa window.showInformationMessage(l10n.t('JSON schema cache cleared.')); })); + toDispose.push(commands.registerCommand('json.validate', async (schemaUri: Uri, content: string) => { + const diagnostics: LSPDiagnostic[] = await client.sendRequest(ValidateContentRequest.type, { schemaUri: schemaUri.toString(), content }); + return diagnostics.map(client.protocol2CodeConverter.asDiagnostic); + })); toDispose.push(commands.registerCommand('json.sort', async () => { @@ -495,10 +503,19 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + client.sendNotification(SchemaAssociationNotification.type, await getSchemaAssociations()); - toDispose.push(extensions.onDidChange(_ => { - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + toDispose.push(extensions.onDidChange(async _ => { + client.sendNotification(SchemaAssociationNotification.type, await getSchemaAssociations()); + })); + + const associationWatcher = workspace.createFileSystemWatcher(new RelativePattern( + Uri.parse(`vscode://schemas-associations/`), + '**/schemas-associations.json') + ); + toDispose.push(associationWatcher); + toDispose.push(associationWatcher.onDidChange(async _e => { + client.sendNotification(SchemaAssociationNotification.type, await getSchemaAssociations()); })); // manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652. @@ -595,7 +612,12 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa }; } -function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] { +async function getSchemaAssociations(): Promise { + return getSchemaExtensionAssociations() + .concat(await getDynamicSchemaAssociations()); +} + +function getSchemaExtensionAssociations(): ISchemaAssociation[] { const associations: ISchemaAssociation[] = []; extensions.allAcrossExtensionHosts.forEach(extension => { const packageJSON = extension.packageJSON; @@ -631,6 +653,24 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] return associations; } +async function getDynamicSchemaAssociations(): Promise { + const result: ISchemaAssociation[] = []; + try { + const data = await workspace.fs.readFile(Uri.parse(`vscode://schemas-associations/schemas-associations.json`)); + const rawStr = new TextDecoder().decode(data); + const obj = >JSON.parse(rawStr); + for (const item of Object.keys(obj)) { + result.push({ + fileMatch: obj[item], + uri: item + }); + } + } catch { + // ignore + } + return result; +} + function getSettings(): Settings { const configuration = workspace.getConfiguration(); const httpSettings = workspace.getConfiguration('http'); @@ -735,3 +775,5 @@ function updateMarkdownString(h: MarkdownString): MarkdownString { function isSchemaResolveError(d: Diagnostic) { return d.code === /* SchemaResolveError */ 0x300; } + + diff --git a/extensions/json-language-features/client/src/languageStatus.ts b/extensions/json-language-features/client/src/languageStatus.ts index 6f3d7468..1064a0b5 100644 --- a/extensions/json-language-features/client/src/languageStatus.ts +++ b/extensions/json-language-features/client/src/languageStatus.ts @@ -185,13 +185,13 @@ export function createLanguageStatusItem(documentSelector: DocumentSelector, sta const schemas = (await statusRequest(document.uri.toString())).schemas; statusItem.detail = undefined; if (schemas.length === 0) { - statusItem.text = l10n.t('No Schema Validation'); + statusItem.text = l10n.t('No schema validation'); statusItem.detail = l10n.t('no JSON schema configured'); } else if (schemas.length === 1) { - statusItem.text = l10n.t('Schema Validated'); + statusItem.text = l10n.t('Schema validated'); statusItem.detail = l10n.t('JSON schema configured'); } else { - statusItem.text = l10n.t('Schema Validated'); + statusItem.text = l10n.t('Schema validated'); statusItem.detail = l10n.t('multiple JSON schemas configured'); } statusItem.command = { diff --git a/extensions/json-language-features/package-lock.json b/extensions/json-language-features/package-lock.json index bde80cbf..85f31440 100644 --- a/extensions/json-language-features/package-lock.json +++ b/extensions/json-language-features/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@vscode/extension-telemetry": "^0.9.8", "request-light": "^0.8.0", - "vscode-languageclient": "^10.0.0-next.13" + "vscode-languageclient": "^10.0.0-next.14" }, "devDependencies": { "@types/node": "20.x" @@ -168,38 +168,30 @@ } }, "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==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "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==", + "license": "MIT", "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.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -211,12 +203,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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -231,45 +221,43 @@ "dev": true }, "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.7", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.7.tgz", + "integrity": "sha512-7SgnbbbJfYr3off0T2KV/RCMYhVsuLeFPw8l3bkxSiavtoTLsOdu1jyxK3yWbdQuO8QOJC7+no0TXmYjRWSC+g==", + "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.14", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.14.tgz", + "integrity": "sha512-4m/cpNocRgrAkWc8IH4wd3zllAs16NvMmeGcQxFa6xt+mGXJASIeqp0NAFWKZERKg6ClVgBph+SDSZSVvNZ2oA==", "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.1", + "semver": "^7.6.3", + "vscode-languageserver-protocol": "3.17.6-next.12" }, "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.12.tgz", + "integrity": "sha512-EqrbwF0glTWD2HiDpFc32pJOr6/bJvyKSfCpRQrKy3XsfdloH4p3o/rNJYcpujM0OVLmPZgl1i9g57z9g2YRJA==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.7", + "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 cf4a7f16..375afed0 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -16,7 +16,8 @@ "activationEvents": [ "onLanguage:json", "onLanguage:jsonc", - "onLanguage:snippets" + "onLanguage:snippets", + "onCommand:json.validate" ], "main": "./client/out/node/jsonClientMain", "browser": "./client/dist/browser/jsonClientMain", @@ -169,7 +170,7 @@ "dependencies": { "@vscode/extension-telemetry": "^0.9.8", "request-light": "^0.8.0", - "vscode-languageclient": "^10.0.0-next.13" + "vscode-languageclient": "^10.0.0-next.14" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/json-language-features/server/package-lock.json b/extensions/json-language-features/server/package-lock.json index 384ce045..00361def 100644 --- a/extensions/json-language-features/server/package-lock.json +++ b/extensions/json-language-features/server/package-lock.json @@ -12,8 +12,8 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "request-light": "^0.8.0", - "vscode-json-languageservice": "^5.4.3", - "vscode-languageserver": "^10.0.0-next.11", + "vscode-json-languageservice": "^5.4.4", + "vscode-languageserver": "^10.0.0-next.12", "vscode-uri": "^3.0.8" }, "bin": { @@ -64,51 +64,54 @@ "dev": true }, "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.4.4", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.4.tgz", + "integrity": "sha512-dgT16da8VznFv0IrEpBSKYvi29gxnMf5EOq+UfZSPaCiLZ65kgVOo3vMJSPNbZK8557YYbQH/fpMxxa4wRPAQw==", "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.7", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.7.tgz", + "integrity": "sha512-7SgnbbbJfYr3off0T2KV/RCMYhVsuLeFPw8l3bkxSiavtoTLsOdu1jyxK3yWbdQuO8QOJC7+no0TXmYjRWSC+g==", + "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.12.tgz", + "integrity": "sha512-6lT2CJhH93YFmdDrFTwWvuG0/yzEN2Zbw/DfPaRF91sylZ3TSD0NkJU5jug6t/3NLoDh9VjfJZkgkKr6e3UmRw==", "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.12" }, "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.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.12.tgz", + "integrity": "sha512-EqrbwF0glTWD2HiDpFc32pJOr6/bJvyKSfCpRQrKy3XsfdloH4p3o/rNJYcpujM0OVLmPZgl1i9g57z9g2YRJA==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.7", + "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 +124,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 6dcd8293..f503a7e1 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -15,8 +15,8 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "request-light": "^0.8.0", - "vscode-json-languageservice": "^5.4.3", - "vscode-languageserver": "^10.0.0-next.11", + "vscode-json-languageservice": "^5.4.4", + "vscode-languageserver": "^10.0.0-next.12", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -28,7 +28,7 @@ "compile": "npx gulp compile-extension:json-language-features-server", "watch": "npx gulp watch-extension:json-language-features-server", "clean": "../../../node_modules/.bin/rimraf out", - "install-service-next": "npm install vscode-json-languageservice@next", + "install-service-next": "npm install vscode-json-languageservice", "install-service-latest": "npm install vscode-json-languageservice", "install-service-local": "npm link vscode-json-languageservice", "install-server-next": "npm install vscode-languageserver@next", diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 36ca0dc5..6a806b72 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -40,6 +40,10 @@ namespace LanguageStatusRequest { export const type: RequestType = new RequestType('json/languageStatus'); } +namespace ValidateContentRequest { + export const type: RequestType<{ schemaUri: string; content: string }, Diagnostic[], any> = new RequestType('json/validateContent'); +} + export interface DocumentSortingParams { /** * The uri of the document to sort. @@ -299,6 +303,14 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return []; }); + connection.onRequest(ValidateContentRequest.type, async ({ schemaUri, content }) => { + const docURI = 'vscode://schemas/temp/' + new Date().getTime(); + const document = TextDocument.create(docURI, 'json', 1, content); + updateConfiguration([{ uri: schemaUri, fileMatch: [docURI] }]); + return await validateTextDocument(document); + }); + + connection.onRequest(LanguageStatusRequest.type, async uri => { const document = documents.get(uri); if (document) { @@ -319,7 +331,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return []; }); - function updateConfiguration() { + function updateConfiguration(extraSchemas?: SchemaConfiguration[]) { const languageSettings = { validate: validateEnabled, allowComments: true, @@ -350,6 +362,10 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } }); } + if (extraSchemas) { + languageSettings.schemas.push(...extraSchemas); + } + languageService.configure(languageSettings); diagnosticsSupport?.requestRefresh(); @@ -529,3 +545,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) function getFullRange(document: TextDocument): Range { return Range.create(Position.create(0, 0), document.positionAt(document.getText().length)); } + + + + diff --git a/extensions/mangle-loader.js b/extensions/mangle-loader.js index 73d62576..016d0f69 100644 --- a/extensions/mangle-loader.js +++ b/extensions/mangle-loader.js @@ -37,7 +37,7 @@ function getMangledFileContents(projectPath) { * @type {webpack.LoaderDefinitionFunction} */ module.exports = async function (source, sourceMap, meta) { - if (true) { // Void - extensions-disable-mangler + if (this.mode !== 'production') { // Only enable mangling in production builds return source; } diff --git a/extensions/markdown-language-features/package-lock.json b/extensions/markdown-language-features/package-lock.json index 63d93c40..2b8430bb 100644 --- a/extensions/markdown-language-features/package-lock.json +++ b/extensions/markdown-language-features/package-lock.json @@ -14,12 +14,12 @@ "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.4", - "morphdom": "^2.6.1", + "morphdom": "^2.7.4", "picomatch": "^2.3.1", "punycode": "^2.3.1", "vscode-languageclient": "^8.0.2", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-markdown-languageserver": "^0.5.0-alpha.9", + "vscode-markdown-languageserver": "^0.5.0-alpha.10", "vscode-uri": "^3.0.3" }, "devDependencies": { @@ -275,7 +275,8 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -305,6 +306,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -320,6 +322,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -331,6 +334,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -349,12 +353,14 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -375,9 +381,10 @@ } }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -391,6 +398,7 @@ "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" }, @@ -402,6 +410,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", "bin": { "he": "bin/he" } @@ -501,14 +510,16 @@ } }, "node_modules/morphdom": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.6.1.tgz", - "integrity": "sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==" + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.4.tgz", + "integrity": "sha512-ATTbWMgGa+FaMU3FhnFYB6WgulCqwf6opOll4CBzmVDTLvPMmUPrEv8CudmLPK0MESa64+6B89fWOxP3+YIlxQ==", + "license": "MIT" }, "node_modules/node-html-parser": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", + "license": "MIT", "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" @@ -518,6 +529,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -637,15 +649,16 @@ "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.10", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageserver/-/vscode-markdown-languageserver-0.5.0-alpha.10.tgz", + "integrity": "sha512-e/Vzd2M34CvmkFYAF+MVh/mF2jmZQ8lQoxMB9V14JUs5UXbyJO2avTv6XF3GEB0EfHlQQ7qVOlCRghzPyskB8A==", + "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.9", "vscode-uri": "^3.0.7" }, "engines": { @@ -658,9 +671,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "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.9", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.9.tgz", + "integrity": "sha512-OrE8homBOuXX9FOUhqRXgx/Iw0qA94yj3FBRSMztn8VveeO1Y0Eqej/9HBb5ga4sYdlFtQRIZ19lie37TsI+cQ==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.10", "node-html-parser": "^6.1.5", @@ -676,7 +690,8 @@ "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", diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 51b9f80e..5d76a322 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -768,12 +768,12 @@ "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.4", - "morphdom": "^2.6.1", + "morphdom": "^2.7.4", "picomatch": "^2.3.1", "punycode": "^2.3.1", "vscode-languageclient": "^8.0.2", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-markdown-languageserver": "^0.5.0-alpha.9", + "vscode-markdown-languageserver": "^0.5.0-alpha.10", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 08c6759f..6c70e58e 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -68,7 +68,14 @@ onceDocumentLoaded(() => { getRawData('data-initial-md-content'), 'text/html' ); - document.body.appendChild(markDownHtml.body); + + const newElements = [...markDownHtml.body.children]; + document.body.append(...newElements); + for (const el of newElements) { + if (el instanceof HTMLElement) { + domEval(el); + } + } // Restore const scrollProgress = state.scrollProgress; @@ -177,6 +184,17 @@ async function copyImage(image: HTMLImageElement, retries = 5) { })]); } catch (e) { console.error(e); + const selection = window.getSelection(); + if (!selection) { + await navigator.clipboard.writeText(image.getAttribute('data-src') ?? image.src); + return; + } + selection.removeAllRanges(); + const range = document.createRange(); + range.selectNode(image); + selection.addRange(range); + document.execCommand('copy'); + selection.removeAllRanges(); } } @@ -216,46 +234,11 @@ window.addEventListener('message', async event => { } if (data.source !== documentResource) { - root.replaceWith(newContent.querySelector('.markdown-body')!); documentResource = data.source; + const newBody = newContent.querySelector('.markdown-body')!; + root.replaceWith(newBody); + domEval(newBody); } else { - const skippedAttrs = [ - 'open', // for details - ]; - - // Compare two elements but some elements - const areEqual = (a: Element, b: Element): boolean => { - if (a.isEqualNode(b)) { - return true; - } - - if (a.tagName !== b.tagName || a.textContent !== b.textContent) { - return false; - } - - const aAttrs = [...a.attributes].filter(attr => !skippedAttrs.includes(attr.name)); - const bAttrs = [...b.attributes].filter(attr => !skippedAttrs.includes(attr.name)); - if (aAttrs.length !== bAttrs.length) { - return false; - } - - for (let i = 0; i < aAttrs.length; ++i) { - const aAttr = aAttrs[i]; - const bAttr = bAttrs[i]; - if (aAttr.name !== bAttr.name) { - return false; - } - if (aAttr.value !== bAttr.value && aAttr.name !== 'data-line') { - return false; - } - } - - const aChildren = Array.from(a.children); - const bChildren = Array.from(b.children); - - return aChildren.length === bChildren.length && aChildren.every((x, i) => areEqual(x, bChildren[i])); - }; - const newRoot = newContent.querySelector('.markdown-body')!; // Move styles to head @@ -265,10 +248,11 @@ window.addEventListener('message', async event => { style.remove(); } newRoot.prepend(...styles); + morphdom(root, newRoot, { childrenOnly: true, - onBeforeElUpdated: (fromEl, toEl) => { - if (areEqual(fromEl, toEl)) { + onBeforeElUpdated: (fromEl: Element, toEl: Element) => { + if (areNodesEqual(fromEl, toEl)) { // areEqual doesn't look at `data-line` so copy those over manually const fromLines = fromEl.querySelectorAll('[data-line]'); const toLines = toEl.querySelectorAll('[data-line]'); @@ -294,8 +278,14 @@ window.addEventListener('message', async event => { } return true; + }, + addChild: (parentNode: Node, childNode: Node) => { + parentNode.appendChild(childNode); + if (childNode instanceof HTMLElement) { + domEval(childNode); + } } - }); + } as any); } ++documentVersion; @@ -383,3 +373,72 @@ function updateScrollProgress() { vscode.setState(state); } + +/** + * Compares two nodes for morphdom to see if they are equal. + * + * This skips some attributes that should not cause equality to fail. + */ +function areNodesEqual(a: Element, b: Element): boolean { + const skippedAttrs = [ + 'open', // for details + ]; + + if (a.isEqualNode(b)) { + return true; + } + + if (a.tagName !== b.tagName || a.textContent !== b.textContent) { + return false; + } + + const aAttrs = [...a.attributes].filter(attr => !skippedAttrs.includes(attr.name)); + const bAttrs = [...b.attributes].filter(attr => !skippedAttrs.includes(attr.name)); + if (aAttrs.length !== bAttrs.length) { + return false; + } + + for (let i = 0; i < aAttrs.length; ++i) { + const aAttr = aAttrs[i]; + const bAttr = bAttrs[i]; + if (aAttr.name !== bAttr.name) { + return false; + } + if (aAttr.value !== bAttr.value && aAttr.name !== 'data-line') { + return false; + } + } + + const aChildren = Array.from(a.children); + const bChildren = Array.from(b.children); + + return aChildren.length === bChildren.length && aChildren.every((x, i) => areNodesEqual(x, bChildren[i])); +} + + +function domEval(el: Element): void { + const preservedScriptAttributes: (keyof HTMLScriptElement)[] = [ + 'type', 'src', 'nonce', 'noModule', 'async', + ]; + + const scriptNodes = el.tagName === 'SCRIPT' ? [el] : Array.from(el.getElementsByTagName('script')); + + for (const node of scriptNodes) { + if (!(node instanceof HTMLElement)) { + continue; + } + + const scriptTag = document.createElement('script'); + const trustedScript = node.innerText; + scriptTag.text = trustedScript as string; + for (const key of preservedScriptAttributes) { + const val = node.getAttribute && node.getAttribute(key); + if (val) { + scriptTag.setAttribute(key, val as any); + } + } + + node.insertAdjacentElement('afterend', scriptTag); + node.remove(); + } +} diff --git a/extensions/microsoft-authentication/package-lock.json b/extensions/microsoft-authentication/package-lock.json index 4aae40c5..5024815f 100644 --- a/extensions/microsoft-authentication/package-lock.json +++ b/extensions/microsoft-authentication/package-lock.json @@ -71,9 +71,9 @@ } }, "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.18.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.18.1.tgz", + "integrity": "sha512-vaUkpSiXD33/iDyZt1VZDEyOxvlNMT5o9D4ruIqkUmULyKgUik0y86DK2dsqZql/LU04T5siuq1AMTus+15SvA==", "hasInstallScript": true, "license": "MIT" }, diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index 4170a778..f9eb8198 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" - ] } } } @@ -162,6 +145,9 @@ "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, + "overrides": { + "@azure/msal-node-runtime": "^0.18.1" + }, "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" diff --git a/extensions/microsoft-authentication/package.nls.json b/extensions/microsoft-authentication/package.nls.json index ece95ac7..c8e0189c 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 a8fdeefe..27d56b0b 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 91b4f0ee..b87fdb78 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 925a4d1a..acc8ba0d 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 a43f2c43..88a0aad6 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 getScopesToSend(scopes: string[]) { + 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[]): 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 9250d7ce..4c70e4fd 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 9610af37..978603ad 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 40000e08..a26008fb 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 8d081f1a..f0f4eb7b 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 c6f40a94..ac678d83 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 f4f19ff8..16ccb803 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/terminal-suggest/.vscodeignore b/extensions/terminal-suggest/.vscodeignore index f05a7941..21df0d1f 100644 --- a/extensions/terminal-suggest/.vscodeignore +++ b/extensions/terminal-suggest/.vscodeignore @@ -5,3 +5,6 @@ tsconfig.json extension.webpack.config.js extension-browser.webpack.config.js package-lock.json +fixtures/** +scripts/** +testWorkspace/** diff --git a/extensions/terminal-suggest/ThirdPartyNotices.txt b/extensions/terminal-suggest/ThirdPartyNotices.txt index 761d5b01..f6de57f6 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 f39b1fae..ead3479e 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 dcc02bff..5ce429f4 100644 --- a/extensions/terminal-suggest/package.json +++ b/extensions/terminal-suggest/package.json @@ -15,13 +15,13 @@ ], "enabledApiProposals": [ "terminalCompletionProvider", - "terminalShellEnv", - "terminalShellType" + "terminalShellEnv" ], "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 00000000..8ac6b870 --- /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 cedfc85c..a05866b1 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 = ''; @@ -259,10 +254,4 @@ const main = async () => { } }; -const copyright = ` -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/`; - main(); diff --git a/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts b/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts new file mode 100644 index 00000000..402e6d5d --- /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 5f6e0ec5..775c5dd3 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 89d01dc5..1eb4f04a 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-tunnel-insiders.ts b/extensions/terminal-suggest/src/completions/code-tunnel-insiders.ts new file mode 100644 index 00000000..dd54a243 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/code-tunnel-insiders.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 { commonOptions, extensionManagementOptions, troubleshootingOptions, globalTunnelOptions, codeTunnelSubcommands, extTunnelSubcommand, codeTunnelOptions } from './code'; +import codeTunnelCompletionSpec, { codeTunnelSpecOptions } from './code-tunnel'; + +const codeTunnelInsidersCompletionSpec: Fig.Spec = { + ...codeTunnelCompletionSpec, + name: 'code-tunnel-insiders', + description: 'Create a tunnel that\'s accessible on vscode.dev from anywhere, with insider features.', + subcommands: [...codeTunnelSubcommands, extTunnelSubcommand], + options: [ + ...commonOptions, + ...extensionManagementOptions('code-tunnel-insiders'), + ...troubleshootingOptions('code-tunnel-insiders'), + ...globalTunnelOptions, + ...codeTunnelOptions, + ...codeTunnelSpecOptions + ] +}; + +export default codeTunnelInsidersCompletionSpec; diff --git a/extensions/terminal-suggest/src/completions/code-tunnel.ts b/extensions/terminal-suggest/src/completions/code-tunnel.ts new file mode 100644 index 00000000..a53edd52 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/code-tunnel.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import code, { codeTunnelSubcommands, commonOptions, extensionManagementOptions, troubleshootingOptions, globalTunnelOptions, extTunnelSubcommand, codeTunnelOptions } from './code'; + + +export const codeTunnelSpecOptions: Fig.Option[] = [ + { + name: '--cli-data-dir', + description: 'Directory where CLI metadata should be stored', + isRepeatable: true, + args: { + name: 'cli_data_dir', + isOptional: true, + }, + }, + { + name: '--log-to-file', + description: 'Log to a file in addition to stdout. Used when running as a service', + hidden: true, + isRepeatable: true, + args: { + name: 'log_to_file', + isOptional: true, + template: 'filepaths', + }, + }, + { + name: '--log', + description: 'Log level to use', + isRepeatable: true, + args: { + name: 'log', + isOptional: true, + suggestions: [ + 'trace', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'off', + ], + }, + }, + { + name: '--telemetry-level', + description: 'Sets the initial telemetry level', + hidden: true, + isRepeatable: true, + args: { + name: 'telemetry_level', + isOptional: true, + suggestions: [ + 'off', + 'crash', + 'error', + 'all', + ], + }, + }, + { + name: '--verbose', + description: 'Print verbose output (implies --wait)', + }, + { + name: '--disable-telemetry', + description: 'Disable telemetry for the current command, even if it was previously accepted as part of the license prompt or specified in \'--telemetry-level\'', + }, + { + name: ['-h', '--help'], + description: 'Print help', + }, +]; + +const codeTunnelCompletionSpec: Fig.Spec = { + ...code, + name: 'code-tunnel', + description: 'Create a tunnel that\'s accessible on vscode.dev from anywhere.', + subcommands: [ + ...codeTunnelSubcommands, + extTunnelSubcommand + ], + options: [ + ...commonOptions, + ...extensionManagementOptions('code-tunnel'), + ...troubleshootingOptions('code-tunnel'), + ...globalTunnelOptions, + ...codeTunnelOptions + ] +}; + +export default codeTunnelCompletionSpec; diff --git a/extensions/terminal-suggest/src/completions/code.ts b/extensions/terminal-suggest/src/completions/code.ts index c514a32e..1ae5957a 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,644 @@ 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; +} + +export const commonAuthOptions: Fig.Option[] = [ + { + name: '--access-token', + description: 'An access token to store for authentication', + isRepeatable: true, + args: { + name: 'access_token', + isOptional: true, + }, + }, + { + name: '--refresh-token', + description: 'An access token to store for authentication', + isRepeatable: true, + args: { + name: 'refresh_token', + isOptional: true, + }, + }, + { + name: '--provider', + description: 'The auth provider to use. If not provided, a prompt will be shown', + isRepeatable: true, + args: { + name: 'provider', + isOptional: true, + suggestions: [ + 'microsoft', + 'github', + ], + }, + } +]; + +export const tunnelHelpOptions: Fig.Option[] = [ + { + name: ['-h', '--help'], + description: 'Print help', + }, +]; + +export const globalTunnelOptions: Fig.Option[] = [ + { + name: '--cli-data-dir', + description: 'Directory where CLI metadata should be stored', + args: { + name: 'cli_data_dir', + }, + }, + { + name: '--verbose', + description: 'Print verbose output (implies --wait)', + }, + { + name: '--log', + description: 'Log level to use', + isRepeatable: true, + args: { + name: 'log', + isOptional: true, + suggestions: [ + 'trace', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'off', + ], + }, + }, +]; + + +export const codeTunnelOptions = [ + { + name: '--extensions-dir', + description: 'Set the root path for extensions', + isRepeatable: true, + args: { + name: 'extensions_dir', + isOptional: true, + }, + }, + { + name: '--user-data-dir', + description: 'Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of the editor', + isRepeatable: true, + args: { + name: 'user_data_dir', + isOptional: true, + }, + }, + { + name: '--use-version', + description: 'Sets the editor version to use for this command. The preferred version can be persisted with `code version use `. Can be \'stable\', \'insiders\', a version number, or an absolute path to an existing install', + isRepeatable: true, + args: { + name: 'use_version', + isOptional: true, + }, + }, +]; + +export const extTunnelSubcommand = { + name: 'ext', + description: 'Manage editor extensions', + subcommands: [ + { + name: 'list', + description: 'List installed extensions', + options: [...globalTunnelOptions, ...tunnelHelpOptions, + { + name: '--category', + description: 'Filters installed extensions by provided category, when using --list-extensions', + isRepeatable: true, + args: { + name: 'category', + isOptional: true, + }, + }, + { + name: '--show-versions', + description: 'Show versions of installed extensions, when using --list-extensions', + }, + ] + }, + { + name: 'install', + description: 'Install an extension', + options: [...globalTunnelOptions, ...tunnelHelpOptions, + { + name: '--pre-release', + description: 'Installs the pre-release version of the extension', + }, + { + name: '--donot-include-pack-and-dependencies', + description: `Don't include installing pack and dependencies of the extension`, + }, + { + name: '--force', + description: `Update to the latest version of the extension if it's already installed`, + }, + ], + args: { + name: 'ext-id | id', + isVariadic: true, + isOptional: true, + }, + }, + { + name: 'uninstall', + description: 'Uninstall an extension', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + args: { + name: 'ext-id | id', + isVariadic: true, + isOptional: true, + }, + }, + { + name: 'update', + description: 'Update the installed extensions', + options: [...globalTunnelOptions, ...tunnelHelpOptions] + }, + ], + ...globalTunnelOptions, + ...codeTunnelOptions +}; + + +export const codeTunnelSubcommands = [ + { + name: 'tunnel', + description: 'Create a tunnel that\'s accessible on vscode.dev from anywhere. Run`code tunnel --help` for more usage info', + subcommands: [ + { + name: 'prune', + description: 'Delete all servers which are currently not running', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'kill', + description: 'Stops any running tunnel on the system', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'restart', + description: 'Restarts any running tunnel on the system', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'status', + description: 'Gets whether there is a tunnel running on the current machine', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'rename', + description: 'Rename the name of this machine associated with port forwarding service', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + args: { + name: 'name', + }, + }, + { + name: 'status', + description: 'Print process usage and diagnostics information', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'unregister', + description: 'Remove this machine\'s association with the port forwarding service', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'user', + subcommands: [ + { + name: 'login', + description: 'Log in to port forwarding service', + options: [...globalTunnelOptions, ...tunnelHelpOptions, ...commonAuthOptions], + }, + { + name: 'logout', + description: 'Log out of port forwarding service', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'show', + description: 'Show the account that\'s logged into port forwarding service', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'help', + description: 'Print this message or the help of the given subcommand(s)', + subcommands: [ + { name: 'login', description: 'Log in to port forwarding service' }, + { name: 'logout', description: 'Log out of port forwarding service' }, + { name: 'show', description: 'Show the account that\'s logged into port forwarding service' }, + { name: 'help', description: 'Print this message or the help of the given subcommand(s)' }, + ], + }, + ], + }, + { + name: 'service', + description: '(Preview) Manages the tunnel when installed as a system service,', + subcommands: [ + { + name: 'install', + description: 'Installs or re-installs the tunnel service on the machine', + options: [ + { + name: '--name', + description: 'Sets the machine name for port forwarding service', + + args: { + name: 'name', + + }, + }, + { + name: '--accept-server-license-terms', + description: 'If set, the user accepts the server license terms and the server will be started without a user prompt', + }, + ...globalTunnelOptions, ...tunnelHelpOptions + ], + }, + { + name: 'uninstall', + description: 'Uninstalls and stops the tunnel service', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'log', + description: 'Shows logs for the running service', + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'help', + description: 'Print this message or the help of the given subcommand(s)', + subcommands: [ + { name: 'install', description: 'Installs or re-installs the tunnel service on the machine' }, + { name: 'uninstall', description: 'Uninstalls and stops the tunnel service' }, + { name: 'log', description: 'Shows logs for the running service' }, + { name: 'help', description: 'Print this message or the help of the given subcommand(s)' }, + ], + }, + ], + options: [...globalTunnelOptions, ...tunnelHelpOptions], + }, + { + name: 'help', + description: 'Print this message or the help of the given subcommand(s)', + subcommands: [ + { name: 'prune', description: 'Delete all servers which are currently not running' }, + { name: 'kill', description: 'Stops any running tunnel on the system' }, + { name: 'restart', description: 'Restarts any running tunnel on the system' }, + { name: 'status', description: 'Gets whether there is a tunnel running on the current machine' }, + { name: 'rename', description: 'Rename the name of this machine associated with port forwarding service' }, + { name: 'unregister', description: 'Remove this machine\'s association with the port forwarding service' }, + { + name: 'user', + subcommands: [ + { name: 'login', description: 'Log in to port forwarding service' }, + { name: 'logout', description: 'Log out of port forwarding service' }, + { name: 'show', description: 'Show the account that\'s logged into port forwarding service' }, + ], + }, + { + name: 'service', + description: '(Preview) Manages the tunnel when installed as a system service,', + subcommands: [ + { name: 'install', description: 'Installs or re-installs the tunnel service on the machine' }, + { name: 'uninstall', description: 'Uninstalls and stops the tunnel service' }, + { name: 'log', description: 'Shows logs for the running service' }, + ], + }, + { name: 'help', description: 'Print this message or the help of the given subcommand(s)' }, + ], + }, + ], + options: [ + { + name: '--install-extension', + description: 'Requests that extensions be preloaded and installed on connecting servers', + isRepeatable: true, + args: { + name: 'install_extension', + isOptional: true, + }, + }, + { + name: '--server-data-dir', + description: 'Specifies the directory that server data is kept in', + isRepeatable: true, + args: { + name: 'server_data_dir', + isOptional: true, + }, + }, + { + name: '--extensions-dir', + description: 'Set the root path for extensions', + isRepeatable: true, + args: { + name: 'extensions_dir', + isOptional: true, + }, + }, + { + name: '--user-data-dir', + description: 'Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of the editor', + isRepeatable: true, + args: { + name: 'user_data_dir', + isOptional: true, + }, + }, + { + name: '--use-version', + description: 'Sets the editor version to use for this command. The preferred version can be persisted with `code version use `. Can be \'stable\', \'insiders\', a version number, or an absolute path to an existing install', + isRepeatable: true, + args: { + name: 'use_version', + isOptional: true, + }, + }, + { + name: '--random-name', + description: 'Randomly name machine for port forwarding service', + }, + { + name: '--no-sleep', + description: 'Prevents the machine going to sleep while this command runs', + }, + { + name: '--accept-server-license-terms', + description: 'If set, the user accepts the server license terms and the server will be started without a user prompt', + }, + { + name: '--name', + description: 'Sets the machine name for port forwarding service', + isRepeatable: true, + args: { + name: 'name', + isOptional: true, + }, + }, + { + name: ['-h', '--help'], + description: 'Print help', + }, + { + name: '--log', + description: 'Log level to use', + isRepeatable: true, + args: { + name: 'log', + isOptional: true, + suggestions: [ + 'trace', + 'debug', + 'info', + 'warn', + 'error', + 'critical', + 'off', + ], + }, + }, + { + name: '--verbose', + description: 'Print verbose output (implies --wait)', + }, + { + name: '--cli-data-dir', + description: 'Directory where CLI metadata should be stored', + args: { + name: 'cli_data_dir', + }, + }, + ], + }, + { + name: 'status', + description: 'Print process usage and diagnostics information', + }, + { + name: 'version', + description: `Changes the version of the editor you're using`, + options: globalTunnelOptions + }, + { + name: 'serve-web', + description: 'Runs a local web version of Code - OSS', + options: [ + { + name: '--host', + description: 'Host to listen on, defaults to \'localhost\'', + isRepeatable: true, + args: { + name: 'host', + isOptional: true, + }, + }, + { + name: '--socket-path', + isRepeatable: true, + args: { + name: 'socket_path', + isOptional: true, + }, + }, + { + name: '--port', + description: 'Port to listen on. If 0 is passed a random free port is picked', + isRepeatable: true, + args: { + name: 'port', + isOptional: true, + }, + }, + { + name: '--connection-token', + description: 'A secret that must be included with all requests', + isRepeatable: true, + args: { + name: 'connection_token', + isOptional: true, + }, + }, + { + name: '--connection-token-file', + description: 'A file containing a secret that must be included with all requests', + isRepeatable: true, + args: { + name: 'connection_token_file', + isOptional: true, + }, + }, + { + name: '--server-base-path', + description: 'Specifies the path under which the web UI and the code server is provided', + isRepeatable: true, + args: { + name: 'server_base_path', + isOptional: true, + }, + }, + { + name: '--server-data-dir', + description: 'Specifies the directory that server data is kept in', + isRepeatable: true, + args: { + name: 'server_data_dir', + isOptional: true, + }, + }, + { + name: '--without-connection-token', + description: 'Run without a connection token. Only use this if the connection is secured by other means', + }, + { + name: '--accept-server-license-terms', + description: 'If set, the user accepts the server license terms and the server will be started without a user prompt', + }, + ...globalTunnelOptions, ...tunnelHelpOptions, + ] + }, + { + name: 'help', + description: 'Print this message or the help of the given subcommand(s)', + subcommands: [ + { + name: 'tunnel', + description: 'Create a tunnel that\'s accessible on vscode.dev from anywhere. Run`code tunnel --help` for more usage info', + subcommands: [ + { + name: 'prune', + description: 'Delete all servers which are currently not running', + }, + { + name: 'kill', + description: 'Stops any running tunnel on the system', + }, + { + name: 'restart', + description: 'Restarts any running tunnel on the system', + }, + { + name: 'status', + description: 'Gets whether there is a tunnel running on the current machine', + }, + { + name: 'rename', + description: 'Rename the name of this machine associated with port forwarding service', + }, + { + name: 'unregister', + description: `Remove this machine's association with the port forwarding service`, + }, + { + name: 'user', + subcommands: [ + { + name: 'login', + description: 'Log in to port forwarding service', + }, + { + name: 'logout', + description: 'Log out of port forwarding service', + }, + { + name: 'show', + description: 'Show the account that\'s logged into port forwarding service', + }, + ], + }, + { + name: 'service', + description: '(Preview) Manages the tunnel when installed as a system service,', + subcommands: [ + { + name: 'install', + description: 'Installs or re-installs the tunnel service on the machine', + }, + { + name: 'uninstall', + description: 'Uninstalls and stops the tunnel service', + }, + { + name: 'log', + description: 'Shows logs for the running service', + }, + ], + } + ], + }, + extTunnelSubcommand, + { + name: 'status', + description: 'Print process usage and diagnostics information', + }, + { + name: 'version', + description: `Changes the version of the editor you're using`, + subcommands: [ + { + name: 'use', + description: 'Switches the version of the editor in use', + }, + { + name: 'show', + description: 'Shows the currently configured editor version', + }, + ], + }, + { + name: 'serve-web', + description: 'Runs a local web version of Code - OSS', + }, + { + name: 'command-shell', + description: 'Runs the control server on process stdin/stdout', + hidden: true, + }, + { + name: 'update', + description: 'Updates the CLI', + }, + { + name: 'help', + description: 'Print this message or the help of the given subcommand(s)', + }, + ], + }, +]; + const codeCompletionSpec: Fig.Spec = { name: 'code', description: 'Visual Studio Code', @@ -308,11 +968,13 @@ const codeCompletionSpec: Fig.Spec = { template: ['filepaths', 'folders'], isVariadic: true, }, + subcommands: codeTunnelSubcommands, options: [ ...commonOptions, - ...extensionManagementOptions, - ...troubleshootingOptions, + ...extensionManagementOptions('code'), + ...troubleshootingOptions('code'), ], }; export default codeCompletionSpec; + diff --git a/extensions/terminal-suggest/src/completions/npx.ts b/extensions/terminal-suggest/src/completions/npx.ts new file mode 100644 index 00000000..36010391 --- /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 e3ebbb89..2d44332b 100644 --- a/extensions/terminal-suggest/src/completions/upstream/apt.ts +++ b/extensions/terminal-suggest/src/completions/upstream/apt.ts @@ -134,10 +134,7 @@ const completionSpec: Fig.Spec = { name: "package", description: "The package you want to install", isVariadic: true, - generators: [ - packages, - filepaths({ extensions: ["deb"] }) - ], + generators: [packages, filepaths({ extensions: ["deb"] })], }, options: [ ...installationOptions, diff --git a/extensions/terminal-suggest/src/completions/upstream/brew.ts b/extensions/terminal-suggest/src/completions/upstream/brew.ts index 99b87c45..4e0f0cbe 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 4e06dacd..b67e19b9 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: "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: "-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: "-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: "-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: "-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: "-t", + description: + "Display non-printing characters (see the -v option), and display tab characters as ‘^I’", + }, - { name: "-u", description: "Disable output buffering" }, + { 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: "-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 93bc7264..c3b72a01 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 c271be0b..3ee4f0f0 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 65d3d065..91a05f48 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 d47732b6..eb6cfd31 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 e7e00546..42f577d8 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 833fa526..ff3d6e42 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 8ca21b85..7b1452e2 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 5b5245cb..08b4d6a5 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 62d90d63..56468610 100644 --- a/extensions/terminal-suggest/src/completions/upstream/git.ts +++ b/extensions/terminal-suggest/src/completions/upstream/git.ts @@ -1,4 +1,4 @@ -// import { ai } from "../../fig/ai/ai"; +function ai(...args: any[]): undefined { return undefined; } const filterMessages = (out: string): string => { return out.startsWith("warning:") || out.startsWith("error:") @@ -29,7 +29,7 @@ const postProcessTrackedFiles: Fig.Generator["postProcess"] = ( try { ext = file.split(".").slice(-1)[0]; - } catch (e) { } + } catch (e) {} if (file.endsWith("/")) { ext = "folder"; @@ -53,69 +53,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,14 +296,16 @@ 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]; + 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]; @@ -426,7 +426,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 +4026,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 +4401,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 a7c15281..a897d463 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 bc837e09..a7ae0a7a 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 c22e55dd..c963a971 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 f8994808..f50abbd4 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 90a6530c..2f9ff425 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 a70c8ee9..6a9d6e82 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 bbf98bf4..2109b0dd 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 6064072e..caa84c34 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 b967b854..d6e7b52e 100644 --- a/extensions/terminal-suggest/src/completions/upstream/node.ts +++ b/extensions/terminal-suggest/src/completions/upstream/node.ts @@ -82,423 +82,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 b48d8126..aa142e05 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 []; + 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`; + + // 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; } - // 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`; - - // 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; - } - - 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 4143ed79..00000000 --- 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 843ffef5..6adafbcb 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 17105495..9ce7c798 100644 --- a/extensions/terminal-suggest/src/completions/upstream/pnpm.ts +++ b/extensions/terminal-suggest/src/completions/upstream/pnpm.ts @@ -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 37c34815..17dd6623 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 42126db3..11c390ad 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 2374ee6b..beca86b3 100644 --- a/extensions/terminal-suggest/src/completions/upstream/python.ts +++ b/extensions/terminal-suggest/src/completions/upstream/python.ts @@ -21,6 +21,7 @@ const completionSpec: Fig.Spec = { }, args: { name: "python script", + isScript: true, 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 7b52f909..9aaf1855 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 92790e75..430b9a67 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 93da019b..3971e94d 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 9b0f5bd4..4998530d 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 657c610b..fd905588 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 df922164..5831fd7b 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 45208878..6987272a 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 e73b9efe..77538770 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 7e41109f..267edb38 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 fd8442d9..ff14b34f 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 a3de493a..04c573a1 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 91e0dfa1..25ada5a0 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/env/pathExecutableCache.ts b/extensions/terminal-suggest/src/env/pathExecutableCache.ts index 4ce8090f..9932bc88 100644 --- a/extensions/terminal-suggest/src/env/pathExecutableCache.ts +++ b/extensions/terminal-suggest/src/env/pathExecutableCache.ts @@ -10,6 +10,8 @@ import { osIsWindows } from '../helpers/os'; import type { ICompletionResource } from '../types'; import { getFriendlyResourcePath } from '../helpers/uri'; import { SettingsIds } from '../constants'; +import * as filesystem from 'fs'; +import * as path from 'path'; const isWindows = osIsWindows(); @@ -38,7 +40,12 @@ export class PathExecutableCache implements vscode.Disposable { } } - async getExecutablesInPath(env: { [key: string]: string | undefined } = process.env): Promise<{ completionResources: Set | undefined; labels: Set | undefined } | undefined> { + refresh(): void { + this._cachedExes = undefined; + this._cachedPathValue = undefined; + } + + async getExecutablesInPath(env: ITerminalEnvironment = process.env): Promise<{ completionResources: Set | undefined; labels: Set | undefined } | undefined> { // Create cache key let pathValue: string | undefined; if (isWindows) { @@ -96,7 +103,7 @@ export class PathExecutableCache implements vscode.Disposable { for (const [file, fileType] of files) { const formattedPath = getFriendlyResourcePath(vscode.Uri.joinPath(fileResource, file), pathSeparator); if (!labels.has(file) && fileType !== vscode.FileType.Unknown && fileType !== vscode.FileType.Directory && await isExecutable(formattedPath, this._cachedWindowsExeExtensions)) { - result.add({ label: file, detail: formattedPath }); + result.add({ label: file, documentation: formattedPath, kind: vscode.TerminalCompletionItemKind.Method }); labels.add(file); } } @@ -107,3 +114,47 @@ export class PathExecutableCache implements vscode.Disposable { } } } + +export async function watchPathDirectories(context: vscode.ExtensionContext, env: ITerminalEnvironment, pathExecutableCache: PathExecutableCache | undefined): Promise { + const pathDirectories = new Set(); + + const envPath = env.PATH; + if (envPath) { + envPath.split(path.delimiter).forEach(p => pathDirectories.add(p)); + } + + const activeWatchers = new Set(); + + // Watch each directory + for (const dir of pathDirectories) { + try { + if (activeWatchers.has(dir)) { + // Skip if already watching or directory doesn't exist + continue; + } + + const stat = await fs.stat(dir); + if (!stat.isDirectory()) { + continue; + } + + const watcher = filesystem.watch(dir, { persistent: false }, () => { + if (pathExecutableCache) { + // Refresh cache when directory contents change + pathExecutableCache.refresh(); + } + }); + + activeWatchers.add(dir); + + context.subscriptions.push(new vscode.Disposable(() => { + try { + watcher.close(); + activeWatchers.delete(dir); + } catch { } { } + })); + } catch { } + } +} + +export type ITerminalEnvironment = { [key: string]: string | undefined }; diff --git a/extensions/terminal-suggest/src/fig/autocomplete-parser/parseArguments.ts b/extensions/terminal-suggest/src/fig/autocomplete-parser/parseArguments.ts index c26f3a77..3436414c 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; @@ -605,8 +606,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, @@ -790,13 +798,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); @@ -1124,6 +1133,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 +1167,7 @@ export const parseArguments = async ( context, // authClient, spec, + executeExternals, isParsingHistory, 0, ); diff --git a/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts b/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts index 07e90069..bd846326 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/execute.ts b/extensions/terminal-suggest/src/fig/execute.ts index ab9792f6..2c642019 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 7e09c8b6..cb764e4e 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 { @@ -52,11 +52,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,17 +80,20 @@ 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; @@ -131,7 +133,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 @@ -273,11 +275,30 @@ export async function collectCompletionItemResult( itemKind = vscode.TerminalCompletionItemKind.OptionValue; } + // Add for every argument + let detail: string | undefined; + if (typeof item === 'object' && 'args' in item) { + const args = asArray(item.args); + if (args.every(e => !!e?.name)) { + if (args.length > 0) { + detail = ' ' + args.map(e => { + let result = `<${e!.name}>`; + if (e?.isOptional) { + result = `[${result}]`; + } + return result; + }).join(' '); + } + } + } + items.push( createCompletionItem( terminalContext.cursorPosition, prefix, - { label }, + { + label: detail ? { label, detail } : label + }, undefined, typeof item === 'string' ? item : item.description, itemKind, diff --git a/extensions/terminal-suggest/src/helpers/promise.ts b/extensions/terminal-suggest/src/helpers/promise.ts new file mode 100644 index 00000000..14e86bc0 --- /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 8db683af..15744908 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 cc2fbaff..98c1ac09 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 00000000..2a5aefc4 --- /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 989d49a5..64ba4ed3 100644 --- a/extensions/terminal-suggest/src/shell/zsh.ts +++ b/extensions/terminal-suggest/src/shell/zsh.ts @@ -19,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( diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 3f181bcd..8ca8f977 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -10,9 +10,10 @@ import * as vscode from 'vscode'; import cdSpec from './completions/cd'; import codeCompletionSpec from './completions/code'; import codeInsidersCompletionSpec from './completions/code-insiders'; +import npxCompletionSpec from './completions/npx'; import setLocationSpec from './completions/set-location'; import { upstreamSpecs } from './constants'; -import { PathExecutableCache } from './env/pathExecutableCache'; +import { ITerminalEnvironment, PathExecutableCache, watchPathDirectories } from './env/pathExecutableCache'; import { osIsWindows } from './helpers/os'; import { getFriendlyResourcePath } from './helpers/uri'; import { getBashGlobals } from './shell/bash'; @@ -24,22 +25,16 @@ 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'; +import codeTunnelCompletionSpec from './completions/code-tunnel'; +import codeTunnelInsidersCompletionSpec from './completions/code-tunnel-insiders'; -// 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 +45,9 @@ export const availableSpecs: Fig.Spec[] = [ cdSpec, codeInsidersCompletionSpec, codeCompletionSpec, + codeTunnelCompletionSpec, + codeTunnelInsidersCompletionSpec, + npxCompletionSpec, setLocationSpec, ]; for (const spec of upstreamSpecs) { @@ -70,11 +68,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); @@ -86,33 +83,56 @@ async function getShellGlobals(shellType: TerminalShellType, existingCommands?: } } + export async function activate(context: vscode.ExtensionContext) { pathExecutableCache = new PathExecutableCache(); context.subscriptions.push(pathExecutableCache); - + let currentTerminalEnv: ITerminalEnvironment = process.env; context.subscriptions.push(vscode.window.registerTerminalCompletionProvider({ id: 'terminal-suggest', async provideTerminalCompletions(terminal: vscode.Terminal, terminalContext: vscode.TerminalCompletionContext, token: vscode.CancellationToken): Promise { + currentTerminalEnv = terminal.shellIntegration?.env?.value ?? process.env; 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(currentTerminalEnv), + 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) { @@ -127,6 +147,7 @@ export async function activate(context: vscode.ExtensionContext) { return result.items; } }, '/', '\\')); + await watchPathDirectories(context, currentTerminalEnv, pathExecutableCache); } /** @@ -169,7 +190,7 @@ export async function resolveCwdFromPrefix(prefix: string, currentCwd?: vscode.U // Ignore errors } - // If the prefix is not a folder, return the current cwd + // No valid path found return undefined; } @@ -250,7 +271,8 @@ export async function getCompletionItemsFromSpecs( prefix, command, command.detail, - command.documentation + command.documentation, + vscode.TerminalCompletionItemKind.Method )); labels.add(commandTextLabel); } else { @@ -260,6 +282,7 @@ export async function getCompletionItemsFromSpecs( } const preferredItem = compareItems(existingItem, command); if (preferredItem) { + preferredItem.kind = vscode.TerminalCompletionItemKind.Method; items.splice(items.indexOf(existingItem), 1, preferredItem); } } @@ -297,30 +320,56 @@ function compareItems(existingItem: vscode.TerminalCompletionItem, command: ICom } } -function getShell(shellType: TerminalShellType): string | undefined { - switch (shellType) { - case TerminalShellType.Bash: - return 'bash'; - case TerminalShellType.Fish: - return 'fish'; - case TerminalShellType.Zsh: - return 'zsh'; - case TerminalShellType.PowerShell: - return 'pwsh'; - default: { - return undefined; +function getEnvAsRecord(shellIntegrationEnv: ITerminalEnvironment): Record { + const env: Record = {}; + for (const [key, value] of Object.entries(shellIntegrationEnv ?? process.env)) { + if (typeof value === 'string') { + env[key] = value; } } -} - -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; - } - } + if (!shellIntegrationEnv) { + sanitizeProcessEnvironment(env); } return env; } + +function getTerminalShellType(shellType: string | undefined): TerminalShellType | undefined { + switch (shellType) { + 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; + } +} + +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; + } + } + }); +} diff --git a/extensions/terminal-suggest/src/test/completions/code.test.ts b/extensions/terminal-suggest/src/test/completions/code.test.ts index e90d3120..b327e3e4 100644 --- a/extensions/terminal-suggest/src/test/completions/code.test.ts +++ b/extensions/terminal-suggest/src/test/completions/code.test.ts @@ -8,8 +8,57 @@ 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 codeSpecOptionsAndSubcommands = [ + '-a ', + '-d ', + '-g ', + '-h', + '-m ', + '-n', + '-r', + '-s', + '-v', + '-w', + '-', + '--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', + 'tunnel', + 'serve-web', + 'help', + 'status', + 'version' +]; 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']; @@ -29,7 +78,7 @@ export function createCodeTestSpecs(executable: string): ITestSpec[] { ...typingTests, // Basic arguments - { input: `${executable} |`, expectedCompletions: codeSpecOptions, expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, + { input: `${executable} |`, expectedCompletions: codeSpecOptionsAndSubcommands, expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, { input: `${executable} --locale |`, expectedCompletions: localeOptions }, { input: `${executable} --diff |`, expectedResourceRequests: { type: 'files', cwd: testPaths.cwd } }, { input: `${executable} --diff ./file1 |`, expectedResourceRequests: { type: 'files', cwd: testPaths.cwd } }, @@ -39,18 +88,19 @@ 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 } }, - { input: `${executable} --list-extensions |`, expectedCompletions: codeSpecOptions.filter(c => c !== '--list-extensions'), expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, - { input: `${executable} --show-versions |`, expectedCompletions: codeSpecOptions.filter(c => c !== '--show-versions'), expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, + { input: `${executable} --list-extensions |`, expectedCompletions: codeSpecOptionsAndSubcommands.filter(c => c !== '--list-extensions'), expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, + { input: `${executable} --show-versions |`, expectedCompletions: codeSpecOptionsAndSubcommands.filter(c => c !== '--show-versions'), expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, { input: `${executable} --category |`, expectedCompletions: categoryOptions }, { input: `${executable} --category a|`, expectedCompletions: categoryOptions }, // Middle of command - { input: `${executable} | --locale`, expectedCompletions: codeSpecOptions, expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, + { input: `${executable} | --locale`, expectedCompletions: codeSpecOptionsAndSubcommands, expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, ]; } diff --git a/extensions/terminal-suggest/src/test/completions/upstream/ls.test.ts b/extensions/terminal-suggest/src/test/completions/upstream/ls.test.ts index 63ada99f..aab14e3d 100644 --- a/extensions/terminal-suggest/src/test/completions/upstream/ls.test.ts +++ b/extensions/terminal-suggest/src/test/completions/upstream/ls.test.ts @@ -10,7 +10,7 @@ import lsSpec from '../../../completions/upstream/ls'; const allOptions = [ '-%', '-,', - '--color', + '--color ', '-1', '-@', '-A', diff --git a/extensions/terminal-suggest/src/test/completions/upstream/mkdir.test.ts b/extensions/terminal-suggest/src/test/completions/upstream/mkdir.test.ts index 3935191e..5daeeb25 100644 --- a/extensions/terminal-suggest/src/test/completions/upstream/mkdir.test.ts +++ b/extensions/terminal-suggest/src/test/completions/upstream/mkdir.test.ts @@ -8,14 +8,14 @@ import { testPaths, type ISuiteSpec } from '../../helpers'; import mkdirSpec from '../../../completions/upstream/mkdir'; const allOptions = [ - '--context', + '--context ', '--help', - '--mode', + '--mode ', '--parents', '--verbose', '--version', - '-Z', - '-m', + '-Z ', + '-m ', '-p', '-v', ]; diff --git a/extensions/terminal-suggest/src/test/completions/upstream/touch.test.ts b/extensions/terminal-suggest/src/test/completions/upstream/touch.test.ts index 4373025c..dd03b062 100644 --- a/extensions/terminal-suggest/src/test/completions/upstream/touch.test.ts +++ b/extensions/terminal-suggest/src/test/completions/upstream/touch.test.ts @@ -8,14 +8,14 @@ import { testPaths, type ISuiteSpec } from '../../helpers'; import touchSpec from '../../../completions/upstream/touch'; const allOptions = [ - '-A', + '-A
  • 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

    =Qa_VpI17vKlsj% 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

    G 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
    avSg&uTa|v`cKr#cv^bMf?w(vcAFj{ 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

    OOa3(>i1V_uk8PHgpX_3m`fT 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

    nXf04mv;P`OSQcX9O6j16*<(;Uio@)-{ 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℞Elh7U&noN^;(i1 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 literal 832666 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

    PUJlXP%ZBLP~N*$d%=gV9XTS2l< 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

    Jgc$ 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 rV}|} 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

    DB?Z;!Zc= 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

    ovucbxREe0v5mc5pMo7u5Hz}#{x)sG6 z%QPpVE>%o;*u&<{aL$Zn zP#xC!5s=g!Kglpw*A+%c4& zIqtLOBerZ?8fgc426PYWFA+UGVh_d6j}iweScaZU5w9%vPud}nonI0|QBS4WbZ1lc zm(Rx0=k(U8MRqE@s)y4i3iQ09Q#KXVH@!^{sjq#LZexG~C&@bDrIW9RCi}e3Pnvk; zwIStn#?VhBAJHx;G!`?&?CaWl6y5U5jYGG|1kNS+gNIt@dlV4iN5_F8bIfW|;~MiP z!I$~8ZF7AbzINS)^!<+k#l5ZP;rs#wJ11!MXf`t4g(EY@4XSvYtC&^^c`hPIICn66 z)@l`+yhN2TW1l^+=wWMmB1cihUWPZ)*Be__cJ5K__vRAMZ?FB=(w~Uh4ezUlOvJ{% zd-ccT+Ttey=x~)2p1$6d7m8w!JpET1|=!gXb zKzC&t8-%5|cZ1JhBjM?bY3_Jocp{Oh1?`!uvAJUV-Xp#oBtUg4PVP|3V5!LKX+s>Ba?YFmj`?Z|{#({sKq`q#axR+Hl{5?Z z;-MhnK|ws~A?vL9E8)@*?!hX`_n8Scww2?j0?|Ts4U1KW<+)}01Zw;=>{dlH(YfNp zO5r&UnAfQh{lEh1^R^AWI$T;%M;gZsfzX}y)l`MZhO3bTP0|Hdy0nD{UR4)GfV4iU z)OYCoW}$PLJAd?talzLiyNyi8!bM;hXhJ(IZMg1*oF9dpN)^G<%2Gds4qE~$l0;1d z9Dkv-o5E3+UrMM<)H*-!lTPCN9{c%f-F&x6w212@Lo;U9a$+IyE%w2y_)I*y3TVIr zW!>A$8xr>ddZCkJw3Hg8k5XG^I7)SUIcCLboMRZ~vovnpnv%BzmQ0g4f%G-XA|J{h z?=<1|ok6o?CfCT5%y=j}*9LF#e22f*i{W)^8qTUac#aX3mo~}n;VKzsieEwd4ee3> zRPrCDA~8WAD9_Fc*r2P+cm2B(Kcj-_gyGtCJ!q$0djN0t2un!X)b?GAAHJ322xBR< ze3lEdq&!6+0bjR5(91>sh?L7t=OKB&4i&?&%0=+^e&4W^5Z)3+9D1B?Jri+Weuw5j z4Ao69Z*oBLHRLOD^zXsRnudldPJaE;O}%dvLYC^L(?0C0l=qIhZoH2co<=5S#`wL{ zTlIm~wm^oV~hET6UTDqmM!};fsPlQx>HF4qBTm04CJCD5xVED8o zy`?>+h&42T-PajO2kg{yx&v}>>%c_<+E3Goc|-!rWa>t9R2JdUTtlFw_OtO1u`L3S zI@%iqDzk0F)4_qtKIP^-+FBh@nT1A$;L7~}7X+xuxjiZ83CoiYNJ@Cq^+);#>Zsp` zo~ptq1HYm-W1N|qOC7?04~Z&x7+B4c+$P*6R&V>r!O~nM%sg>3zm?d?Tc#;U7@bHv zWGj4v@0KE$1WOaQ6is^b#RO05v^p<4xpWP?des_mOM1br9RkT5Ik($SN37nkto zg`!=rJb)?gyAV5U*WN0+K&nQ9aM}>$KXTUMhAVP;;-^JTbUpo{2aLt4-`0>uHg#Rz zEaZ#eCx*;xj<%Yp}Q_kk93L%-PHLk z%Z$)+V5BN64~t4CH75|y!58evYlreXX_njO7>fONO6{9x$^q3jzE|_52x`xD1qq}M zH{I1Mq=gZW$!LCP-6~3oHO{&|vpzSkXEh@M z>5c-eUknJJG+fnJp(pM8m1o1X-&4hsVPWiuwgFKjL-pP=jc+=33 zQ{HPc6S5w79~|b1uT;!Qb!UIo)PxA965c>sG4V16cZn9k)AaNLve$BxUIl#l4}wHX zd-VQ&0o5{GUKyr?%8_Bepm2x0X%yQsA8Y+x2xKvQo!#sER?u#;ky~JPVdLlZrnN&+ zJxRvRt3NF3Sa}wtl2c03G>J;sTq^*#1R8JsN?3)R)h^SvSbVpiHB4#ka#n}D0`O^g zRpw9d<4(CqZ0Bu|*h>H*W<_yk(dUO!l`rKjKK?vOZyJ4fWBKV-X>*he-h)*i=%BM0*}bz_|4R%y+PT?~0kBt0=G z44?xs;uTZWtti=+7*TIbPDyXzc1O}4_Vf^ZL$vW^>}ux$SiQIN#NwZB)pmnGeWv_o zz;2ar_JDPSQYd)+hlhA5zueic6BgN#24yeUL&5Z9%*cS&| zqvc&OQe!e?BWdgRrSX(X)RX+^L!}o43JTjfeV47iCWUT}s4sJcWhU=?$$ITqJ3}0i04m5_|N-Gn+D(zG`GEu4Te%=}@2akVk!VJ?mg* z@Ib^2c+B0G|05}VppNRDOQ&}xjFrZV%O$-N9Zo2Jf9;fFkl{@&?N!;=;`&LgNw?eY z`qo)jk)J*w)nl6P3Ds6jq~v~}o$7vfP3ZN-*QEo(Q!uNg>)$Yy*;JZZ1^s$U!|jbUY~;$y{ao91h^*-_LC8|9?zjvOSWs@Inw{7vJk>LoG4W{DlC{g#r?c{%Nu zHnO%KI}-ZEFH#LGVM4`nnc~MQGx6mKsVZ>oBB0_X@6Q9QDh+EV<(WOwwpo|1Ugxu* z2N>FWAD4jI=qkpo9_@Xalm3BcVrep{b^J_m>p-XcBeM)t565^yZY@CD4Sk8x2_rN{ z(0&~rLnyRG;#%zVmqlSKSv zkTC{dK!|qO8LnLHAXW^ALI}K5VDaCI=*^+V50>jySAWWb0N*Jn?Q-ThzV?lcDQiXI z-Yr5V9GCKn%X+n6B^P7*2A=e`CE-|BPCE{r-;KGLl^8JdEr^nysH~OJ_HK1db{g|b zHZlqpi9blWUX38B`3d^2Nd7^uI$s4ves*~F4*X6Fou?x7x6TEWyXS@qbu2P2@U*&`u6AasYQ|E$^g z@&nW}=LStHLA`38NC0`@55n(o((q(QNcH%jierUo%h^yLA@8-)36Jy@VA>2U6Dmoo z+g^i8rR_unwiZ|ADT5dg`})pfiRxTD13<>jLDDJ^41LR_jW@7XN{YRvDi(IJXl+!; ztPNQ_duA6R$%KtZbow^1aHK>jU531VPm(eeD>7nz?tl*9EKh;tnVCCY8`+gri9k*` zHPcV6t{APYH=no*n_My&wXADDZ1PC~usON+kK5@%P!dD)vY|!m#n_!*SXL(gQRIv< zgbp}RtScA<>8zGH&Dc>VMbh)Or39H*0Gd~kWtK6iIZ`h_J|NHi(yOt<)w*0faKH>M zYsHM1J%GdwK$p6_*S7-miwx@5ZJ`@yn|%S6(z!?Dj#eJM-Dfbl!Mf(Ys`muPH5;|J z!-!J=WDeAOWF?bvVY!_+ z!*hW{~`_gb(4J2 zNA=o3jmbb4KtczrW zup)iuJ1ZdOZ@gt#c+-xV%(p{fX(H}Dn)rd2L7aSW#XK_+hkOl9UQZ9H9=P2_$mae@ zm^pwHi9};z<*GX53 zsnZFIBQS{%mp<1xe#|6_js@X$#K*<`ME{})Avy$&L$M*co#I-2q`WMguk%e-GFJG* zMHKl@%nhwvTO!o7zREmhO@t#ZlVnGI-!Y_dJy2b4rsFdWBb537(gFN=ZxqeQcB>@U+EKPkCwCbAj`Ffp_v=QEhD-~lF z>z-By6kj9Op_TL=JCG2!j}vwxlG$ToDrF)FbCyK}$uOdo;yCw9yUDsIUStoITq!&S$Mkqt2O9{&LE)kf zKpfzF@G7@7dk1W})@8h3Glmthc|;u)JE|Gmcd3~5&p>f>%DI4Nn()61cWF9a$b?tB zOD6dzyW7BDfyM$>R)nN24?im1o2O*ZWw{}3J`O|?DJ3PQo`QwMS_@V)=4)_k#Ec@h zARBekDZ<3A2R+u?e#D}B_&894%a+7aMR#~k?*I<1oX%bkBVEMGVvZ1+H{5-hr+Kex zRdXQQia(zsw9gfjS7AyPuIGjZ&`sSt!Jz}quuWJntQX!RZ%QXXs=oJoV#v^NF^ArJ zAA>m89Q%I=o@=hs-&$35+P1KpP4UH>9S+{rYw<`B3c&JBZ2dvB44Z(Y#c*U=biBuX zYGzt`?`@}ATFxp&15#~nb-pJm@)V-)R#tOt!5={!PgP)sw8U`G(Q2HA zhq}e&e4s#~4#s6?Agqp}GNGer>5an~JhH}qJ5eP_*{OL5>OY&w>oGFlG8}y*q=S1D zTx5ETvs9hDDmNWKfc8YtkQM^Vk)*lU!+1^PKam^X6D>?N$1GV#3y}$O0&}f+kwLm< zaeHAV)=+GlBU3pwCOw0?g=jg3KpVaY4ihTvY&A}Sur6>vqv6Bqw;FuP@wsj%rGpF~ zC-S~Qw5C-!C;42mSoE*?(bnEg^s>r^RKzEN0lukHWuzMjhBh(ULDlS+il0@{zB*+d z_(Z$#GSfrdRJ^C(RY3Rg2kpbhT8^B>vwYx@imS?Nvr4&<-%)T5lOW^A5K*U+`bAcCv2hHIAg)M_g)E`AGp+SvBMM-#Al>0(V0Ge?T{{JECtz zbb*AUD>^8??%3~#v{G8?Pq_!2+my#ikkqcK&c76$x!0bsK<-UgL0I!jJkQJNi60J= zwOrQGHBJ}2ikfZ=r<3E?CNzMM64Kz@zWhRKu3r(A1|ULCDc{$=fa8x4!Mn0hakaR` zp?3iPdaK`cN2f~Xf}E~=xa~*B`VMmJBS0IjO*j_0R_MW})!iw-e5?X~O>p)XPdq_b z)CyKD=Y@31sd>J~)-jXIuX26%uWRl{ILY)@4o^zn@EYc9Kiy3=kmb}ikRtq^VsT*n zG=$)czIoB~HJxZEj)0T%{}X&_+L1KsmSZ@1%)t^o(@r&tm1&YSgS(-)2+}tTUYEOx zb$N1@EnCV=GFsYr8M<~@9u_A4BQ217cAfSCVJ;hvJsKLB0&1qu)($*IWBQ_`t5*~B z1njp`@qR;H!g^I((kq=%7+x>o|$fV zkI*9t)p2#93mw`78=mSd@Ew$!RjP}v`Na6bkEpiYDYW8~IB+(4Dqds)m0%U!jUYWP zmpZgW(-K4ZYD$iu+2Q);U6R(NeueYWH>N4S>e3##bPTNnMe%ggifXyg;DQzfSAU;h zOt|D@D&leLvFg_=erSF9c{0ukXmqf557GZiIf9IqIUz{h*IFkWQxZ^p>=pd0X?kQ&<9X#zDXY|e?iFHqkD~|aAetYO2gRyRk z#Tga9pY5SxysPz!m^u}*BnL2x6nbT5u2Mo5HN8Kj2v&(y}sZBBW9{iX+v z5@ppszN`9lApa*Jx7-eP88!MdaOc1%jH4TRP!y^(I3hh3pMQ*2kLQzv_Gp!~-G6$w459r^8q7}BzIEVir#jO7SlWPC| ze+Ie#JE_`mKLn)JrVuv-C)5Wxs;pj8oocwdHCjp8)E|Bia&MNoX~|vi(hrP?vP{zg~Am2P}Ie;zx#dmF%gA+Y#ruFg1DqA$gDO zv)ATL-U$QZtJ^@$L;gOs`VHxlHTrMGTc@?$Xm@S?jY@{NX^no0`&m17`MPymLTYT< z;eGDT66`Ygs%+Ngx{bFY01KcNNxcpN-=&o`xO{7RSMb|_OgQ3k zHD&3vS~E^AttIFhw%eE<@>aTSN(OH~m5{-{&euIZP`K1CwNw6qz1j`8$U|^`X}PB` z#l7aV=wETvyqYH*K7=la?TSg(FYEkd1$U}jTk_M40iZ8?mOZn1jp+~;eafVPEi|*> zm%sLMpLu$REn+F?5Z$Bcjjle{w8Susn`y!_m1qZC%3lzp%ig4~&fq7n@G)J{!FrT) z*n~!eQn%*{ufNrg3W}7ofp@qM{<~6Kd&s$eV)MShq)VW2%U~yCBBJmuy#nZ?fCHJQ zJ&{KBx9w3$4!G39Ny>?BrsBcY*5h-7HR&y*cg|BmV9WlSoc&ShI+8@)a*fcvu#upa zCqmx+HcZX9&bt3c)4Mn_-T(jpX=UX!F2`)Blta$RjK(UVuCTh4L(YeCl{t)JGRMW7 z52?^(p;A}EDCfq^X38PyVj{LVU&Myk82#4!`}zG3dp-AfJfHXb?S{L;|ANc)9$GOQ zTSzvFoj;W@c(FlHs6N^GxN-P8Z${6DfAMj=dSFMcQ`;Y=n9sOKRC4iMA8fcbxh_|~ z=x^@7jKi5~tK*Gd*}bX5#to}B4t_Qo9$&p?jfBR?;>~$2WSiA|zMgerIXLR~s8l+( zap-Hi$K#pJXrJ5-KM_FbEfI?NW&)}aEq#-s14!(Sj(=I>o)drQ(8q)gp%0tyR!?}qzHB2OhECNzE%0}~9gvHg z7{ji*LV9Ws9v_P(+^oae0OLa%x;#*79jiUTWS#jgB{KQv^V;dkWHq~Gffh(6^GG{C zIY5SmdZlLF)E`Bn|H`nVl^vDqU&@!~+$h=mP{x{sLqa(+yT1QfV||yNy|MA#Z?dd2 zZ$))pnr}Rtxc|e}p-h{mqj=WstUj%6rk(PR;x;Ns4?7;9p|<+@CJlB{AckZPcE?Zg z#I0d3YP+w*e>^VTD2%?mxj?&XqDA;&?63FhR0M6%yoEnKaDbg2Nq=N6tV{3Eh+^y~ zIM`Zye*%L~lL7(N882+K$RtI4ub=SvkllezQG0Wll7fuLL!$oG0QHm=_Ju#hm3|LW zo@6NML0(Ln>oM}R!;_8LnfO*Uq3VEL_R~8sHx42`S~fuH*GwghN2$-&44pc#JJmv3 zU!dB-UtUVETpt1=zp*%}vHqRj4fndlcG zzPiQQAl&%R%Aw!-Fz$%hE2>Y!<{T+b{sop|5>HbEpAVMw;B{DybUDGZ@~OsVO20zZ z``lM4jq9u1)Ixkad%@SKto3}4!D*JbeI-{ z_drVxMO8TDN{@-txL2fXpKD~IH~^EL9iytQR=22J(?^YfL@nRd`R+MoX2CzQ;V%`t z3TM6`wVD-SNy@HM)0J&UMd-`tgxp%Trf{J<>?HG_cCt{5s9}t&pq^=#2|}Abe%quB z;>p;R($e0RE)2089t#SsRalSVYe%xoa-CH2bvKM_H`eezf=|{K`byI*QRjtIEKS?H zf$G*>dj<&)=a|=cn))15#tZz_*^=Pt`!SE;iw|RL+W6Fj@noRK=y9b{DiZz(Cos%e zr$yBTzv_kyr841zwmRr*IHbMOs9X6RO& z5(xT!H~$S{w;n|F)vp%i3FI4lfA|L(CMmjeD^Q0KXiG`-NzFz+3$w;^9|J=vTB(o!D1WeF0LJa6+~joZ4kkxKHNU4 zWv6%BdlyPIJOJQse4p5?FqcLA{};X8Pj&i;i8!locs1Ye??vO2Wa;lDxae%EkqzH7 z`K+uS9+G%FRwn6 zcbR9zGxJ4XASVO$+pm>bfM!kJ`nce!27eQhpe|}v$8dJmI7NR z3px%GvD=SDB#&Xptibsc@3#E%;k=qV8HXI07aBMkb0O~(XLbW~G*EMuK_vRE$$rvB zImwcM(=xgj)FQsg*5-RdV;DAVU*@kotP>N3EUg6p)NoV~U6hxA1m?6^@oM@6RBqLU z{o}JuJgn`mbV3*IN5t2%%Lx&O&3zY3Oa2bf75y$Ulfi{Q{VLIDiB#^t%;}cqBq_`( z*E_O_#NTzCbX?F1+t2c8X&YlWgR6dOGY!#;Mvu zQ;lMC&mhDh?_f^$wsAoFB9<5K%X-Gw^AUV3s#go8EUzyNO+D+MeJaDBs#HnSUFs@; z-$Q?Xc@=kPbXJXI4C?rDBni`_0XJ2$j(8#DGzOVyBirB)4%#;_JOMajZm;4|@RL5P zkKS&j19te*&TDCyj|V-ce9+pHrfU8ay8t+&9y)gQlOw-r`S5umuS0lZclPi`hp61a zZ5yAf7^(hbUR39y?}f)R83&`jc@}{5KmqIdWAtY6ODgWjaQ$uC9+@T^BUBdqw2zmv ze9b5bph6-VYz^AVB={{`@5ZDR^BH<|yi10RHJOO66i^^X>-3BWVEMxTJDh61!3f?Z ztgz0Ddoao_8JtD04k(#|V1>5ejOQ>7>&@Ag@{O@Gj1m!iXo)}uR z`n^pqOfcO-UP?J0J457C>`?2On!^@k(P-LyW}FdBP66$z8hHnL$n&$1rK7e+#PDm` z;)peIFfBDR+$@HN=KgI1}VjJEp=11wQB!# z<`HjqW!fWkEUvKR_=sgdDB7sEhbyZAPkIzH0UIyNG{E|4Cwz@mlZwrgHtoZ|g)!`z zuio7^P?U(oCnXcuN&I_2k5%Te0V zn#+C_<@WW1^>SA{zkNM%UcpELxc^0VD^&`r>(30#;$e@uik1KEe1?lGE0?{xq`R&_ z#9o%wYf><)4yHG~bh~o?vrX!V#9#ci?~SnvvC~ZhMz*Bn?)qTi4Lk|zFL6041Gjre z=cU5s@Cc2G`1Z_6Q8muDjLCazyrXzN?h~c)p@^>3c!YEMgTHNQ?9zMoPt9#kCotP5 zBND84Pfbg3emE)h=pS0buLH!!^y0||bYl50NuxGnC@thqRFlFj&XH;%-F~D?T3Zo= zLaY7arh4w`Zb4Mpxv}dfa=te$pVLg|1xlENujDFN2xR|g3?HqX4cV&A^o{S9a zV`z%LdNbW9uIK&{3;xo3SiP5+z6Mi&bLL^u#~U#Q=6)B9mK5l#ALID>He`(gJODhC z9p=zHcSbBqMpZCih)q(2c*+}LblwSO-n&s!`k;8lDgJHs2U8PkU}#31Nw>+#IO-oS z1*|;0ddo}>repGEavRqan5G-&(ygU{D>y>pI&Jry`Doj(CkCk zJQCJkd8Qs}rr+ZhOM6p4$MF{i;%fxKfRqXt%Gx(m_{hkAK%1v*gnnN(i>GVfV;FDt zXba`dPi9rvd!7>F-z*vjNMhG-yvhE<1Kl#*Heeq|ONET=Z1}eO%_5{N+fM%aWCCo( zJ~mw%oUdQ#x)k86f@G#^H0j!R;So)Ga||Kqj&}T*aNdK zXzdZmf_Wr}e@tAMj4Pr(8Q@T~Y6Rzq=-L(PeqrbFlOMX}8a^2`qGxrcz8wrvERWnQSzCMb9|TV)R(m~bO&Xo9P`iS@IR5CxyMe~D6?G;zeb zHfXMGu5$a%s%i#6!yb$dXW%mt zBvIphi_^;gJD6$=KCOQEK2q^vWlyk`JHL7rAhMX`eEB8(A5@@W#0AaSa|vDEG|!@N zLglVb>%3BslmMjUmG_(rMMq7R|Bw&ZiPNO{zdk^^d_wEzLY_Tu#W^ryzKN5eXUBW} zO4GTFmDuUObUD$%_TC2m5|yMB6?@;GJ~@AB^!5sJg*Axx25r5SDS8c7t6&w=Vz$LDE+3 z7yE?Po#qaf2o;T-Goz9?>@E2cAXh5LNuE|ijv!csLc zYy-nOYV3$&K7DM@p#h~0%YE2g!JIuMY)H2>!#!&J-r`%_FY;(N!t?6GI{PD+5(tjX zfPjs`^yrC9fE52&=&D^6x7#k!YH1Fk;Vr^$oAd*mLM+w3vC5>i40S@|A~WL~2DLZM z61ME{d)0uWhn>`FGbDGW)6I!9QmsAsM?6j_E@fmX{D3wnTOJ%iOY%V#YC_d(pW#Aa zGfyzJw((yK#Fy&_weZL-9@r2?3y37x7?P54<6HyS_%W~%lM#wGH}ClRQ-;IT*$Ag2 z$?2Mec|UVVTeqSKzQ7SsnyFTd`pp`_%S=uzp82Fg(1>CgSQwkQa^Gjy2@8}l`;35^ zwIe>EB8g6T2XcpbyA7_D=_5)VcIF$BH~XX_la->mNDZi^$nTT@Io|||Et6+g>LgJXWN?@;|@ytKhfXg8j4QO91!yN z=*8?o>~wt|XIqNiq#k<@pg87Lz2CPoGk+eK%Tn~gH01z{2j}yFv!M|mf{Z3T8c{5P zR{TVPU+4o=mAqv9~=|B*T-rk zoMHvxqO!K>Yj{TyvWEZVhm}4}VKa-WIZi`Z=YNVn31_lL;roMPY=G)dBD+3%UVPa+ zWJT_@5wZhzH04JaIJ^pHpF>aQez#0DD`RtQ_6ZNGdChKkmxc*jI|g-lGS-smt2XRf z5F9A{n+J#Okyqmg@95rqu#s>?vAn)JRhD{AMcVfq;u-1hRvN(TQiDq z{ytc;_{MM2!1nE@wmbB}3VRLr$2Zmh z6^k2WK-JH__V@8yqS{K1PhRO{mx$mU2hAkd{;TaJp-4-?9iipeeo3xzyP_{}mdUKz zOZUH3@iMowxAS%zr?V`L_V3=Bn(SQ1((D`?4ar9TSDf!e!xI?&e=_7!E~V&?DagnW z=C2rAi>?oXBpg>1pIi61db+<;1VDSC1`=`&1#jjnTq)%`5nf4)WqIqL?a_ToBZ_hv z2UCjnmV8)>xG-`gqr4a%$8=2PyTjVdA#P1_rCp>!Y%Ohy? zV1lOG(Acb`c2{?q0s-xS;N8^j?FGX^4a-MhCY2)zhCcy6_>vlH|3L1l^aWXuejPkg z+4txPKjTO%gAUkO?6za_{w>)HoZ>#pMwy%E<=S(f0ivLHhII+3Ck`>i%W54w6@uC|tju9|=pg8J6AUh;7e^f)YXZ*73MDT|-pZ zt{IR7$n7(DTM)>7UE*8mnRwi4c3-5Tby^T1;GSPd^{L5w+$7Js`DcY*ciiM#x`B~K z-qhA?tEaPiJ6)H@%{JH{L?bPtWaf?Nu(75}r=r$NSN?l2Ye4>cHCJu+3Z`jAwRq{7 zO{V5j0Iumlw&a~=8B^)db++jBwJW-)K79ad){j|b;5F6RZ?>_};kcHpfI`fJAT?Ga zX8x1)X76?rN+uaJnH8x~xlYp-q$&@eU9s{_9B8`vRuRzh)vC!=!}7yRAW<~3$VIPY z&jGCe`xJeTeILY~LrhS>rK)jTn6T20yG@;g?`I`RCjH|^%9i15?W*-T0!Whgmj?45 z>!0xh9#Q|Ob!9q4t$`?zpm2sN4YhNxYE((L%G~^{WWK^G8jh7h+GRuwdU7vcsP6Ie)_+= zaQCkZzxi-m^`RQ@j=Id&duyR{?bRAK>VcH)xpK^{NKan@ zuVkOuY%Ot6@>R+;4as21cQ;AzpWSl_*A>?n%5*6lQo6Cl2*mC8B6S&xP~T4-2r`xm z?irR~gZ&!dkOzua@gNQL6nU!*amhwbpIL)IA@?oRNy3C6anh;)S(^N8Z+?P=v31K! z<8u2lT4;*62D1v|B3%1Ud@w4jbB1-kl1Jw$_DdX`{Nr*--o^IpjJ@|mptex|X>h<= zK*c7_C?2g4wH>YnY$z)v78G0Bh1O3XqG9A-_bWn7VxV6>wFEhge#?)`Ji=tJA z<-!<)_Hw9#Sq}r5hYGe$Wq&JZL{a<9AMq~`VI94ur{|TlQ}nGQoK1ew&B^GOMb6Xd z08s>QtkFYZ5b7v%h84)GmeWfn-jh?9Fxd1GrnSDEvQ z=k5ax;?|4d<#Jf6F-uc5Cc>-p53-RMEhmX!Xa;T@rKr8bPi#dszwld`oEtO4{?6VgH;Svelo9%EGq7C)O zbP}u;XdYA3j)TEBTKZJ5G&+QRCLO{g)G!$?Eo);UPuJ?KX{6{+G^C2|)M5s`mzGl0 zb7}Q=uz+5a3pubh7`u_#F5>Imjp@(;VUOG65LwKC3Auy-zx%GGyuU<1lhfoPJi}j` z{Dq2GD`_i?^fywE7{=wsd*sE(ajD}r4ScrFIL%SWb?*(i-xpxqE|?`mhqnG`;5q&| zva{S@fV8#{4tMpyZ>cBBJzc#F@5=GSwA!gx-^ug;5x(a}|6R34&pACX`*8bARqR%# zh85}tpk(yF7K?PlNS%by6unt5e!Wi)X|%#7xT(uMjxgn`NM4nz>B7YgpMXZ^?J+Mw zEwkFmv(7uCdZDoWVZd!+?Ji5-i}Th9qXu#3NqV*-A2|%ba{2$-S@N(40EJRg?s63^U5X z(Y@V5`2e@P^hS&!bg4URWJc3EjaZ`Piohzl7A*{jqo3Qfc}@Xa`|{PR7eLxki`^h{ zeG1mn>J$l@YN(QNM9qK2lHjLRIr_crME_EvGImtUiQA1XdXmXV&WuS~T2<5Fx*{6V z(mnHihHYb{K5Vd|wv7^}vc zdA8MP;rpBgJZJ*ESPE0sgbHibSBpXGwcscP8refB9|xbO*Y%*T-*-|DKjk><0f|@W}&w z3LGj)412;{7p$q!vir(!%DIf|eN#Xv1&Z({-hSnm>n)7w34b_4T(M&Z+>H&Em7MoK z^zRjW9M+`Mpa4?}nlgB&6uHH4d8cz(fvt2erFgg0pi>DQz%Q$zjnk=G&vA+=I2+Ke z?g6IV%X8m}##?szM`>bm#S-Ut_^&Td1Fe=`cHeX&RU`(qhhcU9QSg*FOxQ!l)W~jt zFJ#CObZy&{3)F4%l9kD*i0oXUbK)&*gd^J)2kq)sHyza!x#J^26C zQqI1RuG#AhVH~rkR2WCp&J9fR|E_pJIa7tgLYTEXNkDa#_8$O`xR7It>7uA#n8anC zU*ldE4VnqjxBBy}riUVVOE*5`mG8?lHw(@cfKWJ7pLw6RE;hfmgL+jw& z=vsLXI0}&MgQ@(x9|%eIO0ig0nW~i)UscxVeC~!xdmlqJYwq)jVNs%pIrWcn73W1A zO~|>r4`D&gAZ2pF!h01`$}hg141}2(&6yc>+v1;r9zxMgt;g0EB@1Wm+NN-rLqYYI zD#NOa&HFC#L#?@$RkLyd>om!T8LtFyo0t4=(@cJ_O#Ms|AgJV6VVF8!#uRL~+fn$P zMiqYmU+F7e9ffC)L`dHJtz}`(vocb{meaM{;F*XI;Ymr-2A&cep0}azo<#v z5x_47F}01z>o1Icc-~uhQITumvRmh@Q8)hwqV4na0e6^$%@%rZ_13qhH5_8=i&1HW z8&=>z#$)E~&EwaoDiKV)X?S?}nRe^yd=ZwKIJ4)&tb$Mp;Fw_fUAyLPhZ#J}BjQXy zLmLh4AMbJz@GW0=WF3EKiH}su>dK1utN=8= zRhxe3%)Orp>$sdy#7zmCu6rtq;wn$cmwvWauTryiOJHc_PIA`{kCbTBmyQC7=s~xr z4w=gN8(1HeDL zl$`a~?uP93Q05V_@W^cINCP-l^ODo5m(>At6E^Vl*K4Bd(vhxQ^$7l%mp^?HBA7t= z06%>s&o!r3(Ft}g6~;ySjQB1+F)5`K@ z$&9#wY3(@ST&1{&;9?HOY-A|KCon%VD5B`8x^a?NDD^CaF--i?bV2+y|4$JrlXO$h zVbov$(6BVg-*lY&Ik+j~#ZMg%L2h)P{eQ?vQ1kl0|JJ#&pew&b78KD#UEHwPFv171 zQ{UJ6)aR{6`}-J+-)Gl*_i6oCi0#n1mq8lzjFNkw7NfH=tyX7jkH?-5jXEbz zDN2(|t6sUJ>FOvPU2eSc&i7so?6c36H?K(i)3CA;70Ct;(yy{M%F*OSlQXk^`R7$a`}k0_n%{Et@6tuy@j4 ze7>yTaZ&8W<>Qiu*r?AHi7p4e9k4i@)SR5-O;i7{rI+P&DI!EyX&Tz@>^lx^8j&~CVLq-|NJ$2j}P9_d-SE|>WU3gapF*GA?_ zk*^-hw5rybpHEgRZW?`g4$FG0GM%~iWPe?@1JSMJWtvD+EfFo$@(_Cq`^j(x#VHc} zFBak?pgjBh6D$1k`F~x7aq^Xu$N)er&0Xr}3*-c;abyWE=CpnbJ}MDu^3XY4J}m0z z#cJ_L%eFaC%oZoHZ;utW^9C=ax#=TN8HJG zDRD6);A1U@Y31b~SOOfao_a4#I_Lp!4Tbl=k4Yv4qoxo} zf3J!~)jp7fi35uSwH=g4VObAuQFoT=2XX}RbnE$N1Qak6X5SvvaNBtLv~c}jWa-pt z@~TIcSNC1W`(k+@rpSgr&+rf}x#Hf2

    2;f4s*Rb=)VSolV>8q>y;xPelWhIK56=U7sL2cE(yZ*sQfDhP!!5fc0&cht2)h59l=z zz~gJ*VCZL>6EG}V#io66DaAa&ie;EP9#qJrNY?baZxu{EvFy1!lJ$0?T+^CF0m$$m zr4wP3n!tEtyjj+$Hj(l0k|(4jL*S>?uvo^%NgLea1 z!PFmCt)9+%IvtW|PR#t*7a|VO>?Z%T(%GWoOJ|0^}F zr~{Au-Qlv8N5ze0%S?W$qcj%Z{YvG zRU~PJhRbROcsV<8w2Et$f};*q{ykmDXK9xRqhZ#pJn4-1iTZ4BTNCTNfCAt8qfsH_ zU8xSd2uC=RwMS}TW}lVJZL1-@>ec(-YkifAq3^KVxff8AW!*Eh9BENJAkjf+nL1xU5VQxm@t@HM9~b!q63(w^)|$Y6L6v67DZ@qbt!=C? zAsE}O7Qs5YA-sdEW))_iB^;__Ii^x2$Z5Fd{VrQPTg@@?)EZ18f~yRyW9ygFZ=)iL z4mY4qs%!Tt&uS-pp_l$B83K(kFFfwUhkc>hZ>r7LCcVYziO8QxxtNt_!;QU=zz4y* z9mh9mXW`$22mlqlGL*1hPrDnywbiGoj?9?*jQRr>o=NZeb5uQgFZShnFLnoweOH{>zoGZC|+%LzlpKq&Y6>iUG$Q;Xw&V-*e0x)7QoO29w&<~0q`wx`vNyf(q~I-1fy%pydi?iUa76C_ zY@e&8$WSEiy12I_TM+?*)DAC%#q-qX7=FN3X0ql|UJzG4*b?0gQL<+;_DAlY;E45l zRpy4!q4<194J&mlyK#K1-=HnR*eJ|bN_k{-+Su45^DW7L!20Au>i+LNU%O+Y3O)fn z3yi}3+uvXF?9y0)Bys>wYcx0Lou~2lu1|_|ZElZDXJ5eXPj2P0i@o|{=u;}cx<)Cv z%_dvV1E_7eElwvjiz01!>|-SCwVi^p z#q*n#$h-D2-zb{a@9kogl-Fc6=FLleZXXcLQIuQwH%e4UBsWr~XMDP%!}rL(Z-EdL zgDr2x`4jQ$qX~wcT$F+HG!7#fc|Vw=gcjAi3j0i_J2dS3Y^vXYxO;A@R}U@T1G?SG z7r9RUZ+Tm&(@&_~-l_srEsh`)qSr#zy+z4^=D(GkXxY*zTIGIZ(ktV?0^N^=(DggL zgO!46Lhf7X=SfJ}W>Ss8hVAVY9hch*-X7jeZ=emQCQ1<&;%zSGw|>Cr{iDxS@}oI} z!=We6c)44w}n|T_Z)hobJT-WRV(VrSqlaE_|7zMTfG0DWu4|2 z9S7fYjkx_lS5?HqUy%8!f_TEQ5yev(`&Z)Rsj(>m_>&puPp-Kq1r^M@pWWwkk5zdX z>cs9sVz$-Pn3NyB;pZJubsVF66RDZ*+A!}sAV{+=%%9lM^_5m z-0F%w$vmGbl&9Zu=yL0vhYxfm1HH9=4KsLTB9M9pyKuPM9EyT7uDG4}%xA<$guC$U z+spf{;@DJg_;Y+%(+^9FkUtf|$UAI;X50{dMMjSKf;-c?%Rh+Z$pUCqoI~PJPGEWp ze`nqvq1wWgj>qWkVy{Zo1LHY*jrX#nn*vJ4=j|!z^Fl$*0N`k67`pQ~LCv~IlX_{G_;#85nCRuoroM6u^_j=B7z2<|@Et$+sOi&bVzOYK#c>fjm1; z^D#=nC5idN^fAwwQKE9EvWU`M=6FKJ^1Qw}>MJm)FGLtww0x+M zI`K6?YfZa3a)oB>|GGOTt36FeVflgh0G*CtslV}E2|vA9WU7v zy~z}zT5PPPxcjR4cRm?3X5sX}7dIi;?J!(~9hoB19Hk)yEHZGP5d)7K-JB&^cujq64b zrw5wi;dEWhZ{b@hZ%cLHW&rJKsYgu}7>jR5Z2H%a%sQ<0o)sP&5l)-=FmL8efz|w{ zI{sY32JAz67Hae7tzubS-YRRr`0x%nbdpiCzv#N@@DlxE3xjGKsTBh|x-2}Y{UAu6 zNPFzP`WrbeZgV^pfYg92h+<>17-UCaw?RozT9~i=_7|pr5=a-y=RINj(j}Y2@&8 z&Rk;w{nTLT=!j(^fv;KFb-68k@>_}~_Bpb5{#50ZiWKvsQNw$?mI+|Q>}8W@Be9#) z>PuXi!tf5r)b(I(&nz6U7;|15EO>)(u>5+XP(Y}WkySg|EbhAL)om`&A66D7e7<{>gxuDrXNaXVC!Z(1 zI8&!`LKa&=OMZ2@_LYZFF;+rhB>R1u-g$#>NeHSW*@si|6Md@1~|HH!)haJV{z{mWb zi$)f{1@|gy^wf}T!?`~ZO`RXv<<$LrN~d|dko=emZy5bb86d`}YRW*Dus181t{p>V z7jR-&tEs#oPrt&E@Wb{ADr*EEn69;VYc=s#+N~-yG`TiX+OYr8>jeu`q(_=x?{DDMN7ZSQ|SLic(%tmR5|Nh_+mpA`1{45rHllpf(KbCYv6JYT%kGIv7R*eiZ zbAy*h6Vub(>PCWFm>B>}?i@WP?@djzrw=(zZMUxb>>nD;rGWwg%d+&R0ivW3wEFOe zG55Ly+O0Hiv1QyZ*Mw`{o*%UK2?;)H*+a1y`FLYxRKf`2Uo##KQkp$pLdmX8I`V=0 za7Z`;Q4%rWRL2;8_$JQ5EQpXH-2f|5cD3Znl!hnimM(T{G%ye)x!U}X2nf18+gRi{ zlZz)P^KTjs`NTh|?;%|=GNfxwmK+Itw^-92KljOJca?AV!g|(Q!-c!`;vh6UE>>X zn$IBClswVb$Rw-D9y78EWmu(%;Sm4_*I3MV2giRF`xMS+LXmUD}R% z{F;@9%~OG!Y#D$Xx99Gb<%Q<}5W_fOFgHw?cu4)fOb^bR{2qI^>(s99*o{xp@tLh{ zYlkJRZy(?IGMV(CIT^8jJFqKcLv`f?zo5^>#3Z^YOq!SYc05JWhrcJ%EiJ`FW{A}X zieJ1``Yp*wbUo)4p4`f-eWWcsYG2@i?J^&kk$5T!8N9(U%W^GLv}sp&A8v<>ykYoQ z<5aUMrlxlGoX6dQ&00SV!4lUYl^0GifInVD{}Bxb4=AnqnG~#ri~5)X5!QB;W8KC` zE?g9LNw0Qw2XB2Huk}TgOjSSWhl%d7)utcZf-G|gcZ;9Wxr6oZZL4_l)yD7Cr=*%| z{fNB(9XY|j;@lrAi-ZGr6My#o91`|Vq3WMfZ?fO1Z2!}g;zXJsEe{c`Rwpv_6D3q= ziQ5_b2={DrHI6~X}NnTE3KY)25kex8b5D6?7J&j#W|Z0ptx7!AT4kIFlG{V z+4ygzh$tOHgU>S3*uUn+&P!O@DhrfQDMVTc1gdKd;pikPm_e~mj?%5_dak|8`xFl% z>*-)y&k`k$6BMIjQZvulTqzs<`@~=D=a;ADPxG0ShkNbLc7}rCmR`~GqLJ*r&R)U! zob30GxN;l6AC_3S!bz+wKt=ERJnvR*Uvjx^QlJexoR&aY(|%0uE6;P0-IhPO`#{Xx zRy%GYbL6+CP3UV1$Cl@6a4+ZSJ`9Nvedn+XSLVk&1sh-~Q3~qM!SkW*YPK5NzMblm z&u}SW7kG|IAA_b88~B|F;!!Y2k_cD*ByRhn)3YR->b~`TK*sjC#RPRk>>z@tNo$D64Qh)XPu=?erc} zevWM#^{ZC7|6Br!jb`)17Q_X3n%k?OnRE2eZ#-ZErsd&3}UP?2du1 z;jk-cjedW8*sQ8`Yt5iR*u1=COs9Q(v1IsX_|d|JH+#i^-sNltLX8YDOT$wVE-&!U}Mxf8W?(4BYwJNnY?K1 zYdS&D^D|J|YVI~H0!s$%Fm;etT_ro14x`AzUn>9Ay6F2Yv`cU85vv)!)DAzD`_Xje zbQaDz>Qi>OA`qS=mP0CjDw8yC_V_LAN6Y3?R4%bL6|uxPrIA6jQIA6pL#mBr-X9z( zbcifBqxv0ceJc&S2%I#F`g9HQ08GEG%^V;1gQ(4%jcno1ciS_M)!Urj^!sj#@=`XP zqN&-Tt@n8`=~B?^71^4T!RRFOvCe*=ozNHDY}{pJV)ht81G+3#77TF9 zNA6Au!s1iPOt6tegs>x_Wt5_9f|k{z`zLA)i|u|s5)b>c(W1bIe2X61!Z5S%a7Nt4 zL0)EJ<9dd1-9Fcr(OQsEWhE&qkA>C;g#M=gw7)%gTGvydKq6b>)a3PgN+7Ni$$Rzpn)bO2y&}E9tj3FXeZgy}JHit=gj#FD2vY+jj;i_c{C3$>?b=^2(df^4bH_DK)X2@@D}R=RS@s z@5Fo(MUqXIUK_iFcV0}IeSaqK-bR)$r=<)W`Y};{k|f3HW|(vd*mFzxS}_fdp(Ma>x~~xE;t= z^qI;21)p!>PbDL?sU88`{e>vsuDw8o^50!QSkvlT)0jEdKBp!}CH42|%5xzOtbS-y ztdTmsgZ^PPFBg_Hvfj46+=|n#t*l4f;2Ibwv|Mn-L-=;$zqXW!WMNsM3>XhRho!|t zrf+i#)}}iiWpht-XD7X9{xrBW&cadAlYdso7mj{XD*AM~dE#s_!d*Uia!UkUT&G>a zocJ$$Wd3oqj08<0TH)7p3@8W=-RyYcX+3qkZM)zG{sC55-qR32Eiu_wRy}xpcuPqy z5=iWM=BmEEp$6ws&1p=rnut$rdezm%$m|Wx55tjEM-#&zo{&9AN6V=HEeE|JaXx`R()CvJQ~u5RZ3VZb$bF}n1q3W z+89;|X=_UBR~ALDZaxUV>AeUtBKg+{o+9Z}k6;<*Raqm5aoRy|&TTTzN(%Dq6FQ1x z(#zH<y_31D&DOBUFtw|V#VEzLx}*{wR|(9#)gB81s#t9KJ?EWXUY(73pi z4+{LeqUsc5_;P84`I>k))oOjH$8I3}P7}kLN1tmFbKa!UtZD=cqwpi<{MSYv3v%|6 zNhLQ;Lq%?IDEO0g1oOb9x*k(dkKCe>K67rgB;@I%Xnr?x1dW4unyzFjd(6BE7BMuj z)a|7?<+0wr>UxVfRHv)uJgyLA)kRSmzzis(4-egC-o~=@5~c}m*X_GvlRL+d&bg=r*UGP>!1w@7c@WfhMr{VDkzu~x^|mC;WV z^-9WVNYWLP&>{b_YnOE-LNfHHl^pPD@gT-s;zl0n`dj+!8TKoc%a|soTy#d0LxV#IfBIo(lHRQ@=%P zwZ-v7>xpoxG&Srb|#wz;D@P!M{_Wm^(P=C-hYAkCDn(t3j*)iMdxo_r` z@wqmoj3;W@3myYn6+*C$7SD(5E}Fa)$^ZNcI`{1^UC|lLk0*Z9ea^a;)Pr!-`>b%; zIH`b?aeM#sw)V<2v9s#q^S(KUQMJR1Vy>l_g5(GFnUpUODX0IoTJudxol zI|+dD{$jr|Qj@qi)$`N)inP(M6zRh49nFzx)dXLlDKrg~rPgu6*bCRr#L(s59xe3Z zuBuvNBlD(J$3&|~vrcsQfx-z<;M)|Pl!C6E%535ysk)x*H`d(y1WXJv*Ps=rd ztAL=9(9`ePJk`l8TtRj_pCjuLqBE~KSw@Hf`1sPStvkwum9EQrjG4Wq-}Ws4G>iBz zk9_c7*waBNYr_HBvPY0@j4}@m*pt&tNgvP}4%pt+0~_({qgxARMewjuOhov{n|*E# z?ELH#F@YTv&m%6oa(lf$9h&O;j`Nav(>oAecX4%rJXshZnNvK;2O_uTlMc2{S6ImR z*OogcY_L>4`kvlV4bX4qA5O(~isi;GHwOua6$yREXQL*+oZ3v^e`uQW8V5uxBWu`~ za!PL1heCvAywnPEHCs6ra%z4+o#$_k=qkdZX`raZ+-R5~d1?AhvrjKp&&6jgE9NP2 zot++&*B)5kC){IWV!Cm1NJ!mp z^1``4v%(??u@thFZBLJhdb%9z2r?F&9`dnC;tN ziR@)gwn@gWPHPC>_T3q};h1KQf~MB+DWPy^4rD4kCDe^edC&iMT;H1Bnc=y?GG3y6 z8!+hrB`^&(=zOlMhy$Cmmfdc=F<~{rm^Jb~G&Q#4UlPZ`hlRfoxmJX78cd4W@>-$( zZpRB%G2;5&T+p!h+7Wk|QV*NHM&rbk=5smR?5yEK&_|~^6nkb}#ZR-Und2TqeF^-| zfnYaI*AYXHqM~BsFeU0XtJkx>G0Va))OlL#W7l(fHHJfhI{7DNi*v78Xm# zO$N3$RJ>;X`wgqC87}@>qhWP#omrRbp>5=?=`Go*no8ITlEY&QDvv%IA2Sm?O7-n;P?=i7L_rKF_t`t8w* z!@Pq}w(l&wIl!(g$Aw7v@4yvKKt;*#@P;EZjZK)1w+6KIy%L6~#7a(PHY!CePQnu- z*vPhCO0G=K#M%~~%k1OW{1!uKz)`i#<1a!I-P=ian8`x4ZrjGXCy$YO%lS|Hsjp_%q@EaXg7tj+JA` zvE(c_HFIr6l3bN+zkgwm z?Y-CM{d&J%Pa96PT@~LLwRnpV)44(?=b# zbduMM!T~b8m!3q?4y2YgFcY_VSfQznSL2)Fvb6LNi15syos8M2PvrxXpUjuD-f5ym z-k8DCs-3RDx<^ImwfCaoDe7=CLD&6PB;YhMpge{{#Pf^q9(3}2%U!!nnlI0gMlGWK z9Z8aye^+XIeBR<~qnMAX^_^tQI0*n&m{Tz&*9ny(pMeg{*oF0BOWluzz`ArG{M0R% zb{_DTzBEXB0Zskb{BkPBJV|DvuX`nMrQ5fzr;m{W-uM@pANR!SEnn<~@G46j9yP8l zdHv;bYW?tzH4DGW#d5c2I^xAKxP0@J!~&XYx&dJWYoU-4`P96jLn>2WA3m(n+LdoM zgVXV7Xcw;9&-mCG7 zGo&Q6KB46e6+bWzvn-j<%0t}RqpP&`ZOWXi$duT5bM{#8%t8+-_CSi1K9G9*#flH+2{%hV#!F#Mp zmdxJ5CM|n|y+*T)QHUlW*E{u3lu#V!&f2?%YUkn%U!dG_ei5>41d6@|4?Yvw<=fcC zkK5OS+H$-H=p@+HaJ}nz5|S`VtXpEhFqHigP+?}vhnEc z4K=Tpb=+@iyYHWtm=eXurkABvw%Y5^Nm-mX=Qxhk|Hja2U99>hgFk{+Y4t$j-WGQO z$B(xC4caXzcQi902buhtq`0MZXXjRFcIu&&xqf0i z;~)g$fW`(E2|+$g*QcRar6>s%VCc&%J)-l(PM^9dWr_f36c+@fQ#JS3whOkf5N6tn z|J=Pjdwej2OSCIKEz8V&vh_tNqi{Q{Ca4pqrjGq+nkF^HiP!ADr@ouQFLg%4x) zqVdBFoJ-}jdN8RshP|GF(r-`X45TImal^Ql?<_4qg8b6(6}&XuF!*WFsXs*9^Ko$d?9rxZ~ zrIm=c(8(ImnnES{Q=un-HuEYPd-pg3fi>;{BBOpCk0%?Fz_WOcIz04YA2;_Ee3?U6 zvZu7UAMaeCs)ZLdUaY(!V?I%TAv#6gV(p1i^N%Tk{bpTMn~1f<)v#}88rH0l6OlkA z;!!JKpEAAiX=^x!p52&i?hviE5y7rfODI(c&3QlrqB(EXfqUD z=m^|`z_WF#HVmrTht7o2Q~#@NL4RUpAj9ZlAvDWe4+VBtX8^iV?|;8 zw6IO{kBrhHASFI%dDGJi7NZB_ysOUBZ4T>NhV_z5kS#d4Sy#K@#8nqp-;7PA0qpRL>0e3bZ2;7+Nl~^p)Z7yeNR@mK^5Xh1b``zCdJR-T}ag~%l~)I_%*q2MjqdigWqn9eXt1}aRNYA zBX}W0h0*+80P!dIHyvc-!`ic;E6EfGQtob5MJ-(+_tXV|JCwN{I#<*j{ICnfQW^g)Zz5gA`NhxTKbr#d;n>wq{H@TK)5%% zPVUX?kJYSklM)Y?X4hV6bh>0R{yEljYq*@w5_{aD=I8u6mEZc$Ll6+j;_}DpxS-6y znTE=Xm(5KEeTppmq1GbJ-T*HyelG6>sBVL8XTbc}A?VLbFmRVnpE2=UpDVcAQC1*V zeni5^RO>cAr1oXDGxDo#D}3s*_NR~+Z|A2IUGvIcs}p{=SUfv;!uiT!4I$8E$&LtI z?81iDCsaEje>#;70s66|#l@e~N&Vt~Cn8xL{M#0=Q}?|5MOK(t33UgrTXZa9U0U38 zd}95bWeze7b#bWt^({$$Fs;hp%30ZgpYr8nmi88m9eu63Z3| z7(bu-$aURzrKSkTSv7uwItGeLWQJX6?(Qi6?QlWJq7eDd=|Ayt++X_RXi4hyg>_u< z-7Lq@ZF`VAZC`ku*|L8*be_LRHgkO-@b}ljmg)Uol-yQo`;lOXsJQ4%5`KRp^cT@` z*;~1FN3EQ8$MZJv9(3Z z!$a@$#@GJ;(zCP2>qu|;#M!&4%I`{}YMsq0 z;w$UJ_dd}y<7lZpL7Aggz^6Z#5~mOV__hVv8s5^F*}0a7-fNR9X;@JPupYm!yhz z5ETt9Jf@iK>5fX8M706Xw`4MQ{8#)0y-;NI=$Wvc4zC7zyOKb^S}bNOwe;hc`*Se& zjCK1~_d#$S+DN^UMM}g#NxklBfDIY1wT0_OtUl9sV8Z;BIfl;vx7V@%O{BO+eiv?Z zHGd*GjX235fBxp(*`@I$*8l%}D~r0E>c-OEJFF&BL>WCX$4~yhiTb$DEo$-1v3>jg zZrGkb_fMC!Dkx6q(Fw{K=4NfwnEBXo*V94d*qF{J+#5kj2Z477YaY}u)N2m^{v?(K zJ8bw@t)CG%U3U_JjR8dqr6ioonf}~yp6#eK3W{Dc=cQ%FaJdKUhDctYyy-n_@s}@Z!lWc;WUI3FTGD2S=^n@cjaKQ(!$50s#dPUD z;XVON>v}9B8?3K={jfP@_*cFc46-F&NUJ*NSM@yAy=Xr~?!|3MGXrJkQkt;T1!~7= zd5X9g^%^myC|?Kx>sr{Zc{r*n{>&AQ9^n-5#0z@(&|% z#ILPKa8B`9H*QU^l_de<%^Edz`Dlp~&&5Gg;f3PB-I6C(#v>E!8V}Zv5~S9aB?6ks z&QmRPW6a4Pl&^mf{V?YIt*SnO%v`OX9J1H8HxF~JN2N3NiH{i+RH);FT!Z%d` zSo-qWbkXo6K}p{kfv1Rsac&xXSplO6MR~Jm{)ZwtFSE+ve`n$W=%8oFgVl<%1?`Lb8L=P_{_B>$IWyFO?~hc91nW! z<2T#`8QI9oI_70ge;~O=(60J*{Bzjz#*Ubxs{q3W-<43H%Z%`DUscqAlY2b2dD|0> zdfbW-@e(6U$4e>(XHUDSgymxDaEXa}bzcJUm{s-_(arBP^~~kW;h7E>FZbM5V!K9- zN2kgFS{e8Qg~)O(w4>i?=G3|^^N&Pn+^w3xV{s^gE|VoW=dL1oq5A<&L8E?2iI->Q zA*ljg$2(S2?ya%qjmaKhv^ShLeb$j3HYKRL(x@Kiqq$1K!)m&vz&!R?I2Ov7FZ^A? z+*b(Y^SDPBOk){2VxC3e>r*l7`-8t2MYSu%akORn9H?tkpVY3;R`3k6Sd-_71@Gj( z88N;DL;gJ0tXN>YiZ5LvC*(Z7s~9=N_n3u*rJ?4z^!^ouNBLM>;oPb2DK2trN0r2D(qJIEPF zI1G9`qm`%Y()%6CH#FV2{~2umA5mV~J!^bWBCL%&xj9@8S-mjVXEXEP!n@@OV6 z4=E>|SwF4)(gKhc%n^_FZe8maSjvr~KB4|ih^*k6%}pY0J+Y4(RfY1^gG+zlYNbkn zHx>h#=RpK%1*CJua5ro_7E!ft$mZSgE4^(At1t@}nk>M~TDEBO%7K#*DJAZpP2K*> z0@UkuJHk&Gw$o*@bwi=D^TxN`T?#Z0NcX4PamJ82<(%pA{h7O5TGFXzDI216-hXis zvC6%4#V8#H;;KYd`iT43u<!Z)Li?w1|9+HBb^inn=Q-dfaiSCe` zxW4ZW--fPkurAsEwLRr|;e=LY1A@gb7WuoQ+05=1mIpPw_liCMXdV951_1E3;Zgr0 z!~%ia9x{9|UQ0K>01=&CA2zTQSoJxAC!RmTnUaqEkx@S~doH5w zgG2L~zWDv36SV>|f?GMhD=+LSkR{`%dqUR}43-C{Y7LY)izR!k2jvYcISn#&WpN25 z(8>k{quR7uj+KKyqYOQZ@rlNsr0*8@X@NoBl1-t`KUF&w9%TJG-01g&Knwo4>DixJ z7*!}p7b*I2rC|Y!Tb?eD;gz|(=rm8%Kxme5@63w8=+fi-N2NVhq1tiKPN|9V#2v$y z^sJ0=()G{}*C`q54#Bj0!VQkXi=Sm>=^Fw%_U|DL9howPME@57e4_dLl_Ko)0_t1* zgFtaTl#~5uY$3Fbz()XE*U$iF_^gKR>uY^R8#OYH)M>1NKI%Bh(|04Wsv7ey4;ewx zC}^U3eZ*Cx66^0h4Dzf^#;+aNLoXy+xV8;ItGYX+xt~GK1_lGfEZCAv;cRTQpOUl2 zLXY11`#(HyE%WJth=S>po?qmOSt#K4H|e|C{O+za6=GZ9#IGXSn-GPcNoOjFX}hfN zSR^F-H`}L0uTh-|6zhXMX7x9;lJ}Mv2Os;j(a_Za^4~thYAvK=uasvy41` z0DTMG-!3z|QP8>25md|s_Pe_M%D$QMly}Q_is88Re_{0BLJ8Yb!~2T+Em(mYaVnZ* z^k>H5*JOpiXxtX6Cg_uB_vH{8g_a8A|4fPRkI1t@kGkrA@eQsiQCFFJB(j)S8fbM!mtA zeBmPC7k;P0^0BUvR-6uRdG?(Xal?f;nsn;H&ce9x;e+y#QS+n=$?`?9lBvO1+4Q5< zQcu(OQ!>g5u1XD&bfKSCKPGg|ehqA>Jqp3B~2cb6xo$O=74}qlLqX)go>U$*jzmVt|&U+}pdhY|!AE8l+4!hhIG4&xd`$8+@ zD-<#eQ~^9iZI?&yNo9*#Yc*vY1z;7qN+w}`UH1x>EZiu`?(!~M42Znc2h?5SFVqp2 zKUCpAJ1@MrGdgi((bb^Lpz{hxG^vz6*Ifz$bDu)G(09g!fA0Eu?TZ$(K6mV(999eQ z>&%qYcMevD7@*=lqgL_+XkH{pv(M*s22Q2&3FgAP3I}9+KwMVZBqv1WVpg?Z_Q{{n z6QIh%Ci>xR$OdOc3D!1htkLX`+YuBF%d|jb5}+E7#eH`Iig(_I0BQEqDqyD0)Iy@N zHf@nU=<`f={Ot6Z=7qTzfxP;{qmf~pEWNN!g)XPK>#3v-(}x|`wrAwveUd9oJ<)|t z@Das?uYK36-DP|Q1J*yuGIa`%#RO@O3c=Ua+&6q=v3QbK5mb4kg4)NXL&Gw{TwWMmD; zD|7nd`fFNsH;)?3&!ubYR_0qFlj02;60vIz{GhITHzGd;J9`KideT2C(>P4GqDuw` zb4^gw9ef3zoJB`Ja%?eucB}kH>4|76`)J+Wp#hjLpJ$u(q-CQpkUiCWj!@7(n5f4x z8X)4dX*G$zA*-{BYxrBWjme^rdEcPlXQKI1M8V$-N!#b!XbHT55cX2Y)=*51)`OrS z0kg)%lS~z}Ur@8q$9gM6zB+kWj(17BUG#4`yX`DThZv^qym zsli6LA`f7tK!oII*|6;hh_f0dci957C4J+(2vBHEdGY_hfIL? zIjl+A0CyVD`yDXTo@5_!E0K+)3K&Gb2kK?ykNedQ#Iz8FAVlo@CD?&@`8d*ObH{bp zwaGKlqyV64R~Y?AvtG(tmdC86L5E&5JLn%gc%w^X0BL-I*F{l*-ON((PRsf8e>pV1 zRrrli5m-jJ&wtO|`^8;Y4f)wedN=BC=F;~(&|q`nXnHOI>Sk-)!EzBD;YHC8s5Pu?#G&%RcpX{wnRH?nbD$clQd)NSj>mN%|cwv|>$4 z7a4_dN)KK;-$tG;9zjOFeznhsP&S|Zs2aBW4NE{zW1m|ceN9Bj6w_XzuGp_z<@mZ= zH+OU!t8%JZ1EoJRp6S<*N?Bo8qw0dQdoQ~#6nPC;Y)@#Wde65s?0{_e47Cg?Tb_^6 z)WQ`}6V)rCLoW^*rGy;5-=O)0?j++iPCogX-e*%J(_n`3Zc1<}_C6RsBJJimoE3BT zBz2l`_Nei$UEJUI65@wL6zD}G`clT&p!D15a1SJ(lI4CYzXSL~e-NvCKj=B@DVUcc zJ#Mj7J`XZ3T28;;P~#ka7LiB+?oGdKNIk^oitG_t$25F^HY0c+^*~(r5*$Ln$p_+8-{{v+u)pkAtqF4 z_G|slLlGpe%JnG=tUq<_yE+a%wc%qNy}>F3M)@eAp8l=eiI9OytwQ$)?s!**J=G@M zt_7_4eY8yxfKyU|;y}f&^il<_Jznf=VL*zxiF90$Uzbm9qOvc$%VNYQF}&qXDsW>B zi#7@JoeGg?7ZqP_Q;|P$zYCSE8G$y0)0Iq?4MW&Y+8iIHX_cmF-Btw!6WS}0Eh5X| zC}0q|`GUqVavd{(AnA+;*WfEK^tIBk(xa&JQDxVB-K2+4Px*pZr3e#yE`E! zYWf+i5vw=z#AP%=>u#DsE8opJu0Av9v!^<4wbFrejP+rDKJzx(m6_LoD>wJYq2qJ* z5PeR~DMwAX*@pzTYOnBnse?gUyY$Ji6Bnr2a+%x$n~c2hgC-<&)K+x{jZ&ErkG1dx+^I zvp&Txtmo6OlExx5BpcT>)Bcaw_UWxpWuQS9d-71ALXHSAAwK4AuHGu?pYUvwe`u26 zUyGQo$cCJ>RfeDo^Yf310FC9|QYhoeW-%<{t8x`SnNU!Tf4a;tQs|bl`?O)?SjkVi zj*X?|ZL~dpuiq(T>O*ny?cF4HD({^0=Z^NkAu+Oct$=8RAb9Qb7VG9PAfIoe-faQ~ z@XHAs=ua>+ED>El*ZPe-jag{yXt|?rJ=Z}yWA0t?w=AX51fYC{v_7&j4Cq)gIR~zM z?X{ESg~E!&v4yc_slHha9fB}EuW59G%Xes#)CfE0%iN#mS*#Uk!;Zbk!WvZ80yX9( zv}AYA_Qjyt?I)Xd%>GNRH3boDcmLeFfniX#1b*=QZfCt5_%Ao)T*XO zQ3|>g_Z$)bf#r5yryyNOs9tFD`4n{!G4G>%ElT!9)YY@{%o6rtYoW^IhsZvLQ>%5c zPP!&Q$H`*z8wd0_d8DknBUtc_>-#7*5~8BugYvWS{qNzQA(syegpGCsV`(8OU0#qT zr`DyCpq8A=?U7lW!z4pM)l&+t^eeEwBv=3j+XMzi`XiY{79#7GB7e?~tL89!AlZ4GG@H=tdt z59PB^qAN%52F|p7n;nS?lOpP~l^#PBAU66bS%FWUr-PxhgM2CF!=Qg5*nM3Zc*JYM zC?tvfl|piB1hb@Sc=0)jvRD@`T_%_hr@b<<@WM*F%Nww4fol~!2wclZhKrtIA}^4C z+(U<7OpDnQk3vjQK!%B*u^q#iLhi%*+&|`Nw zObAF0&PbbLzfw~g0!zi)`2LdpOIv9vCJr6X(&a>J&QRLSuB5n(sotbqU&B){1oIX^;5q~xPd5&RBMvA%G&}T^I!tK z6pou_#P?MDc2d!{<#2x&}3`BR&cHt2+Rd~YpgYdujXs$Xxft;@aVI-6<+ z;l*vOsLl2-nlc;S)!mu9V!C|%?WvD%zg@~X_QvJSm*b_%C$fd@MQ8cvZ2)MYsvtVH;sBw7I{B}o5y!R5<(Ii|0_*}f8goB1~L`6Y*t#7pnrUi!WBJ1_3;Z1ckH zkcG`?y_l-aih%c@x5xg(3>bjZtXd5AjxDifZY~d6xBh`K3lzCRJ5k>PG8}k76&UB{ zB(HO7@9Y7+(dAANckZFlMMw)%Uc6wsQ)mZ89A=EGmqS&-;_Gjn6mqT0w}_|980n4* z$F@4DsnU?UDP2=5tt-RYSS2+|{lK31Vt&f*#9KmoXqDrmpUdi=z&k4yq zGC=kBeO2c-6i|Gv3iDPsIOAHBG9b8sR`8T1l{Y5uWlk~>-I)m3wE|_3!5u21_;4w1NC z?)TNtzIDbjS2g<7Im2l;a#5R_M=r?FJpVwpo=>84?s1cYgim)b9+$F}HUY({M^pva zf$TR}7cYCCZnRTHd50zgLdphD+CKc&{_7bQ)+((cm*X14oc{XC&Nq~*1oc^{&zcV# z@aj=JD{=IY+@4NX>}N*=qs6cHgtH+<{LnG^=>U-xH5X!A=TDGhrJC*Ci?3BP#yWaC zB~CP`!XO4TyvV=~IYrn}l_+5o_F*6rWD=Esa9vS})v1Agso1=-bJyu9=n=0pyDy^H z?i58l=CiaFwhrRMDGZtc885Eo;Q?$$efdN{(VvZro;uu}3%&ORFT`{k#x6@YA**jw z1owCYg&-ZycX4dc+UB(zxPM1L4$GiKNJrO5KCRz)ab3quM^@TP?BV&`o~f3_Q~d7Z zYS*tJu$^uvad?f}U*~*XbM8M`Uh}1iX!DQIA0ge}t7tf_qM#`DJ&zFC96fP&=IPdt zUDY|MUZ&lH8XkE#H-&T?9;wTPVJ*5SxXsVnr)tPfi@X!N8rxo!tF}tS1sWvF^Kz~8 zVePF@L@k$5Zt!4=4st^ejBwUw3)xl^Zm8)J$K9OYEHg5G4x&zZ=Q`*1i6v^>$W3Nj zDdGf2es+)z3Kwq>v&D+$R+dOy@xtZjReKb5oUS~Lk&|U^gn3)V@AjynxVbZLRHOp# zv*+HQVK2jieZFR~GF40|gg*Nb^j|*E1Ye(m{FbAqo*@q^n_-kp?FblyJay3D4Rog) z*_Lqs{OZeQuv>Wr4?J2K;INNZY+4yEn`C7kL$)6AM9F{*)L)aI$^&FU{j?l8SDU|< z4hl9!@;`ZWNWi!ZrUfr7Y+EJ6-<61dECg=2UM@m#|Dis+(qfmy{sbc z*m@BLD;S$0#*OC^A;GdDxfsGt-9mUO+;+-L2ymUh*Y*N$x_pImIBU8sEnvf>YX*3ag$Pdi z*ir?X)c;pcms|)tTPFMcN5KOo)hSd|UYGs4vf*B;j60W^4rSR0xu=PR7`z?O;KZkC zhiybQO!&sI)qi-C72W1HyY-@ux~0p0tLl=@N14flKq}}E;q37ga3jN>kUVw>H6LH?doRzi3)lew;SEQ9&k|wfA3n;zqU2fFjqzt zUMMy5g7%~W7b2=mfDydhH{iyAy_1k9z7)X>&UKcr=u4NsUEqdv=e2o?%wKj|14&G` zw}_K;D8l!_UXZ(?an5_Zfw(YDYNxhkAQ&p2#W^gDF|g^m_aXUd3020+HXr=NjDA#w z(+jDiD`s(q9>W<=&3Ey-hLp;k!2(egPk;WjvF>SofBjvE+33$U)0VyY)|17z9^U|+ zQacVY)aLs^>OQv5546t&Jb#>Ft8M=wS1K=7{wSzQ;-Ht6!Dx)txiB>W{Tq2IrFr`< zC0t5!__R2v_a^sZL6XjdHp+no>irUtd%W=RMOSxHyKuKyVawXwn5xs`)|&R9_#RNJ z!7sevCpAa)n1U|<{3L6BzWegqxkA5lhTidNa(`ALTg{?so_Qy#c1%~jZb`5n>CtO7 z&yDF8A6IkEJ^WkyxZ3HBYUlmSK}MgnwQoMND^YVpK5$~39DR8E25n>HbQC*&75YLx zzabPnAQ8Iu4QqSiMgqwx*S{MizAG-Ph}O8ml95p5M6JxC3m4m%_MByKUC8lz;hf#`4%ixxamCJy{_&0^Mi?j zv+Ky^tDgnb#_+!Q z*Uj7Le;ok9oNdfIlHKakSBdR@?(a(W-aZvmJg zVk*UWk7Vs6=!OESI72eti8k;dfOqxDMNg3v&FmKx+>{z3&j8TM`3gX%2mGcSj`eG! z)buYZu)m?n=9)1dgHxv+;Hm zS&A3uG>nuIX<#jmnLiHx!A%nbvl&1!#x;xG3QV^~Lujh=#BiFgub}qg@Hq{6lpr=h zpOh|B(|Zku3xZJ|!9D5iu^8375O1QbrN)jQP6&(X^diI=O0Z`qvt#*ectP%YHi0QzGCd2U}%kkEKDFPWA z@>br|(mz4kw{7YhZ%jSFBVk|Xq)jsn7MlPs=2+8WRD`4p^oYvLYZ1wbv3ILkcu3l} zPC@|Hl+T>QHGVOQjTc+|sv*8p4mF4fOf)jE7Z09v&YJq>s}k1$0BUb4Gi935f`sax z@bDIPk|R?s_yg8~Fyskt*8`gd-~z+CPAd^f1qm7h-z8oAlFNC{?Be%}saEKy3!w20 zH-0(XNeteadMv27ssYv7SOyc5f#CsbONjl!Fp{DpEzPCTgI7~%{|awmI4$#{KQ>no z&`&8i;pjt7>~jx!ziy&k+iTeK{8#Iboo_~tb;d-c8*#l;El+MePR7{|sm}ja7C{_J ze@Q}g6K-A#eShc%W3I|9S1AnXZM#oR19V`l$j{-4x_3y~?T}nG zmt3vUN7=6SH}}03$?_x3xLx%z(9Ah5=$ICN-f``m9y(XVHo$vneu{0EGnD>T44}-y?xUqC~81tBmh^#{B+R>LTBr@0X0R z6?2=uRX*cTqdNMig7Ia4WK6raZ|`i@j3AJcJ7Y`iLYCS=V5+5KH(3{>R1;v|r(L+C zYCDhgzv2SrvzPPT*=laPWm(|p*?@`m?sDX(hafvO@;z#MBj1lNJ$dYt$R1HHy+X&ON(GLC_^dc5VyAa%g?+NZkW>^ z5Qlm+^cj~<^iLsO2zs1&aZc>75zF!R>kVrCD3ZU`1^d*vtu*-%X!@-hGX6evwTcCq z+PESjz!a=xJ=go!cOrX($=9qdP)bgVxBG)aSoItH-uX<5lKAvyw=SlmDq^M)2yjQU z`79Gr@VbvH`G8Vayp5YelT8^^dSHfOg`~?lhVBFRvs2|w*-mrc-?;!HViolu7x?&b ziNA;fM|RniT&=gv00%P5isvWSNdirMSJ(clwjI(S1A;%O0U!HS;}v0>KITJHQHd@F z=OFOvqkxq+J#cCrLMDV5bwTDQSx}x*7AkO|fObw?VVwCW+`YZD0f_s9E>pzBf|d}u z_)32Je0zrzh@4LGsBhEZzFP3AM#4Y&nh#Cq>p@+dC3(8_G*))))qti_{a96^!p=9|#EUjQp z#(Tf^(Qu4WK(?MRZO+|;zk^glGvdw7qy2jsaY0?)iHYK}-sCf}eqRGB9s@KHqRft3qe6Qu?4i2!iiQV4gSIphGo ze)g&?xeC)Qyhi?rSstTi?ERC`9@uLrOn3KS-inqqS?rc4OJqPqr*pRlMa-g!-74n3 zO@DYvf$R7`V@*}M(TGH`joL#>FuIdM+L?$QL0R`PmPYJh$2C1U#D351?y%n`#hZa! z>q4z}T>cJib7)|ZK0V%@il_gH9UVA7zE*-Vgb;ryexGA5k^T<z)zGCdHUg3#h`I{1w_f;(< zR1VdX&&J%JKRtaj>W?U{?^F?3-OtuaM7g^->0FIwX-<;efe-a(>t5$`_@Acj#C|t~ zZevcf?Lm948fft%#7%Wv)jJU{5Z8^Us=Q)lV{*kJ zE(Oyk_0Q;a`M4#NXm!EEHKRmXrC*KyJ(wt z0Iv|l=?lj}?y0^4H6W?=0e-eV!R@Xn_KljFPfi=I3(e zvwdq56j3>I#h)ea)KCb?<(wBby%+aW@Sk{k)sixcjCf$sX@;vjW?lNoh<>(6tDpFk z-^I&28LRApP7Lr;SdV%bOVkL4BI_EG!Cp8q+RY4-&h$U3!;5ysp-wP(JJUPS&uzk# zes#brl5%j8BU1~Ciy|j~((kBjJM1fuLoqz#j{ene=9g3Z3Y;saeKX!PQfRl=YfTeb z+ccWEPr}7=jrtXkTU*b>3L%SRrI?Rw1&nK`qptm3JG#z!i^xh@oc5&+YXvCzv1@|e zQFuc*_`>d`zg2&0a$3o{KPd@` zzmY+;q>oYIgKw&8yH`ElFm)~%a^6%IJVlqT;;ww!DQZ0y!d`Or7}!({PoN#$Ue?Fl zGE(UOoGh#nOdkv(zvFU1dC(+}zs6gO+laI*yf3tLJhquFq}Y&>BWi6<#KXV;3Yl1! z>_=L5^b4~OOJ>}yb&6d>fAS~L$U>RZ$H!--im9N1Jb~7z5%B!A`aYqxjcBGjQ<)bv zgPrxM8+xVWl}*y)8WVE5FYIFRJggRh3o{4R^Hv(`(B}Z6V_(&Tr$HKv>mnY!{gM;B zh2mg{2}8!Q_=yO+64pXLQohlF3t@^CePI59T-*7kOr;QheJw0JHV~CNm%X$L@i)Z{nh7$&k<##DtDg0{#pQOq_$;W>B%*E}} zij~#9M?i$TU0AZJ9E@_vgj-kr&(9Qifw}SQJ3)=sX=g&IOU0lTjO~PtK6W93B#vV! zf$5-;s280YUN!PC0^=-lxDQg`(?;Mh;qIwHuz>P9*!M&+r3U&@GeiLaKetFEMX9WO z|L&>CRYZO50q|_|G1Lo&>5`#NfTujay~_hmpeQQF*W4pac|Bo|am3h9N@PRc7;hY= zHo)Fjv}0volM_M#z-3*`*XvFbrUU-l2)+Ek|59i1aqUdLIf8VL{_};9zBjn@iF0qV zkD&G6gKgWxK1uyncYXIy)qit8`luD=pzU;3?bWY#KTfh@m!EovbOV|>LrPOoSgcYjR_)wBBNjnyl7_^|x{LFw zI8Oe?rDHoOcp@{!;x1#r>tc8IjBA74p7v?hadFkaTvV8jDW{r?T08U8BGM7ClUomw zMp;(A9MZt3UPtC$BrUdbMnF{%OWqGj*rkfmF923YCF>{#b#hb^3ePBx) z7;>Z_pH%ZoNP(f=?PT$^ryX+*d8Y$aX4(4yO=?MF(c-NGxwfHi#G-dEevUxf@I!D# zQ>vLf)#zXFSaIVGt=v$k>NJO#?bo{ZN_UU)sw0c1a^>2*UoHNm!fmY|nyzsM4oTW&upzi?D9%VLYhdR4b8J^nmNeD}f> zQLe{^fBE>6{zUIP)*()E1o0HlJ(M|G^}dQOXNAWi=>gntlHkRhTQ8CZLRZ^_{*FF; zJ` z^-UuQj+>M+i2eezAJA*%a1P#4AMeLEY zF&L~By85YN!wjIMBP(%$@~$8OXTO`?hxln?j+bH8j+=~!rW#KyYW)21y^@`qMN^O1 zu^}88L3&rYZm3ML#?Tb(ABuJ(bk@B3tlosa*_|4hRu-Y845j-)y@eib|xsrB@7tA6+>nYk-`vHkB%#Ogm4NzW24LKnX z-mW47;+8K1V4u3A0EbItGnV2l=>!!<(!1}CaUXuo>Zt^3L?|3`Z zj*8p_Lrz!Td-)yVU#wQd@EbVOQo9z<7wrAC<7Gae?UMFXr@56h0n7||E7eSv{YrNf zks@03bTKN$s|BfzsJhQRxC>=>XGKW`u-L#@R*a|i5~%^}CLd9_L?4Qrztd~%pMGPF zLw0xIYh}{&fjc&3Q9qTy8(z$J9V3i!;S4emA`4YD5r>2kkgajM98N5>C6|uJMp*I9wl8mtSXlKuVghOSFu19 zkPA%1Syy+DyhN@gyL8-H%h!Lxa^QhxkPqxUy3?mr5^PS~?kGz{c4sCRN920j3Tuz$ zX~k!wiPviO*Ph?!-~IBBgiX}AXwKO|l~Pt~jO{5Yb2Oi8F4r1<-q>F4$3}aWlS0iT zRS-i9R!)wT-AU9oq-9CsPIPz0ZciFZz-*rgVLLEs67p(rUb=jDw(1o#fav1de>W;N zA>uh$6uB@E7_rPNArE4Kq+r)4I4W^MR6Ewfv)ZuuIO`0D&XgGbfD_KssyU?{% zvJ-RQ+SLlNQQk0OsGShio|p&|Im;#o%-nA}?-6qOMuqKZwM-64pa==I^_ym{=OQ3$ zjk1qV<9ygQmEi^|N?jkWXvochy)-CBqswJZ9tJ8ovpB?r<>1i%_Hs@gYHV~=uKRry zKd#3RzOYx*MiJ9SkfL#>ILv)TZsi<{c+^S4KQ;%yc=Jda+~9c@4axcl$DIuMr!hdq zeyR=IlER$1;n!Z?rir2C)hDs;Eq;5W43}3x()p!EKvM+nE^JAf$qd(@tQ9i=uvA$4ijU^C&Nf&(j)O`Xs(@zKM3>_ImyLJD+W z8piaJ<=7}jG*I)aK+J)%c=mIHqe=!}8BS)*lzP&)aB##YR+1!Bz#Y-|ckECRXKfP! z{Z@UB;=*v4QqBjX7$sGyjQKa^CC5ZA$3c(4^_kc0(^Xoe^T%oisDhK!3Ti~d_i33; zj=g&{wUb*`P=7q~eP&B46V$t`!iUQ57i`UY_xBiyR;c}nwooW5CBhd7lVj|}h-Jga zCK)EIrKz8Tx6D3|v65ZmJ1s9aLp1sp%o<<<;UNL>PG>B z;+!9`4c-Aj3(BLYPN4g8>A0+l?#@H>eOj86bp}nUU3rhK+0Xv8R7* z|7G^lI)Y`VkLIz6H)5cp&u7Aavz2uhX7o7h-zIT^0}#^qw~O4f+Bs#L+b?p4v`R1cwEZb`IJkQX8I z|IDmfrI;J+#Hh9$wu}s~QytLSr0eMOs?(z73cz?{X}kmnZoWQpCa(RtUdQu+wfH&o ze?EtFNJGc_`>ZB)f@g$kzIL^JO1Q!`VCy0T#yA8=TxcfBV%U7 zIVow>Fw%n2^`|MrYi;d}$N%%qtiNpi{PyPioz1Es@7)frZ|nL!cKO;4Ws)zeUKh~p zQMr(`WYyk}wiAfbv`N3)2|E8mc5Kb<_ayeGeYEv@K7G?*;Z7i_qvP`P?+Ja3^EGcS z8mi@jdeproTTLY{K#|8>InhhD2)~-gnr+^-ZAl)vR^>0d4|tsLlgOX9byd9ql7{ff zJ2{%?oNdA5 zhs@+rqFO8N^#`empK>vWB;=qqIs2V(SJ!mLXk{%kVga8^ zsE|@eaODEicZ(%hd^f+j{p3PIHQo@G;wJCDmPmHk-wFS-y$XiA>Wv00Sph9hgECs~ zlfb!#MLHa;om&I#AV00WJ#xOLdq$?a5V~$}W*F*Y)Wx7!bpLv!<&pwa#f;s2n0n~Y zCn4C>82V^pZg};bJgXR-E1TkPx*Pl?X|0y9yF7?9E{fo^72%J&+rFlxMU6H^u3kh? z)HAiEIH|vXHZ!$a%_v@w2igh$fi-8Q&v{Aaqj6eLKKB_X`jL$uL1E$qkU21kPpX=E@f-DU4YXY6J-zenok z1cTp3YQu$Ol4Gl}l3~baOQelO4b&f!P!a1Q5M45?v(oLKzok#Ksend0{rz|(#mzpo zRG&ydGM};kQ84YIs)5;pH_&S08%9xhC#q2xy5`3JcsloZru#SkS7NuELQeOv%1q@r zYUVT{=ToJkoJXn5VL2p*`{JDwSf+Bj&UXcR3U?hp>@Ts?Bj``0f7w9>3qe z{@Nej*XO#f*X#AXFoRW~!44Rgk`<;qnOd0b@$t_j{DM#uQn3_P#rZqFwDyCkar-G) zPxW;Pb<;DUEH!t^Uv0D5!LxbQ!*s3Eb>}@KsE|b|FSsR8O83n?R<@FY*Y+tJEqLji z?)8L-{UJ?%!k^qjz=+F_a53SMX(Qjq_0{%F92tYuWp~uyj_M+SV_ok zCH-s4d6?)Jm6U6Y2o*CjNry3#pLn<}y>{J4p^oD3Vd9LwHr^k ze#TSd*E8-%tn0-M6DYV&Zw$_8R!D)!d70`F6sj^$Ur=b;c;K;Spj-zfx1XL`qT=^( z&S8?Cp*0mAWoW73qDdST)wJ==z1vs-@O?dJK+b=ud~OR+35UrQ28IIElz!KGu3){l zWZ-n46`lN?fW6K_`G0`fedC=)c2ESE9ctYsF%{22%ysX-)rn?xD^7F`xarWbIcyYm~jnLE5RfP@L|$IoO>VUNiw*pa>SN--IT_ z^O4pN<^NxblKG`*7eu7TSKf zp!p!@XyGxe3p{=i&+PhNXSe)(SIN>be*173e>E=0G z6KUey=Q%BfwGm?xr40Qxg8#|QlvG>KPJs-6YLYe@ub8KO5Zx~n0Y3WsqgvD8H(L73 z1TPTKZI%Qi0;pSsPM9h&b`h1opr$tjqT zzt5^?r0~ZQ07CfX+*K1{B23DS&grkGT&d~ImNH!pZeDu*{3jm@+DS}S6tUT>Jt0JB zyc)Xq)m|It4213uf=$m1-_fCGdd6dqrKI;#9U9WKH)jMO86F#)d}H=W zY`x9ZZXg_{68e0a@%6Q8+xVi-LPsLNn)je6GdK!$c@emdmDr`7o7hel-AkQNn@%6V zt2t*;CLrn<*bpzP06acws>!2DU+2sLVx8l=H9x~gHgWn>^5`rVv>Iev2@%I0Gij;e zCqoC-8hW}@1Mi_fyK*Z9B|iQ%@`ov7PZLV9!Vyae1Mn^ce=sN+fF)|KQVOcOUFFzR z1Oe&hiRrj|qALKSf}p|{?wv6SJ%{_7-+IfQztGN^A^^I8-F;PM2Og)cTS7Y~_(@}$2*_@Bb@7+%E5tv~E$7WSA zb8l7RTI$Q%KM6(qc8SG(R{=#rPsR1jszfrMIbz-qG#=20&7>PwFSfVg1Y-rX;GuJY z>s(DyRJM`fW14@#BGYy41R*c*{d_b?`m-z0G`GqRfpHfB z*)+ph5i(1Q?CYz($LZ5!mz6pO#wvHu%U}$AlHH9Wd^@F;;4UD2#F&GuiFnZW)It-T zy8~9$Syf1DjiUYDxv_(e11AVweOZ4CJvw6HsD(Ot}O^ix(}xpIf*a{~hod3h%(fN{40*4Upd?uL1UJ z`rLw7WTZ>+uW)WsD$Go+O`?c()Y)5D`^aturQg5|rO=-N#EeT(bu#;?wPMU}`uE>= z%B%Q?tZ(|E1wcxest{rs4l;W*b3Nxr>E`lsJWC?`gi(?N#vO&JNGK7YF`~-?7MQG1Ev_gKwy9 zupWu*sU*fd$RnTF50_Y2rsi)SMnshSY=kw>91r{7q6ziK(bMz8wfJeFZ>gi8_!*36 z91#I)`)u%k$2fCWhc|gp5eaO5a=CE7YpX+Cp7k?@aIIqJxm2==#&XmxrH#@oHu#x{=C4ZeWQ#kpT@u2kzeAr`Y&6 zJ4s(C!O(z%GmfXQX)n!A&fGsjM?UX}8!hdc9d}&<`$}EgP*vmP4(30$ZHZR@OJXIr zxczu8-zUEHrW+y>091t&ud6Ki@Lyn3k*MLeV7BaYslbp!Hg`jRJxo@#6?UrYYP!Jh zX?pK4`1+1Ef;g5K7E;~Oa;Bgs6ks?r}4$? zaj?@OC0GC6%#(Gyx*k?rzikc5W-thU(U^XW(%PpWT<(9_V@4LT$+bHs-g@bg^gVEd zECDGhp(&)gj(TYQ;h`y+i4d=*cxJAX43r@vQCtUaCNg1G@7tomMR z5<_EN)#d(2;C7SULYjTPaenqI@|bqzhW5brc!F~c)je+*&*eR*<^>kIBW@wrL z`c_A37@Reby3Ta>PQbTmUfZ^fKPIsL)lv&38xcN!_)ESfc|BHd`^2V`!`84famXV z+}m4vf|;PLIakaF^@Za<1<0CIQqNw8OW$XTMY4SyhRW%d%BBoA(5j4iG2Ss%{<1Xz z-G6Pb{vVk&QbblfFU^tTGb0ksMjCfG775eb3aIki{l4GiSXt7e<7ujq#MD3AmoP*T zNR-K}DJ%^Lmx)+%0U z>KJw9?JLH^5^Qt1ZKl0q=W$4@iJj2I;t!Q#=5>uXRjupo>!Ra!M&{Ea@7f$6ZWs3KT|-=Jz1AwKgmiNv~5KR~||@ErbSIr^L|iz&d_6 zb2SFnSg$w3=E29S3@b2Vk+)z)aSt|kkhb?w0{hYUve}qH*CGG>1s0 zcqSzy-30FSo$b7E_n=SSQ`FmvQrnTasuIgQ;crDs5VUPQwlq$-wh`E)*R=EaK>Hbk z9Qxo}a`H2a7|TbqVhS6-n-)qh4U9^v28`SP_08`yam+lKH+AvLx0BRE8N1<~8e{c? z{t`{%*n?W&v*GqBs{T{amn6LNj=O2J^yY`RSK{XEUz{B4tP+D3p>^Co+s`dgfhKd; zJyzN@4rSP*`yw$!ThE$MNiq#=CMPWY-Sv=iYoTdbz#5o z&g&wv2_Hzp6$N~x?wdbvE8qcbWTu1?-(iC|luhE#V^Sd6jZGGy2seVsRQtur{yx{0 zWl^1F`m41sZFY*Dl>3KM&|2E@#&cFxk)~1uG@?G`Q-S&tM4*pALF}1!F<__=Fc>Gu zav!{FPmX(}{%h>fC|B|KGeG@FRdL4kS%K1Zxm0*37K#_YH&!@+POwHWUAWXdM-z61 z*bZ7$55ydGvuWHOg1{#h%iKK+hQqSdHjJek;ltCuo1$tuvZK5ziRM%b`Z_d%(Qz(o zxVud(U}5TF(iezPEDxzMZ!x-3pP;6ksfj#f8s2eU0`yaE?oO8#Fo7lr2wp$E*PTH3 zRVya4%bjaB>}Tb6qt0b*&kFyh>WH(-1`O4@Nw3IZ#$)R#9|}@&;;?pr8gSUvda?#@ zI{eB$CTQeKe-rxCIZ%7L+^!6P>7@xTPhys?thX^z8u>#O6>ugvH& z>}`hg-<;a(43XMuo3K5kd9;2_2#+m^mDBQ&?``TOh!@?P>ss-6p{x4Zomrs+g+1e7|7Z~!NQp^DQDp){x+AHJ{2+JcE?$Fb1MfwAqc;1Jy^f0rl+D3 zx?iLRZ-CFEI-xC6&{0?e_*8mZn!WM%(?0)+H+8n{o5bt4Z~xTCu>?V;w#D?m@;rEN zt^ap}{u?|x*tfQAHC=3S4TwNz>tzIzh65#$dcufiDqjB$&nq|Tl67bQE#aD|I-8sp ztQ=62)ZUbw?Vs!1i&UFx5mvd=RiB)Bc$oPpoANH{OUmoWyy#+k)L5VRJ(fEEvZQN9 zhJC&kj%*eja=Sx?%t zfEAcm5c2kb^8Pt}#cAHWJ|MEXj{sX+q*lWp4x|*rQ*$+{&E-mcIt5oqkCgb2#{>?^ zj$b#6U}byX^EswYoO$Y0&?}t?gW*I%N#*S)$YYuaoFqojPej^S-n)fit&fn2K;MKi z@zcFIgZPdRj@~9K*^rsj%9Y!j3GLHoe{kZomi(oUIbsl+W_b?>)%jY{79*~|O*=d* zz3=vLt@QV#o#vOmWV1TUqh54%+N0PwH)zA1vbK){${UN1Iwp~mY%3t4 zwj%izd{uNG74~b=WRv}bE}2gnoZ>yyHq5jTpCuHm6>EySc~pE7kGmqLj~RTeD$f4d ze$ay}nn~ zD=r%SzO-{qM@n0&t@O8v9sg$Q40cVy?$(Vy)!5oN9h;*~QTRP33^brAuwP`)-f#y1 zhflZwcg}#RmN_#l6^Q~1d^{xl-|5gUiQ4FKrlV-JwI{j0I?KCg2zda+AZ?e2QK?Tmb-ID6*Ir&lBi-pyr znnbe`1OS!kA~iCjXmJ0=!F$wP_^k~3BNv9itadETn7)2a5W)q9nCNdZsIk=#V`96{ zDbMK4lNM$I{L-RB84>{`y?LgXGh7Yi)sUB}Hz}XLrmjI+6TK!M^!o3xF{<_aHCXjk z6DU$k2c`g8>ZkDqxF50kzgpk6x$$SWF`;kW-#~3YF`n~6-}pTgf6Ri|+meKDTn78X z7}WwVV}mt1tM!_|8+l-1sg{299Sf^Nzx6}GetVC8 zR#@h#%hT^31V7IctsOmNQIjsh2@17|p3yL47#Z8lq3z3&Xe<3D%#XgK7}jaiFW0}m zuWXxVJoF+zPGaPG`rEzcq=QD}?yVo6^1C|^EUZWZHE(l@eQnamG-v6Xob0-KxRuha8%AEB0}$KQ=~~Bq&{s>Xwcb_c%R1{`iaj7|O3- zkDm|_JDH)>d#*D59H)0qn*GU&gKF>8hEQ;B-}Z+iD%)7ry2jG@{_We-(5Xan9 ziOsxjEhw1X=zCfgr^f%TKXPVUXQ6RQJhcS148DP=ftYO)x^{YRGHg&gYyJK&_99}c zhe!eoSn)bvX@8MWQ(Eq#?6;bA%EnrmuAPp=2_F%sTZ(JM;hju%8pe|R^@~@eLXg5j zd$K^3ZzFlmU?n`pDqBpW;cvORI~GD-P=^MnBW6`_OlMUgj#^m#NEE2ZdsBD3%T?qI zFU=qtt#&KZQ+ADZF9>}>ZHIEEb?)c%yg+1Hh;DBJR@4})@T&)s7#C|p7#g4`wQV0W z3VO5T9mUiR>WK`lJqo~_`1XC&%X5P>a_;lWxm1=>Y|uA(^=+|Rs^?I zyY@2McWXN%o^2=Vx#DjWH)bxUY;s>bql=yt!=_pvo44`KxW6_7pe!|SCXL-wC z%<>~w^aq$Z(;42IUD?*q6!!rJvMLheTg>jZ(xu$lZHh~g=K3-cSwCmzk+bSk)Kl+~ zg`UefA$t#9wJfq9P5jRUP}8cv;aV}W924a8ow&zIXT2&~_RKJvs>lOku7FFd zqv#Er9l|kc6{ouC-JkYXVlkR^i}L`(bv2;&SOO@nJrPP%s~oddCR2~Etatn#cMafV z21C2M`M~BjthdNzwpyukF&s)JziQpHFHhDr{TZ;%^>2e(6B0m9%@m>O5mSm_30cS{ z#lquu0JOI|3s&lT8T4not9N?O)i$W-hbdxbz1-A<<@PxP;5L?NKba1tYrHq^00ns4{T}9ES^hYSUEHYc--$ZoB}Q&}exRI7)8Cqr+JUY5)EWdTQUGN7pW}qx@%BZ{YjM)$#2BZB z?GE{0nsB#uQxaqFni9dYR%Vnb7sn1Zdncxa?h@a9{KvOL(8)x)J1WQ{#6d-Z;QtdJ z^+-k;YNh-c1%t6tn=#!^&Ff+tJ&~ka(rxWpPMHeKYBo*_Sh-vV;&TYWAlItE)+3SN($TngL3A&=y*gb%csH$an2 zhffSeeGcGU?QZ#Dde{f>9l?Ih2MkN^SN5t(2XCwc(3D0VK!1nz1Mk$5m#QB3#VK}+ zW6%3)ALmD8{#}I_(Qdp%kuAk7%q>bR-_4NrSmhVmyej3k_QfU=OXDLY5$b9rNLZl- z_(@Js$$Lo_TNK$6OogW6^3cL~Q?Zp2`*S%{qAFaSIp!fz-tt%5dZ%@kaivH5+Hd^q zB;1Fb;2eC?i2^V{3Oc2dlfs&kpjojzz5hS2-KxLQIwAC}?`O7m7+EW9?JnQkt)Y7G zzq>y+w><3Ec_U*8t)j)j5FBVLW=xy5{*Sv;0 z{2=(t+|_vD;>fXi7bLP;BY|xtGN#iBe(8ql*FAb2yAJj&i;8q*)!2B4}ze&#{RW&Py(y!z*K-E##@{ z&O8!aSV{eYd>W?<4&!y-5)n1*<&(8?%m6FIaSZ-w?x!}4Ri(w(*NETXtFkhfZ0YKg zg&UiB`ckNwFM?`q*D<-4`E6EqJ)s@m%QYZ^lzTJ#q(X`dGVuA5zUg8|y`ZkcN?lKW zPmP=$%k3!kdXYxdT1r9G+RioJnQOhsGX311mv}Mc`OF1AP340(D>g%g#OXIr4$?$$ zI?caLc&*#mXuhXHMVXl;n)5RwPaQC)=9t%7*6znzcsAVdxSnZGsuh$7`fIhk;Au}q zw_JK$f_26X*xIWLB%t&V`gofT^3W1n;eKbG> z+Z+(P{I<+{-QUOS16;n35F=0r>jO} zdV1=Ahb=iB#(piG{KPp#{lW6GtN-ok;@0?gnFdE7&=*>wJPUCr-(zJMx4omyNyR!tB# z$^%HZrY~^wxjKAFwqQ9Mdl>N@JBMm;)}gO;!3W{UqP zatn4?i=*?$q4uwvI_P)w3U<@s9WrCYLhbdd?1X@Vp3&6LKe?wrhszU`kP4~rF9ZB# zU(zn`k3tNkf}=Z+72^##rg9$k(%b*QKKzSbyy`4E!0&%D9{)F>x$_6KVWDqR8{OIl z1peOWsV3yS^ou0#7k*!EG9QZ;9CI9pJiun#l9J4HHPN}pZ4TTBw{r_RP~f9!s45EK zuZqTR^LN)Dd&ggfe7SY-&etbrZEmZI&E;s?);&DwY<}EKW9bysC*y*bQjw4BNS<|# z=~mN#*bXMPKTNnF>3rwOJ1iMhY{|=My^CW8+fwfk;XUtd* zhvxNZDA62ygomtjo;0u8{p%aCi7}NWVs9DQU!sBh`=6OHA&C+lTc6<{D#9-sEsG}< z1JPZLa6EjBp7ZeO`L{#JLFdu4jXzuDT*hsfrFgheG7X|Zbi9*x6r}OSa#LCbP zO`ZJoS}aC2duocI4OIXAAmh`?ZzdsWSX*7qd#as#iGt8Gku5iqtGZGpBotYlZL}&>h;n52w~vn=|59 zF+XyhpciDt(^W)#NbA~c2!xu`=L1y{Urh3Di;%Yt1v_adQ|jUlo3`CiE9J8j_!xiE zKQ4p>dyibIDYpooY!6%=1J9duCBs&ZP9t+bb$3LQzmM7lb#|y^aXt&>N3-f!tjVTG z9sF~p((MZ%$qJY?>rE>aT)A;IXoi--9)Y}1KH?e5xNMBI&Nv11MzLiT<9g)&OxaP} z$o36;Wv12HA2eTnU$X_huXJ}?l?g8SxL`nNk~Lj9s}5-_NvEJ327Bi2rXW)3$7O`Ta@y%c5zv<*V# zz9mOp%+}hVlycWVL*)TYM4tXyT$Gq*U)*tY6m#pnWI3#C`*Co!FNPve zqLZQ7@J@wB$uO12hO9EKW@jrByOxe?BQWXiyOj^@TI%28)ZyR5Zd(WiD{^KmTGCI{ zP>Wz2mXUZ7_1~0p#>gCvXpf)#?$2ScR2*hd9HXVYZFvHZ!7u^YoNp4#`xx8OFhJAt zDbBGi@uHD%B3{wbs5;?iR51m);pk&|#NjM4dS_wU~6_0`y@$C+ca$ z*js)C70eH4tIK=VCOEm#&sW8So({)4qmfg&A#b}8y2m2f2HyXAr=LdK$$I~13o|!krpeq5G8r_!6|xZl#XD({?Lyzraxf+E(fx0xv$Q}e?k-Dr2W-f>2$+@?53lG3rx7=}|d)`x6fSDn9;eczjX(Hg_nn_e{lvi&dFRJ9F%4JdoF zzy0AtjzfIkF;kcj$9qMcWyevDnn_^0yO5s-bXa9^Vdp~=)#I0v+$YU<$X94cAU0(D%`@d0p>ccOp z)C7V1@C4@SHHJpO+7Xdt%&wo=_Q>@=A!=+SP`mYm3ii7oyB!Fy*_el2wX zK%JJb2TV`Jw?AFHHD1Ru$C~74M0T6dw~J!#QE`@~aZNE>zkLZ%io6A3$6ujnu`7cr ztd)r+BhxI3GGvX`9hJ#GqZ^>Q3^m~vG*YtpUD<1NqaQR&t-#^n;%cublBNS&L_7FCv~9sn^e6TW3{ ze}Q*6!Iy@tc<*KlVXWKTjo}PM<=6>c)$K%35grzf_Te-7h?h(gT&Kf{W`uGJkdD(D zfs{<;g_KH}mUi^G(qyXNh2)lreb>PT0zeAdGwf9HUsQ$tbff6)e6f}1*&2#ptR+6y z;t-04lieR;I6aUShBN}Rw18TOiOB3giikI~zf|I@>ya~=SzHm2Oc(Q$n!-5oIhW@I zSmE_)rE*S+KnBn+2t>Kad&mAWz8M`8u%dJUA*VSF>;D$RQ}oS~^M za4yNI(Sk|amnH+d)WR7``p80r$EzJ_0*7+jnoMRCe`lSmG}saatPGd4Uj5kyXFK_w zA!JB5hfYQbWFwMA6r0O6m7-oqL$-{!PQyZnFyIg!@s2Kt+xpF#P^15bG%s@7jSv0x z2@$t!b2yZL?ZiXTC(YbJR50Zm5VJ4>?CE*UvGauIpD4ai4GY(yFFx`vLhvF zi|rG(O}3qU)Zxt>yIGSzXY+z?O^D8u^0r<5h{(IQUt4_Pds2vn*sYKbzM>|^vMu(> zVddDC9v%!cHptf)4DoCCwo(l};vRIqeZ?S4SQ%eG&@^z(Bgapm=K&)3XcHoi$7wGoI_C*i=z}=k<}b-q z0VP@IBhwwpB5sV3+~6=8a`C_8W9`Vq1notdSPQd#v_xFV@a*r7)R5j2rn4Qf^3+1A zj*09)D9q+qK)3mql+zES4B1G=XKm!P`7v))gyxA9Yu@#OoIsThp`;jI4?+Z?IMXKt z7KQV*_C}#Cu7okF9f#;JUB_A$sm%?(EwwNqH~1!4@2PkEuwFpbzBwuf|HoC4t^R;X zpMR4K*H|{}`5NHU0Dr9bvm5FBIk&9Ut)7+0r=-BXvn)&%Io+z2oI!d*$i1;2jZf&` z=Py@>_+|r(q>(^xnjAu^@ppz~Mk2G&6Q;TJd#ii6OG1HJMP?Kx7a`7wQyLNfTFWNS zG2S86OMO%XA}5LX*#1`EZm*&9Wj(vZ(q++k^6M z-u{8r0iUJs``?)z^yVDZ;{ME#U1s*u)l+ca#XXp*t-P-8DAv>bKIJwO>#2P5`m5(^ z`)|rv*eIR;>TGeSO>oAg;-RI`fhdt8a$*eBsf}H#xY>b|hC(XWHT{J98JD|B)}~H* z*cbxMQg=Juak)Ss^a7!vOJ7-&?pf~61d%Q- z-fUw(^rG?-?Kj8VzNBcgXNyZ#hC*1X5L`0SjK>h$hj+Bn+f__+yR25Je0l${yJFXZ zIwKSq!?BP5lJ$Xf#V4__kBN2Q)q<>=m#+GRYQ|CjRvKpk44BtJ3(i5j6Xw$!9`Q2( z{xZd4b$H}HL#vYYTf)VkX=M_p<2>_?ZYH!cK6`l7|86#(t9c!HrG#r5OPmqd-+&^{ zdd-N9a!>*;z~(*~Vosm{EtbQ)#5ZQ=c(GGOek~r059KD|Mb}iFMxYHudlX+u16Gy% zR9~T#$2|gU)#=H0X8CO_rHmHzO6YvODWDDFytF60n2ySpE}+6qN(&`=y6{lEA{y+q z-CG_*Bzn46za$EXW0>{wKBf=2suSy~++p_H(x@;eAq4+ooK_G{W!cD;^Tc#f(q{H%)EtguN1h^t7?vY5zZ8 zQPg(%$lZu$vG+TpAkziRf4;~Dn@xw$WTyYUvrW%F$J$*1ABI?q9LRug$p|Od4$f7y z+D=eHI^%ylakV;m<~p$l?_l5W@U?V8G}QUf5B+5Dn>?E>RdJ~k!UC;%q(p#3R#oEl z7B@_%ukKTyLE(%G3C4I`gVGLFtj-<#cRsHO;tMMyPRQq3wo|ST!MiLwp%$!Phgf-x zlw=S;9*}py^n0A1@GYQ#<~#6XaS?&ZYMd9wE~TgL;gd9*H>&yT+jh_9{)Hqpqw0w4 zC!h^q6_M;X02TSjDe^%-#86d7_AesG|07NU!)=ySKs|l)u1neWNu-oDR@!swVove( z_qSCN^6G3KFRB~d67cT)aeYrM+XGH57Bd50Lms}*8dMd&nT?Y}+cfm)kttPz38O!@ z=}mKA<^frZx||vVn-qVKlPEOvVK=i>69(Y`2atl^$c!MVYXw1WCfjp(0TnGx*o4Oy z{hr3LJ1*@eA^~&?L>$b|DJWGTGy7nEXdlTe=Yh}zZQv&aikucjTcfg9QVkn%5{N32fo(@L^LvK-X@Vy8G6pT*%50^Db2c{holw z1qj#2Ft4=LjRhU#o7~kKu3s?#uSMNV+(89YC5EgEa#_q;gj{6xWWh;Ue*PFPlL5=^ zSxxzeJ|e!wJ=MknX;CvD@v{b`;V=mVhSYFgA2FB#f-go!RhGsjxfo9v2UILsQrB&29Dau8y$fm#zg~PwHlEaQl!gr?gF!+;b+0Ud5uxWB+3y<72yxsc zNpD#AhNL3@iXQKs^Xq2XaoDUQ;pi;#kPsR&g{zfq=H`h3b~EFk)bdjHIZuq8#Mtso zLe=E`{(NMo-l~sZQQ++B>ZI6}q!CDsnCHOv1*1LM$G|XbBrcD`ViseJbDp22-T5RJb)=T$^D&^ZXmspA8+z!qLLo<5np-E&{Ik+6 zIpzzNb{g35g;UQDu))DIFGl%^neKdN41eCQmkFdf5>WpJ`<-a39(!FD7WLD`_n|FX zOuzuw8YL?tzDw9+b^SwTJ9z7+=4s1P08J~>ek8qtv)zFKE9D~P{uO@4kCfWDSQ5pz zXpiPUi;Kf~laGBuCjO$y=`zYD+{#0`x*^*S1CZUIXgc#DP`qJr0x9Lp&588?J8y&1M5NZk1AEP25{-Avwi$Q8rvGHA

    SfmrZS`CQX>*bsTiAM@7(jMG&9-BCq6xP#(x1BxxR?2p z^xD;1j=<|ojbge-C@wF~&O7PSH6Ysw!PSpc$U0VU0N!ERCfxF-Y0yfK&NrA=np#+f zmR&&^va+EzmDpi>ikq{&)iR8&GMguG%K= zij9^d3E}t@;`GZd*cgA&j^gkV;wGGUxT97|Q6z3l^Dg-Cvb`PHBTr14nDkFow~aFI zk7u44zyWp`+&9I{U9o=yhR7<97cHtD_wTd^c#2CmC#PLdjEfhmrxV{)>*OxJk+agT6eE!vcW}zUweH&z;L)x(G|fv(>#+o%^Yo zRMCk#{Ac@ri!(sBCH~kp-+NisZmqTpxPuX@D^WS{+Dq>DeEELYV)q$4$TsDLoq2W` z`KGqJ4Dc{BDu!_)3mioLdO~)o;Wiiq%WxyhRZU&HY|XR(d+fr~IgmE8-&gX)X)4e= z?I2d$0C@~CI(~h4Ce;&@_D5Ee*{3IRJZP-b6-h0M3}QboBa~u98_FqvOiV*fc)7Y?(=9*qo2(MmV zZ<8)c#ghLb(2uHzvyJv;w&qsr1K{_fR1%!mt$%60iOnmo+&&*0qLMBg`>?gNR?^E- zgUJVrd81~YYO}E+u~Gw}bJ7uXGr;uBc?=(Gv?~Un88`E{;*HP}Px#r*HvtY(3+w9e zuj2c2U3UeFOxfT_RCJ)47o)q$)SQ!a$@nK8^P$!9Lm*ZD-#?sA!H?^JAXb0swwWS+ z09M293K9P`cE$_+nGB1VTyp@DsLE3utCjkr-}LMK)F=M4hi{)>A-Ya?Lye;KL`VJ8 zEem>+GfdZwB0~ElR=#)v_l{>f(do`vxCaje$i^s^1V>=D|WV(AM) z^vdq*FnS7vKgxg0nqnS=R8K@~vHIgICu~Y3g zHJd#M8f1NtkEi4r9%!oppM-Z2zAmbvwpI^fjD2;X_rj@zIS=hl zIsDGk$Jd7mXJ5Fu>T~E^VG+f#Kq_Fsas7Ah%@++1%o~$U#-zZ)^1I7Lk6Y1xx((Qq zjKM5pY&tauJYmwTdRIyUn;9moU_9kj!^u5wt21d&7+u|!89r&*`BIFx^2jmop+L~m zVnnoUN|m{tM_r>Y_`%IG!A3_ZY%66PctT2SfUW{X+Y~UU<(pG~`!w!Mz3vDUK?&zu z)+NzU*K5U(E+F!BE? z2r6U>c60C;Tf^;V9*C!;w-$|aVr?8Bbp&T}KRm$iUx@HW@E!QFm~sfV-KLG=i1z;a zPFw-@*Y{eRq>|uK*jcJ>VJP1kmTpFnvckAMcE7@Tmszl8(hKb_N zm3#uR*mZ7#qHHW{D~^x}K+U+;P@WxjB}n>>vntkjjjc8_u83nHD?UckUAOQzhzv0SzI z=GR>(%?U&CKWCYaEvygx6Lg+jSyQZL^bHdKiZ#i&yN!JWB4Qpf<|y7q%tx&bqX(j9 zYjKx0n^R3EXAj29PLDGXO~?^is(>r)CL$T9$y$92wR!)h>JQd;uV(OhMmCG0wXgvi z^-nX94}DI!4N8IK`(7P|mx`4(WxJ!P3tXwz^^e^l%!FUe{(^wu&T>nY7~Bq*^dPZh zJ*^PwI?YIwoe5=7%!hL08CqU!mCCUmx$(ct*DZDHpQkGIc}7XvBah{;xA$oo&ra1h zFnbP3JI}GpV^BAZ;xvtM36*HkY{WZdr|)`GMx$zrDcJAfd9Sia3!s*CD4I!j>k`?U z38+OEIj>v(@*RYXVP0$30E2m0XWkbT<-Y8o^e?sm2h1bCHFIc!+U8QsGCyS(Rqkzq zsmAZI+MP?$Y4JX41F@FCAZoHj%qr^xIeMK@Qy`m z4gGyi=u3tLIZ_kW8~K4?NPnc>%y~>Ey~r1@PLW>wOPqsOj9MC*st@DTrFzG}R&kt* z111ryhw9`6oY-%J$;8AKj4&X6tvYOjE}%F4($#37KBE1NmU3nWw`2!t5JQdGtjGo# z-i1Mby@BGT0UqECGeB6(S7SFpZCaTuR%ib*S)9l z;YBkJix8jQ#Hz6CbGGYt1>p8^yG`%`RX50No}zU1%g)2^$&ph+x$Czr2));gif-k# z?4?(ysAW7-@0OBKsv3D5hg~QbSR- ziIhXiQ7L?sZxZfiGYe45yPt8kH|KCK5SHiLt2-9Xt!M>btYO~AgGclJzBfjdwdDil zzPz~TPR>0%!KwEB(cR2Q0)|9&=U%7-+vWAeMQtlXV70wgVj$L&Ijo742W?2zw<<*jI>yd7)+eCNJ};Pzpy{r4~jHXdrEnaJGkYOAQs$`r{*-WCM4N9$N1E=z)Z;=`->mcU*N4c z3SiYIs1K8^i=xBRZJ~Cr6UBj-;(+3&Q^SVwdpzQYxrwk}lrxCVnbajlJD_!WwT;?D zIw}BvGB*)L!9^YWLu$`2V40rfPD^bkcw>UTltVAXR-bZOoCh^5hmC(t)rO|nZ-Hlc zDpnKVtw~6M;W+7OPnVN&k?|M1&^KNd zVJdB{9Ic2tF>t7)jYV}{CQgUV0z8@SCaDGbR)dy}eDrECK3TJW2~9BGj9Po-5bMw( zg7O_MUylHaL?PAU{tltrwYjP04`j-ObD!mLx>SLo|Foy6S&HUHyg>{AhNAA zDNOVHeM=DSL=*3Y^IP?AV<{5Jec{V~bDf7`e$hjW=S3ny`$Lej-7GA0gPsj+NLR5` ziUxKmt~2t)S10x27`B9mVr`@o-l(WsTHw^+-GqT{D`3rHf~P3!paJoYKtB1dK#mhs z^J!*2g8ffPSme(EuA~y@>*&(ML~q}(qpYu!man0QmG80MPA?A4dqwvqLMfJa0>SFF9%u8FvPAVgWG(by_DXLRtpX#Y2TGHR zK&?I2wX6XF8`B(L8C_7-L9#m}0>khb}J7-yb3df}Oq8%Hj_pK&5F zu9_MgH_{)Q8biez{{Hg36rN~d`c23y!X)+TrBtnA__{rLuvBAX7z#@lLqlMn^ZjS9 zDY8|kdN(~*7{ZXq*)(U=>b2|=rg`Z*W$&SGz-kWdIMhrT$d)J%+smKLsrvJ=yNY=< z@)yUlgr+ViL4080?$i;t_207j$EUmX+-2uV0Nj+rqH_mDZeA{$g8$DxQc24x zohW0BsLWwGw6c+$&2cJ;3&)*Y*1sH-EU@ z_VRi?pO44=uDj^#8phq{ak%-+d!y$rjif;3*E;RA>z}pF&VD6q7!HtL>B~FJ@8Na{{xzS^XQYaf5g(X3&FIK_DuahaSGx?+dx|op;mN_?^)6Dxw$~ z>X`$VEw!4oG^eFXQ<8kC23l-wvlm@@uhc$y!d4@I2mitJp@;H~HchXhjPsT{g&1yR ztDoFAQg~}X^@}{HD6fAySPnIi3Te#z z_1ld#IG0;kwq;J}?BG_k&6*@fnL?nRw)~jk-deZMSsyihUcmQmIb`LrB#p~75gerG z*l4Y?ctvj`gcgq`)lcjK{_C$N-%54dC%=Al{;@oa)3w>)Hs)TwFDOhHJU;B!HgJo+ z$!7Sf1)H&3`V#+umT(s76B;pm9C-fQJz->91uFWem`(z_ZzhguOvv~sdDtd=Iezn# zQ73HKU&Vs4Z#^~Dx3yu%O*hr=^kPT*Sff5*2_-t)GAggEEV*twXwQAOH%5WHegn{~ zQm5`-9-loSVBOuRx|F5~$A)22K>D*8o7Yr87*4s$SCw>%Dp4Z;0cN^b7WZpNbeb%l zyb-=w;A5IEZU3EF>ORH@;j66D4Px!SYUv&AHhY;zU&np%WQCp|$DPQDErEP`LJYv3 zN@)c8y2D=ur&|AtJf=G=F7rq}3|SWTsn%um&aj2i(;So95TtS}`t zkdX_5163ka*u~<(tPs)e#u>Q`xU4>5bisyPQxIp%dtw}*@HYb#0M?ow@U

    fxF|@O%}J@AFDeD>8@;6)qlu8r3%Ngw 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%0Aox1WrBwhY} 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�%(p_tEf|baLVyv>J^Y?1#E5kYQ73!*k#1pd|RaE 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 bp8lVEDf{%jZ$ygo8Gyjk)z4*}Q$iB}l+M~k diff --git a/resources/server/bin/code-server-linux.sh b/resources/server/bin/code-server-linux.sh index 3df32dfd..20df7cda 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 0db77676..00000000 --- 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 8ef07a2f..8ea4c0b5 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/resources/server/code-192.png b/resources/server/code-192.png index d038046498781ce6e5f2f05ef6c43a6248e9adc0..8d8646f8282c77316dcfde05b9f339678079a5c5 100644 GIT binary patch literal 2721 zcmeAS@N?(olHy`uVBq!ia0y~yU;#2&7+9ErRIjnw1`sdZ(btiIVPik{pF~z5Um@8e z$d`ekN{xY`p@o6r7f`6-1p`B=0RzLU1O^7H84L{K1#@-<+5jcg1AIbUf%O0X{{v>9 zUvc(%&6e9@Rr|a5-p^fg{ph{-PLofoHXSzZK0fp4V|&MS%YaH5OM?7@862M7NCR<_ zyxmrx$9H+pg(s_~zt{uxnQDn^L`h0wNvc(H zQ7VvPFfuT-&^0vFH82Y?GO#i+wK6i-HZZU0 t8-nxGO3D+9QW+dm@{>{(JaZG%Q-e|yQz{Ejrh?jy44$rjF6*2UngGKW=cNDu literal 260284 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

    &3F7Y9GTF(Luj;3E>~s`Z=+WD35r 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

    OZbS(2QS)0a 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­XtR_UWHj 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 literal 260284 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

    &3F7Y9GTF(Luj;3E>~s`Z=+WD35r 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

    OZbS(2QS)0a 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­XtR_UWHj z0(jAFpnrdJz{47}?%3B=)HOGQ;9Gllf?J^Yo{a#MrAXFx4j?wSkEOH*!bqVS?KaPI zX>}bO&3%|9L~`drNj@=W)Iocb@;;^Ak7?-;Nxhq>x|q9AcaESqPytCDe~uv>sI_a9 zH@2U_;V%u}Iq;-`OcMeSzN}OvqTr}PpaXI=+T&^(ZGhD~)EbqF|0NQNI9&VpzcxYj z9UAv=?xxnHQ6ja!tjAEA+hY`oxCfQc+lE9nXSQ+d$8&mM8NlqC@vl_qU@LL7W6*1Z zPtSQ9Z&U;l)ab;=!hKr2G3zy_f;I{YanqjbFex=)AAoWocVwpZEw(qvckWf~OUpvZoyX*ZE3(m)BNiFJEwUPYjT4+67(qNAwShlw8P~ z7Gbv1+?OW40Ut~QPC(88m{1V~TE?;>8P+x3xT}GymBm^Ldtb{#xQYOxxB5I<;jA-7 z0qF#eEKs6FeOKkic|Th<-U2|Q@l=_1QB(s6)wqt#!-d0@C!&`J{q0f~0Ft}~t6q`< z{MrS2-30K*4<8J$=B*rOX7kErlhkTiM@Zrk){$a)h}hT;TUV7)C{`dvN9;P^vZWNf zrvw)twvQTgCzYl%Ja8{>5hB{lj!yusCKR=${gAY$HD&zJseT zlo8=XZl|u$;7BfFqUIheU^IR1q@}2zwVon)8EE&MT-o7Vo5L;Nn?xx#>wHXQ z`dy!2e%iTFADI!=vR?ULivo{=w?(}j8^H2HUXa*wt;iFe8~-7p)XH?3Dxl~ieIa}h zeTE|ec1uVAxZ?HF+1H|NmeYQ~{_Lb{X*vI|Jmp(GrBZm}ZxOcmLN;$VjtppiK$nZC z7GUq!EdL_O-$Boj5d5VW%+0{tB9N+iuopO%A^G8(K-m&pBQij_EnzXhILc4tI#mNM zi`&Ds-l-bC`Ze4^fS;0G;slo4=0FvT8Fyr!4td;DkZUy>6$CJlnsXM| zp?^KV+mG|y1?o`(ng``iwD{Jj`;y&(ZPNv1jqRyJ8Kir};5~>u_V+&@5q0Qnp#pB8 z@CzDW0bcX4-FjD+r0LPa1=#+0@9d=&YgFq1?Q{{5g<3Vhr`#+!xUCFw%w;2>_+xO= zzOc}XW8lUG1;G!%_xs*OHDpSzSHmg=+&n%%zWh9n zOziKtT|@Ch(2Gkp7zAX9_it?n?BU_O*St2WYiNoJpybJQ+wt6DkmhKayewLULe*U4 zrm}dVlC-9BH`h68kpS2_t||IJ*>1bB3vZ<(01@;}Xycj_5;Ow?8F-$q@|1<{hnKn^ zUCk_LcLiag9RnBsgyrPTEjXla2UbpLF5q$QA#%yjsIYj?!G6)$08n%uMEarzs9DGa z5dZf(vI?F+86#u}HeXD*+WN>CC>){EbI>?Sx7xy;h(t%UL>I!i-b-4zy%F(E6refP zhDhlSP|`$P7u)K1(FDLRPP*!$RA%lP?_OANFzNz1 zS^@K1QhS9_n}DM<&&vVJ?~5PJ6tH~+Vo2s14qgov8t{3hJameb*EHb$q}CfPOT^Zx zXG~LK`jx~>!ozVD$waqz3H)_696`vqMx+|GUO9Y}5~8}SfNBL5Jo>U!n-Ih7wVHr$ zQ{%%d0&E_j3tZ-I01~1rbZA-d^yR)Rf;7g^fB{Sg&z2Mt6j|Tw!2J-{AXlWeT8TDR zyo#(5iBUy5v^aplweXorSTKtyU(+4<<8PmSXZ(No*QeLq1^Ct+Za~2eJpj9i3W_SP zdA{amF3lBiw8xf0Iu69}>SEl7rVVF|oV86AD0>XvG9Um25V@-xopF6T4^*Tc)%JY2 zw(l`U|M<`6zN4@y#g)M9+rHgzVfFj*Hh?>5*3EXG-ury(a%{Hm(-lK1()7Y9-At9P zr2^n5Lp{E6zRhiSA-Qkw&1ezGb=~9h*;NqSCA2z#rq3xCG0!=8@Oa5;XbuVCq5Mf} zz%G$CzHRj6k?hWasMB<^qPMP$v(p=(am#;y%xlJ`b7k__eGzAT{>YTwj)~5k8w%PA z_|4M*w^4zB>c_Zv`X^!6k;aq|`^X3@y5(;8cvGM#z%@!-Wv4x1?{r7t zz%Ll|)%3qiws^n~ZZtQVp+&a%=FpNZ&YI4r$S3?YNf?lqiw2|emkv2$_=;>VSJ!Ca zX#qXV{kCwN*W!}-Vl4jnF{OWu0c%U?78ZaL3O6eX7jb|`aL)k0kVP7x-^s1eU3gKQ zhn21(hu!&QXN4#7%8>vAk2UZItYRrlT$DjezFGaEqrlfL5EH;_9s~XmxC0m3OZW{X zJ@}C~QNY_!9eZAA3f88hh>f?$!5g``007X$0yer)nGQ9Z|BeUHxaJD2if{l@7pvRf zzn#;G{orJfd;fX@fj3Uta|CbBZ3(m<`SN*^7LJ{pf{4g-@AS4ub;Z5S;|1Dm%4-Pj zoF;6&^`dAjn#NxzZN(I?Gi}qyUkiHRp9OIqi){J$d2F4=IDjF(25^ehyopG3PW+F$ zYg>*Rw}Q2)iZ>6zbqRpr03ZNzk)pZr+8+5LYySVc(oI$6AEx{wYix0bBKmX#=X5V3 zu~I6>OpfF>@Dqpkb4MlB47jg}DWZ!Mm)V9ieDT0a6fyB2n@ccl@$s!ClyV7zz7No( zM9oc`!$g}c$EiSjndujv>F29QV3|#G1&ans2fV4qcun7dUT9hxG98R`URU6tWDh@X zF(?BwLn=6alvfJ60KIo?EJ%PkT&E@W4e+`KjDWGcDEGjFak}}k0tz$ZKV{Xn>h^hELiE^xHcC{Qjirz<;}7@wB3B zMRowq8Uo&~QDr>u=g{f=`&;i7C1B=GzFq{yy>HTzsp@{;=u3e|4bOfbN9Rrybf}Dh zZR|O0KR>;V!$I^V<+fapy&ui$=46&M^I^H=aKQ2PbabnzLxgzq_XbK~v>TsZP}1f( zF_AcOb)-)ZZo~IhAPNfYyt?v7&C{(oZ~`ph4pDkj+|Rrw1;%-jzpr7MTVtqq+N?WX zX|dzkcxTkUGymK`SBriMZUnr&L8EFgat9$0Q>P0jw+r|CebXcsRp8T{gTwf(JMr?F z&ioL!QuC04Or6w7VZk+Em<8+bnu`b|n8mten()}sTFMa64B9b9TTb*`l$h%XOulHT z*a;NL7!)WJ<$vsno}-plURv;QAbPw*XX)3;6G8xi-*A1}EjaQG+B2mM%UUFE?9GZd zzBOCjHL#F%aeFJX)zdBGrm}l+odP#g8FUWp2WJ5OqgE?uMF6PJ5fsEob~a1Me43d7 zJm?Eah`@bx%!B4n7uxs@qC%}}h78E4D>N$x-U9ycYpN>P5O6z;HqROl{`)%sJZC4+ zpZ~>jN?Hii8a#)z?qMHVOL#_5pU30z@s;zmc-|~Qormm@m=J#|f#-rOI`*tn#Gw?9 zE;wz+VlPOyojOQ4Vl8h)LJ=Bnujlmio}Q98A)BY|8;!NeM^=`{eQZfngU+-e7A z(bu@O^X!&}nlT2ux$SXi&Hz|9X!3V{J{oF18-NZT?(coK1!Y(7Uw%}BFL!Q(9Aj%+ zO?W5Uug@&@cz?-4NYEfX&^u260|d~@N>YE#C|yY=gGG%(Jj;zf#+2!{+}2qub_8Q3 zvFm4|{J=f+Vlf)5x-f@?sKbFiM*Ky92kDmL0&`6;Y%DU1z`GP9?Z|XwzeM0EeGKm( zZu4vj-je7j!yMq7@JAf_Vu_cnuW+DJ9Yyc*4ZX-V_0DO1{ zfPa4Y_fsANg1in;L$Ee8nX8)}#JEG|O1D?GuRVAiAX~=6i@%O>Y|wZq(qkF~FHlcY z+@ij)9S}>{p(5`Km*wLJ76}x))eO2tsj1`s^{l8Z(7bqPQ7H3eNy_xQO1Bx#pcnD> zYnPx6i-Ev>daJ&caOAwiG4@)rtp)PB z?mLJ%6s<}XD+?=o@7hz;yOb8T)U}^)ZFBu~r``rB8227$GHUP%?jy)3Ifzhoyq`Sl z;~MYx9YSR14rkiFeX*#%+NI8QQdzA%4FM!>4>)?kDxhf*im`6o7UbSSZ8il?1@P!x z&X+02!p1sOP^Jf>c23vKe67G!!$8mLJzld7y zZ)3yLqS@DK8EP99iUs{%Z;X2LV9_WtuG;;z?Vmm#&I#bgNQ|cd-QV|5f8EFHTV*dT zgs)aJ&4|FmQ-|o5JPEY2KfVme`9ia+%02#zY2$J$^rqq{X;FKhGTr;V-=WV{rbi~K zaVpJP00D@1o)yipY7kzzt|mh(f6aR6y<;XY-h!XFmDnFg9c%VQWfZGGVvdXIg<`l_TqgjSrAbsW=;MrI_BTj?m`284l!9(FP`vP!@m9Bv$ z2yQ1`A|y^uEVqNuM5(xcw&B9fyT_|M(U44bOAP?AB-fe0`}_7PA0xR8P5- z<+l1Wc_s_SGK;R<$xdOl9isj9HaJmN81!>ZI8D~%0MV(TPYMtBQiW(?a-N|Q-~I6z zqooI!z}sWrohgF;V;xi>jW6GS?mLJ}@2PM1`?=Ta(DQyaZ3p3Nx&L5PrU#9oTbZk# zNrB38h=={g5ZzE9O#BIu*T!>1kQ<3~*GNq)cIzpI*>mgy#N2Qs2=_;K=$JrLVTdv# znbf61XxH|slx##uz1zCcALt_Xxcfka)Y$01(Ub!fJyvclCYmIfrO=s~aezgHWeuKw zd4Hlb|hXy5aklp(JdL;Ka0-)#IO5~ zG5MAxmWi^zDgppI4h`p+pQnDjB5CP1-1+$`6dt4ucteIj`R)6O*8|J++bXnyo#e5X z=c85X3ze)Me@eRS_3PjXT*>t~_{CL$+Z2^X-&FPu)XkU|ZqwW7QKl8>wH7{zQEX}a z*NQ-2UIGn;gsK@>dybX1$(np=O$H^Dx-4`)&$v4;EET}|(eds(n~{*YsnwuRVm+^h z%-}Y|Ena{tm)B_y^cE6q%b`S*uyh6#ikp(RDsY&PtQYVG5wk8JaGnMS21%CQU~AMU z{V`0&>%`6;Fn(?VObHob&Fj1%G3_3aj%+T#`Wju~)2e_sik*W-J_K+uw-BhIILSOm z%Ml0pA$LRE1F3P((g zRkC3Z6)G7^6)>KSQ^6u)Ex6qUc6BP{n?Q-`O0i8$(QBj@d3`MlSGtOi-5HDcLnUwT zZ)e6TQt8DGEMV1L!ivtx>ha^+!-8UN03j`EI)K1|zuBf@@sc(g2iCpS^?JOY4_p;Q zpZcQy#@Xkyf~hpEcFRaR50AjKu2jE(n){e7`0XNFHVEiW_v7{Y+GxLdoF%$*cLmj6 zaZCvxEKpbURzWYL7r>qCtw_2E%E!0r=%AU4X6H?AUw@LGxh$5w@BN0OtG z832!to>orW;Sd#hy6u_HEPy7C+UXtvPniM0d5MhexN!&MFM?9bEg*!vOouWFL;>i9 z(vJ9-+eYi}6a=>?7F*aIxPky!TAoKW3r$fVR+5kr8g0&&qnFQx!ih};*KfnHr|4;w^pr-&>B<=N_?W{5ay}N!`{WzUc6*++7 z{G2Xvh@Z5mvR8MHuet0bYAT{%V0|0vb0VUbjjS7MZ+i(WCwYi+vI(B*d_vGmP%Xa2 zj74JG*}JGwnd2Qani~QNOwkgu(~DCmRLJ)I_hY8Yrpi320NyCHuZaoC%Cc2}4W*84 zE=)<|Iy)Af*N4)B2Vwer2pRkVYF%)L{riK%jb11U=5gwvu;F!f9Rvt#wWDOJ_uroT z-a2YHn-}^%5Lu<4eK_fOFmkYs0hyqWeLNqTC;T})zV+VDD{~9p@#wH$yIM>|a=it0 zDUGYSg-6kR+K6rw=OdJH4aFSs32Jj7|KRE!H-?}B$XbP8@6JR}AJ5sLJdU&TJkJrR zn=B~3$Z;MD0$1b$nRh3t@-e7(mg)8&9ByJop;fdAH2AFC+XHMAha2kNm&&}=11o9#S_ z7H91eN2KRzHb4z)O1E5Ul4{m87q-AOc4ia!2NebVs}bvu zs5EKE^J+eVQL<&rDNYODnp5l|<<2f@A(kFUBT6TpEdC3q_(I8CSFpf#*xT_nv7z5b z+R#!KtIE|PTWnYny94^G4*+Bf(EekmnsvJm}e@0_d@{F$GbI9Dv>?>3nnzoC8q3_n&QjzMyFDH7%l2aJUo; zV8YITRo5aCrC7#2)O&aQP-qSGje@||(*=+!ANY1WvM#U3p^gH$BLhu}66H;FBKpFw zh!u4g$bum#=WUI6TKB*;Zxoq|N4-j}(g`vKJ#p~I-~It1h|YN*G2uffhcsu>N|A>| zJz|u1%Ms-{ahywW#z}Xv&1srm=9P0`G#hl(X$*1=(}PmA!~g&w07*naR7=E{fnWE& zw~yKh_{-l3;A1)ieXVTBrIMX8sBn~sLcCz@NlN_MWirN{O@$;;{O3en9fb;oj4&-! z+ShtPfdfk}Sb!Y*ni>f6K%p?_vs0uZw(i_Go8_U?k_?4#TikSUcrzu++vTVYR)LTM zVE@Wt!iyY)9qt+V=>U-c9oGpwV4Ow!1n|)+t+9be4J~=n7-dJ5rcqg=9=YW5r3glt zQSOgoMg>!Gi;ak+b)b@FEBf>K<=s=)H>Uk`i?(H0cX*>!p;bV+YNW(>-)$Wswvi)3 zaR7t>8IXTmhXu^GQcQ9gH1t3V+8B+X_V5`1=P6UFU$o^${^+%}*PYLJ5Ppk~Z+dD-O3{Xolxfz{e% z00_W0Pz8{?rRm*_4`~hd&%YDEzdmFm!2iA-=h>U{x*a)0uj&C1TfoWBCN1UbSMScQ z!nSxChbR=ujw;}nE#Y}9V?ZatIQ!!g%d%jBX(et2&=`&*Pr>)dhG7ovlmGUS*%C`( z5!AWXmRKnrpqCZt!J;P6lsK0^et$HTR=b%}_MvFlef!z(*PmUuv2OktSk@vdI+WRNw^}`H{`HeT+&>` zB9%3=Dx=l^e^I3 zRR|3rhx#N&{A`M9Cb}8&GUc(8yCiE=SsD4eN>(7Tp=K8}2cyfj?~AYNzNxJD9V>!R zKW;TW9#%VLTyDHO0?eb4NU|$>1|GF6DZR^h@W1~#2lU4vz&os8zxMI0^nn2COI6zg z^}ru}DbGV-LDL{0iF25T8NR4Hk-}e1W2+TZ`lMpEF%%6L8?Xdu)EU&q$X_9OCTvQ} z8#)ZH&juZaD-AO_#@53lGR{l0Nyu%N85@swXY@#TST5; zW(^jR=nSq=>v$N0dNa;(ONBt@GPXR8gn4docoaax$*1SAtIJ1-6h}s86?G6QJU&2_ zCg=$V66FKmRP7C1Cauwk#PlAl3?`?!0;EA>5+?VsSu-Y23aws$!S{MQ)3do2L}`eo#)?Hw_P zGAx~KgQ;M;5TS!&syuV*-Ruk2?q>fr}=eJ=Bnw>o}*sGHB0?t*z z_-^bqC}H6+BDMRae}7fr*7Mu-dX4kSk}fpFsQyUI$2|$-L!+Qf3?Q!~iHu?7JSy>U3 zsRs%qZ{_i)Qw6dg&C+O>-ux?v>nJZaVA2~M@={n`q9214=-nU;?EWF>nkci!Zt>F4 zD2)E1XvIZ>3i&*nC1}T>{JA2S;^gnaA(X~cKz}xdv-1GP zqJ|D;Arr(=SUZvDWi>%eDoMRExH* z=)tE-6j)Qk4)B2SJPaCFN{f2DllM6YE&R= zYUVY%EC8;x(HvZ8Y*x8qo9d9pd5o*k6al3S+nl*J2SWq{KnEy;r)(wF*Y|Ub1Eitb zl-szy%}4n135_2Qi7_XVR{>Nf3gau#`;iJIJ4Mj6&vM7r;57I>7*4__3msj1VU{Nk zO>gQi@7zE~aU*X40m1_V&ry;s9|^tC0;_?>9ugn{$3PLL>=55sYLy28+VJW&Q%b}M zz|%|5X(T*VO{>MYxMC<(puMW)93P|MOseIfDCO%76b6rL1JS3x65n-*E>BxD?TAEC zsc?kY#M=T9^r+s>Dr)nsgyCa+JXS$rXe@;et`*S6DR7IlE{i0M;Rsm-)AB{5g2;7? z^IcMaAOAN7@WT&3zDGxZU%ocE^6U(opq+;}UT;&F{&vyadMGW!9FrcHql2{Ow_iWy z9T@M4A|K62OG$AnwD&Hl&MO$bM71(-B2p%qDKhH-xExV_cq5gjn2aT_>%mBt5`URO4`&etn5Y|5Z5 zNv3=8sk#)qDc@$oAKK$dDnbY5_CFrx`d6`8qB{U4x8psSZocv8VS zMhp4kaIwl7Tu@?@XY?1YgesM@p{Ct$Y@vQ;S(WG?O0!~3(Wl-} zdUH3<&yFgw1F0!}zC#4?w}1Zl=l=iUr}uaZ@bg!oVM@WoVm$q-nepYs`F4#qpIFr6 z^9c4huWzrA=iBwV#Qt#dQ@+jwVE-;T9fK_x<^WP@&d3du5_Y9Unh=L;JH^^rwAiv`Ow0)h0$t z-xhRx{96KG9E?ba4A$Wg9zFqf2m$rF7W^b-vD}s<2rD)H$`yw8DmHU^EM{wf z01DxY>zn~(n44-G-MJ581hac-GL#a!cp_IS3NL-z5a@}w&B)<$dvr0F*e#kaDIwT( zQ=Ia5ksf6z?TNvN$!F!?Ioj_%S60R}se&&A4Hv4qU_ zTDhiraJdq}d!x_yJhyl?Wf|9)@TyBYLj=bsMv8KlNfl0kFo|h-7 zXhMwYrX?VLo*9AbBB&Qqb=nnQq}l)>fC>ESX|#ssze-~1yI4UeEeg{=Vx8+`d z>K!)Kt$upz7Z!}V4sM*M$v99!FWG>HAD9_5bPTzn+k?INOpU(l_fKOSlFIRV*1Q3C z%Z-*_7|G|i^V5+rlq7?}@GJs;hH<3m`f&oD#xka7bpU2dUG`fp0MBaQ>tpCGahtGr zTRi&%X6C53b@6I6Ei5qnpC231u;6Yl(d0CkOCvFeKH1Ao>lm+(gr7}3bVkv10d#CF zSw-Av+viefXDxwNJYn2z;3j<#DAc2Jn~vg|BP{299YiHVx5j4Z1g%A{o43>s@%Wt~ zf#B(_NrvWNHUo0l%L7nw4+@xB0<;WfqgKN>U`c&)Mi1Ca0HP^tVL)icXTj7FgATy+ z(_;cqqbHfm_+U;t6eW-Xyb@DQ6VMsn-=MkALZ<|Z9|Y}oE;%qby`m=&*d2Jj*Gr{Z z!h)j-!xS&Ttz{94UZ5IZFc-KJ(+HUf&ft$&Bxu$ajim5cmbfoDn9SX79~ttw(c)*@ zcWSV`$50S7Q4|y*W{Qf@eNtL)9@GjAZryL2vF$jL(x&`PO27n`jm`vU9Zz@$@b1_5 zzkF{1KYw`l;R!bZ+ZC|2)R8f3PwPSs6%QLgv8V?M0R3Pu9S9o}Im#$I*db*k26I)> zaqsQR%VC-mW?haRlxNno0Z2`E%)lgiUELLDnef}4>j3xvPGE|8p5DX-FJQoIncq#g zuE20B9=3Wv-2(WzxcU09naI%ypD?6~?teYBShUA@nxm^C=Gy^kInGg4ObjieVAseT zI*PyBP>-4;hNVTVdy($PdW#NVhIYO5)lLiG2?jL?J`hlMizs58&3l8VwTo)PIUh~Y z=#;ic2Ui0vYKTTTKHn1}O9103SbrfeL5K0OOeC^edrYc~r`QoNEW(RY3c?B7X$S&N#Xi3rY#g z1}aB0)oy}i(vnj?>(5E5_+Qqkl$F5;CMiJ&e%H9+_~u+SJpe1t{EIkk%2)!h|$~g-tn!v;L+C6 z@Vpbim=W#j3B^Dt`JcZxfFFN)_k8z&A6`-h`qClfwJ3^ehmzG8jG)M6-U7r2px)nJ zU(Z2ZX8}%6cA;)na+cw-0}TcCw`$*54z~6KW7CX$h zFuE#kjLeilvLs+XkVXPpX`m+!rs2`IK(C;X6=jO{Xo@ z)7J;hIj3a<%*8A|UmI@;lF9B4QcPp;N)N0E_NiAdT6b~1?Vz~~?>cK_8mL+^hgA}7 z;CaBPp}XkB3<9HP7l^d@=h6-tN3ygPz{sZQpfJGplg|2=k)^8%vvPL1N6}BS?WKSj zqb~IBQRhqCTv_MWvnNbxvUms34M@MK1Y!}I?QbUq4d_?@?VGI+|$oxs0` zD;iqxF9T4vWxI9~ z=&{8#1*A`gR1^r6zPf+s?E$1E)RmytT#nb%%5r=;0shA0>$mCDbxn+ibzha>?gzku zi1qR3$-MXwB+%%B0Vm>)$HCq|_bp55hDE!7oID32*9akXy67%hp=@}{wQ$VRUX1P? z`MOsKc)0OaF>nA}(Im2#RLC%e!&wSjKmPmWPD_sByy$uL^VA;G*shTlwM~MN*ZEpq zIwxjyFk~66M7%T}$<&%CC52L)U}7jez^|PdmjEh~%s$${)bKk4j(VL(@2!sN9HEZ+ z_yRtVdZpVK`b4eA{eCb>omU)rGrbkYdllw}kQp=Ggrq=&ei_H(wiVqF@I+oqf<_Y2 z1171(RF7W4ZQaCdgwgx`&hh^7^^mhSW$Y6Roi(@znkjnq5yB6ifZ6tX1 zU6M;KcpgCz5C{^FVwR!Tx!C(s*ik7`4?2RwdGyOA|(Mc-I$qf1IR?X=+&>3`?$=BlBb*wluG=95cl-UzzS`2Co;otM zjt;mWurvrbOcOBcUT;tPoVGHa-hvhxlDeNB`SaN9d@c>p(ZqHU#2Y-$M~-bId|<&F z&{fZlKGuG?ctA`WN9>KzRvKPNx@(P_!gfH;)zatER{ZL5e15hpwQG|q0#4NzQ%bRe z1`?hJyB*ip>!+2v;ef@?M!!9mNzSzCt7PVhh9m*p8LM8jPyg9CTH{=l;NAfB;Y{)F zbk!AW!FEsV14=@#{pHI@b@6si7m%B8R0=BwRCV|A8ZT)%fYJs?vIP2LR#>UW2TSISb12a~2B$37OnrM=t;c>pIW?XfM%>D`$!T zT>vy3Sb)+aC?ajZz?qyFXhD$uvP?dl_%ts@&zHL#2maT)0PxGhzwfRL@Zk%%El_zmTU_mk^+TE{a@$%v`bw$5Ev*SS z8f+Thth@d3@nyH69i?xn9KBsD0C;r8mQ8r)uT8ji~MMV$*E_OyD=z#84 z#4HZdvYX8#fx8A4JC(y-KmqX9ls>+GcmavQezL9rV$jzcTpv3uiHsFq62PpZh)FTo zZr{FjU9KD7v^ze2Jr4r`{L=;^ju6QqV~`f$2PymZ^p#Eac!gtM!HYc=QnXEl%qHNI z^+3KVv-MB`ePdhOkv(;m4ZyWla~UEyG|LYT@7?vlbHT9frvd5dkT9NQIt6CI`$jNw zlI|eq83eTfahu8p4lMNjyEQ=H3b<_Vw$zh?$S&?NAphyhX#gHd?s5`&JzB5-+1 zoPq{u1a6gQYa{SRU<1Gek|F(I5mWR;X|VbJT7j|Qe|Z-GetvkoyE4FUADaQpb*n~7 zt1K}Kj)~daK#cpCk)O8>Ysz+~u>?Wm-oE_RtqFY-&)>$3gjs?4LfIRLGBcRjZ`Y22L&96Fy%Ehn0Qc}RVyRwM+gwxf$SYrIa01^vUt~`G z$|7#a?%xf2@BsdkV?#8}KJFQ&mCCn(Z9&y?j^^9Q_2EQKnfP@Gzi)Z z6L3Cg8NME>5aatleMNFMCThJR=;=%)aYWSNlEL&8Cle*MuB4OEg7~bd*2c~v1x}=8 zo@pWwCn}B4Qnj8zE0Jy{?|JHA*0g^w$omk9>ai z(`TKirh}{TP>uDcvssTFXCQQ9xCHLFWY2DTPc%Dz1^?Ao$+7U@of5j$uxIDm(CgFBoQ^uPU656fw z`V=JE3nu&av8TF$F_o4a?aZ({fGc?PhTW8rzO$kvpw86uZo$aWf-8d*_-tEMNz73N zfB+3z921-K#AqnR85Qk90E*-sjleCiErLVNU_fOB(?}X&rEm=4{MuwlL9%_(k;H+i zAR39xIEqgYSH*{%0QkX+JRMR!Q2Q)E$>{+&*|Lr`7@#M^<{=T}0>(11rgxcSF!b>7 zgYp0|wEE&ARbc6U|9U@@%Mz=&{P(Sv?)WN&J)iUfj}rdvj)aM&_jSkfloe9 zbTD^31Gu{Y@Z;m-PwxW2504LbR|fd-dG*Rl>^g(2(&;MC6EKw<*zK?Y#)%0O6AZUr z8=ltt@mjHdT3r7+#+bL)eLJLB+1AQwIw1Gz+RL^-Z&V8yN53F)fc*lA0;3xyNe=LI z1?EptZs6E>Wz%&ET@2y5v0V_O{ji<--#< z8Y5pRzy<|4eXDH)Ofu557>8Bg-wwpQ^%N1PU;iks08^y3wBErI96`W|W#npSTBPGf z4B!ngfTEIjGM~BYyVs`kWKKvFfPF*zUk~IEmxw5zY|g7%mIdfQ zrg0b(OD|>u)dEv690)?FP!VXsG~ug}x>AGB>@Qb(=U`cux5MUCj#f8)77dXAMV#u(i6nH9wpG7{Bmiv}x*rq}}Xx&W&K{Eqs% zM>%@O`Psg@v&5b`Yl~IR)>s-(B4JV>gXlikC~=o75DubSYg)0NrlwYo1Gl>Z-&^bs zm2o4rL|lA5p0C$Ei2qV|tvzz%M$ma2kmSNAL{j9C6iE>}E3>(rZym?u-T(i)QYC*m z|Abi>u;IsgG{f$$YF1a%1aLpkH@+R??H%v{?1K6y516qZkJoQsUuSOTh{DfrpA_q` z^Q}#UK0wO=TCSG8I+v0(+Hp)DVBGIwQHYgAeqR6pAOJ~3K~zhCYOorYi@?k0-lAf% z-)c(USuh4feMy(g7eZahI8e|zA8o>A-oh{X0QS)%8}7n#@_?g`_`BR@>X0RDtQ-$k zz(oAbw3fi-06`YJduws8o;wt{b=9&%9}zpOxOcTY&QdL|U=%3Of7H%^Xf) z&LYo79skG@Sl}z*H;|t#b-$mv*<>x5_P`mT=Qr3bj${B;KPQEuTz%u@qR%OluKx7W z29Bv`o+)IA&YNb7j3GdL=W*{~mDVJhU^p8sqIsaYaF4CAEv~)m1qTu@Ygd96Svc%; zwK8C}sFvWP2;{q4QOLkqeL-|8nu2ti>2{9i94CEy+c8ZVTvATbk0+VP636SI+eyhH zD0}byIA6a0{`DPn0Yn0%;N>^T%L+@~Bv<$9Hn`%Ca{)X48wqFwu8XR^63I}AtMS8W z?-#}OnZDpFMud$TgHH)gvd~kJx?=8G97rlO1igtCqQ6)%eS@=IC`wHds-!>0h^ot9ur@sWnWK2%R}BD;4sM?dMxC|n50WRZX|sl3qqsq$1@)uUdTe@f@v%0YDH%-AV}^NdgfL0^W~HtyiEJ(k2|d{fVU$!1jpYw!sGO&JTKL?6uXwpVp4K* zEC%m!GR0m-sZCT+pEZWt#TSleqX3BZpZ}NuKK$dKe}1+mz{h{@^mCq%2P&8wXLG*p zIOm9lMIEnq8fN_G*jBaQI|U!-q~IGAS#%@>PzGY)=(>r^5|P3QvrK! zp=e-wXBSS1@1K7?0XcPz?f?PIvma9>oLl7{ulpQ^Tgj@?*RZ`Uylrd>M}5p(+_kHl zi-mOJ6&kzlnhQb*AL+PZZnr?oISwJ)9W@OQ6oEP4j?Ni55E#(hc+4D4JKj$reQ;BN%_gK|W+glzPBz zAa~CT2lAE5p+c@J9w_K^C*odb>yRld7q;&nh^liP8~!XANLu4e>4KC77$dF zHXsd9o#WeCkr6-#CW^E~brMKsboZfYHDwWYiRTUke;!W9raYKCRV%auZ2-jc?QzgG zrCB`O=iee;apTS~>*!=gRRskPf|fxwNWnp31S-fBsJx@cAC2@$u>io>pUb}5tm&fZ zM&|&ZUj2rXMNIg68CEA9soPn`Ey~ z;X#`+ULf6qo@;Xhr}t^GIh!K5!koJV!-D7Nh*eORm99YbYVIc91ujy1?imiq32W-R z+6;Gr$+Rfm0vZJh`P~4RhO2lVP!8~LKtijX?gsi*l$Wbf_{qf;5P-Xr*n|*w7VyJ|pZ@f86yTTtihv@5%6nPU&zJYi=|Emsaq9$tU{z`rQ=(EW`Z${fTaK=G zoG3pYho!}>oyZP8=i>w!?JiISB*qazG5u+UO@gZf*}VoY--Nd&fe;CJE-9U7>9Kfsr0D$+8W>ZL38MWVyu!5(_BzgDT z{94xDC4dXa#nbfw5bi(ym;jz|9QaEt^s-2u3wybj0%sDq75Tiq4X(~JX657@&Ku$f zrw1pp-tRuj>S-^Jj1JVA+0zps)Lf29-MY5@jMj5z&SybyJbwSGXncvE98S@AgyAI6|EasO9XW0!2v`g3E?}PKQDl)U9)+^otu6h z=0(s*nC?25nGs}W#1IssmH?LyRjS%>UcK#mN2XvW$w&%|=6YRmmk8hUO1UEB=0L5d zNEjgS4dv3K*fy1|&sG84X`2k8D*XNh5l%V0x zKguF^*0ZK$mEahT21&->tu6w1d4T)M{&ZcYA22hhXxOIztMGUwT@lmn6qRV#Sl@NZ~^8hm!Z20q^%LxQ|K| zzkT1Uv6Fik`fy+jxB2hkZsU?rG%m$C_Av_ZzjAQS=Ov7{_De%XIY7svxx8Os3+kiGiX^A4!#4>cyYuZVOoZF)kEOIlyXsL1e0o5?NX}M{KYE&h1D+ zGf*d*-JJ0PD7*1x9mS%*V_E^af@zoG=i4j>z)j{29o19_5ma|4heQP(a5NC z6i!&0igCC(iA+IYaXtt8Z4*j45P-A>cd%(xS|A>jM|rFE3$r07a~w-3N}qYj`KLeu z!!^67J|Wcz$hp9!c^rUmP}<_(z_akKa7k<*fI}Qr0Co1G+-* z?|n1)V!UW|>=raX2g011ITKX^dF5WGIF5~?N>nS|+T z9s=CQ0Pk!U@Ylb*TEUk#P_j-+SzOO2pdb5qK$4NB1Ku$*fEAr%!WNb5*k}o9cqD*T z4~B;Yf8M$i<6Nl+V7~=gAF;?dGJ}ZNBwZtE3uOJrm)}r-{PSC)`7M>KbtpjXY+eN} z?QM$vu$`kkZ5y6qcFj+3frQ-=sW^9iU3x1gu)8#_DZ=w}0PR-I#&^Sk`N<G8aYuRbLBh)=*>GqS~R*fVHzrZ@ira#cy6c8AAh`V8u|Ng$7iVrksyI zo?yeJnCu^eRvp}j4tEDZQxXe$>0>jLu2w+5h_#HWb^UoEF?Al`Sz*-UfhFlVd)zjT z>{MAOMsQ9VmVjha*H+cN6@$?Jx`45(DT|1B(pxn8x4(XS^8hROWK`S)cIyI?;N{bK ziuH!5>7n&$H{H%B$85N<;Z=Eje8d-zTOY7gdXq4gw;C8VdmJ%|wJ4 z`)WXzjcoX}6h+T2+v~xmrXDJt*A_b?MUs^1qkK<{`Z_0-O2HT+=<|06B07kO1o|<^ zHfRxa4{ZAq7hTS&JO-#ik5?n9Da3OLx-zz+H(Hfv*X{--quXKV?UPiFcrv+dY!WU- z9{}t?B!K$u7B6Q9pCRz4pRaKh`K1)eX#ylV!zKYx4em^YTQ}Gm89*trZ7y7Pi=ba@ zQ0PEc5l@29=>ODR+p^rY5wzpPu`2=yAOuKoK@hhn2*48!xF16~ALY_|$eqXsmRA2V}SXNv2y_Q-j zLd$VDAhSgq#D4;{)uH#qZ9QO2o2E@FS&rLr6wkdv;m_Is72y5b)gt1ZbBKJaoQU|# z#ifHWIQFcd9MUak$zt@qvD+@qsvnOB%|CzuT;{f(QWWsa=h=ZcQMYM(M@*y-U#}f& zJzoJ-ygZ$<5Dj+Th-@WIPC`$z*OdF77xua7T9q>wS1D3H(f=#;w$Msv0FcL{cPqWL zN3942>Q~XW7jzzczh3R2Q&@*|?&BkI014qib9QdOWzANi{P;@M9COa-T7ZaR3%~?T zfFrPzqC2YkqbGNu4uE23L9luq0;1Ct+@{u?HCT1qK{i%Gn7v3mE8<8Zqc$G+;iY9f zC|?B%c@VsR_7w715T9Ox;w?~hJPyh!TtDYrLEaVe6u#A@_2~Ap@NfY!zBiiv4J#-0 zP~5>>4A25Xe7g8j$}mo}D5CbyikCVXRk|6sbwbXfG9X(GX%`)_e4NzV%26xIjjYLJ66(Oa@$!qXJO&P&$0*dLt2kYhO5Wt(C|Muqu@XMQ@o{Iy# z{oP692lw(QAxoQUtH+TCRyJd$K=cM4!vUq`nlt-mIX52myaFY%Q>OFWFriv?0NI#8 z|18*S75a49ngHL~j=Qe2F98+K8(>sK8)!R+>p1p8drL3+?Xmkc_2LYvv!dDkybN@| zosgFbduM~&U+>lKcbdsjf|nao!bAh^a;qzicUPcRo=q?~v!%5dhg2qzT~vOK(*ejv zLAR0Jy8RyNfjp7GX-l=?ieCWSaL!o#=NRX>_dpLjj<6!&+tJPw05Ra%3RnomL2|A2 z!?)9hm;Q<-7m#)uczXJBqFkSX2!Q>k<+t)Q^J)du#tU`;vZX)wBlF-qZVLGiaCpK0 zouV)7Ig&cnl4Z)I1Dw1QhW+>spM?;VAT)t(h&;hW(anWYjmTERMB_%!^G7q#w)|w> zTROVFj81KlMvcS(vk;?lQAtT{ZeT5E#zlu_E;B-v-4~I8st3e0Td64aiMQ&K-^R1` zmmkiPhH)OLX{eEthsY5l;b$-beA#;+Hjb2m65>14rw~ZQDu^~v`&TOTnXS`|FJQkcA)O}gs9^DTH$oy=?&$w?JwEc8IUcc>y7rFxfKLZ z1bFoLO+;>y?slvI&1PCsnO876U}PWY^Fg(mRyYSo*J_!ZJmXf1Yi+q#kVG|LU5VUY z-@o4T0yqc`qE<~?s|?1A)utS!u*PK)l-WaNP1OsRP!I!3)Pgk}ulKTNs)4C#0O+Wk z$KA=LQLI-wU{??L9o$gUv;gLEdKmb|RwO(;e?VBK78L&>gM&;K^d9h?Mx8WGINGTQ zR`z$d*1moUuJ)4ZIfm1aY-;)P8R5rXTZ@u^Dj7sQ0I2(#JuQMN%QWzL24z;3n%*gM zpvg$wE9gQwj_M;AzM$qIJu~AlEVzuzjR^>m+&Ti?OsZukl0{NUY!0xN=YZe}9*=7H zK*&KzP{^(76iz3>Mtnvj3?!X(3?VGzif?KHyu%l4*_by@!1qf43;|L?b)J_g8a<=@ zUoMwf$c^f(v(zIEjMcK36jx-fWgR7_0!K?v7qiX43}QuO1B~E~IQBDdlaI1CGXt(5 zIZvUvnQ|^O_|%1$U&jkT1@L5=o^2fX=kE>y&S`+>;s9^oSKAzQ0ec%|-PzGz_v)ID zPSP+u7r!9HK=<6yg=XnXgG6~Q*FdExu^&3NFndC4k=+zP_NsHp z9zZi8vQYhNy7$2Y5{l%@?cKNDK3xSUd~5hsFpWetzREn~26ccKFSb^{3S8Y#jRSPK z00+~hF5EudI*6oJL7F4E>lIOfirj9uqau?uM46I{P1^5GYs-!mI(bQt^6i8L1CJBC z^(#Iwf(W~pFSg!V&?8uF1|7&v0LZ4D3TTHtdv3WOYN!;RYWw!tBLAd<+rsT4$N;7# zyD9Oct>UwvbWSw|znVaqa7r@Z?snQL@WmMe49~D) zGoU!nUO8sX1VAzX^bIP)69=#mAaP6AM4=}hJ*dH+>f*dDI&<>mR2VzqI~-O^ZGdII zY`9)2abz3ew2s{61z_SJ|1|2jEr4rU<9QtFH%&u0rhow7XwEuZG**m4_L?KLRY2_l zR}4LW01H;ROh{+!bixO(oQRq?w8&6DzI$9jXI&ra?S7|#Ez$}=2W3TD>4F-GX!qr;RoCWfZlt)%(L$w)JjjoIZ&V! z?R&U`;E|9b+bnYhbmDmhuS+c92agK`Ff^3^(*BTZ1n6oUMgqPn;6($%eBy=!PXygG z<2B-C={QPu64(i^h;}~XOe3(FdXQ?}X!fxTRV#nKu>3Ex2)= zx2Mws`0>~862RY|O%mYkXFKU3T(3J-Y@}s38i&w@nsosxK08-bpgM=|_m@DGC!SBC zIANiel3jlJ0to6rzzr5mEnw{3%s_|16>c9G@Jb6HZ1o~haH$H^A3>KIKK%CD6+L$W z0cCEzH}<;+=|d3{_vnRMm+b-;iWorzIMuHX#akmwc;AZvJFseY)z#~fty|>n{S8@Z z0L`cT6$%eW&4BNhb-=3B>pU>sOoh|FWBK^o3-|)oXMK4zksjBuca(0_D&q#KorfwU zKz=ks%E`#IS5DSZDnI|1x~prF+ctt#aYcD>@L2>vU_lTB5Lz_tQ>0a7ulD}`?@Bi- zPm%w?s`Q}J9q+g#W~LjM>E38&?3}1ypTF(LAD^YfL&}y1TDduHnc(4Bq!Pu!d~VWd zZV)Km93ME{N7xUInhJ7)Orof(-9Qrz+Yof1L0>=_#gprMr~07ao~7lq1TP!8(ATRs z`WaUqh$^d*3*2!4Q{a6B1S#-FzEnZYq8n062ua9A;L|Or)e{Q4LXegPs3b`p&Eiua zgs_6fL{?r~m4&lbQkQfc3l^j08s-FKuW<*UVf2Gj>;e*WG0UI=VObI5CD7JTi!>qT zYtNj;T$#7UsxoH}3VV!KtCkfc6Tk4~hJil(X;me~%oX z%`S%7RV)RB>*#-e>^2w9s#9dzrF7Hhp+Ri%RYWU=_4nM*2CiJa31duYR21AYm(uEL z*gqRAo5VE_`OaV8rSFx6AUafS3f5%G*f}H4doN3@n&DQrPN{vZ=Gz>S6Rj5x^v$b0 zdcYb&O%}Bl&55Nd>$xkV5jyvObC4Mj1StvC9oRNFRF`BoU&5^O(f()hYr_P*@bUM1 z4g&eZ^Kc^ou7fn=KZ*tF*6kVu1f{P^g98M!v9hu=hc;)Ik9ceMw=a+5&p(u-Q4j7M zx71LUML%eo5rJMdd_PdV$s3}L8|8%}e-VL)B#02U*3nVP5{jTyM}Q1`4d}y^LXDz= z!cODw+JI`P^@m)bIenp&ZRbJ0L=Uw{9rEhh{mhE{{G=V6EG{xwA*CDJb#@Q(u+icP zG=JfnFogpF)L*6Yl3aKiieOd1`5L7gIKRY8mKfKv9hho?h;UW?G=;Rx5j2m()*6^1 z1nPw?Hp~mP7)6{HtQ=JUHn3jIZPru5k4O{MMdu_k4JGDFi%cdfUQ879rymaiu4#Z* zO8}cT;?Z;-kCEiM0zMjP+t5p-&rJsc;4+pd=-c6Eft7K5;a1;%XB!Bxzi5>AJ-};G zBoMP=vy;kY1&Qo4-M{F9LnBU3=I!T3iBHL0qLkvfa|*C28VTk5``L=5m;C;GzW3V2 z-YLVS7OCqNZff6vsdI!>w?hB`AOJ~3K~xn}Q4w@IIzu-dWvFOUu>KHSVCM+R&wHzK z&LggDI*DvO27s9Tao%gHz2YZ=-lY8Hm+!!%`52U_p+_gGp!VCSSg%^r#gt;cwW642 zwz*~`hD_T~wh;ClkH`4(=;!hNEdv56T45@UIu4dy>9+_JATI`k5K%}Bqzyp#fEnGP z9@7FTI*=v=>LwE1z?@Wc1$P{4cE~xJnj0Vzc>ZnduBgietRS5hJQ>mO05!Ny^fvcO zFSdr8r=xd!Wd{RluBn7<(rwO;N7eGNuju-$q+K>l{ zf9rX|Yo!PPM~nh(%4aMR`(np{AO3$5;QI6Pi#Y`U^oboMh#Jt#NWbgc%vVl zP(O$4c`{ukjSpFsqpWn?Mm;7Yr`v`bJ2evfzTa^fhk*oTI=rCx4x1Hun!ttz;Cnul zCNJY%n%odAXYT#ptG!xvkUfwk{kBrie+z4NS%38C|r;b6h}eu1r`G z0pAt`QUR?&Q9wkwfqzgFVNJj|1qcBvC&cCD8i9WPG5+Zh{#yGOpDz14!!b6nx%+U( zP;D3N3GW?mmL?t@UvYB+hPGe;i$HY0xL9@Xqitfah1kkg3rZc^e1jCW(gGw=`-Z!A zMsZ9<+P8NUK??05(YKb_Z=N&a79e{^1WSKu?mpg+OdmpzpfJT!jHgw$>t-eQ`vI`e zjlV>F>bbmsI&f*_KA9kv^c^%-&@8-DM-)VM|Niwk=tXyoX9tD|x??RP_CZU9lBkYs zlPlh#el3(00TWUU2qjYZGsdHpWjs1VR;e=GO_@(Xx@4TxK#DP2sBF;S^HP|50% zMyQAe5MofglU2sMu--tVNXRWnl0?Br#^3nn3)>bIh#UQc0T)6*-bhnGaO0<2#V&Cj zBCu7o3r#|_pUvit30M2zG%GzxN)Bj45INI-z)}&J7Rn1gJjbJ7%@^P=_5iP4z?Vt^ z|N6%G!}73zZ9K9H^*W>C@$pt^5dum|Nk}2rg{Bjlc2v)O1J+h*bfk`ho;e+9Ka}1( zrL({b3kpo^-OKUZsBG(~!dD%>@z|}#N*bFnK&9_*dq=rqE$`_qsig?LIu7 zkN^4^z{1m;h|`C(6ec5}#*2X*2 zyMQls5b*IogFZEzLjaYS*|F^?ebKjm?wpQyZ3q0P(M7T6-BV^$`O)_C*+P5_8$=r% zPQMQ7sMjgXL=BOG$a{>eVw7qQdC$%&AKUYu5r=c4!v<$s?_Y;2SC;uNbyv0|w~YiX zD^`wh1kNP@f(??m0tzl?ZFySOsJHe1-?f?atA1dfbj+xGnnG1&7OFByC@Q76Q0J^c zZH|5w2Pz)t05uMIKU>Z&jZU#5JHC%1XD;5M79%c7rOj0c8;E!R-+feW#23`>@jNRf zG(|h<1JY_k9BsE9XgIZ(lVwT>xdK_E0#i$@9EUq!dq0`k3Q`un8c?f>-0J=N%R%`> z`izd}p%+gJmF4k;)*)_?=^%W)MDSDRgC+|IEfo+=?1IEgXA{W*Z{8}gH>CsdWKiq@ zka=;asFPGGNAK{c>C)&Gf}YETnwA9D>pLI~SDR;m^RA9&eE3JbJ6ejjPRpYTCPhjB z2tS2O8+^VAZhdRBdNZv{6kN7g!pebNHWvZlH@)5#!Uk>xfafdE&cQ_v@sK9rrAEZB zNc~k*!Le(n7W_1;PGRCUAf6@Hk9GzAKXHWHsAc0AA<(`~tR8~?$5PVjP_xBgmD5rJ zEXwzl06zV!2Y7V>-xUJ*{EcGX+M&8SPVWhK1;!Yo(BNhqC0RSVkq(F8Ksn}cZlZ5T zcSatKZEi))C}2zaX@TnkDO#`WtPiA)FiY=NohhiJkmdbZQszicr$LUc?eL4E+ZKmh zk}F?nzjt>))LZ?LHJHh8p@YTemFC=jJimSSl=u-DnFzH=H+BO;{_&E+yc-?c-|*kN);R>(l|iyDvv%srPFpN zb4YODtz^*mcMMPMH-SA0KBO47C}xlS56mLn0Cv%{A$&=OqD)nA(6 zfjTW~q)ohoj z6D49IY8Qp}xMd$?0M7($Han6yeYR0lgoyo*vE8QaGLxgxq}Ip@h;X}r22{5kFQZa< zP=XWCj|&lX98Dza?{9b4UK{rV>PwquG493FiktiXwwCimU~V%-vcW!W6dOg0kbIv1 zeLWIqOjUk4P`AfsO5P|{2pBJ1q#Fl{`~CIV1YM22FJF)4rU(*nfaN}OpkS-5w+23( zc_k?5ii%GS@-Kj=8_-bWK9jEMj1Zh$kFNkRxI%?&epjf%&zH(cFN3qQaO@SF9(=x*$7_~zC<7o4XErw#xErA7CDEX+rt;&dX3)Ax zd%cF@a5hk05IoJ#x_i+7$4&2MpTX~t_4;4iyxJ*o~) zkhL?4ll4`x?X6p-yH>qFAIN0$E(3Tr6)+Hacpnk#{n1d^X2(HZhjfJ)#Y?gmF6Z&m zfm2l*BCdkMb!5w!YMp^_ly9a*w>XCsmRT9$6H>wK&k|)jy zxJHw(%B8`76hS~mkbx);=#WW7^lS)-h=Q21)%a5C8HsENO(QdEAV|-d+5@p6fg|vQ z6L$@G^-uVafy%jFWOaX@B^IiFT9SSwDE!v}Wmvux-VX0=cy|#DK>e%~-dFSfz;h`F z2;Ps3gwq{w@FUgFmPslQl}&6L8;R6sLtgOss2U+^J?Oe&wV$TTsr|Nw$?%pj1bPiE z!kc-H#Q-8WM_E8C_jQdmg9kyn^RAAsdI2GujW`Y^=_ZG>wv6H9@dJOPJT*d5a;!9^c)WF%Jk(B{V`EVu zD;2&_tBHtR>&YV-Sc<`BZ$Xk#!T{#0>;7iAs8i_)8X0NhOOt|@AEmmOszqT#)C2h% zWS}-04nG$IK>z!_nX3_i><|$(^~je=P!Wqn67dw^5jdm)MM}AJJbVM60Qm-?sTxO_ z6a4BH(kAohGOVGs&`;Xxl!FcfT(QFsEAeVg@5)liqtJ5`xCejv=$ozyAHf&^mgMf7 zkZ{yLbuWK5>WI+Q%OLe|eJ^|FC|-;4kx^7p`0W}qh>4PG_y`nQ6nIaz5lE`4pn)Jg zH8zd18!9*yzR^+gm|&!k^+?JKbGiz@Lsmq2OlDGC(6+j}~=12`eI=2PH zdC*)OM`*9~bu){eHO>*m;^}kXK`Evs_paxC+9F$pvvrDA0-7^bc>9I@2&97dH46Oc z!^hu#ngIUw>0LR5e*T6+vYI&F{3=%fN7gj2aMZP_0^p$j@yz#O*sjhvcdi|yd)rZ? z<7USo>c$D;)c+-rOI<24o_W9NQ6-}DOeqUHPJq(x04_mwef$2_*}yWOp2vU29N7@x zHmVCLz(g={`haQf(dTr3J#wZ!sp5KZ1hV##3gK(J4^OWjcBoc@;BBLqPXlM)Wy$Ar zoDa7W#9DtKRcM-Rgh^B!Z?^k@MXIH_Au1-tMG&bG-y5dYr#J@+Z95Je^TTm8AUm&l z69>R;fs>fAxD`;HSs#Dno%LlDgpdY7DC~du%g>AVDfOc7bRpNgB0%!XO3^z6K*|CJ zWXVUHSQ4qg8MMfDo+))EsQ62jv%qWGjHHS5>?^Q=qdc;wDp;=I8F9l?bA{pw_#~P3 z6vJuM;L{B*&HZcL(=S8ji zy5DFLTFk5_WB?gw$#!hy6(c196kaT@GrlbG&@(=Mir2a!aAg}B3_@BYu-onb)Lre4 z9Jdja93(&x@KZx_$RoMrkfuFYC9<`0fYaUkKUb;-`4J%U1MES<0e9M+*%7S(mr>)`TKcxbu8AwIEDd0hyc|* z?auH2{JK2FsW@iig6N)dQWI2_%vj0RDA7?O;vNW`{R7d1wCZzs0{BQsy#v0MRDn9( zI(Bf=1Qam@RX2cKqd#7M`!*N`$8?;}N1|FD)n~Syz5a9Umd*jd&q*V>xT_Pw^_XWw zny8V}g4VBzn8TaQ^JJ~2Br)}q)Z!$f#nS)`3gz|RJoRhm^Dj?yo|Xa`*ov%G1zpfm^xVadq3qNQXcQu=n{|jjlyqD_&gnt*E8PMONQKnR zg`0}EJ<#Td24&LP0!$CUek)!}9{r;{pqOu)q5!C1OLP|;38Sc??_VC6U_qH!XA!3IWDN#RxW1syKc1E4@}xTn=Q zg5SNmsjhfRmjnd|xnu9BVlqFo#Th_6KOS0~h^u4Wk>HRv*monxqF{h{|GiqF@}(^# zG)1rvBrt|$q*{^2qhYf&{my{)gGOfUt_JIL1xC2KeE<%2KZa+!9({NKtc|CqDn9K3 zGr?~vpw6+*AYuc=Zy{cPMSK|+uKSu@YcJ1lNRE(0e2ny`<1c>9Yk>`W~t|i&m zyA{-a>MV0eV`Jy?&Z7f|kEMFult|$Gz9(_mnhO0)k*@ zhz@WafQTcY2x_wqGzr%_Nd$u95bIh4$R=C%)v>dg{#(KX*Q*?UukN@XdC^v!j=VG% zMUj60&hB>FAQ1E;>&6m$*vs{8@rIS%cBYD)JdqyxDUQx%~B=~}IQ@m_}g8;@OO>Xo9pyFYvDA-rq9-uz>kzy{k zJk3v=)mS!-3emXyUX;Qg!)}IBxufv%LO%zMw87g0x!{CDre1Kh1RvUf3T)3r0shYw z?0+@^etM<^@XMFBgAH;oEGE_1Nlz<2ENnKL>6Z0ngyTXS_iy*WS@_ut_1>LKBm&ym z_BvfI=6rZ$0UF{Vu(QFM;2FKsPDu*ENgn)z4vzzscOY7oos{NWD59oQWS?@IxFj93 z7^C-@Ou+9*x&3&IIp_QL*}AvkrP5~4(=H1v6=8pqGFi0&UMp(s()XKZ;p_fBVh#C- z$#Zt`x{6m~f=e8TT!47*0hmqL=s}Foz%7dP_4ij1IXa7|1S;qFQK`)u51!JPdKya> zkk0b??cR*|%mm>0sn`lXnvLA0vmHrbj82^{FF2s7!Zc%QhbuBRvRWJ|C;OD0B$yHUJoiqNW;> z1U6w?RlBwwDj@i-NjZ?(W;M#BC#!wWXh*zkgrSpri-mBO6Pw2tx#Z1y{f9Kamu$>Z-DF(MEJGx?v6= z@|>H^5ol#lNH53 zO(X>X;sKICH$?sv_n3^il|YAbaXA4}HVF6%_+Q!@XptC9cR_B{g$4OxS2oQ+u>@%E z8w#Zr4I7l`Vv@qXDJ+djMY0&BkhdxmdlYutis9qw zz~aTYbK*7>K|2iE$+7_eC3bK&P=OaHYuoVaetFSSW3oil-=?_;wwGH4I?dn@Cu+rI zIRN~PRgF4FP*~Xxm(7cDHhJ3Yp65Dr;MujH`50Zc06I|u@iQ6(APMw&UkG}MDDA(! z#bO)YzEP+rbCkgNjAb~!y{Z?2+Ckl|){n3Sx_Ntx+F#ib09HVAG?1Wij9iLB`Hf{f zRK6dsb81*o=0Z^PwQ`Q6Y1$R08^`L=c z+z=+BkmpD(fsAONL>UEIyrVu(Qg{mr4d+@FI|03<320#dGdD~w??I#v9+Y0=$p4}4 zYP#dbjo{nOA(sFI5aM$HkRT9x87h8>N0vR7{{Qbv6@AHJo%CKBBNpG(xDi#Ybl>xa1rDI<=Lto(Z1n9ZLYd z%6avQay;$dNs=9ppH@XH47R2UHkYP3cbe2#nq$!)8yK=W9$4`>U~daii@Ofa2v&e` z&+jLSD1y)XI*Fr)*?|kJW&yAj+xGcUu+FAr2hyALx5AT4EL#w8Es(!}zS)={9Tj`U zpnjzoNO8dRAE)I0p`hIX4M@Qz?x#JD2o?)cPiZ~>{>@u%O00_Qy9!vFZMl{M_*mUY z3IO+`?;GBdt&!<~`+`SXgIvmVr|BoiN9Vo*=``&HB3tOzctLA`e?{?7wj1C^DE00d z4C49drM?Q@54I#}&&%zCi1$2d)MnrSaItI!56rRg?S3KAcUoaIzdnO#ovBuk>v z57K~+8=};;IIFFtWMuGp#JMP2mx6=U+*hhU5Qw)1E^Zucw^0lNT?B1pVsDd2lrc~bFDESKS$@rR-t z_)Nyx+i^cKjn)k=BigpH7lAs=4sl&61hdO~mwkehu`G-?!W}Gf!Id=)i3B z3>PcDr3E-(r z!vA>d?6#31yMof?6gPIAxBJI2KyCq?z1^q+kK)IZJAh41X-5w@iIA+odz=SuV+aYP z{=R=a_88JCQvVE0&^H8PzV@&5BkO|kZs_=v7FA=aU38Niay8EK3*OpQ+P|o zEh+%;?{7RKrAqI$E64b!@evg|EbTfdn6Ry2BZBfkj+4kLxyF5dBnyyt8n0FsR29LX z0Ax4^r0u@eirj?kfc)tI&vM@AE+9~yY1|+#e@@3{V!RKI2L)5@fFvBzHCknts-_CM z&?zCQXc+=%fL1)SPhb-gS4Iw!NM`fQ<9@9H1!&q)=8(#a+mDe`1-&l?pf37(z6Bk$ z(q@`(?EJ&)wT9rN$u7)F_=GF~%E$%;uo2(|I+4utXr`x03ZNKL_t)eZAZ>htY+)`%Z?mz|9r-FcFNlEU>n%l=EON4K*M98 z0IEJV*L#vGsBJB7G@6rwaoeNKiRPNo1LC7{b>TOVd2re!9%s%R5#Etr-T}1Rw|Bv2 zG$m9+LTMjEC*zLYoF|G;JLtV7P%0JRW-wIeZ1(7)$dwIEwslZuV4MT5o;!WSki}XT z^IXq2gg8=3WW&0jx36EmjC?dO_5I)^-eVxA=m4;+`)=FkZ)(fjWm9x2pA0qs!92j6 zILEVEBse0H4Ou4B**nv%BXK zj9;V!GV!S2UVFFZYB`92_lnUwDG&If*!hq83K|YFK6@&j8%-1N5ZK2HaIhdm>1RZR zk7aQLShN`jbfNk;+k%zbM7Yg(A%V9mF^8~(%NVrTFnL^;&OwwnLXOaMjs_5$XfUJz zV}RvkQF2*IS`szZIV)KFq6M=YuIn?0rKv{~WPP4ffWQC#f3^Ug%Ow0C|JvmQ9+t2u zk)IJqipy@LLyg>4noI!u#pJ;&sdnpLUiY(=c?Kh&X1Pj48o=i^2H*p|FYlD`d3j`Q zw;Y^VcTHXINw{LqJB{vbqg9Z}LK-X)T%suu0Es+c{f+Qm#{-}uGnqW1_oj->!Z8JN zfMs9(b#=Bq_-Xy|&4h6@n(IDxfB%thxaHZG_R<4|xEy!taVJk@U><0NDd>udW!jEw znzCrr`X&p*NEXq>+M4sKosU)QBA}|WG}n&YCzuhMK3+MQFsz?wmSmNC8S8lcR@s{` z*$ROQ?u-HQnEL%NfbL*D^!37Xfa*H>hVpz2FKkic$0(E1GC=zTjDtv}^PkKOEQ@11 zjj~ES90k1CET~Z2kPbLH=ze;tB78GtMV%r0V9P)CRdfUlFhxe05*{h*P}ziN6r~Ng z6Od4%o8nL(SVWT=k&8%mqqDi06AKBkjgRxPN4eb=ut6$%G!O(ZgrafhoOrr23B^_e z=L&Vr01v2u;(e%9u4?A^PZlWoWdTa?MYYHjBE|}oXpvHQ9=aq2Sdhx|3r4V z;da{(tGF9fIlm*eamb4BXU9tJE5m7RhWbxfC;Cz&L=?GdjLOuA6)2<=57@!D+xc|^ zEj2Pu9w`KMODNESw0O0kzR>S(zvsp5?1yjrCeM+)03`n}b=S7!wv7cVo7&x%;2Pp8 z00KaUHjCa=?D5zod;b5swwr#-Zuwo$GZH0=IHwyp-JSaOU`~+0N<*P>LvV0K*Gl{l z7ik&CZE0;_mpr`mn!%U2aG&GziZAB2mzQ;Jis|)^r)|kb>g_3Ikw3&v)ESrk6k-1k zMDYC!IFGvOoGVw@37h`P`CX3gTl#dbS2k*MoC&ZXy_WQ!_GrvP19CT+R$$jI!6oyz z^!iim;~b$M9R8=WX{;0@D7&wC??fXf065ey5Oh;I!9I;giz}*{+Uylogd4B~%ArsT zWA;4IOBq-|QN!CbQ=SDrKZmG}$&7Id^jio_gX?qxxZg+&lQ3T1Zn5-SY3gSLiWJ2i zAyVVy@tfXnIb^C80gxK*D&tmGw*XEU9d37{Lezl#f(8J6(-pU6%DY=r1x9$D0`Pxw zf&Z2f{4~SBzx=0w;4RW^UytIX#KYotG6H4;fL&`+G>>(sm%eWsO2u{_owhYx{Adts ziVMjwjli^Q;+UqdW&eD*x;{7r2R;n=d!B-ZSv1Q+#X#9t`SPzlPO57d$467f&@0P$U)@ng7LT<(w#VspVMYeNd#BkbCyKqR{+Qk7 zT&85+FT=P1RXzfhaMS<=K+H)1;0{0wS_cX40dTsJQ_PYE#hS*UssbbcVKCoAO4^N6 zyvMhj^e-s70m}1KLD4@IwLt1rhWEf<+z16=;`t);jqt9T<;)mgC>Nyon0iFTI1k3X z02SoC#DFD@K98k_U(8g7k;k}1Y7%LS=3K#7z^$hwR5x^O$y0`*B3*lPG}oJR14yBY zX2WyLlmH%eu95lCD@x9CyYWJdKw#j43PsXoL0<#T_l(z64mQ{vR6_5_Nl^6#{-~Ub z&*KO1`~T(w|2-VwNg%*4f4mklwv@!<)20nAu7IfnI`4XgdwM+eWiNdzRhPLtTHjIS z)#N-=%+!Uq=olIb2pu}Ta=l;wdjD}3YJI$Y`M6Q(YCTq7E$%YR5D|R;E;%xE>~5}U zS<3E1WAZmGM17}C-96j=^SW--5;4m{VnNU+R~}#XEYVT%BB3*SBHejhn*j|9NfkoC3WyU3|p)eocUZQHrD0i&kEKvbO+14LgpKYJR z&to;6vB-X&Z<>9K(5G=Hj-)u9lCjX=B^EHCDIDV zvPgBFJ%9oMODE+=;wbNr0Y_!Hp>U;fad=2(6f?koth26Br3*_N-F3h^c*!V zsSMMWCb*Q~c7bVqNGhh3ry@7TDpH@Vg0IuD7r2F)#>(_#pQdv7xZPC(U|1H_u`nA; zM+JeuQ(C~-u-sBeC`s9H7?4f^btz64$544xi<)(k3%50$2hr%L?@C)nqba3K#gB2t zic!P?45Jf30*KE*0eF@@fM0(7?O*@=Nde#{z_W0Gcdwnt;hezl>7J!h`=`gGI&||g znZB=R_*++L3_0*XFZhK6Kw>~iuq^R+#n3x?ZLE&S6{}+jZ(olwwxb`951)29CP==? zOv{sLt{V*?O&R6o+xLxjkv#D$Wp1LPx0*m8q1B?@JkL#FSvU?MIN*5{A1xkVSFEFB z#Vd`>2`jMiNW#qN(5sE1ugS9DYV!dYvb}7+4tj#(3Oi^&y}SDgR;;j$E9YW#TRX5m z-LTwB9-?;L>(t!jcc;56f94A7uz z0!V^+9$c#h&A!lYsNk`8T?XZ+qXntp01vo90)c1jAMJBE#BXQ<$RuGJ0BfmGS0oJ* zK%oZhx>2NP*LUsP@WhtNrm-eQpcsn%oRAFTmdrS5pz^2`GRK%vMfTbDo=y|W=k+m~ z7ljV?A0CsN_muN=%4>vJH;49$ev00T_Qwg;(v?2mCU9WLEt@0(K?)wS{e{N8$uNw| zrejd-ir)K=cdP^}a2+*L}L66K;r^|T)*nIsi?n|Oi;Er-~ zqS?!e&QIff6g7oMz4r!G;s1L7LPLBb>2`}|9mYOYa;Ppo^mDuXc2TJ;Xn=Zre6jk& zD@d%N$*HATQNqWhjF;BAg_dIU4gl(xmev6DRO)mSo7CS}dH~SnZ_?11lQlk}&Il}M zTKGE@FAv<{aM!$-oIG9uyakYrmpnA2W%~~e=LEyze3WR0E@Lei<_}$7{Y*o}oz+&> zYljl`T3By%?|lf64-`yxchz*hWtF2AobmK3aD0d-f;^xhTyK>gkHR=p{mSR1-stvZ zB`Wr9(T#~ib*T%W#!YPl#lz68D8y#LA{L?1Sv)-l=s^(g_vIEdzM1mz_Cy5$%ll_r z0CWX<8fVaVud7cW_l1VpXaR0xBF_@MqdO#eF8AM4H_=gp>b&usJO_v8STM~#1=F2l zNE>OvaZa1hKY_jh7ftZ1tH7y+2_wJ>D|&FMc+7Y3U~mldarDPc+R5qF{oFR1{{Zki zXBG-fsT|~L<3<*`{>ubW6^+N06WIZB#Q?}Ht2(0I4!2v z<=BNgU%wrw0-ONu-gdHfGLCT>g984$e$7auKCbBUiBbv2tu}5%0NigQ@vx3VP2-xv zq-Y`qO?{xF6;I*X-`2VBs9Y!$6C48n+{dw_*8@KQH;j>JK&KqO}<+|o@85WIc9uD z)aV%81ksNRSVP4k50v~_>Z)(?I{OccVH$$*!*YncsaC-$P+%57$liG*fX`!FRulD? z*Ue8e3jB}Xe*O1P3&0b3gTH$%Af2DT?%GMtxNO_jL%5sHfiWP-v7ws?_h0I+ZAWh0 z3I+&}4Fd4GA}OjVilhjx71>T^d%YXE=l{PeRr34p^Voom8GE{063;nR=T;Sfy(8#* zU*5l6V9FsdJwMJvZHlvJSv#xi_3FVKPqDJQP8omL)eAV1{(g*th<}1Y)C$rJjDh&+ zT@5hpesPM_Nq1fdlmZ|FOd3JOy8ih9iq+o3O|)wd7X03VAguFEqgBbKUMFzshBz+~ zi6YQ(+0U!A;}q_AzTO*Hw6zI5AD?5LW5yegox&yQ`OXj3fIfS}c})Y9l@o&A))o)| zy;)`e5o?bDkD+>!#>&Rwnx5L($wLyr%sBM(`wYnBSv~^>(qJ=M-ck$v3xU(&TE+=5 z%p^eKo^x&`6u=?-f$v53`iR7)rYgt>6mCvcoVKy4;H}EN_=i&$f<48K{})b`d%c;` zEX~tXI=Syr;xXnxL88k3s(uV`b3}KomY|%K-p{O}zzH94Y+Hi<|T5L1gs0EW}2s*9rs8EI3Z0xK7nN5 zs(>7eUN`t*FrWv>SIz)#z$}lD1B-(nrVrpBf1U#PP`kl@`I6#jC+w4ZD)xlqG)?O& zIW=3HWD#oz^gPdeT;uB@g+<(}Y=LJ9yMcAX6Mzb@`q^iLuPBif3};;so7}eO^hz-$fmIJne$0;&O;Kq%B>*T?HZL>`q{^x6y5YZL+7&Se7ZNfI=c(Ig#MFk^xA9 za|DzM=g4to6CjhSmO92vA4JNCvz)-528&OoR|&jOfdw2l{Dp;m{&<2~zz1ZXqPJSrlKBvg)C56NuG&tvM-?_)q9VHxfnXkNL(fxKSvaX0L0v^e zdv5;@etAe?*b#^rGI2PO%BU0D&EXU_Du#fr%v?AC>XJW@1NhT6pda%4howiFTR@KmT8U-ksrtiaoM51LHzzASga#ZRdW%1^hH>{9%;Ez*RE zUHu8JqqdqR50YytUOp^#9C@1wf$uDwv+S8O)Q8!DJ`%IrN3S^N6{l(@Cg`6}*jfnS z2_vb56Sa6hacDU&7AO`c8)(?7+7Z(-0$62$wax>?YQW##nJ$mFzY>1&?;ycExw7_+<9V`(XxT^Xh8okvAfy;(JMCUY+!+i55Nvg(gZlj3AA9}@ zfBWh0KYx&cfKPAZ!ZH5+eE_{{R*vMnNG=9Jv@wC>v0%)sd2gDdBwQ;^k$RT*J*(V+ z=ZLz=uE5JSIkN=t1;jozrP1Qd0T{gxL7>+gK=gS!I1E;uUF+4ZwpYEwy?lOJ4M;t_ zQexQ*5JoMT<9BV|f~FU8l~s9Qh1EJG-8ei~V${Y?rsZL{c4X8@`Cz-?C#Xs#IGf&t z$<(FgYOPYo6A|33Zn*Kh@pJOEtn>4~Zfb4`0&qQZ;Rko*sQEZx>-}>ry|?4OR3-X( zTvdYTsIR~89zMaT*PFvT1B3(o2AX)}bc486(n>4b)WaTLru)(u4OR*VPz%Rx@6-wy zmf*5sDzZ`ZL~=eUv3e+0BPltEpJ$5|&D-98P($FZ;4v>6@ioV|@OW4p)Z7aI$7|Wi z%XwUlz>MDt(0kS?nwl1C@}iqJa$^F}AVlZv@xEpfPJ*U$iwD;UgXFIZp6j;E3eITT zHrz|I8naRgcwRH>w0c=G3kbMq!^MEMr^f@~AOJoJ$NJ`7D-c;XVFL*Y{6D8hE<3RaPgSBTMS$Y zEyw-(8_)%3x%Q^q z{`y}pTsO*M9ydc}bmaYM9cE#UBYeLiZA5Qj%6nT1eR3q2JM*Z|?WpwwziIC+rEpX*bS^!|z2}B- zFJ{R}VnkMoJ#tQq7tuIl6c$aq-~=uXw|BN-DKbBTbG&WytVIqcvZE)24R>EpK>E`x zS1U4?1MA2c{@V`#=E2-41_x*HSnzx^oZq~r9oMH+(hD4?RtuN^Dnx@qL1^WJS*F=i z#1zlGcv;d4e+(u7at8KO)D)(Vf&l(91%O}w{3wOMPhZ9uUD5gDbk}}eCqS&5U_qs7 zqV?##$wqNOMin#D1if|`9b$jT+EP{`s+uT5S%K&Tn6Yq;CZCeVv!;dxW72ACms~;P zJmqfK5hwtPSy@zo2>$5HBOqRsJ#IKdBIY=boyiG6+h_u!q-IT;iWrsPJ!OP5ifm03 z>oD`YrSTFRLP6v?x^sKWa>;J@`fdu;x}Kx%XELcE0GkD%u^jv}qHu{GL2rn$ zcEs_X?t!*CT)M7`ME8Gb8{B4vt-n+j3Fs`*dE~0p5@!0O{{Qbv_v$Gx z(etTPS5h61wX&!|cd@YXD|5TmqE0l`{mA zu5nvlQ3W`!m3atD5t5+mR?dCBMa2GQtx?&$nO%Ti|Mu7K#s3e#yn7?=0RQ;7qX?Bs zp`FR~mQJ?XGwy=>tftT=ywH1Ajcbih^n=ZS2JVbI?wE1~$TyKbANT}!!}_K?l7Lca z-1l@IX5R%VD*x2ij3&ppwcP6deWrcef3C% z9#mm)r^q{sh0v=95UgD|+G{!zlL`Qu4Jw9Hk_`V*fCe=13li@urGpH)cdd&k!CenF0<@aIiJT1hAl$nI*FW*f7Kfizf zHaNhCf1CcF*NHo)EUieErG<49Q9O2FaiF+T6i0zmKlJ8MFYj}AYvj2&h&#S_7t!xS z7_PsU*O$lWlk;rLay*N6`w}?M%8_+Y_Ahsnbw5mSo${5+?Rr%KINe?HVw1kwo#ApR zV6hu#R2ZBH1b+Yg)#+f`q~|X0!DE0K4OO;yZ$Vy%I z=VJh-xb*r-iG!={xEk*7uq6mAaEv67{>)_qNCP4c0)q%G)iWS3sXR9T9Y=t<2;d!cNbs330ho-s3)m)9yOb5^ z#p04{fcZ$Xx&}g`tZ3MxJhd9)YCLlRR}%1L?jlyMd(^yv(j{rR`Rzeu3Rkk0AW8^T z+J(_AwOK$>kaMa4lMhs9Ek4#nZVxh}cWzDsSMXRdTk5r}72pf7;BtwnF(BE39c=k{ z8wubq@8AFY-30LCPjAE_@WUtPp8q-pupHH^JLLcj0HRwi;g~gBy=%p-Fa)K}`(eiG z!F3rKoL>ex_OtKq~Iq z_C!hohNbbAq_ep@Pmg#^cVr2F-~FI$Ca4PT^R2u*+p@UURrDyUN5{Qx)Jj}aM6Pm` zeHyFSw|CdbjVo#YG9tVAh9Y4=_eDKF2{#8ta%uj_HlFYR4PZyy(-WM;ZGy7FgY6NI zii?*=c0)PD=6T}JL215E$Oho+m)8fS!8xjb4Mb6#s4%ezdJD0osA>4+zhVC|edX$t z0Np_50$luT`X2HJuZy=LQ0j#krD zB;x_A;8JspiiaO^TNk^&m1Dqnzy9>&_Y=TdQ3n0+hY!b}j}$XQf+Fu_f)QxM)3WBg zZA(kHfcT(jE&XYg0aVYY83||gGqI@5;k6KjBgouFW=GuXG zM}JHs=~26_G0%JH-T-m~s*6i}Yp0Ih+1Yh}hQjYh z32Ph=y&6HNp;w#6$p(ioeZ0IJ?DPqwpy0V)c=_qlo50TpoLz~O~v=e2ucEx*;dJM1`a%`{C)~;JVAJxkUItcr1_nO{`|I96rpP-;50qwIbjbAo5G%W_xZJKy&}rwsG6+E64u%fSaSu=+tA)BCu`f1KUNwa+^R)qQ zfV;4{nE*hFjv3F}b)e@9s(@MTs-wl#gg4>~Kt0eOzW?Wq*n_wzrTvkoS z286D<5$5892Nd+lQY_Lpv6#vV5xB9oa|p!Y(=C2aYqgt3X{uZip47b=&-$&6?2>5<~`E6!;9&%e&5#>#3vhJN2EYcLIzZ*diSGfyITeAy=)Vc3A2s(V5STt?Hvfde$1CHC zfRCywXSN%;xcIi*?REhAhzxkl8ZR$X!!~2h?B8-;I3tbF!>A1;1}8!Pg#~T1t&*Pr z03ZNKL_t&v$wcW4ioK*!01y4EGa+aIeQP8jMO_5DB`G4^7;6`>K+37Skz>IBV+r!V z_8@Nr0sONwkqZfO9SnQQ&X1?QCCb+mo(1VZ__(XuRJDD8MsFxAQ^XydK}x;0qalSf zN7eZ`K`Q6{7>{$JhU))PcWpa%+ei?bMP7E77ZD;U@=z2>5wsQAlOXbrb29V)-?dfp z`@*jSi~-LTIy&m^s%Cf9ngKgGmbW()h)n%^z(b)Uus<>ho}_VIXTcLVYaT6%elTK? zFzOzbP^eDFJs9EXFvh_|u}&!fmK&5*`rj6o$Ydl3i<)ex*OVqNcBK{VdJ`?%@1E zTe0nWzn)FWDxY6#%Be|7Q@+G@h~^d$5)Q|J5>+{PE@0_29w>sW6a~HU&!!w0)wlD9 z6AG0Wa9!;Q50*U=S@ia(!_JWhBZ_;zbEKdM-wd^R%OA9KW0cj+vojBHAWNrULC1D2r`b^H zgA>Ou3mr`FW(vN;l`drsq2k-bJ>%rpR3793c;}ZnK{e2U} z^*wWV9>*9+2d(v_>QJ*()b1|F==E0L&Uy_Kb@&83Yc4)mUFe;SB-BiQ>*6>MLKnT@ zu~!i|-5Q|Stfoo04#-cnaYP2U5gjRZDsKG!+n?Y5+C?hkedLFq{oCi4y`5krU_ew2 zdMx-QlR5Ti{6ZfzJB~rMj9!Dfj{3lufNC5x(&xhaupI_;6na%3=qgxl<+uyqw zK*F9Kq)Lz1HtCSSK;_CIcvM|3+_=27E~?GbQbv~bx`0(lK?SGG`!3k(WeW>55H&4u z8_b^H{<`WoXn~E2%DLJt9m%6^DEeiL(FP)`^a%OOJF6HGU;uv~pfqjGjiZe$-m_>XGw4~O#zS{a6Wp#``MWFvCR`tApp&a zT$LR=%D)42lNle8?j#HS#RTUJ7V41|%W)794G7mXa}X%-p!q`}07Uh_ezvxRy92> z4QOF|xqx1!mz)}3X-1Dat>gaX1%GLGv)BV$+QP+H8~EV5PE`Gv5hv~K3F@1v$ZzH? zhP(M-)w-09XEG=D)A49ic!Zfo^pa=k4D= zUvxvidNu?6(<8{*JAlKSo^N0&_^ZU`bV@tF(Nqi|+qBdA^DKU{6y0fs6YsZ%fs-`3 z(k+=rVWt+71E5Yqinz}(14sOIXzVH8 z^dE17;Nt#aDWBizgT%u;YO0jG!&;ad*cSl9kUVyvJZRzRX5zg7cCy`p-&;2+4I<&_ zKJGo0ctGLSsJ7V&u>6ItJ&No8DB{yPPcs#kobwVXP54KyDXw_|3<97kyY}2PZIl9>J*SCqW1<_o-zMbN41Az~998to20RdHJurDTmesAwNPd-<)li!?DlvVC zIe`D`2=D>j0Y1GJTc6!klpq>Q_zkF_n_%AJujYSyMs#drGj7zZpaOk_&^y+u~X-RQvIzvK=`&(xKl0){Q7L* zyK4ZBW<3P3-CF);y5ZN^YM9WwCbd^ zhoqGj5+=jr?Ue!trGnljD*vbKC14As{7R8Org%#W8Tc4ct3qhRRtR=LZ>8=7HpCA| zIr5365?RG}C`we1gCm1`q3$0lFj)mHPAgJnqaqhA2W7cjrJ3FNwY2=0krX3aGMu}{ zEu*Q>_VCF3;6_GM3J_3-W0F)!n;%q1;r>XB(#aT?&1cZHKtdrG%79Fg6BE@uHe9QU z8DK$ukYm7q{`yl1;DfjWeENEweT<5S2%%Mb0aOdrcn^8ynH{BPItAmWLP-nc^+1uT zbu}R`Sqa7$p72%JZ1!r;F?Q7Y(oec=^`;lidA9RX(pE=jj|$pg=s77lgn|cJlvd7{ zx7}zU;fxa4%pyfx>QTvU={k_&5>U;-b5ZG#sixVB)P(2MAH~ci&))B06nnB&$T@BoE2FzJ*7;d;cDdU@{Cp)IUp}*s2H3rbRXvpg{zXqG)rxyC5`!)$Kwf znK&ds+J6EwamOB@<42OrjhG56bq8|7F9_|Ng7FmT|;n_xMekQFo!!i(fyCQE8CIdID)1PSb*WW zT3zQ>)zr~wsy*_;p-SZ zyJD&i=n@kA>H3~Mm8MrDo5k~L zIn%^B0Yp**sXRj$;M4zK1^%B)fM-Ym0L8SeMDX7Zy+3w?)2_V&6-P^8L$@N^(~)%1o~tdGb>w8u{pPEGBICtF0)Hj~-IQ@xLFHPS!ZC{kCNjltGd3 z+eVtH1(M9;>ouPSBJ7z9v*4g_gI!6y9D05T#{qbRz2;DS)U?p?dfBeCNI|WvQYv*z z6jyq6p7ZVVn+P0kdOVOS#5m(%zHVYij_O>b+a|kq3@kryw&L`2a23D=VCed5HuUmb z9CbdyVH}N%tykOqw$mDfiAw=0TSsHFbo*@;TgUVBGM|@uMr79KiZ|RMd+Vpdk3Gpa zCw1Mq&!dq86hVPx6~|1{ebXUFbe*pS%Q~$qRRlYg&soF9|gOKY)i4{^FG?Gy*ira;r%O zeDyd8Ac$aZfSF)=)k-`@c#;J0w?BRCpPxQGgC@W)|J-mU^Ke|(2X^hEume`q03t>y&??L3acGSK!q)7Vqxwn@FIRsM0!is$1@pJL)38 zzg}@J+09$aL?BW~d&!->JDe-S`6Hb{xh;*gtTn18J;4@GG7??(3~;bBkv0@KgKoCf zaH?im3&1Oz>nm7D?&HVjZ?WS4wD;TbwcCdcls)AP;ZBdkj>l-x(4R!>P>)Af?%dU1 zz=PNP_Ko6zO3leDdkToOPB(Au*wCcl@4nd@Ru60s5TfpHzRjV)H;SR2x@Ip8KpdRi zomU4vo}R$WH~H+E2}G}|ZaYV07HxUMNpJLJ;gs-c@zF_c+R}$Sw|zjhnDz$PLNKyPxyY~g^Al3A|A?VWQ5r?k0W(yA@K0v#l63;R%>TLQ?wUPASBu2e>g3Jj$>!qY?Vk5u`cL`am;!FiBKqG55(GUIeOmC=5WHm4@2@92|Bx z-c(yIQC(1L)Og(5c{z^vz%^B#ZTip6_c^y6@H>KoU%&hdI6fMij}6cURHWndZa9NP z+@hv{a*dald3Bigqfqo{^8QTaYxib!M&dS>XtaKOXMgTg z6G6d4q2?tZpz)+$LAH{{B%E7d>z{XJ?C3`z_I*&$Cu$%keBd1ZL6K&VQNtMZpG&~| z_y`73>5sSoAt2*$+fact419Ge zTEMq;=^_Eea4rM1yp#|?9|XTt1T>8V0SanBR(>&hT}7sYr>fC;oBhg!Q42E~UXxzA zKEfeDO>1%>kW%pbx}t6bUSW z{LfF20RFKHS;uidP{43zIKd_hz9Th{xt#0vv2Rj8zHEy=u1-b@`ga*!(`!_r3d+I* z>w(-iL_yCSOOY1Tri;8$My_aD88JU4~BFkkn@ zF4fVD&tFPfs6s+2?>7PsUT_H+voJW3RnG%qf`<~=9%S9C}nm7ST?Q(FA zVCIxhfB-&yvsqBo@$LiRw;wnU)~!BhCFX)CG2ZPPktzK~6gz7r*fzCk&En`mQB3`&oMcw=?j1ulYG6{E8eb2@LLxS)5r(Me zR`1w%3jCdlfD0Fxh>Gy&_(&!|JMBHV%nN=%C^p$21Wd@p5y%bu(Lg%zJ>>?##Ti5P zA){bty&=&GN+n6o94O6cU4!SKjL{s6p~E=xe5wCyg{ehkOb>fDg?9|K}3muTLNi`uThIt^@a>F4(GE!FNjyih$qq*m>g?`+W~# zyYjZ3PC7V^w;hZh2$Q|^szNzUX{tslw^{LYvoFNvm1R^>d1?_MIv$v9h_ucb0aHOd zIRS|9MJ54JAP8OqrR?o#7ogT9=O|kaNo(qK7-b1h961C9NDyvg00XO33HAXmV#OcG z&?lFHHIju1WRIp}GK{56QXItniS}k>x9DDTf)DoeVZv<8O zH+5IjB)4q^OFkr3vGHjFATR(35D;y8R*B_Nt!usd|Gz8UR4Rv@l-sT@tCD6poFU$O z{eXVmv65EfTgQD_gf`YF3+9LV4mJ3qoci$ zR361cvgCq*w(K;ylh46?uy$400AAb7V-@iKnybEpMkIy2=j=INXn0EkSDF7-< zA~GLmj#v!ocsL-!1Pl%|PWf6AV|)hab7rdu|j*d>%nxYW@z^Q>F0fs~p2kfCxRuljx zy@v41o$FDREzHIc41pfzyXKXwo7qr*Ya({Ybk0r#vaI$x*QaN&%UYZ}E=P8Z5Y5yj zp(Vdeed-b?lEllPhK5;^d*L3ta<=6Aqg%winFoRnE%@tSn?$m}yBi=1ORW^ip8t64Yw(%@Xx7%(R_Dw7Q19a{N+hPxp3{M%8KWM?#b^w^J|EI z5`a8C_r%3da1}X3I*KRJPB`z8GK3Xb9l3zAV{%@+;{GSp=R@lDFmhHBv}`GjE*&>&#$N1(7s0tG-)>4W96ufq4kAJ zE7}5yvJskIQCCs>0H0sKWMGMs@_s*nyK#j+xe?&G7q&Okjy_*`PF0Eletu~Qb38Qrh7PrX$hoB(sJt|Pov(O1o`<_JI6}~<4@ycQkLx?< zDEuYCq%J4$HjyhjzN*NP3%ri_;F1cUw5B3gP@xxNKZX3LDA^$?8 z2OW_DDF&L*Wbq79CL-0rp?u$QkzKqV+@KnRlk?~6pu%)Q{I|G|m)9wR^Fi)D1Uh2hpHQ+S zN;o18i+oNcvncWY$4P3O%XUjKTm+}Abpo)q0)-yEY@~x(3}{_vubvhc2f#uig(wwA zJMcLiQ(6`OeC9TR1U?}z0MMd$Ps1}yf+UGm01jm8eKQQYOoM>!#~3bhCK#RnemM>x zo<|dx+Bc5`^Ci+cDeo}{xqqGJNoWXYmI7eW{51*$ET%5G0BIEJJ;0ApQEQf1P2Zif;)GR9chma!Vxi187-?WY0N?ihkZw>SKyj6Fm;lnOB7}Zlnt94JeEXZ5A-6 z8e9uMY40VoMdkk{2+w|RBkjn0&iy;2Y!5&aw)JL^VbK>-JF4Rm7h|lHG8hhHXQV_F zo_BBz_#Jw{|1%Nz=6isTpS?=xJqJ0t6r60sqsivpUI1JKo((k7+4tAydp86D>E_3! zW4&uUJ_1@4+a4B#$e_UGunKh%KzzRZ_06sZA!3~`rerN{it~1KuQbIjm#jP0E^{RG!d`jM; zp=hm>!^~}Zwb1C=4e}JXmuGV83`NBom3uQfY{Lya&h!rY?^SV~#?45ETx7eIc|gLU zCfb5Ea-_)Y<0O8|nNxrquF3#Sv>~7(Ys0^^wokf?qAzGjipUX~bU?~ESla&~0Wi)L zume)CBMT3?D>P4CH-ir+699nHnsn()XLxoX zf294LCsq4Nofxboq<;Lx0t3_XVGT65z{+>}_?oxz)K-TQbPlXu8c*uumD9sA6)8|* zoklcsr%L!H_2Ws3EUVHK0GgSC^ahQ(YmebM`9mh<^b1CY@)Fwc>D2?!!6CB$y&xU=Yco+5BUS0Jo4Ie~a8 zNF$EZOP;)XB!Gy@d{GQ_b!dD%8#D?WRPN>@{jGpoWkFA+tHEdfh8yVAVHVPPNCpPH zqf{VA1d%$dK;2H;f^W|2ZNU{5#PAl<20(SI8L1u>f6<~)S6m-QUAQ=Gk1JWlYsEWr zJj4T;puHs1i*%2L49_bORkR7luN=up>)USuKK%UikN;2nzXxg1Uw(Z=B=1x&*lI%p z9z{tB^mdMZzUIXsI$pX0Oh1oHLKZbVkR&)Vr#VeB9!qqMvx2&2-dR zwQF%#EA4iH;3?9<#K3XaiY7Hbecij{F2#AS9J?c`HvgvX>YCifjbQEiHTX0E0uV?7 zAPpByXYXuoF4pS)|L;mS?*lo0y6vUyot|V_4Ai{aS1j%H^$+TDbKuA6 zOY#S#oSOhME+6Cw_s1gOITg|VLI{BJg%a89=N0an#G;TlJAUbR=Jk~Wgx2*u8#fa< zQMj(XT2A(JyovM8mCtA%_=5?Mjwz`OI0Z+kvP2Yvh|{oTnrBW&QOGk$ZVu`+QH(JI z@i%NsrgWjO>2F+5oua-0Uhvmjg(tL)rnLHl<{w818i`d|n%s ztJ^q7Nx`X7P%6rH9wqEM3SN__tn+rK5}}PpqvWRwvH_O6{p@S)PU7NDJ<;8YWEoY6 zGlVI7Fr573#ksQXRk6w8IyeIhkt!qMOmcGD4qQL=@Ary`GfUeu{jY%c^V&P-@f-?g z&LO#@mz}EU=Qlw8jXQaO-V>OB1}eE)11+6RJ=sSA4r*It2P6cDEoh}<9qz;bzrDCIe?0ftmoJ;gG=Oi<|UWR(?V(iZfy0jms*IYih8k} z=>f48{A1EcO=F({u2n=bL8=}{OYc>LaBv(*u7ZX=CX8)El|~h}312^tYM$ugtL?BX z<2V7JhoNn80Klh!`XXwKK#oR%At`VIc5Qg%51mU2(p#wp9;6}zAmbR3!T6OC)P+wY zbzmoU2oDRJ#i|2431M)LFP}dA;?eT_@u%AW|LYI(@t1D~eERhHrAeOKAXuX4=h3g( zxqj9;Y&7F+9*DWp2*8_qtaYU@Ut-%S=&c)o?B}ur8$XWq@Y7QFZFGH=^cHQI^XF>` z1J-GF0z{U+AW^TB>OtY)el;*h;DB#B{qR&(z*u`;1uToE`wi z?5LJwQ_`XTI2H25Xi$_nkl0vt<0v;L))izYk$eL$Nlr+{5#&5S-oR^o43k7W5+Cs> zK3=+Cl=r_>_KX3cZYzfn+S*zNeqZ+u7XV<;%Y`2tJ%Td|^d%s84gK=ieWi|&^=&Kk z;{a$_3!daLzr8XAkOl>RX}Q5}V>NStNE6nS6<;;5+$zg1{n^m+Dnmj^*t4Fkua24f(T+n4%m8L=Lz));YtMed_yfkM{kgP!BF} z^%tPv8Fkaeswn_7TStk^QrMSE>1n}E1FUK@#>?;`zP@cqT8T%tGi#wH!!5F+5K@H$ z{lIBL{T)aeTJuy4p9hzc^~e&~fqQh@Ct<5j(lK;Otm zc-x{B`N=SWtc+L7{#?7rtI#N<)ZWiN1!ouTNHzeS;C#x+C}0WFYg9YI`M^Ja1Py$Q z|1jK%&mMSMI}ZMo96j)dr8g!UhfqJ+?7$ZP(&*itH!zF6_`0pVQ5}Su!Rzj5sN|6W zaD&(58rRO;9Q4#H-yBc>xbM%_VXHY>bBU1veO%V9_&{DfP|il56N6i?2g(WHhYeG; z$q@xnP!BR7J^~IM=W7jB;}fs1&l03^MC@#VZpQ0GEqHs_WSRj-+_kaPs)M5LsgLH z=THB9YbhkZ0m-AZ^NfbbHR@3y)hjHlK?5pdi8wcD`cezd2~GjUA4_=>Cx+u2fKhln$1^=nJX7>8)7^{>J2M`vsQa8OzVr!rl2Nx_X=F@|6G!oJ zHYVX-P^hJdOERPZ15vt<4TG67Cqz|*9ta}}0pv1U;4>S7W#=DfEy2GLo~}j1(Op#F zG0kA-v{)PlNh@e|Sq@eKFFjU_h|q=fTDm8q~hK}QdBj> zw=?`aWDtDhJcMD0t#IrY1kO+zAaJNj>T#m2td^w}{I<7Aje+O@KUG|WuHX+Qv7bP& z4`m8lckEVWPhi{VULQ#<%ni{9p6jU$jyFi7KmK@ec7OTpj+m%D>yZ{6_5L`DpjL)h zMgMw#WP{?9lGsn~4=4^P6^n-q0Cn5X8Y#N5s1^nC336Ni_Iqd3v0{smRS4FGm)CvY zdsD-S1TdvYp-i5PnCnc9k^z+PaJZFC#KN%~aUBBK%i@EM8Gu`rx+{*JWLss#vL!PD zL|g+tY%2ivq1Tq+P)4s5W(9#l;dE3%iGRt3*tS(qw*y5jU_*^iYJx->HqU@jY}TDX z$N`EXhx!8Ba)OcsPWZnPeFcdcMm(6W|n1&6G#98iV`~b=R^Tx2*(Ydv}lVG>O*$Bmh!Km`R-2Bg>J;|NmXL zir(D2barEJBw0ryX?Ay2qr0ki4;*|&*^X|T$LrsByl=kVcB|^h`_(0&Wcpgri`STEAnNO9Yx_7nq zYu3kKU%uSH_SwqU*XMD+W~u3A+4ib!3dnhVLkbX{w!C=dy+LTC$k`%FdA)qR9>Uhc zvE#`aTn)c-JYQ)Ns_tKl5~y%}aC`zab74wzoYup);gR-ikpwxn0~o*2Gl1uLj#kU{ z%(i&u9XBhuIK*ZLXMu4Gb*GX59aBjF266;Q0h@#Po(j?ghNh{M#;r|Ii{?z>lD48K zph|XDL=V9IJiGU*fQhHVe_Yqarj@%H7N9O0NqyIn!juvv2cuC`2SDbc6)C z1doK%co^xTp(^`1(CHQc{xV%Ieqncj1C_}|&vnV*8}fY7nsS5oq>_FTV_rQNg)(M7 zo?o6GB}Cr7|5*lba)1x-A0z|3dH4J4*we|>Gnf9iC4PT7y4yZ}fBy9Oc}I1M+c-M6 z$-w!U+l8aIMdaaARH5Cj2r&R_ZUN8Jc+L&4%yDpG{q@@xRyUmZ=j9N7Z$RXD--&ju z8=c}GmF7AKbDk00??47#gC=i0q@K*_NeZB>ibqs|U4RAHZJQ-m&*+M~-DbHUklaxx zWlkd?(P(^JM3qzh^yl;abU4Hn<7Xmi0a8ocEEvcvq{3xdz0*A85O`Tp(=0;;bqhsQ zxR>7s-~Kt$7!%R!xNO5Sy}Aut)b~$_D(xdLS^^!(g$yUk>JS9nSg5mNlN$Y$L=!rv^Ln!dA)Tx7z{ikI(kZ`0bn#-cKPg2MHGpc zbM-RnGUpg2k`>3a5sM`xO%Y?dyWj!J1w22`AtcH$AW0BEEsHIe3w71D2*!{6C;`y% z`-Gm&Y65-1X1cE;t(%Z!NQy>Z7e5OFrfd-=@19jWFWYhFd7f}fy&fImA?#AS z8%J|e99*_Rf;Yk?QuIq2&)<%!ywlqN++4sIP!)k9K`Yet7X9RgkoRFk1whbS7cPoIWR?udl%3UG_b9e@P*M>00LkyHP=L{sw?`2iv!q^5Fp0`>EEU$94BI6pQ3# z!=$K$4d`;M>;g7jNuYP$bh^dhssQ^X5E++rZJCT_1GSkbki*o9 zM36bJH%-Yjds*s+yk-);4B21+;xcT}Xe1cae)a^utx&C4tUtFvp(9!M!!qhRFcs&ef=_cf@`_ zPonQ#7^*^4fntDiv+b3;p6#G0OdoWl1TA*At&G=3l`+&uV@Gd2BAANC%f@rB%)J3r zcin5#MrL!FywzTl-s2D0r_@$)r}xJRcu~^Ru9QK3yN<}Vll1`cUhXxR5)l~+NBFm0;+~IlR5VyNOda zt3^P5yq1JJ1eu-7?FCgfDJv)7URzJ)4esfjUMLlpwQ%<@gP}M2m?3a!6vQPSuy#_T zUfI)g+~F6L_RVzGbzG3nTc4ZD__&T^uZ;h8P9=-e4&2Q&JyUY=yf6^-&W?bZ)}ncm zgQq1DYPm&rGs>DWr54xK&e301ZvRL6eRFvU^=#J(;Affgj7{0j$9rj`~osTnH&>zKpNCeT#rr4uIqO<>}1> zmB)X>03JjX;D3q0+h0HL=3?l;sY7dNyK}f)P%hSpv>i3;b-xbB*L58VXW_LWygX-7$e0w|PUfY&TH$#fvRg5{o5R$`Eq* z1b-3DEdoh$PLNHr09uJykyGNd?l}0T##M}ST2j*e&?6rxTr z3VsxIA3y+9PIP?$@^F8UKa)?MHX)hPB630H#;*jkaZZw`6r+F{^g@FqIado{;U}cg zu$Gn$wLZN`p27qw0muW|<+7@}w}eb@X9dtMKsI=8$d&_pBB1+R+7tK%Aqe3;QXe5r z2IO-*GokE?44nMYjtWB*703SvH#^u>CQ=ZyakPj6^|RyCuW#QxP<8w%DS-dy03Y5z zQVMwc^ydv2IZH$FXEEh7ckI@Sc%Qb%q4|F zj6v@qrSius1w}HALI6a1oa4)=X@pqssF zO~GRndKy$XrNr*^aT~8lN=0V7YScTu+XyQ zGKIoJ-P|}wKszu_C~=u90DCC_zCa;iTnFbf045C22a@(8FCnOO0W=5~k0d?KR0aX; z0S2K;x--blToztSDZpY=qz5=)R4j|5ePrIe_d$=3zrA~;@PGf|-!Xt+e)-S)hl>C6 zJmBdI3i;xJ1?d&^NBu8#SJzy(Z3QRxLuWe0X9Hq((?$Uiq zpW-)7Jc*NBOIH%lIlI8w-PF!4`rBcuqSC-fJ56L{aT)-!wY)csat+!b#_yj2#!aW? zC-*-05vQdHDQWE0S_PBWb{}}a2g)DM_Hkqs`;WEO$omKSL|KPU7Xy-(oVFI%=JQN?R@=#`j5cd zhPUI4DKFqFt)l<9PWdiN7Q>%r-Jk_`n1NsrFWQ-pnK^^_i`oK8obU95V>n&`4$vhU z`PRm@Az<^&BERhtX)3}$WqE*#4gpe4zfyuEu$ZD41)u=krfCUtkX!(Wb1H!JHUO}L zi3mEYf9w3{o+A1p`{BqVN+}az)+KCW7IK8gRp+3C>|D zl4(MEp$Y+gBs@|8m0R!+G@^q4GaIxB6=rw+>)q?Be|iD@{b%a`wQ+#|T?qX8`<@uI zcYo3E-7yvFxh+H z*-#R-t=5hMHL1+J9}boo10m%jf|GsOC69pzw~- zoVl1h+=70A_7KrcRH4n!TyD`KKSE_3Jaq{nP_V6KcyNt>rCYMOH~NdX$v@42fQ0UC zA#JC}G_9-m%Q`K*HeB@a_1&9SR{lTB0A7hP*#Gx{x4*r#n1I~O(g5s-D73J3%+_ho z$9DRQ$Mbup>viU-*zko>8sj`L2@_tTm{uiy zmwInz!sKV@PUDfJc2$?D& z?FzkcQ3-nnqCTE>E;|7wqnJed{LP7l8$S#%2F0NM*E`N%r5T(V{kHG7B?y-e?`T1w z`%Xu6;Pz37`PC1_VP&c^qlPWB*6A9QsoSgdu6+Rr0E)y05NRTqmBRcsCVEt>P^oZJ zSOtt*%<-5OWf|%*8@yF{AMBzOO)OcfNR^lG2c|ylfxIxl8NfR&Cq73!rg&7fQGHEiPTH`;z zzPj@Nt7c$-G6wtQ6?T9(Z$CYawCKpfZ<6WQ_;F6>=OONUEs#r<7JpnP)BM2N4Nxye z2+z6c|47k#w^bT?X7O08pC2#3&OYxi#}I0q=e03Z6BSzcc*bRRPs8&qm0(P*Y05hl zHCM;ft)+m6cr6Rnx#@$H@Q*mM7-^ zN7ARj=jSy(0{}e;21dW2THk{X+)I_>mh)4ajK4`x{hT$GTiIzaBS4CIiCj!V6pJ~QapmHfL(_|Pb6lDJJ{_X24|G#Pm_9tVoR|o+J0^WVv zQ0mNslK177HOsQU0Ap7@F&?I)?6=(tJO;62d=3T#Q8ZQ_W^>vd*Y(6W5F6EGhkwLF zh;7@iJTaeUFs7$Ruv5W2FFaxrL|^p#px7?;L}jUtSp~o>^md%PtEkFOt7+dZ^rU+2 z7EhVB`M{Wz8>$yH7M+6mz7#QqDPqD|BA4d4tb89_M!nHGc$~RC9gYE2C>>F#5{wpI z<$C@o9`(74a{wAM1yJSfrKj663GccwT*NMz*gTfYy*KyrjW3&-nt=~(l2nFXBp@n0 zgkyG5^|$W`8UUU6xxYg}RIQb34zvi+_jXkV32=--5j@=Vq5b4EZ&M=!@XLJXt z9Nh@bBM52>mvv?kF?%EJ17`3mH3J+ZrvE&2rZGe*cxxITfJsOa=!5%y%FxkiPUtAq z1Dbt+qPYaPwed3IeE;^%E1ZW^0{`tN8vr=KKVM}7_~p%;cYkaYeru4_c5-P}Y}Zz~ zlwxUB$*rF-gZzbdJwN-}Ys&!Rcn00^T*Y>9nH2+#u~f~8{lKHJZ@aVjmsmINkFlUc zffD2t_Xzrr;s91{81~zFKk#_2$AsXNgo3_=eS;K%beK7QX@>x|oW3W%Xe3C>fGi)NfPu*Qr5m=>~R zZ=^*7;MM*l@Ya5W^xzYY^GRG^BU85VjVKI7x+_aMrh}AFXj&|-1V6tm!g#dKkesN| z02_@*o#XJn{r`C~c^?i^1Q4~S7itDXxGh6&D?z++-`PMijlrGd**a6m(Sh^rxmXnH zH54TmBfWko-g|~wY(6ltowt0&fZRyIhFd|c&>0;-KAj1xqGKqr$+`yTQ^FUO7@xFC zxkf#$#)pToU>j1GJ6;&w%`QP2VYypG)iuL_fEbwe101HipHhj*EI};s`tQ;4&65O6`D~bu97PUB*L{?=j*%``V0Y=*a9x+k6-*zl)jFFH;rL|1BY>O? zM~$i~+)5Do8njAoIxZ1Z-~)+EEZ4AJLjU)NH!m~%-@bnJ(+q(70PkMtkA0pA{_7FH zM1&`Gc2`3a4z78w$F#P?c4{Y3pV1THs>bbG3aerb7o~-#O+Y42E;8KuLBz9 z2wODnc5f##RC+WHp{j@*IB~o*i?}@iKe)N4>Tf{9(hI|;7wYL60a+dKYprVk{jpES zk$pt1D5h=3iT*UJ*rHKU#;jH)Rt+FwgpkNa0Mlk1O{93gd1ZN=X|MDY1TIjfA8!1( z5trlpBrXqix9`;89F5k*Ef17ni|8ZFproo3&p+0hmQ?}9ucd<6oXtmoeSAilro$xv z6zR}(Hx~gR38sunj|4$bKnt#YDJ5y<8=}uokscq#8U43B;;o*m&_Y9t9Ix6>=vV2YAUu@xQoU z<<}QU{{M=DcR#TQ@LwF@)w{PZHU+%-@cT3Zx3TDXEl~mgg{VwL1=m7dWuu0k<-iy2 zCS9omYm1@PNWVE$rWS0{$H5!EjkFYcWf!)uvL8>~w&4whZVb&osUwQVt8}}NsB+Z9 z%&1<+cB)FP*EW^02oKbFk2^P2D`{SgSgHD)S-IHuFlq$5uO?mdutriCQwY%YbxAbQ z1dZ^e-3EBZ>9u`4BUmNiw~^pBb7rx9KT3W2cHe-mHJwKC*6?>a$`Lo@23rc3aP}?% z!V%U;01O_k;d;ArY?!6F;i|-IQ0A@TmuIevmDiMl zSxds7Dns33J(m(}$r-O@LCTU+vQiR6TX}qb|LO(T!N0)(e);9scduW+eZd*v&4=f_ zUO3d5P{FLK=G4}nbg!h;^gfT{-WOCHN?~zrO$rABupgd6;M@V%fCMcD?ZH`bL&drw zik0Y9cac`zU05rp)@P2P?}9Uajdo)~rnYhRSyFH^001BWNklBQMhtMk5jdWz*6M~%}3P=Ld400QP5p7C?ePappvw~e;S3qUbZ*mL z!v3)DPknVB6}FkdlVxjtT1Kavyq`lEcBx0R=Cr@#bju&NdtVcZ*oFiVxe8-@qxbf7 zcE5F=j&t**uHXPWK(|kJy0{O~&msA=8t*hX3Esz`LhZM#+c8w$3|)Y^G87QLTo0lg zRX%Q$N_ZlU!?kUp1|Uk61kRsd9yolLIRezu@!{DYd*2YL%s#lB3D^_1UKN#w_N~*V zoSE$0&W+>g*zsncz6YJwnyF;Y&bj8lyns(AtY0A6)6>mXW|eneO0t z40-F=5y$+9N2Wp&{!6PMHdiSm-gXKEIUXBC#W`IL+Ji>SomxO%v(bq(*dl(ipxDPS zX+gA?lc=oALZSvBB9Ng9S0_!m7W{|~JgQvS`x(7-e~tmadi&3R{EY-aqwp7; z{?4g@_n&WMer=$ViAIg!`}f<{j8`?zLj^hk%px**b|w0~b%1l!sU};du&<+k-D1FX zecityW$bRP0Yyx%+z}w~jb>5(paZTO=a@Y1ES^$r^ZDZ=O1iSBYPX(us`pY4hpN_! zVKTqoZE?3xNEJE)98}$OKpOtdZsb(4Yz7PBk;}^u-Nn^4??XwgM>yitgwrgFk>H zP+P&$O=asbQzXdpFaYnW1x0s8jC1E4z`;FW(Pslb@SN!#?EQ6410$)$b8Cq6$K4^F zFz5_xZ{9dvs3^eixOSreZ$Oa0rT4)2+ul*J10vaQ&JcM3`8n2@&&4hvF7J_~7BB>Y z1giVthe7_m8W)XTDsv60y%HfPusb9()%+Ta>)}GR=gU%sUakSZG{82?1KBGrLx2IB z#&>xk0)Ndg4>-tVf>tjm6QadHy)P^A2)aW_FK+}UsSqiXL+VZX*o`*SZz#Zhs|u?+&mUA41aXwn3;wJ)`7nNvkB2s;W!W|4!L{8oY2=6|bfW+VFnf1F zBEuBEqa?es3q?P{<1MdEMiamZe5>?DD`74?R{@B3U}tO5VbL?yhlOERU;)CESc#6$cws}Y?1v|f}{E1*-Hy(p?5#DWBUDT77Sj%IaVgY^+ zs%?3kh}oIqJcw{-dD`a2w@>Y)sSjN}Vr$#&aO5ovEzxn%9b;K1yhr1>z#=p? zG=VH?Hp$1##+H`nd>_I|cfajJxBEbL>aEm$-!9W$VDg%lBFpS-+5WRBKPZ60?f7;( zstAj+-UOLe*KIvN-#`JL)`$cVg0&&^`_FsV=K04{vNjJ3yizC|LZ(d7o1%dS*lCjy zK#UTtm73|)iM&*h{XD5ruGI9jlsDksVsmZZu56hL;>m9KrsLy z3VY2qJK6|Qu?tC8ve_^i$-}xO@ubyK?+?Hn2qhp(<|qR)Phv7?nJVE@lO;qk+Z7yv!O9v>cG|JTF&_uo)DO$S!iw5~x&R82=Mc8_%{L;Q57 zL#|U)h1oZR88Sn3zO2;2RzRJAos&TkG%*E4(KMB4C`<*$Su#~~qn0_JTi_(20F0oC zPF(UXXurp~8tv8j<;?R`9Qd*0fjh9o^Y@X2rZ-=-ND#4+y>INutD7J+^@s)Ok8k%` zQ~Db`F2|;G>yn_7o+LBaF=FYbXEzTlS9+FJl1U0KdJ71aN5w|9SePtf5%f{n)@-5j5qMQn){xlA@f!3}|`}h`;xNY=mmL%ASXx z9W`){PkJ#c$SHzS)|L@YsdrPrmE}U$Oe34ciY{f*V`KOJjKGLg{J;NIo;b?rR6%S4 zJBYR)dlM!-|5znTu_e@>o|f!D119{qP6;^%YzX+iQkYAC-tP^VSt7!CT*>mDcS?D9 z0q&Ez|5q-Q@5QgtK;GoWu|q_0&PV_nIf0^s^_N@{auEf^j4q)3Y`a3bA$(B4@OoLf z*^kr)FtJYaBq{Q}27H6)pQfk4k)Nb01%=Bzhk{3AwWN!UXV-507u;i`v1@ z&LAp4*IOU-ooHCei`+7Z0FWu5^tp9~CD^<7hbvWu!nXFkM-BrPowM6V2KWr@Vc%+R z3Mr*+jU8n90=i%+xH&L8b+$5KD!K54xz&bKeL0n0C zf@z1Vi1a!2W;=lT#SRvZh_f&YzjxrZ>v;%{R+j-wyL|NH^^A@XIb8j5#i5eoHE87%<+vu0~PJ&-T$abS3d z2xDUvhZJJOUFMBCVL%Y|YNy2AUAC%Dq2&k5v@s%TfSI0DK0i z)ip1vz$;PH3FSkXQR@-5`>uiV7mL)ACZq!Jt3}UKDzpgD+!OJJG9mMX*Sgi=>B%6u z8=wZGxqVppTSMZ0D)0(>9qr;Hu16zpq}a1vVqGR<*etfJbNl@JyLa#2$g2PF()fRk zvLOHWQ1s#BA2%NWBty!#8jCnS{}gOsWh?vG-DGmNtKV$zqTY7D*M-6tK3l~->oWIa z+i$z|fmX!FFylaw?dQlz#EKW$;w#UgT3Umseb144H)`X+Zgb}z4GMdTJJL(bb_#$2 zo1UIlb|Ox$jZ<7=)jXb#&PlEFdG4E=L44zFGi=mRwbSa*@(7;h^aYss_?i>N2dK73 zGf`I}&&8}ns#2CmW%GZzySgU1Z6oNDlS*6;K23lC761W)g3a2C6Z@1nd*}cEyV6bh zDXF}~e(~u+I+bo$Tpnkpdoa^&{DGW9!@x_3BhTl@K3)#}cumyimMDk8wp>@X#fkyx zpcH7JNQmMpkj{fTWK{^1PYx~Fwksg~Ir6pf*9e>17%bn%$dnS|o-n>Z;f*x*ri$s+ z2XL3n&`LQlpn1R3SBS55Jb7CyP>Zo@xoo>asYo`Fc6=L>LRm-wlqw;8Ht+?>IWD_L zXW1#-7PBP7elBi;0nXxN=6YKMyBCq>f%`nvwwjx1my!}YrqnP%4qldWOKM5`&)9w@Sy@9Lhx@?v@y`ThB>=fRb6IBU0 z;<(d=Q#3r@qR3S%(As^NO}+i_sTUA+eI6o(MFUz7g+JYXc9Tj_aHrqMcx0{_TX06d;%h<#A92roszE0q6hEpSdx10hQx z@`j7^@lrYDv=pbV>42Pv4nly0C!`!xjMgcDV)z9Yu%LWXnFm(_`U9ZP9^OL56elV- zv{E%Uuz0$Lrn=lLtG{wO0b^0dkQ@I300ci$@P)?n!-^l$9#RTme;z4EBkvi|if#43 z*QMY8aR&J3Km6(h@Wodjp56-t00{W2#%(8~wt#3#gXbkLZu6vRuUMt#vBf zihL~v#<~x#;_r68p7>Iuu&q|3)&U|vI9iOd1nAs*iCyda+X*bu82?Mk7{a+tDOg@=#1pvU%rpq76K5)>iYBRZ36-} zi!`dKMG59W$xdWzE+AN`+QA`4$|M1NT#IyGT6F-zSZd11fp$d+C2Bl-VaMv%6Pm8V*vaW{UgQ?KLXuH zAVL%+!!!1L%;!M|TOdktbR`AA?f!gei z$aK4fVx+a^mRek57iy&|Z~;jO#fvRwpe{O^Be<3267-_i5POeK2SX1cI*uD$QnM)u z+&S4Iy7iZjbsO|{=s{KlBjyUL3*E!CX zo1#^X8!z``>?n4g)r4cB>egQ8o26FlrI>ZRjOfL+^=8BQa$FLar>-%f1RX8Jto?b< zf-C&z9Ms68vH)1c$ZK8t^CQQ)_^gw7`((oweTv$28h@@FE2geC6~qsh#Mz5Zlp7_1 z7#vvQIQ@R^+ReDEz}frI7KL~;*$&EUnHB@Uw?pRYi#`7JIPWiyJ)gsv;+pXD{QE0t z^;%5SlDR_xQpbG~RqDB*`&v%Lb#K^-FWvxZPd2#Lk zQw4bJ2=QHAbG{S|(8@Y({G}O!J4#2PeNe4MQ;ba3S{AM;ND6`j^CYDs+ZeRs0d%R- zQZOwofs^~WXZ#L+1Gd6b3dtG&n*mD^>n%{xfd`7WTLtPV0>lF;OaV>^3OUgX1xzc; z+s&#J0wn?3;~k;@&j8??uYZg9|Kd*{p5D`ax#lCj{poOr2vrnA_1-$BMqx?S3j|Ia zJ7S}+Km#DSwqI7a!1GQ_P;=C}iIUST(RIc{v`8Dc!WP3ml=j9TLK@!KqVQ8-lj=F~ znmph}Or+hEQZ{-aa&>B@6FB|{U>DGwb{(fSPCqz8q)Z`EWV|To1QtQbxn9|s z9B8Hv+{1%W$;sk~D-rYypzxxglmJ(;KP#n#Kn3ntDdQIy^#*TWlfZ3RHMNwv)bA_B zP_v_oaRp4;TPY48Zdo>il+`}B04V9Z0Nzwi7*l9;+m;Y4odJ=pMOjj$qYF}++-}7` z{`&FheLd$-PappDn zS_bg{GR=B&pna28d2wyUMP(wQotrFaDZyBtMpciJvr~IB=M1!}vezr0#g=J^D>B%8 ziucJ)AEQz6IZg{qAt($`(sqp!M6&I@*>(bP*8)1btcXP5iINm|4}nq!H4RWtIbJ1-#Hl2^G#||10 z3c`!aJQVvNLs;4SM+S^^@Rf>_Oh`zLEdd_nd!`PQaE=-9{6C6IoFBRBI2vCmUFQ%W zJ_NoK%!wr8av$V*C|$lhC_X5<`|SIrwuSD?xd!Kv7eVVqO54rksBnuA-Nko07VNtY zVhv%Vfa5(tuzRz}P8%}&R^xx2a^xp5LVfFRkI4B&_Lcy}}_tGlb3-Bld@SL5rik5=a1 zsRj5?r|=KI{PJii;OXV=f<+NgRM!o&za2o+N`e7^B3pF{WUj}tIhDv;tzLih!Xxv( z&*|I_J||!)b{)N@<4Cyxa-!y&0&=j9Mw#GF;ZS#UQF#=d!Nz-25!Db+A7^>INhq`2 z`L$jGm7@@bOQLxmH)qZ@U60s9HI5t+s8p~j30#6}Hd@-K)c7=THN;ZjA%j&@d);|d zC}^WMtF^C41h(~aT=A4CkiFMwHlnRxzX*;aDX#sLqzV54W-97Fm2m^2TCLnqPUnKY zp(Y4B`~^}4cntW z1OIcKFsPuHQ^zStpdrADDQST`fLkhcQb1+M9R1VBr-ysOKm2!&fd9rR{9#T4|JVxH z?KOdHg9~qtbk%rvBco`0ZQgBzwowRMPQdzwM8O5H z(CR$xwi_L9s9E9h3@RW9I?SorOm1B^52_uJeD_uNk%gTUeNDfP_4_mOj;1mYV%#5l+W|gSJD17dxW+cdD^M5j;wk^jn;`X-wu=)G5`tIAiU_|>wa&)d>*X4ZrQeDK4wGC>lmRF|8>Dld z2TCq=@E{@B>qLF97#rZdgTI1=Q;r4&8Uz{TMUej2u9kFW-`WXOQ{ z%Zps2rb;jfI|8hP(7um~`U9L!ER0$RMykcW`6{>3{8;nYkQu-`My~CRPJ}{kt=;as zu(GaxT1KWvAi_os`>W=uBGm$&uGRKdWyViSzuf{iw+41JS%eiC%%U!4Rr-`)#n%--PyD zp>{}kbSxvC5bQ|;lLI}_mfIo+WeNOE_Jo%>^_x_!l8)-M_6(EDhY z&BY*_f?v?u%Y=di8@MSrE5kO-^_&OJv0Sgz6-A=3pe4csHI%66-+=!-Tz3LBm;OjjC2qf?B04Uj zZJvXCEg$Dzu}U_g+B*fj?4({eIx89qJf7)sxH(qt^XW*H>@8P3zrg8BZcD#y0KCy< z9Rf=67_5*qFrC`grZLf7=1vsd4b`~|U@N{eSOK1+rcwH&gj_ql94*sz=w;$I@P_I&l87ST6j+e$E(iq3VAZ|e0HSb-W zBlR#kTKzzkhmeoW+m0r0a3M0G_yXsjUf19Wun_qt6$waC_vl>H1#b^wS!fDE(M2Wa zS+h5Awg$QCC$*(tvZOgr0r$VmAo>YlWTsiDoT@cQN&3_-2MbHoDyDO?!4J^-#nlTk zfGs3X(}iaMbPJQ37Ov~_Uq3wPll|e{T7W;>fDhON`2AM>`uPB$2kiEWjcbZqDxCpk zYgMPBMQg|lW#SM%pr>pH@ps>kjG8T)sA5D8nNbFw zU;KGJRVv_1p-iVWqEv5#^x6S&P!>c|G>0d7d2|Fd2We>rkZ~_i>(8MnhxpTqUItpw zpxi3D^u$H6<>!r_{ftmE@afOg#Z8`07*naRMbj?r*+xKxGmJ-M)d=no)X}+ zp@6=%YaI9X?IqK7h`aOn*P^4{zwfB3o>+uB1^cdD?u#+UkLHD$soA@stShxHsH3Q^ zU@8H{UMf&ziW&lB+#?|{euuT+zX^T2_5E#+rcjR?oL$Ov`>ZJn?g^{$jV=bI@!2AX zfyn0+a18R4=hDu5WHOX$g%Cw8M}CN0qQs>++l$Ym`v%TqbUihcSK0*$W07VmXALv% zE*BtAZ(JSqYgrMR88~yrp53@iYQAeKgi_N2a2VGFpujU`zUCWXi z#}O2l;m|?sr>39iY4l?_;%L?kS)eJcmjC}<&IBDkI6}8Qfk%?SHn3AwnblR9RPD@5 z%tlcY72IgKeBWVYW6z2rx#=6kKCBK`8EA@xT$Cvm%)3eNj6c7AeAtKj&87hVHx2mV z-MfeVvfe#?{PkNAa+RGzw|xl8X54%RvB$Fuc98f#b8^;$e%gb*o@Urzczyq{l=4Usj04hj-Vq8>glI0yP@3X&H#7HB1o(Bq;W2uR{~zfJoo4V^l**TC-H zw)4QrwFG%7d-t<*2!Tt&_yCpxfp=&$=sY+fNNa&LQM};gw_ux9$!6eXSD4e>6bKV@ z68tKx{1l>UiRcUTUR>wkSZ8d7)Ho+R9S6|H_zFUdQ6A zvs9%7;2&dqnKgE>cFz0Gvn(<8$qtyqL*35(+zbBo=KY@tOk?a4WJvCQoga<6oo?)} zpKoH{dKuvo4F$Wm{XANXgOfw1_xonRF+@Lly=bl@D3FW!{0u~)X5We`eRiohzMBf7 zuWfy}%%+=p`L{PB0b56K{zMhCCYrw1UR33@-&!5F9_W-#X;9gnr<&(oK1(6UM_Q_; zSAchvI}~?JFlZ`fG=7|L);H@XJ|!gqIJvKo4*`8lEizM(4RnhpEwa!y06&YK;=G1g zLlVrVfb`sM@+#9mL~~2%KE1Czhn`wjb&VP^O$CKbUJRE4tjEE5fGDXOQU&LNuS*0= z39Am`_~-klhkl;lwgvcbOTm9P74VF5)HY7iHm4KHs|l8y^HYFcDs~3+BEx;?uFrZ| zY)^=^qftcGqFSORCGb-lsO8n}a`@XkHXkf9L~&vDVAQKSd)D-_$P3CLm-_t1y*P)s z@0(&5n{xJmyT>bvdEbuDhurq(TcZzH>%G%u+o=`kHqnN&OqO-!$Lr}AGY;ByZO85T za?7pOODAbv$+*Vqbc60#Vurpw5PC)B&oI9RWLjnA4BCFipwEDE5 z6b)}~%k4nPzMUm1#1|bga!gwIIVl%5rdu!S^ViL;V!CryY4&fU^;CGto}7_mOKQC! zU{HNU0bxm{Z$n)#>vsD#t!v{PiB`r4&Z;E$bP)T-E8W@6bJe2S7y0^^vG^NjQ6hP5 zXF1Lcps_?HL9>CJ@ihROJ2u?hhQI|ALUI%+@pJ?B$=87V;LB(@K_S~fCg@T-awi(1 zAwyY;fwj8?XrdMHzUwuBh}2q;jG92*2e@cvMIunt@mk|*49x{-;YTgBHDm#*pfnXq zX|AfXp3~*3RuF$cd&IlU1{{b+LXA$R0l0>3e;un0Kxd#CCg(7J|K;h?ALVy%+yeZ* z6#Vq`;nBah_aA>fQEXh=938a7MA@adX6Tds=A1JsDIOj#X=7i| zqHYZ*J9%#oO4LYEoGuWpoGzBG&_*nY+rIt&{CfD+dQF{DWktFyvC+yb6|MEciC(?h z$t9j8`mt}4#hNyuDsTxyw5G~;h&AUv zGC;Oa+paQ%$y= zUaH6iE@^HHX-6dhV5UB3X-Z2WP;(D>fv=M4z}u9uy8CqQ0JqZI)jg+_jY}?myF1x`I{tt7$J4t<|K7f92mdiq@pykFOF+p+^|AROe|~jK zqj2B=5NFR@0JVefYP{d(eA7Z0dXZ+c4q=ahe6s2>$Jv5JIg8hAKhIfAm9lu5fUkko*uJFh9-jB{}5Gc;d-AiR`mwuXO+accp4Bc?b{(d5~W21OYl7Zf|yG+1*uS zca^pi3`mqFY1d8}kU*8*I;x^52w=#0?mb0Xh~PqHSz1Q=Yi{Y*YC86S2OtZ$wA>uh zz+yGGGihDY@yjgdIAzy9G~+FBM5hN*aw+z_YJLCP_itbwzkB!oivxhG8}QXbfR{nx z=VKbGaDa~oEKERBG3s&2)(Lg|{du^Ue65$%Buy?Tbl2%WLq|?3@PYSJ1yJw5{x-Cs ziv|cTKm;4|k7}{p5l==)weQw5g0~8k4w8W|;lvvsES}=e-CBD zua-LH&!NxVDlT#wY%Dy2TOS9ph`yCAq#ozbKW+t8oQ*$Sb4Wl@!FStaL&$&TI54%t zuTy>Y)CyqwUV20B^N#I-06f6{cw5K?gy_*%plT&44x~FE5H6YwPZd>`RMz&}034zW z@B{!rRGW^+|MW0F_U+AeL!>x(|B(qTI8Iz{HmD z{uITEinelD-0eo20Q}b!*CG-$u}Cu;eTSqKUOD!5DCA(gAc^zi>#zP70QlltfY(Xj zyEgy<{%yRhhP`$Uysw+q^43CVbv_=`#4Ph3 zRK^1D)3&h(T-tNLey#1?){|Bwc8$O3%PX=jx3rQkuhLKD%RhaW4C@Y(H) z?4WHF)$8>f7xBlph1UWMTuM44S2hI*!Tx;{!J_sVTQibz&jvvOu)sNF>VHw6n<+4N z11Qj9*jMR*M})!0!B}ToV2%qF7=VSq5sGaRX>;P!S&wz*6dOnzva(^ij(FamfB5G8 zn^?i`-u>yz$N%dc2>5SZ0YBaSX2FLo&29}i^Wea1R*L6NSeab!Vjuw+*DCOFrot_X zrsWPht;Z->snR^3IP}g_=>52KYh|pSlXikmKq4p3*0jRGphOC*-sueRjr=u9MgM`I?eqOd4Y3kf>O=^oSaf#nOlolx!6D*vrkme^G31gW6#;!N?C=L2Nwf&CU}Xz#4{w)7N1*);NTL{LfN`97 zZHiz2WBGm+0B{Wiyp05KZ3TRI1f$aU>}^fdpYn4TYV5k0ZBi1rI{@H1Sdcs?-=#Rb z4HiGT-BCMD)pF%ow~fotW}5{k*({dmZ${PaR7j8FnYTS)kD?2jmHEX zkwzj}tw)G$-{exv^G6%?y6qKjj!HxxfV}Y>qk02i?vqK;EvdXXeSiuHNt4bL>pnY} zkLI9{x(rSXImuRwLYWVo4Wt~3SHajBu9|OXGGah8=q+3lXhVyI?baMakW68#U5s1| zQjx8NgZ>hEfI8uprftfac?u`NP@+o65*gqH;9vw~E~M*5JFpIR(vhLLUC>NifDy7c zY5(~BH*aM5zWcIc0RK4<@ZGm>CI$TPJd5)?(twnfrr|~ue`wcM-w-+()sK{V1WAJ) zTkMumK07Tn2KU3dWsTd<5Mw?OhpNX+U(T9a0XAjl@Ol4niv=VYaNfBjbzh3^qN1#C zhi6e0^-hy7z-pnRv_^vC=9pBdE3(tv@6+rb9>eE;Y;^gaS|;iBST(3|13Ec2)I!`q zkrTjQHOqGBfVd^eR&Awy`Lb3rO%tp^nlf`k51%qd$(^t+^l#_Pddzg@!K%0qnF3i#{O9HN5wV~LIloQvh%mY$=&=I&0QVx9Yn znxx~pQHHy;2zGGZr{5lHw9@bAVbfx=K=HFya4UQV4c0F+w!+p;v=WAv(xJZ7@dkyK z798ubc@)se9sN5Xfhs(_mu&sZoLStTP9E^z&!``V22#dvRkZMvI?r|86p)0_9tpr+ z%3P}ZNgU(Z8(?$n7Tfk_V!#s?+v?0^tZP=))7eo^tk)b3Tp1y$qNv?u^WWaB_|+M1PHDjWjn5i zQhv2!+)oM<)Jp_dB}506IF8}ED)RI5^QR54`8-zk(E|n>hh)Sq9YFjb_6-0;y-57{ zNZGNb0sZGXY(S*SlK1;SUO9jM)P%bH%Um;vp@t*`Ai$0}IcJliwn%S30)1L`DM8VV z`83R>7$`J%0x1RRX{6dIS_rBzIZSCemZz9p#MLK31D;Unu$n~Ga+1z(Q)!b5{#cJp zi!>x{b0(A3mrI1Sl4Llx^%$^wTs7oG!7-YS;d8>AA_1n+8xu7aH7f}5`*z<6MmS0$5)z87cbHLnM zYewK@5YJ0ds?l#qNuG}Rc{-|JQH`_m09!@t&%+xJylQX1?S}QdSF_U$FsU4~_Yt3} z_Rkl0&LH#5ar&j$rnE550&f(#-*0D+qF-OWPN4x9ngG{D8nGGo=US5>2(8iR8~ zr2eP`Gv9|V!f`-fI)rb%f2vf*w{y3gL(+xzgUDoxmf;Z(2VTS3B>bAL-$HjFb=j_m zW-9=t`C~s%m%9Z+QId8FK{|o352~t2<4?q-?%h@Z0#FO<8mRov!vzzfC;;+EJq6WT zB<|;_E;s%nA*z@1*afscq`HL^)(aAYZWJB_ky@=t7vm~eK1Y@=DGyK|HvdYQaAOus ziBo^Sym|dptM)refj!ww;Gdoc{QV_wfg1eM9a!Y<*5`p`Rr~hA+JKTRMA9$w;@--{+m56015fb%J)8V%~Pv{8at<-FV}W- zMD&rzJqL~m+9t-y&fo0E{lwRdK3|4WxpUhMH8KkP8-35f=RgpT#KEG7 z1i)S*w*MwvS$#X7s0LV{w%*`04v$1z{=*{+Q0-qR}{jo|})hAaHz2Y#`^j9#xbb zw-_BoKNOXX+dLehfUJp5ih}w`q1H#Mp$y)XNJx;UsEZ8Aff+A>I{DsQmicwBwF#8E+cXleIqO^7SOk4$8y$76 zHzSWLz_DfP?V=io@pCmUCl$Sn9K#j((Yi@L*$A_QoX7Qczag}Y7hNx4&bKecRg`fF z9-!&rJvyg<4C*Om%*hC%p^AB3w5L6HMozIS)isO(Oc&nZ`3%UsVf%iR_`)JRoBnfF z?QF?ZgM5$x`#FweCA~XT2{M?9YtwsSA1W*h_xDiyRW?Nj+!}`%)GCoCO5$EZola`1 zidEG83bGXh%}Q`R;VLe?6yz)bi)95{3YSIp`-dm1{y+baIlzB|06+cwbgz^|;N5#_ z@D~+|^H^`}3aI{eKlZw=MFY~G9TjSV#TF1?K|vea?|OW>N-$M1P?ZnF21=}qVB=r} z({1biavfQ4UPqOQ!Y-`RR_(hfNEx8%^aedAs=ljh?lNt`avYA^amWBkD0>Wc*wK!7 z?A$934NtH91_xPi-<{WiR$rXQyhz~=8?!pM=SASoW^>4&s_xLiu$ zu0M;a1+51NVDGQ4926F{I$J7!RBcTw`tHtwL?8l9i606>P9ZlWH)+DFHPzLerwwY7 zDw;=QkT;3<26Uk;-r^#SsN43CBSaW;Dw=lT+G?j(?x`x5oxpNr z2b&WKMIIlaP;xgfDD8Tgzu#!Evt*m7N*Puq0Zp5;-p^U3KJJ}*d~=A9)d~=wgBzbe z(tJy(MUL)fu2jwedmo5DpQUe<%1?_tzV!#NsV2|__tBNCV(#r^!eC>u8^`pLW6Koq zF!q)iatTS`zKWo52fER2eG~>`PZ5iDcrKLXKbGzJ9*= z2LO2WBLTnz0zBbsWq0+P52p(fj2uYT0pKIpmPL=Yt!_X2>_Dy4E1-YJKA!B24cmBs z--THhl7eLcL3a{Cab=&1a9)&%Ad2kFk42@+R_1Q^!I^ty9fymX>|#s46cpGtuNgF- zVtbt1jiB-a;SuroLpz@?5p~$Ad>#X|e*-7*?zsQDgKm43Jco3@c7W@kYTnN=&$)A) z@BQx}Gcn1o*OeM39%Y|?#K=IX7z^J6}C7$M)Hk{9q67+ z-Az>lI}kV@U;&t@^4F$j%TPrQEJZyA2J)O@LCwZf)Q}w&6@Wq{?Z?Vil9Cl!g5rHS zrnv8odyGPu<+7y3$P`;xd1TO}k_*U@V9PSzdE&&hZ})d^UOnSF{c%k|&({z9pNA{2 z-~MrgmEAexoa&nxl{;oadCI^A2o(C^g7FWI5ITpy;3P4 z#-&2h*P(#!X0cqhhU)@7czPC(lMTR)9W%1bqs8N62HOCE*0kyoIlJ~R@~&h#jvEVF zmQNmGHTDKjKvk)vj`B#hT1!ym|9{tHwl6c|vwUe@6bh2PF#6@o1m4RW1cUJOEG=|= zQknT=gMnS$@Wg-r^yi<47JgVU@NDrAt{VX@kwC5YbsV9d-@l;fLthf4bK$5{Pm5*d zAY=)u5b8#mQuO^6z8IxTN`r<%t9SZ7_Lt(_KE19YIV;eME8u3%waT$&-p8d98)!Vu z{Cww;snngZz?P;X9?+?rt;(*$7RL)f9RL7{UIBCkN%4LbV1Yc`chU=dhm5On=xE@c+&OKO_QhQTYAK4EuQ=Putz5N*q_leXkJj z^uU!UiR)fa*EAKa%m&#ucHINDvvP~bZC!awzE;nTxE)mb z_PnqDI47eCTb*G((L6{p2qP-e@SG)sNSrwae%lA9eGJS2)@X{FqxJiTr>8CWF{c-K z_(j&%K&@pg9e9!2x}V$2HaIPq^*9_qXJm;x(nxw zNQxeR;37g9$nbldr^sAc?lLg6htA|xxyXQS$C*ON1?4Lw3DZdc3FhuW))z2|lr9?)50}SRd<7FwzSnW!<{_*bBgLbJO zPXK=U<=00AfWH!jZ~M2Wk);>Ga=SmsE(@pc^n^<9c?M_ov=;Qf7r}M6t~Kp*f)N$1 zO4jSshWe9C4cpP;4=%|wUnB?b!JGFQQMomn>unz38v7h|_ORLYdCT<(mQEWLz`($b zB*8#JFpwRCDR`==)-|nwGxNH_zHVMHut#v3XVgtqZbH%#%Ol^1_9F?s*7XFQtWElr+_xvf1g7%;o?M^j7+ihb6 z7#=@LqoENLUUjRhA6u7sKve_*@grqw7iKK-6eKI18f^!HwowBwq-$Uj09pD06n7~e z3WJt^S`V92qyq|I)B#ju+djR2{ivT%0Kl)m{PYh{fZrYz0XQA}@#}496wz88YKaEB zQx%l;_h;ZjS{ApcDV@v~a<*6WfA57+>)JaCaA=0o`w3EistX5!vu38IpDO^O3tz_C zc&0d3P;^wk4?}F%dS2zc9%29EIs#KeZ=Z(R&)tJ}n40OI2Rbr`xLi!8=YB%_6}nZQ zY@Q|s+pIsHw}GmfLK@XGs(%Bo=+EDsLHiZV?c@!Gi*z67c0&*`DDDDtu7+6$&b8?B z=GTEot5z2wO4SE-K>z?C07*naR0<6DB(bnLX4;@uFpRcH!#TZThwm!TUD*N@Ew4-^ zbG*ZyB19+{08?Mk))#k)QEkV{F+|4yFtWT0fxJkh)Le%ctr=$FIEL0tuifi?Ug^&UL|Dh2q?O zx6^Q+Z}0*T!X3Y4N)v0Wyn zhzc+77T_hh3p2oI?hP4q0m9-LB5)56rkO7W^54Sk%iD+Lr|eJv00DTom7u>v!0Y!f z^|GKSn1Lbu6S%qY+fhShP!~ELKJzMu+yyPI^uFl~k(vz$^}+YM#82D1E1$NlS+37p z?`4xguFnJCK^G14`uW3Ir;<^q{WkI(a%w-(7e#Ii)3W;}Z5UgCMA)p{x2mdi0{`57 z_X0$^oWj>`w-$)@L?Df&ZXW$ zr;p-Hf=rp_B1Zt%kx(No(h0FA5P%i6v7E!gYfhmZcceEJ9ilyHNG%QOsPN_fTw_(fIfhA@hlKzU@`mm zcR#;*==%S0Ye4@^0si&Kf8;yUdi(L_$-8+Sr=d-2o^!vg^>%Kz(V&8bwK5bnue)(5 z4u5_N#8#iA^y|CIAYZrL_*{R zfmAvv;vcP(Qlc4Q)X4!-EC)m z?=Q_-E)L`8C4Q$c(QLQnB#gJC;~c! zVuNvEMSpjUy)C=Q#wZEikiZO*mclYna12O{UGvPpc=h_s#8DA$fAcakMx-!|j0xK* zG{7!ebXky+(7Z$q$(s3S3SGVO)bA3Y^J~6K`>blt>ynD0e@OuR@laI;DKMRhNwI=v zNVSHxT=3TDy>|TA;{V>3{`9-90sR*R_`?rgYu^g)r<)7r{60*x$NBy6!g;!1_n{m| zdDjq7Arg(lj@uzX;T2G76hwau%FQviuJksN0Hq%ST)@t=TP>xVt_k;gBxv2%)IChZ zh^czC$*QmzU4X-P=eI$`B|-2IXR;%-Q|4?R_NKn19+l6U*vl@s!(OBy`$N!!2vDfx(_m zE=#Ow!^bkps1jD`_XEWUH7FYVN_%%f>`l#cXA*!$T>*DVAo>9n3HLfct3Rji6yTtv3xZpp;OSVDIzkhuC@T2!6fbx9^z}r^vqXO_K;1p@U zLcIeh0G}EKJ@x?k4ItkQSd`n#wX_(LtF{w~CJTW6*oPeZew+-`wjj}NYciz1Z;tu9 zPDQ!Rt(YcKz9$gO;kO;Xx(8=N*|pks0US*|Zil~i73B3wQ&1O}FHCFBdECyvS-raj zqN5j(6tD{5PO09rbG|5^``PDjOB)BEUjq;tB-*(Z9l9lCyr0A8x&Y{5Wgagt^V7}^ z9K7kcECNzK^Mo$=ff9hG9RXInD<4df)ns3wkA|o+aHFROW*`+ea5-3Y*)0(^K- z0F;1_zaD;{N%^B>G|lsX7TD~4oSw6ER_@oM*civA>li?yHN}?Km*Jsi0qId%!uPA{ zsb--$;S1UA$9>v2X>>?V)913geoQ*x`R_NKt)EA){+MU!=L>^AQ#zkTh{yA=?w%HW z-mb^y@?u?Dszo(HTkpFtsf9ASwwh?qQE$)RB!Dg$or}iXpdoyYVmOgA^tz5gEo;wzn@Cd8!T+M88{T+ zllD*uIytmQ5CG5hIV-NcP0KK`mf5^FCzw4(Q&IrW-ELi>AawlFIribl^T}(&swQOn ze%t4HocngXzMQi&jznkyrDMrcJroCFgsggv`ie)vN*K~XF)6GmReNBa~hxK{a_8i z><}&~(`nf?`rKUEk2#AYi@-mJXMC_1fi~u)R|(-V{g}7waKHQ`8rhQK31ZX)n#l`r zrd9?cLbJq0aO2mD3T2?tfA7a|5h@@=I(Ii(q%y~^3cMu5$&pNFv}n#kYH$IP37aY- zO@mGMn}9haU<@&Y!dFGtRx^hA3FVV1>@HM@<@N3!9wg9GTbsusDfB)JZV3R&># z{maLvM=N@}0(?gTaGwQuJokS+`KO=%y0?qJUeU=EO}7$L@&vs#+i{5Gt;Tn_EE|9t zYq%=IMHrD{orjV!=+^3jy3mj0==cG-9W5I8=AoiZQb42=&K-;tD`4nANy||Q6|@em z7njD4FLUQNDZTBS{!4BivpZSN{X4`!p%It>>u{j&;2Cot=WzuYc>cBo9Rz*Pos~i4 zh0A~qKbCCYi-IOEt_CJ8+ms;`B1Qd$#sBkGPOc#Y7;K}Zl50P|2G2g$9E(EcM9-02K-HYef$Cuu+sz_RC^|qQz^8s>ql=|80m`#5~rA7 zj!xsg`^_9WGCF%8Z>TE>t|+VtOK6FHnc^&ZFRKh~7C^q1;`Z)n%o}RBtYVD0DLpb5 zdcfU*K$-kVmP?2QZGN8Hd9lvR_FMQt=|cd^W+`26@fEKcE&B zi`NdxGRfB%trZzdZ3R@Hz5`Y*LGr3z1>4#^zFRWf5T7+`mjd~Pkr~QnOjaeu7(a5C(eh9;?4>Hru2 z|6R^nIl?laF|RcR+)O9*l%dij+ODbQu>t^g}JkRxIl9Z;y&bvDiv1;Stv!{A8?{5 zQba*2)>oh4QL z-bI|C-)`vqiMOI~rT$)Y`8@GlgCDkHaw_K!d- z=%^1QU_r!FijZivK>_%p`6e2OKH2Kjv>$EQFPcp!i8x28pm~lkhig}twMiO)=v{?q#*1C)E`}# zP0I7MoLjE$(c|Rja~IE&vV&G!Q}Wx-(D|~UhuW8r(I=pmqlN`FM&0-r{NhRT*UU)CD?w?a6fmI3`yXwe%mxAdH7jRxy68zs z2%18Q3MvHU(Pw->&)H};3rZrw$L08V^e|E+NukJ<$MeTG5Apw>ukXKm)gyoh1^9Xu zetP@f+NEmKxYtn!K>qXHnalBgT9KP(`}2~N*5$@7X*Dpp+d}6Siaf5eqvcrZH%(@s z!MI~41?^vj8(r&2Lqd6URoBi{JCC(nF#r7Ng}aVgK^dM&X>u&Lk3#bBQNyB@$J~hCc(e6MBp<1pjH;^SY@}PVI0pPj@)n{W~P-1f75wB>< zW@OsPn=D0qE;ceH{v?h6Fv=G!7Z4AnGbqLShCGOKsU;a92By zCVU(KzaEePv>zmUXEL*~j-9c&Y_WT9=;X5+=c-ojIG&pki_!&|ohws%2StmaCZxa_ zn&k9yPCBWhsGvMD4fR6n>z*O&aV72O&36YNT*2mrH+CP63ucV&-Ra{vI*Yl_*>Yd4 zJbw-{0Ioo1VNQ|_@fMgjoQeVPdI@D?yS_j#a8w!Pfn6GO^pydy=+@QcfvdcpJ7soR zrmC#Q6wySfoSeX@768W)#Y4KzO7InAXh4i4C=|nJudjrX3iQAC2|prat(-I3GrSlM zg0h9*)4r6~dH?N)2mJs0zWe?g5CEb84-xjK!7dUppu_3);EWf~f!j4!DB%i!JC_66y33VwCH!UJnLMPsT`GX}sMK z=%Akh)GG=g0MD`v$>=7NXI*Y8%2Mfw5A>pGNs|G1XDXsL7zmH544|()RL7+g^Y-)c>0L$NoeNs^>_D_l_O|lM9f}|KJh^AJ7bxLO z$-q{Z`SBz`k{ve(ESc6;M>JHLgahJLggy_Hswq5^8|y4vUvMSu*K2p)-Y=VQ{UEcg z)^yvi=VYWAdQ^;7f?tl5S3C|f5{1Cd&=ROlz}%^?pSK`gtRBoCx}Ba~Lf30e-QLg; zP6@onL_q_9CsaFW6hj^cH0)0v9pLGe<`L$ME^DE-5XA-=5Ni>IntN=HJbM*%kucMIu}f3lX1-!kPE!$;n4GCc7YNA zaE+#+`s-D=WWa zL2==Vd_yGT0pcPiFHw48w~DO2LEvfaP-vnGa_kU4h7!IFO51W2fmZQx6eSTV7Vk%e zebVNFr173WOU)8``xf~BOB)3L&nE#-Kio2I z^hBJU9DpO8qO$V;WgKmdBK@3iS;U1w_TdRSao}8A8?jiTPGtwg9iwRh{B#jzvq%L0e;mYMO z0^lL_5h?<+$jp!s=F*{h<@j+iH45CXE4&=g5wcKwvqY}YJ{@9ME>Yq$PtNtpQ0_gS zw`+D1%AOSdm$m9oYQbFrwXmr;ny=`c|<#kSog zjp(FRe>NJ5HspJh6*vk#-Mp)tX|B##sI{J+}DzPi5jjf z;#$`(q>|l^E_j}7p3^=*B@}ZNj&LM$w`M|IZs?8AY^T1+>dxwUaZ~7Q|Ri*b6d-Z@FKfk@$hAVCj!cG&s&!|Hd%;W z&taX1u$LTU-XR{8-7+N(WnmjGzuimC zH-2f~4*>A@^Q&L(-^({w1K-|k2^GBGK>+h%I+-14V4n>!fjeS@q zHRKQ)Q^w zhhHEI7_dT8HPL3LFmy1`dj$Yd`{~WS?f+Zx|I4eNzhC?l0^GltZ>zIkKb_Hpv0QIO z*t~C>+5>O9yPoH6u5atL8Ys+22_(bb$Felu45H+m)4+uh1P;*vE5q1U!qZ&KBcos7o6U^1eO~%ku5<{X>ql>MqZn zcgsb)!yp;lJ~GcNtxleuwXSYYAb**TfUrhUXN>mn8Cm<+V5B~13LvyeIgAFqH0x-3 z7pK1dsHkL***8*I^3hciy&?F2t_Hq&$efXUM%mwwx2x{g zD~b30@#%#0mk0<1x^FUw$n@ILOIio)nm3?2u{Dt4%6Ol#F~&l&j?=k$W=Y&8owjjz@zxQYDiDG61!BF9l*ic~2&>wFa=D1g^ApU;VtkPZRJR0samJ zy#D>M2ZgfSW`BHo=1>A(t`xM-SHV_8)g||iMpH0YKCcLKIiMf0C2Ku~5gtgGUGr=4fgWL0*oiSYM+}g_4SbOmVvI9BE&39Xc z@yF)??w2jov@bnvIIK0E1=`2c712uG+tnuyML;7!gW4lpN{n>4|44SuSd!31NZn>u z$w?C+C3H2kRSD$h9DxsnFvi+?c*YMv0Ykddni~AU3W~G`*LWIj3#ugl%igu^R+U^q z1s+C<=1LgUZlm{g+dazt8#KQ0HTras7^Ev9-cxz4;F<0)6NpQINDeN;eG177%GKpXaKGtvB$dX1Co7C={lu>GobTj z#&OUX&8)rWeI~yg9j)oHUwTUY%l9pKRHMu^ojddvv@TSymc|t6B0vgN0pw8jK5${u z%;5OBOMEo1I{6Byu~5t%R6tZ71$u?0DFV~Bw&8W`X7XgRuqi3xi;rG8O%jk<=v1OES=px>_~?Z++P&8v4`A1-#f`=S#j{nF}Cp>7WZ< zuFJQ}+H(NG))AP$x@I%)0DTSB@9>S=$DPg(;Yk;{@ftL^^eKfir>@tNyR_5e_vX0mkW3HmURgh#=)>?%yP=UhElSh}+ z!83@6mL@)<5WE&G5{ODyQYv6Rsy+l32MgXxffqf$zP)nGpPVwkdHvD^;NR0g*9P#j zlYrOn|FQ46ihjGRy8ij$`|306LMw9M>r7-OwJ}Oa@N8YZr0F5ed0^o2G7ZrE%7yeS zFQ;ucd1L?pAOJ~3K~x2#|60)8RY=Bj?>o9d@cs5?uG4xt?9XHugf?(UIXYwWjOuut z`(@<5oCw7<*|pverEfk%X=^F+$Rg3jW7Vm+D4tBc0N2p;K;{&LDs8KswT6f5l$@7} zvIm2t`G9VDDuO2-ib!5D@;&CA$!rxc-U$wJ%38vaV;g-`4g5WMdtyYoDOv6 zXcPDjS(^v$YU<;O&yx)}s$DP}HCJ(1%KGh)3(XXeiy{}pJ|{s*I|I?{J7qIx~HMHJF)m?7S9_~LU&Sa3TcN;yY6L=p}|d*uaOsr7N)Z|B1S1eQyf`-B)n?X&No^Gq-%_1 zj4viJALwZ^nPzB3(T2_4e|mdG|7S?cH-G;X>i;?r{A?ER?#qI{2ek&3KWKvY$?v<) z3xt4Bn%3#|YL8>AB`GJMlM!F;$L_i^MEpB&U34)05$)`FmU4THb(4E^9i+kTw`ZC6 z7fz1jl9ITTU1N%|_70eJOwCEl$H#8tyxoEC_uqlM9_s!$1RgJ(F3M$T+NLryQHXuf z#y@^}V6%fnplPA4bZIjf1ts-D5P)wKVJRJ~$Or-B8DoBH6d=|%X#dTGBx$36YExw` z6a&PC@I^yZByA}USg7Q=C|<0u@2>FwoGXA|gaFW0;I#uhj|9B_aPNSd+*ilEbCR-u zU0h@i;34x9M=h|Z0N~*E-yZB%_`F?|4(gO<5qv-!KMzSG0CS88MDCntrsQ#OzWTfz zG~;(XjSJ*G6D6M!te@@Kkl)70dE)g>aE|@VAs>(5RnN6;5Emto80iio-NsaC84nu3 z?isQ_+6EeLIIRI2se@sTb)nb98?j2q7AE!Xp;8!A-pR}=B@CxR7Js~Kl!U}X)hq?Z zG}#PJ=#gSHEebPm&K%E+&3ioXb!t%`|&ZVUMlVKy1trnl@z@dYe__0G0OlSd03{MdH=_z4UNpC!l zRq1k;A{56MW$6=Gy*B-$jn8VBzXd&K(#Ja8csKdc=1-G7nW@Nlv@ix zuY=;n6kk2mLgkJ*zZE(kOhuuO0n@p(lmke+`!))DER}2u3ZwzVp?K*&q8Upb9RN&1f^Us$1+x=u+xNgaa`Y|D05Ga?vhFRqu~H)!pyYh5 z4eys?j5LrG06BrS5-|t(Y$As9^TyliD8`kc-QV5`M?1ezxo;TfEFlO8lSPq<+>Vl6 zlW(F3sYF0(QHU1M`CVNpvSCfG5R@y*dtgXRxN$ym9VEYu0h#UKHRrT(H1}>RofL$? zY=~k1bgTVcK>+UF|MZ&T|A#;S?bZ!+9R%F}_VNUUd3gE9Q<^{ ztiH4vDnF)>8s$Az3tzru66DmBV`8Rs&12|E7HafCp^nUh#Z4jVsAyP|Y&5O~jVt-> zVEncTP#zgVAjc1N@eD^|ueO8g_}?*?Y2w#;r|W3i9Afk(BHBc+DwQ@42ZJA#+4}b3 zcJy~mCFvDa0RQj{zkP#Ol>+X68Pr~9o2BWSC{i*^9Ky17q`DT@G$9n*Ou@30lGff` z=yqt8)0)1@JtpQk2?xf2$qh9LCzKX!oOes{Us3P_hHY2_Rru^yS^;xase(+6ECtVB zFe@J})p!#x)4Q8$n7A>wi^3uyQpg;Wy7Ojp1rs|$jY7#UF#Z8bEZRru{1zo%Z6Yv4 z5y3Ur9hC-Cns$Dej1r`GR11tw6ArX+yc8b=EgG>R_s3i9?>Z6r?#*9cSNu~Q@Ky!5 z5&}MaItfgM+qT6Nb3}5#vx5qGd|u=)7N~6J>!@U?r5D3IdvX1P8pfXNY)&YndJQ~e zWot!Z&pKI&bZco@IEwhTv_XDQ`xCUeKYUvpWI|lgLc;M_xzj&Ca!U(4=*)g2_ucJ~ zyUkmcz)Pe2 zr_(W^Tu%}cdys*n!UiFu!5j;y#gmNnIPGPeFJJ&kBLtipS#~&{m7ul%AVSeCko^wy1P%T%aG2+51>pHPgVezbYo(`6z{RK9~|_V##`2w9=lCm!Gp zm%KRI@P%}S@sc{(B1FfTLcUO3G@^mPE2DF+j##XozkHzL-`(Af1APBu^X~5NuP^>D zBLO!KaJ8EI@#DEQ=6GQeMp~9;bDZNa8cS<$=joj;C`{|z#1CxJG(xnr^hY>cUC7&K+=2ixU)SoNK zQKe^}5GEqR610xWLtUEr6a?;&JUCP=PV=adKjgy6K(J~~#%0vo=e(ufUZIeF$N;sm zY>-_r=d+g(OR;2rg6}Y4btR9F_x}n18w7lJ{(tkzjsX9t1KxIoUybEd3ix)ixgdkD zg9d8E5d1fA{OV(7^rc!6d$jA|Pft3u~bUnq%#J(vwMb*fHVft-Lg21uyA&l>dMV94D_ zW@Cq?^e%$Q9XzKILh=$ekxRUgXNNSwajh$=`=EnFSO~_G5un=?@(QPvrUQqJSc2&F zbP@k||MhcwDE=n^ynpl4YmEON{`B_N3~+57@bdh>x&Li26!a_6;;(l8`0La0>$$14 zPt7n`ndic-X2(dVp@j~?9X7%6wcx!avWQE;REMEDi$e;Dx&*zV{rQT9BS_*SE5UvTgP2I z4JA5@%$PTz@B`6|XAx(k%l_!Ba{$u-^;{Ys;xIa9fj0-KLFbNwfslln z@4)x_(xi@@vtXma+z%zZsrc*>YzjyZq2b-wK*1RRE8uW2tCYi$z{=q%0uh2!+#MIm zlP5Ui#JQu(ik3MG{5z3gi*O+Q@jUD6N&(+(8ZXoUoX-Out{LGkfIqDGo7*?r)FO@v z_1EK+jxhlL7`_ccS{ou{1e4$sl>7`ZCj)^rD^y#r898m4^~rpVrtg!*(^}cM-%mY8 zp2TP*O4+)pb8Qr8!&vD`%kW#({*J=0Qj{PDOrAsLA_2v96yQM49jNgW=u1={`H&CTJ@ zuj>N&(}nY&!>dch|G6*t`q_Oq{BgY>Zf@UC92W|OlbLiheGKgb^-%Xc0KHUWw9KNf zasiyyArlS6GvPEaM=Jt!zY*Z@_0wxY))CEz_j`+4f_euD!Jz=hY@8rG=mDA`P$`Ja zYJbFAc2JX{oa50S06eh)Cv+shFPES=Y^EFtAmDs436egGpk$Fw2n_RH0NUOOuT8@F zp^`GU*jkmMHl2gOsqlp=rGP0?img~+pwllmw}&4OhbslVKmh#nhl?cuJPdICI_Ob)Wm#OPeF=eeEksXg#8gk(H+?j0|!#Syp&u)FD`5%W3L;CxTZ) zPp>-xIZ;tLq$QP=J5UbNl(l0Bx1VzilTPmv^#=Ybhvy`S{KR>(TmYnnz}=X(MD*bo+YMmS6c1F6x0cMPx#P!+fo%RnzA#+Z}dfG+?}pk9MyS>N0q zzTvtqlrL@@-~Qur@qZl{{!;w^Mgg~XpH?Q#*w!g)(JX^eud!4wO&xQcJr>i572KKk zuR8#@pBgDua|Z?iO1*u0JK8p7mISch#@fJy_p;r0gJ}XnuUqxwStk>Po*gqm?jN1V zJW4O|UOk9^fGG76@J_Pe`JCUeSWg8-Br8EFyqp8zkBV6auy$Y&NyI`2UXretPq~m;Kid zyBRp*Z_JL9_dvs|ZLKYm$h`+Y?fBD&p%RY~lfMcW^H@m|dVd((Y!Qcu2vzT&2M`B5 zdf2+>+yIUAqUTD=#GO9R6gBssmz0u|(&d!1Ww`4A{@w|zxgrsqeWFX&Ra4*sYppsi zENVgBQPBi~0SUmN-6wfjY~VzDnk@6igHXXF0u2mCmDiCA!6WJCVL#t|UDwP13xLbr z0sJE$@Y)mb68QgqL*Q=TpZ2Y86Xg2(fc^M}G@tiv-E=k*lzCpH<^ueAY1)`RBc-6c zrBzJuzFIT4(+-I5I}~{^1PqS?F-F18m&*11Db|u1)MT%+e13E`8v)flINUDCivrib zEQ^!b0q&(xi?Re82XSL?S(v5GFdPrKSVFV|+$X_gD{MJ3?lM#A$mRql$u0;C{9@cL zmIsg-HlE(x-roFpxKhANHvn#4T{iyDO+i-^@M8GGdcV1QhcW&JBRpl(V4HI=rO)+Y ztGa28Ekeu`O$^lb4yrtMt&PpxM9)VhPS2uCff65UYwM{9O;jjd@=Ui6vp+mV%pi}v zY3I!I+n8uUVK2bp3o0-PBol2oW5N(kC?-)AIlRJVhU}XFF(^vQYuMF^)2H@X6TsQ%o!-1|J+Dq|&zHWbc zu0Dwdu0LWD2hI-Z+w0G3AKNgDDJGdVKmx`znUrYVaL!tt%@-|wBUut^4H0BWKLLx0 zc~V*<9CsH+)f}rheVPIY;8#OA=5%^WDlh~13l-|RuZ78oVgEK0dkj(pP#F}k9C7-m zNRE29939600v%}r+;GnRmTJMxK}HPD_w?{SWlA6cL~Qf(-Cu3}Um4)*>*wMPz;A~E z4%d5ue*^x9^BAGckWqXc>(E5U63hDbe1TMA&n< z+in}l6^zIL$r|a~ao?xAr!S+i$J{u!Y1o$J|NpL>Ls_=G$R@G04=b4SAP9s==z&$I zsxMW@0$c(>gdn`86#{n(1Xf59WtxxU_z%(QKmkfC zETT0^2M|6|)9>!$;!FXLTLJvXlP~~h6Yz(?{}wNoFJB{~Qvz8>5XD#+hYbX6`Ei+Q zkV`FNWr0=d6;115F+AY(N8xQ#103bnNxIvEl3=M$C}|MS*5>Ha>jf2r)5niB;E{N4~(JE`y~-f7?unsiHxcwah2up#ALJq=C^%pBV+eQw3cE10kth zgwK;MZFzmW^Z$4bI2(W`LI9_DurmWZYW&|V1-#voqC-4}1~Id3SX9McW;$&Y< zW)K6~eiZG+Z&xYY1_sJl+QFERN(5ByuCMr4D$4|hQo1=Z!p-%pn7~hDv=x|)QXx)C zutEhy!hoj>0MX%07do&A)cu*WvFJcb2r>Jts4j?0T-IAxXW$CcBm-kL;RD>NDNbo?l48Z8*0zjB`w-f}WfLrOpy4b_J_akvyi#GxX zINE#WyhP+@wk*7u)|?xkuDqwPeuJ{0p-FH`A3sp^XJbN06%|!=IFnE)2o5;j^`sRQ z3exf+FdH}$fc8WH1+7T6KALX1i!-n5wlWQlhf0NoQt?yS&jq1>|A%tGPx(E1e(~&S z;s0y`9*Y0((f@@4_LnbT2gggcgRM6L)?snF2R}``RS?{%Wo@nU3Y>wPW>-X_3jrJ$ zW%wJ47I1*VAm=$+3K|0oX<A6#s1Z1I3F9LRrC&Nt-{IZ<9H<44-!XVg2!#j+ZX{18xEC>xv&If0gPMja3ZSI85~f)as30-{#T zGVY|CwL^Lc%2Q!L_7RxM{fd8%kpVkh*{foh8`2Xd!zpz`Uyt( z>V9FH`}&=W1=6d%t~_4$HL5#MBu%b zF;c5lW_@$5B~VU(aUua~d0t{|h8zZV0)bpmGw_^@b1mZq*NuCgavo+YXr{Wp8p+SEp_L<8R4+}ayG z(c6+n8qJI|@rFD|HVgRw|E}A@HrU3_F6@^JwlKD9ih`la=kz(QUS;GQFKi<<@` z3j82jFa(k_;}~Mbt3Vh$sAIb36|5u;quM48KV>li(nzXK_^tIn#Y9tQf@sauV%GPM z;eXFzdpY1)D&S~7NiWm@9JdMhH}Jp5{|@@s`02lV{wp(OBLS11Qv2g38zuAL1@_i8 zR3gQt)Fj2aSI1|CEc~4Gog`h3fw$Cm3DHp7c6ZC2fZP~ElET4h4lCBVBa9=ff<_A7 zS}%#11{s-UhJ)V+H9(mmjR~}EwV3KSt%*W=^|7BET<5Elz`kU_GgQFI0K5njaB*?M z|1-q@O8f7RrL(_^RaRr0xgT;WS8d6vLxO? znbA1+oN6EfzPJ%g`KGBBp*9vqSz1H@(;*e$1)ih}pcOjb2!oNE#)u-878?WPKwU5j zxlm^VX-CVKCH(v4cYja_+%wz*1w2Cq9LWxV7i$3SYJuk$M`!|`3;(U6**^5IA=Q7p z?hutSS>&;BT??TcB_!+qKZLQ!AHF84Ji2ShQ)C&itqs1UO5=zKRlNDEHq`(CAOJ~3 zK~yWHqtr_tu5Sck%m4udN_EhfW9F4^rVI@Ilq!`&;{<6l2?k<3;2fT0#sQpw5EeYyBw&I!0U;r}NGejcL#WYhj1 zfd3u5>g%Vuq?}u>unCS>p4u#>C8Q1!XylK}Wa|M!Z~$#ic^t3-zd+=0J-0X|?^Rm} zw=R;!53Pb@f&tF&hI~^>NbUR+RBidqeMXHD#woA0FcB$*Si|d_55ss6nsO0xAzEF1 zSi*k={5AIb$v!D?KG`IJt(^fU0@&IC@c%vs;BQU95w-?+Ugd8)?SK9l{P$?;?_Op< zItD=)D+LfAyZIuc5IK#SBO3jfYF}R|8B*|Eu8~d$p1R$ zv+f_3cna2EVjPW#h%^y?2`9o#g%hHl_;tt^WkLkK(1<$d*GMU*0;BPJh5Q}-`^oOt zehS#C3v3=2Ix)b;N9o;vetq+z*YULmc%uA&ocPt{p z<=kNlu^a-tsx5w4DQ!~V6F?QT(zhkGsr3jk+IB;RHyAryfB@=LP(U|RsO@MYG?82g zL68$u>M^Dw)ME&Z2&QQ?C(07g7GVAz4HSn6SJl;**_}G+R}`?m$qh1C{TGibgIh9S zNr@*0c&GsU_V&#SU&po~@Hz7TCwaeqO#yxXV6E5C`gq+bBU@F6iVua=qq;x`o}!Y# z5Wq@fi@9valyj0bYY;U8E*MJU*jY-FzrGPshi0m|1k>PvK~PrO)GnZ5ioBq%(ZnJ$ z)l!8f){O#4p@`v68KD^?+9fdmYxM7uzyk+N_6ULV$>e;eB5?M!egUt%0`PJxfSa|z zi(}dk^smDIPvE~+0FyPm>dX1$&w(IEsAek|jtI>&et$jyWn;ch@bAc`-^0@}GI|2iQxx zxG~ad%$cB`0zEKhldKH|BdZNQ!2}pa31coq;)Dqj1s&?>z&aFAI6wd1A71xNHR%sl z1rF>AI1xZa&2PWHc?Ik3WGHn^HLxizurVd+0%h%cRK4$%FR-gWk1rDSGHupY0F%$5b69AL9Z(hav{p?sw zz>~B82g-gMS-->RpWQubN$_^)#v+Ws6h~F8NLO7TT+)aHu7kqM?e&nok{n6if&oxt zmz4<~x#s$d6%! zv9uwTxrh`(Dnc1c2r-pqFf;e(cmF--bC&mh&i1~~^SqsX-N!pgd6{w4I+dMd{JBuS z@)ILC&-~ZfMgARqx6Rq+OoCMZkFMU_*6U3Lv&oy77OSeZKzoA-1$pF{Hw50~e1Ja@ z`fNThswbphM|;&-Q5(?t+%xCJ-{em`B0hHYS#|TM&edx#cc%;QNOtl{GQH0b^LP`R z7^^nJF}7_V0wSH|)5?z6`~{{OC9eb%;zcgJSOI?u)yXOyg^pew&gEpOcnG?p&7N@1HvV2Eh-(k zhWCc6VZ`ux(NzAi*EPqy~`(Xe>r$UN>v4#vC{v6JxKxYf!Z z6SI*F?_L}BcghhE>(E`_S%JzYxO&s`yo#={y~|uPdRA1mXKhB}==`tHXy^A{zD zI>3KE#v3|QZ~XXt?DtdiAz*3_{hxmD%l!J|*0-;37&je#McchjR_oNq&GUVCe+$F5 zUvu-IZTVENpD(kWr|)*jiC|gHPGzqUv@K#r{h-$i=2}A5!dbF8f@%xrJLvN0&e-1x zCHR8(*p(K`=l4#e`a_nk^9E%V+a@r4^qQ6Ywgsz=_VBM)Ql6MV?WnfQ_I5qX?6-fI zj}`cmDVtoMm_ojiOJ}#wp@+Xr{R&s*Q%sI{*-6J{{;t?-VG`WcE_rP|otUPP>9wiC6M3P7 zoHxW=xWDhJWfd~dob_GT(mAQnjKyXX&2y>P)UPY@j?1h0;Hn=Whu0%56u3g2Hzy-5 zt6s9*HdeqF_jpy0ojbXWuVv?}cBs_94>6zEdbDl_Eqbu;a7x|wtDn>VewXJRQhwLG zBK29ye{XrCIvzDnll5-j;#ntpVe(sC?qq)^CelR8tnm{OSJ&>@A+z*aWFW24eiwFH z8MD+&wBiIPYFOZ7w_{r-(NDjRTY*J$mQo@^^XU<|#O-Umws!0|ka%bBG@wS;Ek9jQ7LMgQAoL%>C{1A7; z(&^3|H1%fwKZ|T}>XW~8T<_B{PWfH9>+`CbtHW0OjeLVP@{WT#quirK~UU zQu|}}viG2#TcO_m%h$4_-X_aqGV2U8Z6z^w7fukv&KrMg{$TrTo!+jFEQC$o<(ueW z!Fk3w8nq;5Hrv*^BiOVh(M z_#x6Ot7h`0|JKB9W1QP4d(FyarRce0maIN@MY#T5Wr>UX&ncLAYHyLdba8kDyS@B_ ze`{#zM0M=BopN>^&6GxJ`>*|Hjb+BlWaz2Tk=>ARDInm;nUQmWV?;?%U(2nq6yN0a zZ=W=sROj#ArzW94zD&AcoKCos;D=|-3I`>m@Uw2$aW(|mM%7MX7xuE{D71QARMEEd zvFLDM+J4?}OrEDd*kga-R8YR(^q>A3b;I0jY(uT4K4Ii=0A8aPL(pjVK)zfsMZQp? zK%Ry~?nFNS-yhb7anW?kqI5lrF1C2eHEJy6l0Srk7lxctOkp5gNDcqM@VINbol-jV z_>#f!K82=1(p3Hm9?raT-{g z49PQ_cq3IqhfI4EPlc=TjwxA#A1|QNb*gAr{R~<)b!S>+@uUAXQGC#FPK(iv`-$D2 zI(VP_+YT;HG)-D(Z?tEW*6PX3in}VpzDo~$5r4OR8~LfaSfdcw_mSh8bRNf!(|)-O zTrlbP6=?DMH~n;%%lm}+S+$$#B@>(uiyvkN2Jkmg`0s!~*BqH$HN>4o`(L1Xb*%d% z=5Rdm!mVfPscyEbso~>+uN-VB``(%jx3K$aoit6)U5J?xeIxps6q%4Pg)4QPc!9S@ z_AmxK3k3PwS!#l=KtXgc=PEVr58Yi^u$j28f94(5I1wr>I}!q(ix`zT+X0ancJNPI zmB7LT-BZbx5u5X63Q+<}y{!4*?xTa-QvwIhY=7hyv~SV5Co@ad@o3ulqCzLq?SQ-KV$taYV$XACoubN7n5}FwE;*^f4mv^lZyON_c-Dy8 zR3_%pfysH)acH`GA$0SLi%oQCVW^;wJ1vE`S*KulC(q^R;LCSi23xjw7^ObFB9$X199w_EEvYbZ)kZBzlHc3B%(L45T?FxbazOP%nKDmDle@S}26T?9mZmA!A#!y@*Uim`X*kcG zbAY0JK*BQB^i9{cU)hCln6e$cG?yEHcdJ11;>M^aZcV1Tfb6k!m$taguhe|L)vsiA zE~oG&gr`IzGc8CHDOW?vYN?0Qb`tb`>;cPW&bbDqO^H=@qIqLN)p|5{exBE#^E0%a zURSa2@l(d?kijPb(JrlKL1!p%-MZ`_i(mfP57(tgzMH~zLAyVkB%ASB52S=Lg35iT zSeGO&?_uUr()QOzr@_A(c{=!(TVUiQh)P5JUYyMT4w=ZI$#eY&c}vmh+$-au_9%76 zXYC=WgyzAZntf_s+-zgn++lj^RBZZUs&p?X-&Xxj4Urh3xT;)j*HDNz}+@>l0lEw6kSCOFz{lfCZU zd;7;pi=ob;1oCFL`E16fp9Jd92lwDBgy1O|>u2)(|7KCV3j9v$Kv(56KWRQ&k@1~? zG`K+;bK`UMEMq}5k437&@+;dbt3sg=?(xb;w_}~d_t+H%YA~?E`-VzxuWjTl5N-U@ z&7}P+CllN<>C~Ug4zs2D3I4RU)XW8BJX^wYju5dGpFuU5gG%WhlEM}O@Aqt_OYL$5 zvwNQzqPiBmQQ|4gqJ@iE$>>w%r3pE~O%5Fy@BMbNGV;m$PD9Mmj|^%VHz=){Q4)aP zcG;11u%2?o+aKKC@AtUk_czJ~B(8pqLn6logAFLxP)~R#=FRU(u-e5-I}P>%!F*S$ zHms`34w8T!0zWKF->m7|TuD?@+{1YD%K`mub3EiKv8eQ8F6^ha9h2=2_Fj&Y z2pm)>#YNeVd*8erAA7Q+-&$vj$ssmdr#(c~_6pwUQoNGMSMV+gQ5^-W09kQR#0kN+ zvmglzTiowqS=~jKpC7YA>#=}H42pKY`ZQIMt{V)de}+YLW*y!(H*Ru)ydy!^=#tr; z^x|} z&DrVE#*azK{&vB_>4fMyLU9fxy8^62&-^L)v{7V~3w+}9^Y`te+i0?C79-Ba#-EBi7ZDpJVzxw>=Qm|^B3{qHZj3!KYy;xKaQ*-q zK=9`6w~QEjrI7x3*1y#z3?DpW@IAOQL!T8>VuU5wZGSAjxK_OVbG)Jmeit4e9Vr+X z<_kJI7CMFB-!HtIe;1TIAFX9vlim_+uAWiwvhEMGpXS7D5`j{TuQgbfw)&n~@)e60 zR_ZBeM8nW{C^RSqmM`Ns(e8ycsqu6C(-+qruwKXgcJSEZ(fD&wQQ`6D{tlchkBJtF zSvguCcTG=mceRAQ&Q0$a9brGvWqS~5s&3)%9%YXcl*j8*>b`>&ULgFw4-o8y-iSPa zHz)>-{vY2MCG<7oUqkd8@~V~^hQ9OE!UD;B@QaDvZ2@5Y__qkE?Aq2 zHprtc$|9b8AMD{9tm`-LireY1IXMWl(l&c0J3^`p|?Xx*F5^h^i z!bgQ2{R5rY^SI!fna!{V{_#V#Pg2>ESVf^i_=fV+@=S69cTOJNAB_Jz4vnwnoWg8R z7pr$HHcy7hoD*sbR#a*2-EKTG)RH!xk-_bwr?*Xs7fzw&M&i_ic69!dt^#}s`2co8 zO%l9T5UmIIQGP2ylLxTyPWWO-TdF17tM3Er~^E5{4GB~B_~BmR=w z#m_NOzo6BIn3l?TrzO_*&-AmxTipRDKJjdRn_i>Q$(jt~8ryqEyp8_QSz@L94AILC zGGx9pa06}l4?CE*F?PqD!jef$x;nUZ0A7^l=zweqQGwb4)Ta%te(BvK{y9|E5o-F- zuB~QeK}O4=8&cOBSXY!!k0zgCwm8Az$Y&4m2x{oy(ND3XJUiN@0%U5ZZZ6fi*PPB0c(OUjsov-weJ~` z#`>kb5)>(Swm?5Mps*W zbcuB#Ui`OBbuM*NqaX{)Cp3&qFSsImIPxn}Fh*F)wx0*xJqRjM9ubK+=DWITJJ3-> z0A3m#&L_V>y-QeAB2z(IQEzg=GBhX$eH?$7&ji9VkPcA4uhDoQIG{;&Jdst$4~e%!LBSNrN$lTi^1ndk<`b zD?S2Zu$fV4^C(M#=h{Avi2nr|1OMUpm6_;keO+aJdFEYUfE!zB`VdON6?Om2#hL;eh0iB3B*8XH=JRyu zSU+||34DYki40ZnS5zs;;S(Eb>}R|KqOs<*3AFygeHHwbO2_daGiy9mWVrWR5yhi9gq71AExerYN z25nISI;%mIn8@!6r6abOWxTy!u^KNDmW6g{F>shOnwfbL;T2HzZEzcA_4_hDtKxYo zgk6gV6;<4T5%w*PaTxU!$()Yo{jQWP`?uSS6A2%uFr=7qum>iELDP~LUX$RYNE<@d z`T%!h35d5V4gc94RO8_l2=y?0ep%uGZlG6+PdF6RvaBa1)iU=m+!+rrhZH%h5b8!u z_Y0HT;5oVcHXM8v7i=b$!HC~b+VlM`TWP{UvdCF5Ov=2X1mu^G9a&S>?94PA_W)1B zm-~V&5U-4sU!-Lc`W{HN^Tz2EaN-DDg~c&lF`o{@HpJ~R?4rlS?Np!uUV~g0$gO>z zB{@Sc;ij7*E>Msn?<#nUck`^>I*s3R1{STYM<8^qrev@b_OlQ8xSA1g0wbplKX3pX z4Dv@vk3j;oS)!prHv0&eB47yZ^1KGTz9JH)KL}frGiPc~wnxb`s07%`Os(n;X1k*T zau!x~h06#1`FiFKu zM#wu_K-(4CRcr{f^^E5jo({_ySmv|z<_sh)Ujtp}vCLQ$^q33>VbblEYJZ;OnXiFi z^rR_c1)fvMPEdipz%EH<-B#F(C@Vn6X3N;|#zdMHhyN1nfzkxt;EWCutw}ptvb+YS zRj{A;5l6a-!CmZJ>NMsd6}jU9o??l`F+GDsOK4eoawAd~h^e{?3@qidExi*f zTp^q=IvpDSj{7r-&Dlij#Xc?p49Uz!R@eg9T6_T)W(ecGz-uX5J%V6hV#0VXfKWCR zA4isLzQy@@c}+5=$^R}0BQ9H~%pG8PSf%q&g*$K6i+bGBt zOy6B$^e7m13%jMkXtcl-4cf}?!c=yz6&VORMQI=YhMZ7@dx(cJ67H>&K{iTqc7k76im(JI zOA78oeUc7OK)cfN7MhP?R6=_mfI)u*Exl^TGCN~lE_QCu)gtVcpfy<3ZuP~qaByKs z(D(0f$td*4D6Z{w2~NigB#*+a>p28`m3dMbf&KRc9&t0q*_qNdQ}Q0;87`-m|5#ZkU~R0F zNU2E2%qa{*Dp88GL(xyh*& zDHKfOAu4CB1dWHjpvYQ0S9P->H>t6Ur4VDRycDbp#&MR98E)h-C6$;Das0_UJq3A^ zdFOtx)`HK6WxK3^k4hC@f_7PY5Jr7AAiY)pDLYi`263iRoGy%l0_|Tv{<#aJv}ouy zuvdDFiK!$1*lTxeMe!QUvP`<=b*Md#@za^X#ka4-%O;Ds=bC|OfaDYjQ}po#GQJMH zq(>03A#4S)E`5P=qn*!FxmxL8J*klL8irkMzNRL|6@61ZUJWe&G5O2>d7f1T7Mg?~?<)fJ~^< ztnqxjE@pWLO7{V6^YnUA*~tyaR=n)))i7#R44Ko8;w;U;CRu_djrgkW%4(Qin!%(; z`xEcC(Kk!moU&d%DQ7m{xAT)O{-(*OLYES`{j3mwW5gJY*#q<_EnI^UK>XuRx^Bn_ zV_aGC<4gs)#K$S;rLz7CQ|GmgV~sMf_YY~)4#Bzp_>>zpN;a1j=wJ{;?4TB(c{ry8gO_&?pLo997Tfld8ppo1k^Hk z&$_DRCDsKU)wGN}&mKDiiXdl+Tz3he!88qdvs9uf%JUJbh$d(C+UT!H-U9)t z6*xmTuqu*anlTL)$=zko>523llmjs~z_Hqbd$)KuH^TuZ&@BgqL9Q$9Zazh>R7)*8 ze`%Ved)w?Ije!TF0op($9zV#A#m+5McEj;vVLguX8!(CGjx4QVFmz;y{+2bD1dSUk zcNOD%w%znk^uHjp)vrfq>fu^(^OMt`YiZv7jG!$AD9_ z0Y5wqgnO3vepChnxvNtV!;;%O8MlZPTWOo*4UdDuWz-^bC*(y;gUDXCem(#wnK?Py zc`?$8TH2Vp9#hU&j9B zE#qA$bE!@(QZ)Tk+98>ueH9;`{Ji$6qq9?`WhKJ{9tMwb5o(OsuqDM`+6#rO_x8Ne zv48>kO|*HgY>cG=?OJ5b(D?iIZ%kncx^pRLWDmPU1zIrYoW6vUEdgHm$2xVy$B>c^ z!x!?C=N)7wlI1x$O?vGaynqg^Tl55!Ens1Ph|4~dAeXQv$>7FLC|0;*&r+E*_Iamt z-v%Yh%o8x?MY{r=b%S?9?gU4P6kYxjD}ku1O!M$f87RY9vo}Mw3F6QhwI7j!cT@B$ z=vyG0h*(X(u&yhq(Ak6dQ+mwZ&=pLCJntBsWtSzbJBEI6 zP2iI3(N{3KW<9!H9K=h8mxkwD_kz-Isdla6viAV3+1J?t|JadEW87LPgc2nZnlsgd)=XP;2kcQ<06hQ|LdwdNrQ!|OwEgr>U{!^f-GOs~ir0q)W7vX1^g$_(5}qo+4wLE}SKK&mRneM$Btn z{lf{Tx}WVYNYt7ar3kb}J{@7ByC0#men_(sB86YZn``BLLq~ntrN7j`H&}BPV{NLY z-$B{oj=+}S8upZ^<3HYp2~mc?5Vy2O(0P+y02l)WY2c$aGwfwOV^jqkL2n(xFaB$W z_e;RfF~hZtt&*vcq28pYv_?jYCvkn*r& zE)q;5^n_sa;sd1J1Pg9Zh7GV0>t$1134gc4Cb;mm3@4fc)?&5(;lz3B_^KJFX4jMA z7(u0_II)=(rLG?Wj820+$1w3ttY_%1jhIHl@vArxVQFH3@hD{O!1#Ps$Sdt=C_bpm zcizrSB~9%Qm0zGP-I&SYPk&K`pDb>>!+39mjNp)9)Ng@x%8-9Zu3V9DFZz5;Q!qGy zn^9F{bV3i6e9!Psd-0e3E`F1#C0gIbJ6q5-t?29n9q^Av%h&@;k>T*A%9#M{R@nL) z^fVgF-;1&927|%0Qk<`6n5+iJ06HRVI3Zeluv%l1B9NF-6?DBR@P~F0;3iXEELCs= z1n&fC^eAt!#TBwuv=@(nZQsRMCs*|r8cUHqLf|{owDDO9JWm&1zIA-rQGVMX`eB%E z4CI&SG`lY(sC#lcDGkoB=WN9I;z1?E&kl3FOqi{seD*?icS4fnU^5fb+MP;GiT?fne%cj;+@4rQ zBhTM50S_>lo`|^(a*3~1CNBTWeHqX5^R%y60uwQe$Q@Nar)yXEfVi?V2@^+du1a`r z+hlv`>$PGrn~ct0OC2cqB=$)Hd}*d6206kq^CJjn?tj-D>#L9gXV)w{C{z{8vyilh zIH83vFH#>L`JP+Vm|U?iAO0_AOz#W#2zvDu;>@2mtm#z_9YB?u;hbitmB>qsIx^r5 zoNz~V>~mF9?&13!VJgO@>x9ZD&4IwL*O=%-GP=-6uUXLRC>ueWbot&Ap!^vsHILmN zLXl;Ku|E=54nf)bn3Kd5W_t@{xa0rT8GnF4TTso-s9gsfFz{eu7hm1z*;xc!d3|AbqDD$ zei^HCpZ**2WKqTbs?Y~37@rLGy{P;)%$g7(o{osOT>1Hg{kPR~3qi@+nDpce9vVm! z@QZLzs~P5ZTw6F>zcE>qFAzN)?TWM)PE64sppsGyA9wNI`7+UZ&2S&HguBEH(p=G;K5A+eA{tb{;qXk72%S23+0r-zxv;qZ~ti%I97jwl;-O#kU%d5F$|c$sB&8yPKu zj4`SrF3_Rk^fctwF+race~*z$5XRT1d?L_?=7e=j-d5yKuf@g1-gd^$>a3jA-^*(A zVx9U+{14r7513zpPF)wV%#F*%zc})E+DF3ED4MO#TS;~x+8&urPEDhUPnmezUiuZ|xY_(*X>$kco@1mp0=k!&xe&3AXY6 z{H}iC;{EFb)4P)c*zw9zT-zt}se=W+=s651t(()zJB${TP*jqW95!qL;!WOLao`x? zX%gI}&v|!`aUGB2pIa&OSavp8wwiuv$QfqnbjFLI)`APPj;0Y?G~)4OID?cKrCEteVJ0c1aBgtt7~1OS?k2?ji?U<^oIJMvf7L> z5BIP?Uh@Ffn)=4lds<;5Jpb%+fcI6+C~IP>`={KX_2-@d?v1QJI#R3x1=ME2rJlZ! z%E-`zGvq&2p!b!b!iNfOEr7EsarQdRyUkB??4ta%`>5=!+`P?pzy6`%^{+nNcd?Fd zn(-NsdA{U&o}PCRHQjln7yV~}d$*ykbc-BkC*`XpvvKFP33i?)XUQ~yPB>L-{OOa< zU`<>6pH=VRkob~W<~45t0i9Lhaj%ks9k8+m&1;qW;i}Dt)Ks$u!KJ00noVKYxT#p4@#N|5fQfkH zkjNeNV#f1ws|l=U-{z|u!Z?#rT(1@M&tjk)r>F5U3fMp z@|&1;6uQ1=Et_EM#xA1QU(9}0Jm>Ar6nsq69KJexlkX0E^weBW*RP6lt;#qnzC(*h z{|Y))Wzl+@72uEaI!4Sk1@B*bs>1CBr?C>LiGACO=I7rOcw)Kp{^K`N;tiJyn}Fpf zVdZZ$Bo!_&&T$)sGuIS?;b=)!#!|xY9@DB+i4V-F)bm_daaY39iyq?hf1Ryb4We4b zUKVjci?;9ettIsDO5Rtw^No3o5rXAcRjM3x0cXWM9r^+APLN;)Fk0(OtV#kci6fFtq3kpPmS3U04s}^Ll@?h!M7*SfE|PyR$Ju<(h^2H9w#6=@0*J z=mJ&CZ?>Zw;Dp4Oj9di@<6TOjKP1(ov04&QSR+5uYs25Cun*T>(hoKLbHkseOTR@A zk-5u#{r*b#)CV`1KvmM0&-n#jKY8l5;=)SI8kXCant8kNvnv-BdDzn6JeSR`J>mn$ zM0@$@xjAfIG6}w}0)GNLPI>1H_$@83^7O=}A6rwZNqE=aSNVnoQ%m5+$mllFTs=w> zW`wd#rqSN7P~^R&>QfRtvvMoknoAxB87tS@LESn3_O`u!mkDP{$M#GRZGT1^3o;&x z*Dm~PHQ?ox$w@^lje}LXDo)7V6!7`PCWzSR9>Ix0#K(tb%u!VuO*hbKDe-%n%O)1KGh*d`{gSS`nCa90zG%Ja<+iQ z+Gj|SG(i2LGvhpzx|x`!FVh!z^W}yA8XDU|;!n;O$SlJ*PmcG0{UmQAHthH;6J3>x z?;Di%I30FXTC-BaedQA~JED4@W+&Y%nfpR~yoAaN4KF{fLYpiCpS8n6a?%<6?d!uN z@RnbPu123bu`2%9AdAB8d7`zqPr}khG4pHHl0JMlh)Jfjb2eX+?{Mo_y}{ADFe))H zIp%Ab(gx9?8(P`b@!IKVq7~{b?Df#6X~bPf>0N{MkcTH<#026Wf^vK2diLxurxr#b zJhs{LY+{arIN!w(rY`NS=c;!W5LkO3s>fKL7&DOP9BEU{nnq~a-Vf`OQbCLr*4p%$OB$@n(-&u8?P*3 zZ+b!2LW6?Xe#^gpzsY`Y2w&Cd0cpEnK9H@xX=m#Y#!6evFqPt0o9!8&7&o7x$0wW4q>{B{D*w#O{7bpcD&Di$e~cCSm0NO)8upjWZcRVc zxysEsvS=!Dbk~tNPg^wKB=ijXbbFyzBDZ9jOVBE1^Gse}|6233ro1L&@%TFZxMb@L zesFhLs17lF;S@eAbhm{;z@hObHU!Ec{xg z8?cxD%*2jNU09*oI25{Yq4%}S@?f^PBe7dKbz4?$kUT%EGc9|{F!L=O{R^#(zR-H9 z&Lr$pn&O4f1r(jVQKWyCV{^@e*>%%GuPu7|P~VBCPMxFEl;Ob1hiNnVO(|P8jXufE zb#B^>a_Ic!V0&ww*D&)>iyBG&K--n8BBi>r~yv9n5*0#ol zF04{P8BekpPUIL_%)&zAkvAi$B<8}otG{H1wDb=W%${Guwft7p*MfHVDIT7>*ioQX zBxXD@;gCHJ-EUvBqIeTVKSu9!mfUk8=I|=s=d(K*-kosiQxeB!{@jqUW6L7+wF!4> zC!;#@&s}SNB+e~wKJH7|yhv^dJMm>%kM)JEcMrMz>#IlwLJhtAlnU=^qP>r4h08yu z{x&@k^AFLGgzcU$a3WIo2uqYxp9rv#DhWCJsKBDxOVl#AjZ1gCrY>7;ABesDdhT4Q zV&(!?u^KYfyWfPr-ZK_5lhGt(8-~{`l4_kFVwUc3r4}5x#%`f1Ol!QE%LOg|#Or<~ zbV4e`@w{9d1m(up&wbVs@|8#QWksmv2=Sm?G?(wd( zfh@^R8B16@69dh;+Wx7+j#yI!X`IkP8l^S%0{mu}59@h;`V1m}((==qQ{+ zXzKYM=uDE%mMNNl80z;b_B)@r#2)HNP=bPKVI)t6LIHE9dtJE@@D8oyk^g+WW%qBX zZ_ecJogN}MN!}M*%&9*3eI1d~+qhki<@QR;i4JU18ACWHjO^U=NXA9u_z3%8Mm)$h zBW%ut1<88iIwX?MJ^`_31=ygSO5o*eBM!h(VxjH$E#*8TY6V|PN4IQJ=?0U{i_ zn~}&4yPtc^?(lkAOW~_#&-TMweMC6m0o=?Oc~jtcLmvCEb|AxHT)}#Fxd*`uitdW- z>t#1-*^LF*&x~<$J%h#h;~5**kD^;Vz~zgJdcJX>{4vGXyWO#b+Ro{_egVGzhg?h- z%*>_!J-n-J^AF-I#Hq68IGZqSzLOt%$kw=xvwrW< z-i_h*#9cYFZTv!?PxY$CiJXK5cShR$iNVc(V?pr_(ypLu38rgRaJWA|1N8y=#Q$hP zjLaaG@qKme`_G)5#7X5tDFbI{W$d^92d+3QlYCG4Z;0hCbHre1GRkaR1Z4j~fm#{O@U-bBN)u$L3n1F%zOc+0mB!`rrTb>YCoL zok_>nAMLp9NpGTo{)?kWdw=}!RSQoK5B9XeEpFEFYqaI_xg_2+t2m=WmD1s88NZhN zb<8TA{#iWTTO1u)0rmvA>buH88?l7^#`9--j$n@R@=%=Ol&k$V(TA=)o&m?bz@>kT zeV!cLTAR0sUx?#<(09@BmB+|Wbjpj~=B(KkOmCHf}0qzAeHJnzjk%r)RS za+&Y|II^T=o!5kraftDT6w)?y45OVHCw%h?ZIK{{uU%{%=!MV}{_FPV_fL+S_am2I zQtglD$gV`s#4g?_-&FpvEw?wvhqS-XrZIJ?_9q_LlOP&hx$Sw1`Vc)=?wbGLADY06 z)6eB^+tY)rHozLXZ~mNKEWYXbkHP%0baPA-K!^=7D8vndqEZzry9`}4WmoJxp zZU5Ki4}1FTZPwZDN4fUr{*1*_`G0rgUF)8j`CfqD3HW46gfL_BdQz95|pjQ_T>3k+4sJcSYAU zwEGmVX++YfETCOgb)NRXzm2yPcSCaQRZ_w^gpfXcwn5CL6mn1Du^+Nf!aA1M4~?5^ zG?8EIA3NS;{A}m^#TwrB9@|%D+r#M^PeY9#$P$~NTbI1NUgWore~h2l`z+2Ta{G4S zL)pKJZ(sEyzkU30`hx4u5Lh5Y$Hvp(yFvVWg4`e>Q*!{@R081wxl&Y;Hj&utHxw;_${Fn|an7AS0i z)f}dCLxq`7fE~9l$8k<;uHm|d_M8?9B#8yY0<8t~ytzJcScQ%W|LyCX9XDafH+{af z{Ic$&OU>N@fWDD|%%j#{@0E4hD4~t$zxqa2{4e8Q=70P-Gh0j=B>pGFHk)j`OyXbp z_!sgWe8~P8_hkxR!~ZGo0Qj83`;QcTc^}cg{P-9D$Dcvprty#XmviO=eZRl)&c@Dv z9)oJ)HR4}QeK~K@zkK}XK8X1}82Bp`5GObzAm%oo z?;Ib){GYo2r~drI$7D%-4CB9~zMPlnU;qB!$9HhH{ZzzN?r)Bd!T(c_|DwOFe`3~k zp=~j@`F!X280J4@3VGih+s5_V1f~tfqQ5Cq$!pQS{{6qCzd5fO{>NG+xbV;Er`pf!aID|QScwD4ek;E literal 29449 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 G2~7ZYM22tx delta 1873 zcmV-X2d?@_CMG;QJp23mSy@@o(9pfTy?^%h_FrFL=H}+_@9*5) z+{eeqtgNhwiHUM@alsxT1rXg#WZk^q4OLJ$-ckN*GPz1esr&@=7pYqcF`XC~_=gTa^U zeK}qHo6Uz<^MBdD#dP^T`0IL|Oh>bMwsO~eHkwXe{{Zr`9L-mm4?-);=A-4yZ$YNx z;i`{u1zHWq)87ESzKy?tctDNcek^S=8m@j?*>E)Zfy%7^SBFFYvDtToUKTwn+5V&C zr(F+cxcE|`_fhY+x0g6f(~upvy{ATB#Aq_^QK`=+On<{P57VaY2|u3v4&`tPk`!F> zFi(=;bodR!qX1dQ$-;1xARn!W^7T&}=#qjXFBN zF8t?76rz!kz@ddQLBykT?Mt-qeSh?fIK&(3K628BF+QTF@N~x$?K+Wf0WP! z_2l%|iGSsi=cx%ekBU5%oqq@TuTb`!sG-KKPN>$!n8ys+LUi6hbY?$-3K z{;nZ+tfJQD&{jp?q=_j26B&ZWqe)ub(>R=V$$!+bigF|_$;swxYSM(_N3pR^(S!5@ z=bH|l=kTGcR&miseCU0A55TBuR=Kg&;s2b(fi`IYj0m3(m~2kjnLgV$U>d?{ zw147+ljjL!p%|xEHjybnsUVoAHmi(8cA`gYXyL!O#INJEueQN31BpvsDwI8y3Vaa6)YyGh za27+ffT#q9#w6)OrfE(nusY(3iP9zpb-{fX=HFoE z!zd9e77Kt?7Drbsu%RUwsR#uS(8n!mH5<%*7~&(C&@5)!nvWXG0*XeQp5!R06o0BC z@oGMByJss@EK8*XgI`QjT?jzTX>)*$5GBM3%H~`Jp5d!u2L>;wAdZNcAk$D5=#&NJ zmMclxz$kS-mA*C|m{?(<9!V%LOxr}OfXI}B$&&mI3N9k^9UG8{NnpINP*=Agv$UJB zgk}ns734X9eHc$-Fre5#1yG>`*?%-Ml!Dwy%8lJpHpsrjuHiLQQP<#5o`sO)R)cM7 zccqYJ8CVz|_)g9I_{F4)uwP_}0D%ou))LK}m&mr_Ho{a?Cw}g`ZmT6pEAbENNI^JY zqsbvM>Q)p!BOhkrZOxWSVM>7mqu|($s=-DfFi>64UMMI<9c`w5qaYO0n12Ximm~d9 zN#ijE7*M2|u3f9!DEzhsk0hiOlo3VlOQstNk)mf5<%W7_2L^4+Yocg0N|KbqrWxEw zW$ZI{jR3#5t-?$3$hP$7EOF9Qiy$CKfjuFixmKklji|D~`ecftbOsdL(w#S$)w(_d z1bj#vYCQ~8bjfL-X{O46-hX6BoThb#3i}2#@W!?6rXgXd4w43alW7}0M%@O@nM%?} zcf`rAv&qt*sdpchl2&SU6Q>M#X*(#wTNtmN=bNuH3Ooi`(7{$08k-Tk?2e)Yq8bx9 zvyDHQOuLiwx;?`(Duk4Zt<5~LZMbnKt*5}B(xD;$tjTtPFr)hgH-Cp%Z!~tsq|O|A zmq-h{#YD_)Y6FP!$yvskMPEms9h>S?h3sH!pjF}Rig#z?-U#UrwD)r*%s*4lVI`)E zqPpy}tbhKTO}-wlEy{Ym-R_PJ)InV2>9E~y*Xu5hKVJ8SeV}Z&``vEWp0wutygBT4 z`+Xm$GwgSd9>it8JAWMNx;>uHA8lLLhr`a|e0TKd9)vQBQn#DU<#Ie;E|<-wtu0RW z?Lo*tmSr~>pd7F3@_@;Hf4s{cY%W)v%caj|Jo)j+def$EQ5IIaX>TY`n9=*s2WU%& zG=|8u@C~6nYx?E@?H=cCZ1zAR*JqvJgGb^9|E00000 LNkvXXu0mjf1jMWT diff --git a/resources/win32/inno-void.bmp b/resources/win32/inno-void.bmp deleted file mode 100644 index 3c986c74671ca9214cab500d0fc03c67f2f5edb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441078 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

    5D#i)m{AW$7%hb+Cg3g}Tm^yw7Ujp{YAS7M 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,tqikb?5T>ExS>HB}vkswmo>7w*(d1g@dsMk{5xI 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

    46^Z$ zfX3GhbcWBTUv8{nc4(0$++u)9>)NW_wrByLzLbNHyL)+JU+RRd=R1!(dNNP)4~9+e z8{?`!LOUAPYRXJ8^po9@sSrzsSuy9qlkMufzG7&cuxvU6y|wG60(@{q(4|sHJ5D&A zXY!aR{OKDv*v1(Ks|woZv^FxF7FcCfUVio%AT~Z+`CDkvM?2iYu!_fAs9ufwf8mZ! z%R~QzI|kS{uXlNZpi6;1>#+pM@Q?q#{k+i1H427wDbzb9x1U*txUfIqGn#a<|HX_e zDF0*fo8$uKm3sks{(}$i7m9#%qH~=_hOF<+W7SE$TM#!xNXZ?j*Ld9%$AyAU#hk6V z^D3E2;AsD+m07!#Te7wgH@(*lR@%WVI7(7qF|y?(ddCvu%p01H)m_%U?pLI<2{e1g zSZ7m}Zct#IXPs;gt=J7BJ@-W=$nm(x@kG$Ft|Cc`rF&j!yWE-Rh`k*bnobT_M2E)I z&}GtTj$AoeCCO;s?_9My&*QFbuDjq-^XjPA`kx;haPEr;4A>MT-0SchqE8A5|0fTj zud(|pz{%FB$26Hf9Oo9;#;wRmzk&PTb((wSB!hBJTRWAr-`B6x?$O+(3B9B&T?C|O z{T!wPTp6qa)6KN{_XLY(F){k;Y1jT8V4;rLgCy##D~Z(lJ(Cw82fk_aaHL^daCPAQ zxFYc6^$@J_7ur+l))r&Z8ZZ2RQy#}aJ+ErYcQEQ^B7! Z{; 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≶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�w0(nIXBHsO5*6Ty$( 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%0DMBXA_^`HkVaesaz1b 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

    `J_0)Iu5-gTcwlZ6=^eGD#ADCXbm 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
  • (gZMQvC^5!PBMZR5dH+N1ZSzS@|P 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

    o@fWKJOy|e|!Gqus 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 z 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

    S3a9FGD*WG?Dkd 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%0A)SCuNqbC4o_>)H2z0 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

    DbO05AlQ=vy{4~?{@hM8bIj& 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%0DE72vF+KrEw4l1d$48u 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 { process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); process.env['VSCODE_CODE_CACHE_PATH'] = codeCachePath || ''; - resolveUserProduct(); // Bootstrap ESM await bootstrapESM(); @@ -325,11 +311,16 @@ function configureCommandlineSwitchesSync(cliArgs: NativeParsedArgs) { } }); + // Following features are enabled from the runtime: + // `DocumentPolicyIncludeJSCallStacksInCrashReports` - https://www.electronjs.org/docs/latest/api/web-frame-main#framecollectjavascriptcallstack-experimental + const featuresToEnable = + `DocumentPolicyIncludeJSCallStacksInCrashReports, ${app.commandLine.getSwitchValue('enable-features')}`; + app.commandLine.appendSwitch('enable-features', featuresToEnable); + // Following features are disabled from the runtime: // `CalculateNativeWinOcclusion` - Disable native window occlusion tracker (https://groups.google.com/a/chromium.org/g/embedder-dev/c/ZF3uHHyWLKw/m/VDN2hDXMAAAJ) - // `PlzDedicatedWorker` - Refs https://github.com/microsoft/vscode/issues/233060#issuecomment-2523212427 const featuresToDisable = - `CalculateNativeWinOcclusion,PlzDedicatedWorker,${app.commandLine.getSwitchValue('disable-features')}`; + `CalculateNativeWinOcclusion,${app.commandLine.getSwitchValue('disable-features')}`; app.commandLine.appendSwitch('disable-features', featuresToDisable); // Blink features to configure. @@ -536,6 +527,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/tsconfig.json b/src/tsconfig.json index c309f5e9..88d3daa0 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,6 +1,5 @@ { "extends": "./tsconfig.base.json", - // "jsx": "react", // <-- Void added this "compilerOptions": { "esModuleInterop": true, "removeComments": false, diff --git a/src/tsec.exemptions.json b/src/tsec.exemptions.json index d5e979d5..1bfa145b 100644 --- a/src/tsec.exemptions.json +++ b/src/tsec.exemptions.json @@ -4,12 +4,10 @@ "vs/editor/contrib/clipboard/browser/clipboard.ts" ], "ban-eval-calls": [ - "vs/workbench/api/worker/extHostExtensionService.ts", - "vs/base/worker/workerMain.ts" + "vs/workbench/api/worker/extHostExtensionService.ts" ], "ban-function-calls": [ "vs/workbench/api/worker/extHostExtensionService.ts", - "vs/base/worker/workerMain.ts", "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts", "vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts" ], @@ -17,18 +15,16 @@ "bootstrap-window.ts", "vs/amdX.ts", "vs/base/browser/trustedTypes.ts", - "vs/base/worker/workerMain.ts", "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts" ], "ban-worker-calls": [ - "vs/base/browser/defaultWorkerFactory.ts", + "vs/base/browser/webWorkerFactory.ts", "vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts" ], "ban-worker-importscripts": [ "vs/amdX.ts", "vs/workbench/services/extensions/worker/polyfillNestedWorker.ts", - "vs/workbench/api/worker/extensionHostWorker.ts", - "vs/base/worker/workerMain.ts" + "vs/workbench/api/worker/extensionHostWorker.ts" ], "ban-domparser-parsefromstring": [ "vs/base/browser/markdownRenderer.ts", diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts index ee149951..9fd519fd 100644 --- a/src/vs/amdX.ts +++ b/src/vs/amdX.ts @@ -212,7 +212,7 @@ export async function importAMDNodeModule(nodeModuleName: string, pathInsideN let scriptSrc: string; if (/^\w[\w\d+.-]*:\/\//.test(nodeModulePath)) { // looks like a URL - // bit of a special case for: src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts + // bit of a special case for: src/vs/workbench/services/languageDetection/browser/languageDetectionWebWorker.ts scriptSrc = nodeModulePath; } else { const useASAR = (canASAR && isBuilt && !platform.isWeb); diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 568808c0..1f999bb7 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -46,6 +46,10 @@ export interface IContextMenuDelegate { anchorAlignment?: AnchorAlignment; anchorAxisAlignment?: AnchorAxisAlignment; domForShadowRoot?: HTMLElement; + /** + * custom context menus with higher layers are rendered higher in z-index order + */ + layer?: number; } export interface IContextMenuProvider { diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index 879426cf..6ba1f767 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 73d028e7..33f6a7af 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 cf15d43b..bf2739b3 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -5,7 +5,7 @@ import { onUnexpectedError } from '../common/errors.js'; import { Event } from '../common/event.js'; -import { escapeDoubleQuotes, IMarkdownString, MarkdownStringTrustedOptions, parseHrefAndDimensions, removeMarkdownEscapes } from '../common/htmlContent.js'; +import { escapeDoubleQuotes, IMarkdownString, isMarkdownString, MarkdownStringTrustedOptions, parseHrefAndDimensions, removeMarkdownEscapes } from '../common/htmlContent.js'; import { markdownEscapeEscapedIcons } from '../common/iconLabels.js'; import { defaultGenerator } from '../common/idGenerator.js'; import { KeyCode } from '../common/keyCodes.js'; @@ -552,7 +552,7 @@ function getSanitizerOptions(options: IInternalSanitizerOptions): { config: domp * `# Header` would be output as `Header`. If it's not, the string is returned. */ export function renderStringAsPlaintext(string: IMarkdownString | string) { - return typeof string === 'string' ? string : renderMarkdownAsPlaintext(string); + return isMarkdownString(string) ? renderMarkdownAsPlaintext(string) : string; } /** diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 6b4e595d..aea9be08 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -295,6 +295,9 @@ export class Button extends Disposable implements IButton { set icon(icon: ThemeIcon) { this._setAriaLabel(); + + const oldIcons = Array.from(this._element.classList).filter(item => item.startsWith('codicon-')); + this._element.classList.remove(...oldIcons); this._element.classList.add(...ThemeIcon.asClassNameArray(icon)); } @@ -349,13 +352,17 @@ export interface IButtonWithDropdownOptions extends IButtonOptions { readonly actions: readonly IAction[] | IActionProvider; readonly actionRunner?: IActionRunner; readonly addPrimaryActionToDropdown?: boolean; + /** + * dropdown menus with higher layers are rendered higher in z-index order + */ + readonly dropdownLayer?: number; } export class ButtonWithDropdown extends Disposable implements IButton { - private readonly button: Button; + readonly primaryButton: Button; private readonly action: Action; - private readonly dropdownButton: Button; + readonly dropdownButton: Button; private readonly separatorContainer: HTMLDivElement; private readonly separator: HTMLDivElement; @@ -374,9 +381,9 @@ export class ButtonWithDropdown extends Disposable implements IButton { options = { ...options, hoverDelegate: this._register(createInstantHoverDelegate()) }; } - this.button = this._register(new Button(this.element, options)); - this._register(this.button.onDidClick(e => this._onDidClick.fire(e))); - this.action = this._register(new Action('primaryAction', renderStringAsPlaintext(this.button.label), undefined, true, async () => this._onDidClick.fire(undefined))); + this.primaryButton = this._register(new Button(this.element, options)); + this._register(this.primaryButton.onDidClick(e => this._onDidClick.fire(e))); + this.action = this._register(new Action('primaryAction', renderStringAsPlaintext(this.primaryButton.label), undefined, true, async () => this._onDidClick.fire(undefined))); this.separatorContainer = document.createElement('div'); this.separatorContainer.classList.add('monaco-button-dropdown-separator'); @@ -407,7 +414,8 @@ export class ButtonWithDropdown extends Disposable implements IButton { getAnchor: () => this.dropdownButton.element, getActions: () => options.addPrimaryActionToDropdown === false ? [...actions] : [this.action, ...actions], actionRunner: options.actionRunner, - onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false') + onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false'), + layer: options.dropdownLayer }); this.dropdownButton.element.setAttribute('aria-expanded', 'true'); })); @@ -419,39 +427,39 @@ export class ButtonWithDropdown extends Disposable implements IButton { } set label(value: string) { - this.button.label = value; + this.primaryButton.label = value; this.action.label = value; } set icon(icon: ThemeIcon) { - this.button.icon = icon; + this.primaryButton.icon = icon; } set enabled(enabled: boolean) { - this.button.enabled = enabled; + this.primaryButton.enabled = enabled; this.dropdownButton.enabled = enabled; this.element.classList.toggle('disabled', !enabled); } get enabled(): boolean { - return this.button.enabled; + return this.primaryButton.enabled; } set checked(value: boolean) { - this.button.checked = value; + this.primaryButton.checked = value; } get checked() { - return this.button.checked; + return this.primaryButton.checked; } focus(): void { - this.button.focus(); + this.primaryButton.focus(); } hasFocus(): boolean { - return this.button.hasFocus() || this.dropdownButton.hasFocus(); + return this.primaryButton.hasFocus() || this.dropdownButton.hasFocus(); } } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index f38ab6d17c03ac7f5f88e450e177611791f0274f..872f328858f8a9c872b36ff0130e2a0b39b6788b 100644 GIT binary patch delta 744 zcmaKp&yNdH9Khduee>RQ=4CqF{?JxCn66Q2HPT;m*lcZFI5aNeW+!Ar9E25gT5V^x zB2I`VI3PG|LJ%Sn_OLEaB>Vy5;EHS_A=&JKjW_Kst%K8hhnMf?^FH5&p766LT%BUC zv@iPs$jk$X5B6#${8az8uXjNo+W`Q8E3+q0pLy~4;V}U7E4_uC_}1nR*PkuTY&&rK z>~cL?`&id$mz*IE^<=rRg*uG?&k{Td~&O*3H(tw%E4X_u5Mx)^R$o z9qd?+<19L#-QDh?x$(Jc-AeaK_xt($^ZCr%uP)+b0243){2&d$)^f?n!bq`Dp&Fck zlxT?GcfpETPU|E9Npu5@t1Ovh>Z+jnr$mHVP$Ci%CAGoAIE4_oYccgkFf7uXKctA1 z#FB|e3bVv9%2B>QJPj1Vqzgc3Fjzw)I8L2}B_jZJgVVW_cUHMlgitd~vann%L?gfM zv#?J?NFGrM#J4b2J?fVgBtwF0lI%+%i8MBj+Jp8(CdYl@k4Q0CenV?oKp zRP|32S~qfsv2coDZaiE`2f&PkqZ808V&-8Mnw2~ne1dxAJm1j&8(m&cTK#_>Som)t5>MEYhY?9Y7}YgX}r+H zvw4Ze$yfD^3_!pr#mLSe&A?!;q{nE_sLZCvWNv0|$7pUW$0*Av!lup0&X&f($I2pP z!p_6a$gIr4%*(Nyo1dRsGLJ)6O!3x*iEm8 z8n3Rz&c@CrW^ODl%FZUoXk^c1Y-DE3XriW!(_#r$CT;~L7FIh>5k^K9R;D6ONoH17 zW*qLh$SUv5!p_LUsmH|1%#^{*!tBJ%%D_+$^gNO)8HDAS_!*7Ox^|iX diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index e5b95d29..356d813e 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -61,7 +61,9 @@ export interface IDelegate { onDOMEvent?(e: Event, activeElement: HTMLElement): void; onHide?(data?: unknown): void; - // context views with higher layers are rendered over contet views with lower layers + /** + * context views with higher layers are rendered higher in z-index order + */ layer?: number; // Default: 0 } diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 60dba786..c7d4da10 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -8,9 +8,9 @@ position: fixed; height: 100%; width: 100%; - left:0; - top:0; - z-index: 2600; + left: 0; + top: 0; + z-index: 2575; /* Above Context Views, Below Workbench Hover */ display: flex; justify-content: center; align-items: center; @@ -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,31 @@ 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:focus-within { + /** + * This is a trick to make the focus outline appear on the entire + * container of the dropdown button to ensure the dialog box looks + * consistent to dialogs without dropdown buttons. + */ + outline-offset: 2px !important; + outline-width: 1px; + outline-style: solid; + outline-color: var(--vscode-focusBorder); + border-radius: 2px; +} + +.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 0c357a67..6e68f0f8 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, ButtonWithDropdown, 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; @@ -67,7 +69,9 @@ interface ButtonMapEntry { } export class Dialog extends Disposable { + private readonly element: HTMLElement; + private readonly shadowElement: HTMLElement; private modalElement: HTMLElement | undefined; private readonly buttonsContainer: HTMLElement; @@ -97,7 +101,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 +176,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 +204,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 +214,45 @@ 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, + dropdownLayer: 2600, // ensure the dropdown is above the dialog + 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 +262,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); })); }); @@ -315,9 +346,20 @@ export class Dialog extends Disposable { if (this.buttonBar) { for (const button of this.buttonBar.buttons) { - focusableElements.push(button); - if (button.hasFocus()) { - focusedIndex = focusableElements.length - 1; + if (button instanceof ButtonWithDropdown) { + focusableElements.push(button.primaryButton); + if (button.primaryButton.hasFocus()) { + focusedIndex = focusableElements.length - 1; + } + focusableElements.push(button.dropdownButton); + if (button.dropdownButton.hasFocus()) { + focusedIndex = focusableElements.length - 1; + } + } else { + focusableElements.push(button); + if (button.hasFocus()) { + focusedIndex = focusableElements.length - 1; + } } } } @@ -407,7 +449,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 00000000..3dbd92dc --- /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 00000000..39ce04a1 --- /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 dabab279..b4a7d38a 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 be6a8ab1..6b877ec1 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,6 +74,32 @@ 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 unless `force` is set. @@ -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. diff --git a/src/vs/base/browser/ui/hover/hoverDelegate2.ts b/src/vs/base/browser/ui/hover/hoverDelegate2.ts index 05b4c692..b49cb849 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 ad3a358b..ed9278d7 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 cacfa163..4081c2f2 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -160,7 +160,7 @@ export class InputBox extends Widget { this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto }); if (this.options.flexibleWidth) { - this.input.setAttribute('wrap', 'off'); // Void changed this to 'on' at one point, but we're no longer using InputBox + this.input.setAttribute('wrap', 'off'); this.mirror.style.whiteSpace = 'pre'; this.mirror.style.wordWrap = 'initial'; } diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index be273a61..672f03e4 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 4c8af3e9..2db1250f 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,32 +1181,15 @@ 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); @@ -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 78932044..44b59334 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/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 8aec45d8..eb6814c4 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -1170,6 +1170,7 @@ ${formatRule(Codicon.menuSubmenu)} text-align: right; font-size: 12px; line-height: 1; + opacity: 0.7; } .monaco-menu .monaco-action-bar.vertical .submenu-indicator { diff --git a/src/vs/platform/severityIcon/browser/media/severityIcon.css b/src/vs/base/browser/ui/severityIcon/media/severityIcon.css similarity index 100% rename from src/vs/platform/severityIcon/browser/media/severityIcon.css rename to src/vs/base/browser/ui/severityIcon/media/severityIcon.css 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 73bfe9c2..66f55641 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 9dd4bb3d..42d2234a 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/defaultWorkerFactory.ts b/src/vs/base/browser/webWorkerFactory.ts similarity index 70% rename from src/vs/base/browser/defaultWorkerFactory.ts rename to src/vs/base/browser/webWorkerFactory.ts index f6054bf6..0cd7255e 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/webWorkerFactory.ts @@ -5,12 +5,13 @@ import { createTrustedTypesPolicy } from './trustedTypes.js'; import { onUnexpectedError } from '../common/errors.js'; -import { AppResourcePath, COI, FileAccess } from '../common/network.js'; +import { COI } from '../common/network.js'; import { URI } from '../common/uri.js'; -import { IWorker, IWorkerCallback, IWorkerClient, IWorkerDescriptor, IWorkerFactory, logOnceWebWorkerWarning, SimpleWorkerClient } from '../common/worker/simpleWorker.js'; +import { IWebWorker, IWebWorkerClient, Message, WebWorkerClient } from '../common/worker/webWorker.js'; import { Disposable, toDisposable } from '../common/lifecycle.js'; import { coalesce } from '../common/arrays.js'; import { getNLSLanguage, getNLSMessages } from '../../nls.js'; +import { Emitter } from '../common/event.js'; // Reuse the trusted types policy defined from worker bootstrap // when available. @@ -29,7 +30,9 @@ export function createBlobWorker(blobUrl: string, options?: WorkerOptions): Work return new Worker(ttPolicy ? ttPolicy.createScriptURL(blobUrl) as unknown as string : blobUrl, { ...options, type: 'module' }); } -function getWorker(esmWorkerLocation: URI | undefined, label: string): Worker | Promise { +function getWorker(descriptor: IWebWorkerDescriptor, id: number): Worker | Promise { + const label = descriptor.label || 'anonymous' + id; + // Option for hosts to overwrite the worker script (used in the standalone editor) interface IMonacoEnvironment { getWorker?(moduleId: string, label: string): Worker | Promise; @@ -45,11 +48,14 @@ function getWorker(esmWorkerLocation: URI | undefined, label: string): Worker | return new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) as unknown as string : workerUrl, { name: label, type: 'module' }); } } + + const esmWorkerLocation = descriptor.esmModuleLocation; if (esmWorkerLocation) { const workerUrl = getWorkerBootstrapUrl(label, esmWorkerLocation.toString(true)); const worker = new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) as unknown as string : workerUrl, { name: label, type: 'module' }); return whenESMWorkerReady(worker); } + throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`); } @@ -113,37 +119,52 @@ function isPromiseLike(obj: any): obj is PromiseLike { * A worker that uses HTML5 web workers so that is has * its own global scope and its own thread. */ -class WebWorker extends Disposable implements IWorker { +class WebWorker extends Disposable implements IWebWorker { + + private static LAST_WORKER_ID = 0; private readonly id: number; - private readonly label: string; private worker: Promise | null; - constructor(esmWorkerLocation: URI | undefined, moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) { + private readonly _onMessage = this._register(new Emitter()); + public readonly onMessage = this._onMessage.event; + + private readonly _onError = this._register(new Emitter()); + public readonly onError = this._onError.event; + + constructor(descriptorOrWorker: IWebWorkerDescriptor | Worker) { super(); - this.id = id; - this.label = label; - const workerOrPromise = getWorker(esmWorkerLocation, label); + this.id = ++WebWorker.LAST_WORKER_ID; + const workerOrPromise = ( + descriptorOrWorker instanceof Worker + ? descriptorOrWorker + : getWorker(descriptorOrWorker, this.id) + ); if (isPromiseLike(workerOrPromise)) { this.worker = workerOrPromise; } else { this.worker = Promise.resolve(workerOrPromise); } - this.postMessage(moduleId, []); + this.postMessage('-please-ignore-', []); // TODO: Eliminate this extra message + const errorHandler = (ev: ErrorEvent) => { + this._onError.fire(ev); + }; this.worker.then((w) => { - w.onmessage = function (ev) { - onMessageCallback(ev.data); + w.onmessage = (ev) => { + this._onMessage.fire(ev.data); + }; + w.onmessageerror = (ev) => { + this._onError.fire(ev); }; - w.onmessageerror = onErrorCallback; if (typeof w.addEventListener === 'function') { - w.addEventListener('error', onErrorCallback); + w.addEventListener('error', errorHandler); } }); this._register(toDisposable(() => { this.worker?.then(w => { w.onmessage = null; w.onmessageerror = null; - w.removeEventListener('error', onErrorCallback); + w.removeEventListener('error', errorHandler); w.terminate(); }); this.worker = null; @@ -160,51 +181,27 @@ class WebWorker extends Disposable implements IWorker { w.postMessage(message, transfer); } catch (err) { onUnexpectedError(err); - onUnexpectedError(new Error(`FAILED to post message to '${this.label}'-worker`, { cause: err })); + onUnexpectedError(new Error(`FAILED to post message to worker`, { cause: err })); } }); } } -export class WorkerDescriptor implements IWorkerDescriptor { - - public readonly esmModuleLocation: URI | undefined; +export interface IWebWorkerDescriptor { + readonly esmModuleLocation: URI | undefined; + readonly label: string | undefined; +} +export class WebWorkerDescriptor implements IWebWorkerDescriptor { constructor( - public readonly moduleId: string, - readonly label: string | undefined, - ) { - this.esmModuleLocation = FileAccess.asBrowserUri(`${moduleId}Main.js` as AppResourcePath); - } + public readonly esmModuleLocation: URI, + public readonly label: string | undefined, + ) { } } -class DefaultWorkerFactory implements IWorkerFactory { - - private static LAST_WORKER_ID = 0; - private _webWorkerFailedBeforeError: any; - - constructor() { - this._webWorkerFailedBeforeError = false; - } - - public create(desc: IWorkerDescriptor, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker { - const workerId = (++DefaultWorkerFactory.LAST_WORKER_ID); - - if (this._webWorkerFailedBeforeError) { - throw this._webWorkerFailedBeforeError; - } - - return new WebWorker(desc.esmModuleLocation, desc.moduleId, workerId, desc.label || 'anonymous' + workerId, onMessageCallback, (err) => { - logOnceWebWorkerWarning(err); - this._webWorkerFailedBeforeError = err; - onErrorCallback(err); - }); - } -} - -export function createWebWorker(moduleId: string, label: string | undefined): IWorkerClient; -export function createWebWorker(workerDescriptor: IWorkerDescriptor): IWorkerClient; -export function createWebWorker(arg0: string | IWorkerDescriptor, arg1?: string | undefined): IWorkerClient { - const workerDescriptor = (typeof arg0 === 'string' ? new WorkerDescriptor(arg0, arg1) : arg0); - return new SimpleWorkerClient(new DefaultWorkerFactory(), workerDescriptor); +export function createWebWorker(esmModuleLocation: URI, label: string | undefined): IWebWorkerClient; +export function createWebWorker(workerDescriptor: IWebWorkerDescriptor | Worker): IWebWorkerClient; +export function createWebWorker(arg0: URI | IWebWorkerDescriptor | Worker, arg1?: string | undefined): IWebWorkerClient { + const workerDescriptorOrWorker = (URI.isUri(arg0) ? new WebWorkerDescriptor(arg0, arg1) : arg0); + return new WebWorkerClient(new WebWorker(workerDescriptorOrWorker)); } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index d4a40903..98bf168f 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/codecs/baseDecoder.ts b/src/vs/base/common/codecs/baseDecoder.ts index 586ea61f..e483b260 100644 --- a/src/vs/base/common/codecs/baseDecoder.ts +++ b/src/vs/base/common/codecs/baseDecoder.ts @@ -41,7 +41,7 @@ export abstract class BaseDecoder< private readonly _listeners: Map> = new Map(); /** - * This method is called when a new incomming data + * This method is called when a new incoming data * is received from the input stream. */ protected abstract onStreamData(data: K): void; @@ -97,7 +97,7 @@ export abstract class BaseDecoder< } /** - * Start receiveing data from the stream. + * Start receiving data from the stream. * @throws if the decoder stream has already ended. */ public start(): this { @@ -121,7 +121,7 @@ export abstract class BaseDecoder< this.stream.on('end', this.onStreamEnd); // this allows to compose decoders together, - if a decoder - // instance is passed as a readble stream to this decoder, + // instance is passed as a readable stream to this decoder, // then we need to call `start` on it too if (this.stream instanceof BaseDecoder) { this.stream.start(); diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index 45eba8e4..ee207309 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -1137,3 +1137,179 @@ export class LcsDiff { } } } + + +/** + * Precomputed equality array for character codes. + */ +const precomputedEqualityArray = new Uint32Array(0x10000); + +/** + * Computes the Levenshtein distance for strings of length <= 32. + * @param firstString - The first string. + * @param secondString - The second string. + * @returns The Levenshtein distance. + */ +const computeLevenshteinDistanceForShortStrings = (firstString: string, secondString: string): number => { + const firstStringLength = firstString.length; + const secondStringLength = secondString.length; + const lastBitMask = 1 << (firstStringLength - 1); + let positiveVector = -1; + let negativeVector = 0; + let distance = firstStringLength; + let index = firstStringLength; + + // Initialize precomputedEqualityArray for firstString + while (index--) { + precomputedEqualityArray[firstString.charCodeAt(index)] |= 1 << index; + } + + // Process each character of secondString + for (index = 0; index < secondStringLength; index++) { + let equalityMask = precomputedEqualityArray[secondString.charCodeAt(index)]; + const combinedVector = equalityMask | negativeVector; + equalityMask |= ((equalityMask & positiveVector) + positiveVector) ^ positiveVector; + negativeVector |= ~(equalityMask | positiveVector); + positiveVector &= equalityMask; + if (negativeVector & lastBitMask) { + distance++; + } + if (positiveVector & lastBitMask) { + distance--; + } + negativeVector = (negativeVector << 1) | 1; + positiveVector = (positiveVector << 1) | ~(combinedVector | negativeVector); + negativeVector &= combinedVector; + } + + // Reset precomputedEqualityArray + index = firstStringLength; + while (index--) { + precomputedEqualityArray[firstString.charCodeAt(index)] = 0; + } + + return distance; +}; + +/** + * Computes the Levenshtein distance for strings of length > 32. + * @param firstString - The first string. + * @param secondString - The second string. + * @returns The Levenshtein distance. + */ +function computeLevenshteinDistanceForLongStrings(firstString: string, secondString: string): number { + const firstStringLength = firstString.length; + const secondStringLength = secondString.length; + const horizontalBitArray = []; + const verticalBitArray = []; + const horizontalSize = Math.ceil(firstStringLength / 32); + const verticalSize = Math.ceil(secondStringLength / 32); + + // Initialize horizontal and vertical bit arrays + for (let i = 0; i < horizontalSize; i++) { + horizontalBitArray[i] = -1; + verticalBitArray[i] = 0; + } + + let verticalIndex = 0; + for (; verticalIndex < verticalSize - 1; verticalIndex++) { + let negativeVector = 0; + let positiveVector = -1; + const start = verticalIndex * 32; + const verticalLength = Math.min(32, secondStringLength) + start; + + // Initialize precomputedEqualityArray for secondString + for (let k = start; k < verticalLength; k++) { + precomputedEqualityArray[secondString.charCodeAt(k)] |= 1 << k; + } + + // Process each character of firstString + for (let i = 0; i < firstStringLength; i++) { + const equalityMask = precomputedEqualityArray[firstString.charCodeAt(i)]; + const previousBit = (horizontalBitArray[(i / 32) | 0] >>> i) & 1; + const matchBit = (verticalBitArray[(i / 32) | 0] >>> i) & 1; + const combinedVector = equalityMask | negativeVector; + const combinedHorizontalVector = ((((equalityMask | matchBit) & positiveVector) + positiveVector) ^ positiveVector) | equalityMask | matchBit; + let positiveHorizontalVector = negativeVector | ~(combinedHorizontalVector | positiveVector); + let negativeHorizontalVector = positiveVector & combinedHorizontalVector; + if ((positiveHorizontalVector >>> 31) ^ previousBit) { + horizontalBitArray[(i / 32) | 0] ^= 1 << i; + } + if ((negativeHorizontalVector >>> 31) ^ matchBit) { + verticalBitArray[(i / 32) | 0] ^= 1 << i; + } + positiveHorizontalVector = (positiveHorizontalVector << 1) | previousBit; + negativeHorizontalVector = (negativeHorizontalVector << 1) | matchBit; + positiveVector = negativeHorizontalVector | ~(combinedVector | positiveHorizontalVector); + negativeVector = positiveHorizontalVector & combinedVector; + } + + // Reset precomputedEqualityArray + for (let k = start; k < verticalLength; k++) { + precomputedEqualityArray[secondString.charCodeAt(k)] = 0; + } + } + + let negativeVector = 0; + let positiveVector = -1; + const start = verticalIndex * 32; + const verticalLength = Math.min(32, secondStringLength - start) + start; + + // Initialize precomputedEqualityArray for secondString + for (let k = start; k < verticalLength; k++) { + precomputedEqualityArray[secondString.charCodeAt(k)] |= 1 << k; + } + + let distance = secondStringLength; + + // Process each character of firstString + for (let i = 0; i < firstStringLength; i++) { + const equalityMask = precomputedEqualityArray[firstString.charCodeAt(i)]; + const previousBit = (horizontalBitArray[(i / 32) | 0] >>> i) & 1; + const matchBit = (verticalBitArray[(i / 32) | 0] >>> i) & 1; + const combinedVector = equalityMask | negativeVector; + const combinedHorizontalVector = ((((equalityMask | matchBit) & positiveVector) + positiveVector) ^ positiveVector) | equalityMask | matchBit; + let positiveHorizontalVector = negativeVector | ~(combinedHorizontalVector | positiveVector); + let negativeHorizontalVector = positiveVector & combinedHorizontalVector; + distance += (positiveHorizontalVector >>> (secondStringLength - 1)) & 1; + distance -= (negativeHorizontalVector >>> (secondStringLength - 1)) & 1; + if ((positiveHorizontalVector >>> 31) ^ previousBit) { + horizontalBitArray[(i / 32) | 0] ^= 1 << i; + } + if ((negativeHorizontalVector >>> 31) ^ matchBit) { + verticalBitArray[(i / 32) | 0] ^= 1 << i; + } + positiveHorizontalVector = (positiveHorizontalVector << 1) | previousBit; + negativeHorizontalVector = (negativeHorizontalVector << 1) | matchBit; + positiveVector = negativeHorizontalVector | ~(combinedVector | positiveHorizontalVector); + negativeVector = positiveHorizontalVector & combinedVector; + } + + // Reset precomputedEqualityArray + for (let k = start; k < verticalLength; k++) { + precomputedEqualityArray[secondString.charCodeAt(k)] = 0; + } + + return distance; +} + +/** + * Computes the Levenshtein distance between two strings. + * @param firstString - The first string. + * @param secondString - The second string. + * @returns The Levenshtein distance. + */ +export function computeLevenshteinDistance(firstString: string, secondString: string): number { + if (firstString.length < secondString.length) { + const temp = secondString; + secondString = firstString; + firstString = temp; + } + if (secondString.length === 0) { + return firstString.length; + } + if (firstString.length <= 32) { + return computeLevenshteinDistanceForShortStrings(firstString, secondString); + } + return computeLevenshteinDistanceForLongStrings(firstString, secondString); +} diff --git a/src/vs/base/common/envfile.ts b/src/vs/base/common/envfile.ts new file mode 100644 index 00000000..9cb40d93 --- /dev/null +++ b/src/vs/base/common/envfile.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. + *--------------------------------------------------------------------------------------------*/ + +/** + * Parses a standard .env/.envrc file into a map of the environment variables + * it defines. + * + * todo@connor4312: this can go away (if only used in Node.js targets) and be + * replaced with `util.parseEnv`. However, currently calling that makes the + * extension host crash. + */ +export function parseEnvFile(src: string) { + const result = new Map(); + + // Normalize line breaks + const normalizedSrc = src.replace(/\r\n?/g, '\n'); + const lines = normalizedSrc.split('\n'); + + for (let line of lines) { + // Skip empty lines and comments + line = line.trim(); + if (!line || line.startsWith('#')) { + continue; + } + + // Parse the line into key and value + const [key, value] = parseLine(line); + if (key) { + result.set(key, value); + } + } + + return result; + + function parseLine(line: string): [string, string] | [null, null] { + // Handle export prefix + if (line.startsWith('export ')) { + line = line.substring(7).trim(); + } + + // Find the key-value separator + const separatorIndex = findIndexOutsideQuotes(line, c => c === '=' || c === ':'); + if (separatorIndex === -1) { + return [null, null]; + } + + const key = line.substring(0, separatorIndex).trim(); + let value = line.substring(separatorIndex + 1).trim(); + + // Handle comments and remove them + const commentIndex = findIndexOutsideQuotes(value, c => c === '#'); + if (commentIndex !== -1) { + value = value.substring(0, commentIndex).trim(); + } + + // Process quoted values + if (value.length >= 2) { + const firstChar = value[0]; + const lastChar = value[value.length - 1]; + + if ((firstChar === '"' && lastChar === '"') || + (firstChar === '\'' && lastChar === '\'') || + (firstChar === '`' && lastChar === '`')) { + // Remove surrounding quotes + value = value.substring(1, value.length - 1); + + // Handle escaped characters in double quotes + if (firstChar === '"') { + value = value.replace(/\\n/g, '\n').replace(/\\r/g, '\r'); + } + } + } + + return [key, value]; + } + + function findIndexOutsideQuotes(text: string, predicate: (char: string) => boolean): number { + let inQuote = false; + let quoteChar = ''; + + for (let i = 0; i < text.length; i++) { + const char = text[i]; + + if (inQuote) { + if (char === quoteChar && text[i - 1] !== '\\') { + inQuote = false; + } + } else if (char === '"' || char === '\'' || char === '`') { + inQuote = true; + quoteChar = char; + } else if (predicate(char)) { + return i; + } + } + + return -1; + } +} diff --git a/src/vs/base/common/hotReload.ts b/src/vs/base/common/hotReload.ts index 80901b47..4f0e1341 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 070103b8..fe1cbbb3 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -5,6 +5,7 @@ import { illegalArgument } from './errors.js'; import { escapeIcons } from './iconLabels.js'; +import { Schemas } from './network.js'; import { isEqual } from './resources.js'; import { escapeRegExpCharacters } from './strings.js'; import { URI, UriComponents } from './uri.js'; @@ -37,10 +38,6 @@ export class MarkdownString implements IMarkdownString { public uris?: { [href: string]: UriComponents } | undefined; public static lift(dto: IMarkdownString): MarkdownString { - if (dto instanceof MarkdownString) { - return dto; - } - const markdownString = new MarkdownString(dto.value, dto); markdownString.uris = dto.uris; markdownString.baseUri = dto.baseUri ? URI.revive(dto.baseUri) : undefined; @@ -201,3 +198,13 @@ export function parseHrefAndDimensions(href: string): { href: string; dimensions } return { href, dimensions }; } + +export function markdownCommandLink(command: { title: string; id: string; arguments?: unknown[] }): string { + const uri = URI.from({ + scheme: Schemas.command, + path: command.id, + query: command.arguments?.length ? encodeURIComponent(JSON.stringify(command.arguments)) : undefined, + }).toString(); + + return `[${escapeMarkdownSyntaxTokens(command.title)}](${uri})`; +} diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index fedcfe7e..2ec62fe5 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -110,6 +110,14 @@ export namespace Iterable { return value; } + export function length(iterable: Iterable): number { + let count = 0; + for (const _ of iterable) { + count++; + } + return count; + } + /** * Returns an iterable slice of the array, with the same semantics as `array.slice()`. */ diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index e04e62f7..036ff733 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -827,3 +827,19 @@ export class DisposableMap implements ID 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/marshallingIds.ts b/src/vs/base/common/marshallingIds.ts index fcec6b0a..c83402b4 100644 --- a/src/vs/base/common/marshallingIds.ts +++ b/src/vs/base/common/marshallingIds.ts @@ -26,4 +26,5 @@ export const enum MarshalledId { LanguageModelToolResult, LanguageModelTextPart, LanguageModelPromptTsxPart, + LanguageModelDataPart } diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 41fe38f7..2a229597 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -12,6 +12,7 @@ export const Mimes = Object.freeze({ markdown: 'text/markdown', latex: 'text/latex', uriList: 'text/uri-list', + html: 'text/html', }); interface MapExtToMediaMimes { diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 16bbfe74..193f687d 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -333,7 +333,7 @@ class FileAccessImpl { return uri; } - private toUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI { + private toUri(uriOrModule: URI | string): URI { if (URI.isUri(uriOrModule)) { return uriOrModule; } @@ -351,14 +351,18 @@ class FileAccessImpl { return URI.file(modulePath); } - return URI.parse(moduleIdToUrl!.toUrl(uriOrModule)); + throw new Error('Cannot determine URI for module id!'); } } export const FileAccess = new FileAccessImpl(); export const CacheControlheaders: Record = Object.freeze({ - 'Cache-Control': 'no-cache, no-store', + 'Cache-Control': 'no-cache, no-store' +}); + +export const DocumentPolicyheaders: Record = Object.freeze({ + 'Document-Policy': 'include-js-call-stacks-in-crash-reports' }); export namespace COI { diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index 6d3d90d5..1d847fa2 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -102,7 +102,7 @@ export function isPointWithinTriangle( /** * Function to get a (pseudo)random integer from a provided `max`...[`min`] range. * Both `min` and `max` values are inclusive. The `min` value is optional and defaults - * to `0` if not explicitely specified. + * to `0` if not explicitly specified. * * @throws in the next cases: * - if provided `min` or `max` is not a number diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 94c2fb71..cd976d77 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -231,41 +231,6 @@ export function filter(obj: obj, predicate: (key: string, value: any) => boolean return result; } -export function getAllPropertyNames(obj: object): string[] { - let res: string[] = []; - while (Object.prototype !== obj) { - res = res.concat(Object.getOwnPropertyNames(obj)); - obj = Object.getPrototypeOf(obj); - } - return res; -} - -export function getAllMethodNames(obj: object): string[] { - const methods: string[] = []; - for (const prop of getAllPropertyNames(obj)) { - if (typeof (obj as any)[prop] === 'function') { - methods.push(prop); - } - } - return methods; -} - -export function createProxyObject(methodNames: string[], invoke: (method: string, args: unknown[]) => unknown): T { - const createProxyMethod = (method: string): () => unknown => { - return function () { - const args = Array.prototype.slice.call(arguments, 0); - return invoke(method, args); - }; - }; - - // eslint-disable-next-line local/code-no-dangerous-type-assertions - const result = {} as T; - for (const methodName of methodNames) { - (result)[methodName] = createProxyMethod(methodName); - } - return result; -} - export function mapValues(obj: T, fn: (value: T[keyof T], key: string) => R): { [K in keyof T]: R } { const result: { [key: string]: R } = {}; for (const [key, value] of Object.entries(obj)) { diff --git a/src/vs/base/common/observableInternal/autorun.ts b/src/vs/base/common/observableInternal/autorun.ts index b1f8f4e9..03c37aa6 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 31ce4988..bab79532 100644 --- a/src/vs/base/common/observableInternal/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -8,6 +8,7 @@ import { DisposableStore, EqualityComparer, IDisposable, strictEquals } from './ import type { derivedOpts } from './derived.js'; import { getLogger, logObservable } from './logging/logging.js'; import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js'; +import { onUnexpectedError } from '../errors.js'; /** * Represents an observable value. @@ -290,7 +291,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 +330,10 @@ export abstract class BaseObservable extends ConvenientObserv } return this; } + + public debugGetObservers() { + return this._observers; + } } /** @@ -385,7 +390,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); @@ -399,13 +404,29 @@ export class TransactionImpl implements ITransaction { } public updateObserver(observer: IObserver, observable: IObservable): void { + if (!this._updatingObservers) { + // This happens when a transaction is used in a callback or async function. + // If an async transaction is used, make sure the promise awaits all users of the transaction (e.g. no race). + handleBugIndicatingErrorRecovery('Transaction already finished!'); + // Error recovery + transaction(tx => { + tx.updateObserver(observer, observable); + }); + return; + } + // When this gets called while finish is active, they will still get considered - this._updatingObservers!.push({ observer, observable }); + this._updatingObservers.push({ observer, observable }); observer.beginUpdate(observable); } public finish(): void { - const updatingObservers = this._updatingObservers!; + const updatingObservers = this._updatingObservers; + if (!updatingObservers) { + handleBugIndicatingErrorRecovery('transaction.finish() has already been called!'); + return; + } + for (let i = 0; i < updatingObservers.length; i++) { const { observer, observable } = updatingObservers[i]; observer.endUpdate(observable); @@ -414,6 +435,19 @@ export class TransactionImpl implements ITransaction { this._updatingObservers = null; getLogger()?.handleEndTransaction(this); } + + public debugGetUpdatingObservers() { + return this._updatingObservers; + } +} + +/** + * This function is used to indicate that the caller recovered from an error that indicates a bug. +*/ +function handleBugIndicatingErrorRecovery(message: string) { + const err = new Error('BugIndicatingErrorRecovery: ' + message); + onUnexpectedError(err); + console.error('recovered from an error that indicates a bug', err); } /** @@ -495,6 +529,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 0735be62..59d8dc72 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 8fe37f4c..c86501e7 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 e0b5fae1..138732f4 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 a85329ca..208c623c 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; - while (true) { - const l = Error.stackTraceLimit; - Error.stackTraceLimit = shallow ? 6 : 20; - const stack = new Error().stack!; - Error.stackTraceLimit = l; + const Err = Error as any as { stackTraceLimit: number }; // For the monaco editor checks, which don't have the nodejs types. - let result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|[/\\]util(s)?\./); + while (true) { + const l = Err.stackTraceLimit; + Err.stackTraceLimit = shallow ? 6 : 20; + const stack = new Error().stack!; + Err.stackTraceLimit = l; + + 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(); + if (!info) { return; } - info.lastValue = formattedValue; - if (info.listenerCount > 0) { - this._handleChange({ - instances: { [info.instanceId]: { formattedValue: formattedValue, recomputationCount: info.updateCount } } - }); - } + 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/utils.ts b/src/vs/base/common/observableInternal/utils.ts index 3b5bf7d3..530474b3 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/policy.ts b/src/vs/base/common/policy.ts new file mode 100644 index 00000000..8600c438 --- /dev/null +++ b/src/vs/base/common/policy.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export type PolicyName = string; + +export interface IPolicy { + + /** + * The policy name. + */ + readonly name: PolicyName; + + /** + * The Code version in which this policy was introduced. + */ + readonly minimumVersion: `${number}.${number}`; + + /** + * The policy description (optional). + */ + readonly description?: string; + + /** + * Is preview feature + */ + readonly previewFeature?: boolean; + + /** + * Default value when enabled. Default is `false`. + */ + readonly defaultValue?: string | number | boolean; +} diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 07ce2f89..1cbe1a2a 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -5,6 +5,7 @@ import { IStringDictionary } from './collections.js'; import { PlatformName } from './platform.js'; +import { IPolicy } from './policy.js'; export interface IBuiltInExtension { readonly name: string; @@ -97,12 +98,11 @@ 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; + readonly accessSKUs?: string[]; }; readonly extensionPublisherOrgs?: readonly string[]; @@ -186,10 +186,22 @@ export interface IProductConfiguration { readonly msftInternalDomains?: string[]; readonly linkProtectionTrustedDomains?: readonly string[]; + readonly defaultAccount?: { + readonly authenticationProvider: { + readonly id: string; + readonly enterpriseProviderId: string; + readonly enterpriseProviderConfig: string; + readonly scopes: string[]; + }; + readonly tokenEntitlementUrl: string; + readonly chatEntitlementUrl: string; + }; + readonly 'configurationSync.store'?: ConfigurationSyncStore; readonly 'editSessions.store'?: Omit; readonly darwinUniversalAssetId?: string; + readonly darwinBundleIdentifier?: string; readonly profileTemplatesUrl?: string; readonly commonlyUsedSettings?: string[]; @@ -199,6 +211,10 @@ export interface IProductConfiguration { readonly chatParticipantRegistry?: string; readonly emergencyAlertUrl?: string; + + readonly remoteDefaultExtensionsIfInstalledLocally?: string[]; + + readonly extensionConfigurationPolicy?: IStringDictionary; } export interface ITunnelApplicationConfig { @@ -321,10 +337,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/strings.ts b/src/vs/base/common/strings.ts index 9ed7eaa4..1a846717 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -802,14 +802,20 @@ export function rcut(text: string, n: number, suffix = ''): string { return result.trim().replace(/b$/, '') + suffix; } -// Escape codes, compiled from https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ -// Plus additional markers for custom `\x1b]...\x07` instructions. -const CSI_SEQUENCE = /(?:(?:\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])|(:?\x1b\].*?\x07)/g; +// Defacto standard: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~]/; +const OSC_SEQUENCE = /(?:\x1b\]|\x9d).*?(?:\x1b\\|\x07|\x9c)/; +const ESC_SEQUENCE = /\x1b(?:[ #%\(\)\*\+\-\.\/]?[a-zA-Z0-9\|}~@])/; +const CONTROL_SEQUENCES = new RegExp('(?:' + [ + CSI_SEQUENCE.source, + OSC_SEQUENCE.source, + ESC_SEQUENCE.source, +].join('|') + ')', 'g'); /** Iterates over parts of a string with CSI sequences */ export function* forAnsiStringParts(str: string) { let last = 0; - for (const match of str.matchAll(CSI_SEQUENCE)) { + for (const match of str.matchAll(CONTROL_SEQUENCES)) { if (last !== match.index) { yield { isCode: false, str: str.substring(last, match.index) }; } @@ -833,7 +839,7 @@ export function* forAnsiStringParts(str: string) { */ export function removeAnsiEscapeCodes(str: string): string { if (str) { - str = str.replace(CSI_SEQUENCE, ''); + str = str.replace(CONTROL_SEQUENCES, ''); } return str; diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/webWorker.ts similarity index 77% rename from src/vs/base/common/worker/simpleWorker.ts rename to src/vs/base/common/worker/webWorker.ts index b04319bc..666bd15a 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/webWorker.ts @@ -7,33 +7,19 @@ import { CharCode } from '../charCode.js'; import { onUnexpectedError, transformErrorForSerialization } from '../errors.js'; import { Emitter, Event } from '../event.js'; import { Disposable, IDisposable } from '../lifecycle.js'; -import { AppResourcePath, FileAccess } from '../network.js'; import { isWeb } from '../platform.js'; import * as strings from '../strings.js'; -import { URI } from '../uri.js'; const DEFAULT_CHANNEL = 'default'; const INITIALIZE = '$initialize'; -export interface IWorker extends IDisposable { +export interface IWebWorker extends IDisposable { getId(): number; + onMessage: Event; + onError: Event; postMessage(message: Message, transfer: ArrayBuffer[]): void; } -export interface IWorkerCallback { - (message: Message): void; -} - -export interface IWorkerFactory { - create(modules: IWorkerDescriptor, callback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker; -} - -export interface IWorkerDescriptor { - readonly moduleId: string; - readonly esmModuleLocation: URI | undefined; - readonly label: string | undefined; -} - let webWorkerWarningLogged = false; export function logOnceWebWorkerWarning(err: any): void { if (!isWeb) { @@ -98,7 +84,7 @@ class UnsubscribeEventMessage { public readonly req: string ) { } } -type Message = RequestMessage | ReplyMessage | SubscribeEventMessage | EventMessage | UnsubscribeEventMessage; +export type Message = RequestMessage | ReplyMessage | SubscribeEventMessage | EventMessage | UnsubscribeEventMessage; interface IMessageReply { resolve: (value?: any) => void; @@ -111,7 +97,7 @@ interface IMessageHandler { handleEvent(channel: string, eventName: string, arg: any): Event; } -class SimpleWorkerProtocol { +class WebWorkerProtocol { private _workerId: number; private _lastSentReq: number; @@ -300,14 +286,14 @@ export type Proxied = { [K in keyof T]: T[K] extends (...args: infer A) => in : never }; -export interface IWorkerClient { - proxy: Proxied; +export interface IWebWorkerClient { + proxy: Proxied; dispose(): void; setChannel(channel: string, handler: T): void; getChannel(channel: string): Proxied; } -export interface IWorkerServer { +export interface IWebWorkerServer { setChannel(channel: string, handler: T): void; getChannel(channel: string): Proxied; } @@ -315,38 +301,30 @@ export interface IWorkerServer { /** * Main thread side */ -export class SimpleWorkerClient extends Disposable implements IWorkerClient { +export class WebWorkerClient extends Disposable implements IWebWorkerClient { - private readonly _worker: IWorker; + private readonly _worker: IWebWorker; private readonly _onModuleLoaded: Promise; - private readonly _protocol: SimpleWorkerProtocol; + private readonly _protocol: WebWorkerProtocol; public readonly proxy: Proxied; private readonly _localChannels: Map = new Map(); private readonly _remoteChannels: Map = new Map(); constructor( - workerFactory: IWorkerFactory, - workerDescriptor: IWorkerDescriptor, + worker: IWebWorker ) { super(); - this._worker = this._register(workerFactory.create( - { - moduleId: 'vs/base/common/worker/simpleWorker', - esmModuleLocation: workerDescriptor.esmModuleLocation, - label: workerDescriptor.label - }, - (msg: Message) => { - this._protocol.handleMessage(msg); - }, - (err: any) => { - // in Firefox, web workers fail lazily :( - // we will reject the proxy - onUnexpectedError(err); - } - )); + this._worker = worker; + this._register(this._worker.onMessage((msg) => { + this._protocol.handleMessage(msg); + })); + this._register(this._worker.onError((err) => { + logOnceWebWorkerWarning(err); + onUnexpectedError(err); + })); - this._protocol = new SimpleWorkerProtocol({ + this._protocol = new WebWorkerProtocol({ sendMessage: (msg: any, transfer: ArrayBuffer[]): void => { this._worker.postMessage(msg, transfer); }, @@ -359,28 +337,14 @@ export class SimpleWorkerClient extends Disposable implements }); this._protocol.setWorkerId(this._worker.getId()); - // Gather loader configuration - let loaderConfiguration: any = null; - - const globalRequire: { getConfig?(): object } | undefined = (globalThis as any).require; - if (typeof globalRequire !== 'undefined' && typeof globalRequire.getConfig === 'function') { - // Get the configuration from the Monaco AMD Loader - loaderConfiguration = globalRequire.getConfig(); - } else if (typeof (globalThis as any).requirejs !== 'undefined') { - // Get the configuration from requirejs - loaderConfiguration = (globalThis as any).requirejs.s.contexts._.config; - } - // Send initialize message this._onModuleLoaded = this._protocol.sendMessage(DEFAULT_CHANNEL, INITIALIZE, [ this._worker.getId(), - JSON.parse(JSON.stringify(loaderConfiguration)), - workerDescriptor.moduleId, ]); this.proxy = this._protocol.createProxyToRemoteChannel(DEFAULT_CHANNEL, async () => { await this._onModuleLoaded; }); this._onModuleLoaded.catch((e) => { - this._onError('Worker failed to load ' + workerDescriptor.moduleId, e); + this._onError('Worker failed to load ', e); }); } @@ -450,36 +414,34 @@ function propertyIsDynamicEvent(name: string): boolean { return /^onDynamic/.test(name) && strings.isUpperAsciiLetter(name.charCodeAt(9)); } -export interface IRequestHandler { +export interface IWebWorkerServerRequestHandler { _requestHandlerBrand: any; [prop: string]: any; } -export interface IRequestHandlerFactory { - (workerServer: IWorkerServer): IRequestHandler; +export interface IWebWorkerServerRequestHandlerFactory { + (workerServer: IWebWorkerServer): T; } /** * Worker side */ -export class SimpleWorkerServer implements IWorkerServer { +export class WebWorkerServer implements IWebWorkerServer { - private _requestHandlerFactory: IRequestHandlerFactory | null; - private _requestHandler: IRequestHandler | null; - private _protocol: SimpleWorkerProtocol; + public readonly requestHandler: T; + private _protocol: WebWorkerProtocol; private readonly _localChannels: Map = new Map(); private readonly _remoteChannels: Map = new Map(); - constructor(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void, requestHandlerFactory: IRequestHandlerFactory | null) { - this._requestHandlerFactory = requestHandlerFactory; - this._requestHandler = null; - this._protocol = new SimpleWorkerProtocol({ + constructor(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void, requestHandlerFactory: IWebWorkerServerRequestHandlerFactory) { + this._protocol = new WebWorkerProtocol({ sendMessage: (msg: any, transfer: ArrayBuffer[]): void => { postMessage(msg, transfer); }, handleMessage: (channel: string, method: string, args: any[]): Promise => this._handleMessage(channel, method, args), handleEvent: (channel: string, eventName: string, arg: any): Event => this._handleEvent(channel, eventName, arg) }); + this.requestHandler = requestHandlerFactory(this); } public onmessage(msg: any): void { @@ -488,10 +450,10 @@ export class SimpleWorkerServer implements IWorkerServer { private _handleMessage(channel: string, method: string, args: any[]): Promise { if (channel === DEFAULT_CHANNEL && method === INITIALIZE) { - return this.initialize(args[0], args[1], args[2]); + return this.initialize(args[0]); } - const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this._requestHandler : this._localChannels.get(channel)); + const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this.requestHandler : this._localChannels.get(channel)); if (!requestHandler) { return Promise.reject(new Error(`Missing channel ${channel} on worker thread`)); } @@ -507,7 +469,7 @@ export class SimpleWorkerServer implements IWorkerServer { } private _handleEvent(channel: string, eventName: string, arg: any): Event { - const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this._requestHandler : this._localChannels.get(channel)); + const requestHandler: object | null | undefined = (channel === DEFAULT_CHANNEL ? this.requestHandler : this._localChannels.get(channel)); if (!requestHandler) { throw new Error(`Missing channel ${channel} on worker thread`); } @@ -540,50 +502,7 @@ export class SimpleWorkerServer implements IWorkerServer { return this._remoteChannels.get(channel) as Proxied; } - private async initialize(workerId: number, loaderConfig: any, moduleId: string): Promise { + private async initialize(workerId: number): Promise { this._protocol.setWorkerId(workerId); - - if (this._requestHandlerFactory) { - // static request handler - this._requestHandler = this._requestHandlerFactory(this); - return; - } - - if (loaderConfig) { - // Remove 'baseUrl', handling it is beyond scope for now - if (typeof loaderConfig.baseUrl !== 'undefined') { - delete loaderConfig['baseUrl']; - } - if (typeof loaderConfig.paths !== 'undefined') { - if (typeof loaderConfig.paths.vs !== 'undefined') { - delete loaderConfig.paths['vs']; - } - } - if (typeof loaderConfig.trustedTypesPolicy !== 'undefined') { - // don't use, it has been destroyed during serialize - delete loaderConfig['trustedTypesPolicy']; - } - - // Since this is in a web worker, enable catching errors - loaderConfig.catchError = true; - (globalThis as any).require.config(loaderConfig); - } - - const url = FileAccess.asBrowserUri(`${moduleId}.js` as AppResourcePath).toString(true); - return import(`${url}`).then((module: { create: IRequestHandlerFactory }) => { - this._requestHandler = module.create(this); - - if (!this._requestHandler) { - throw new Error(`No RequestHandler!`); - } - }); } } - -/** - * Defines the worker entry point. Must be exported and named `create`. - * @skipMangle - */ -export function create(postMessage: (msg: Message, transfer?: ArrayBuffer[]) => void): SimpleWorkerServer { - return new SimpleWorkerServer(postMessage, null); -} diff --git a/src/vs/base/common/worker/simpleWorkerBootstrap.ts b/src/vs/base/common/worker/webWorkerBootstrap.ts similarity index 64% rename from src/vs/base/common/worker/simpleWorkerBootstrap.ts rename to src/vs/base/common/worker/webWorkerBootstrap.ts index f6b544e4..dce9e79b 100644 --- a/src/vs/base/common/worker/simpleWorkerBootstrap.ts +++ b/src/vs/base/common/worker/webWorkerBootstrap.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRequestHandlerFactory, SimpleWorkerServer } from './simpleWorker.js'; +import { IWebWorkerServerRequestHandler, IWebWorkerServerRequestHandlerFactory, WebWorkerServer } from './webWorker.js'; type MessageEvent = { data: any; @@ -16,23 +16,25 @@ declare const globalThis: { let initialized = false; -function initialize(factory: IRequestHandlerFactory) { +export function initialize(factory: IWebWorkerServerRequestHandlerFactory) { if (initialized) { - return; + throw new Error('WebWorker already initialized!'); } initialized = true; - const simpleWorker = new SimpleWorkerServer( + const webWorkerServer = new WebWorkerServer( msg => globalThis.postMessage(msg), (workerServer) => factory(workerServer) ); globalThis.onmessage = (e: MessageEvent) => { - simpleWorker.onmessage(e.data); + webWorkerServer.onmessage(e.data); }; + + return webWorkerServer; } -export function bootstrapSimpleWorker(factory: IRequestHandlerFactory) { +export function bootstrapWebWorker(factory: IWebWorkerServerRequestHandlerFactory) { globalThis.onmessage = (_e: MessageEvent) => { // Ignore first message in this case and initialize if not yet initialized if (!initialized) { diff --git a/src/vs/base/node/powershell.ts b/src/vs/base/node/powershell.ts index 05d68e3c..63d230f5 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 = (value: T | Promise) => void; export type ErrorCallback = (error?: any) => void; @@ -116,19 +116,23 @@ export async function findExecutable(command: string, cwd?: string, paths?: stri } else { fullPath = path.join(cwd, pathEntry, command); } + if (Platform.isWindows) { + const pathExt = getCaseInsensitive(env, 'PATHEXT') as string || '.COM;.EXE;.BAT;.CMD'; + const pathExtsFound = pathExt.split(';').map(async ext => { + const withExtension = fullPath + ext; + return await fileExists(withExtension) ? withExtension : undefined; + }); + for (const foundPromise of pathExtsFound) { + const found = await foundPromise; + if (found) { + return found; + } + } + } + if (await fileExists(fullPath)) { return fullPath; } - if (Platform.isWindows) { - let withExtension = fullPath + '.com'; - if (await fileExists(withExtension)) { - return withExtension; - } - withExtension = fullPath + '.exe'; - if (await fileExists(withExtension)) { - return withExtension; - } - } } const fullPath = path.join(cwd, command); return await fileExists(fullPath) ? fullPath : undefined; diff --git a/src/vs/base/parts/ipc/common/ipc.mp.ts b/src/vs/base/parts/ipc/common/ipc.mp.ts index 4fc2f0b6..91931de0 100644 --- a/src/vs/base/parts/ipc/common/ipc.mp.ts +++ b/src/vs/base/parts/ipc/common/ipc.mp.ts @@ -41,15 +41,15 @@ export interface MessagePort { */ export class Protocol implements IMessagePassingProtocol { - readonly onMessage = Event.fromDOMEventEmitter(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 87e1016f..df9cd52c 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 2def700f..bf674966 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 32637745..dcda5f10 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 f1144fbb..b9a18cff 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 2260d610..287d192c 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/envfile.test.ts b/src/vs/base/test/common/envfile.test.ts new file mode 100644 index 00000000..753a8c6b --- /dev/null +++ b/src/vs/base/test/common/envfile.test.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. + *--------------------------------------------------------------------------------------------*/ + +import { parseEnvFile } from '../../common/envfile.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import * as assert from 'assert'; + +/* +Test cases from https://github.com/motdotla/dotenv/blob/master/tests/.env + + Copyright (c) 2015, Scott Motte + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +const example = ` +BASIC=basic + +# previous line intentionally left blank +AFTER_LINE=after_line +EMPTY= +EMPTY_SINGLE_QUOTES='' +EMPTY_DOUBLE_QUOTES="" +EMPTY_BACKTICKS=\`\` +SINGLE_QUOTES='single_quotes' +SINGLE_QUOTES_SPACED=' single quotes ' +DOUBLE_QUOTES="double_quotes" +DOUBLE_QUOTES_SPACED=" double quotes " +DOUBLE_QUOTES_INSIDE_SINGLE='double "quotes" work inside single quotes' +DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET="{ port: $MONGOLAB_PORT}" +SINGLE_QUOTES_INSIDE_DOUBLE="single 'quotes' work inside double quotes" +BACKTICKS_INSIDE_SINGLE='\`backticks\` work inside single quotes' +BACKTICKS_INSIDE_DOUBLE="\`backticks\` work inside double quotes" +BACKTICKS=\`backticks\` +BACKTICKS_SPACED=\` backticks \` +DOUBLE_QUOTES_INSIDE_BACKTICKS=\`double "quotes" work inside backticks\` +SINGLE_QUOTES_INSIDE_BACKTICKS=\`single 'quotes' work inside backticks\` +DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS=\`double "quotes" and single 'quotes' work inside backticks\` +EXPAND_NEWLINES="expand\\nnew\\nlines" +DONT_EXPAND_UNQUOTED=dontexpand\\nnewlines +DONT_EXPAND_SQUOTED='dontexpand\\nnewlines' +# COMMENTS=work +INLINE_COMMENTS=inline comments # work #very #well +INLINE_COMMENTS_SINGLE_QUOTES='inline comments outside of #singlequotes' # work +INLINE_COMMENTS_DOUBLE_QUOTES="inline comments outside of #doublequotes" # work +INLINE_COMMENTS_BACKTICKS=\`inline comments outside of #backticks\` # work +INLINE_COMMENTS_SPACE=inline comments start with a#number sign. no space required. +EQUAL_SIGNS=equals== +RETAIN_INNER_QUOTES={"foo": "bar"} +RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}' +RETAIN_INNER_QUOTES_AS_BACKTICKS=\`{"foo": "bar's"}\` +TRIM_SPACE_FROM_UNQUOTED= some spaced out string +USERNAME=therealnerdybeast@example.tld + SPACED_KEY = parsed +`; + +suite('parseEnvFile', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('parses', () => { + const parsed = parseEnvFile(example); + assert.strictEqual(parsed.get('BASIC'), 'basic'); + assert.strictEqual(parsed.get('AFTER_LINE'), 'after_line'); + assert.strictEqual(parsed.get('EMPTY'), ''); + assert.strictEqual(parsed.get('EMPTY_SINGLE_QUOTES'), ''); + assert.strictEqual(parsed.get('EMPTY_DOUBLE_QUOTES'), ''); + assert.strictEqual(parsed.get('EMPTY_BACKTICKS'), ''); + assert.strictEqual(parsed.get('SINGLE_QUOTES'), 'single_quotes'); + assert.strictEqual(parsed.get('SINGLE_QUOTES_SPACED'), ' single quotes '); + assert.strictEqual(parsed.get('DOUBLE_QUOTES'), 'double_quotes'); + assert.strictEqual(parsed.get('DOUBLE_QUOTES_SPACED'), ' double quotes '); + assert.strictEqual(parsed.get('DOUBLE_QUOTES_INSIDE_SINGLE'), 'double "quotes" work inside single quotes'); + assert.strictEqual(parsed.get('DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET'), '{ port: $MONGOLAB_PORT}'); + assert.strictEqual(parsed.get('SINGLE_QUOTES_INSIDE_DOUBLE'), "single 'quotes' work inside double quotes"); + assert.strictEqual(parsed.get('BACKTICKS_INSIDE_SINGLE'), '`backticks` work inside single quotes'); + assert.strictEqual(parsed.get('BACKTICKS_INSIDE_DOUBLE'), '`backticks` work inside double quotes'); + assert.strictEqual(parsed.get('BACKTICKS'), 'backticks'); + assert.strictEqual(parsed.get('BACKTICKS_SPACED'), ' backticks '); + assert.strictEqual(parsed.get('DOUBLE_QUOTES_INSIDE_BACKTICKS'), 'double "quotes" work inside backticks'); + assert.strictEqual(parsed.get('SINGLE_QUOTES_INSIDE_BACKTICKS'), "single 'quotes' work inside backticks"); + assert.strictEqual(parsed.get('DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS'), "double \"quotes\" and single 'quotes' work inside backticks"); + assert.strictEqual(parsed.get('EXPAND_NEWLINES'), 'expand\nnew\nlines'); + assert.strictEqual(parsed.get('DONT_EXPAND_UNQUOTED'), 'dontexpand\\nnewlines'); + assert.strictEqual(parsed.get('DONT_EXPAND_SQUOTED'), 'dontexpand\\nnewlines'); + assert.strictEqual(parsed.get('COMMENTS'), undefined); + assert.strictEqual(parsed.get('INLINE_COMMENTS'), 'inline comments'); + assert.strictEqual(parsed.get('INLINE_COMMENTS_SINGLE_QUOTES'), 'inline comments outside of #singlequotes'); + assert.strictEqual(parsed.get('INLINE_COMMENTS_DOUBLE_QUOTES'), 'inline comments outside of #doublequotes'); + assert.strictEqual(parsed.get('INLINE_COMMENTS_BACKTICKS'), 'inline comments outside of #backticks'); + assert.strictEqual(parsed.get('INLINE_COMMENTS_SPACE'), 'inline comments start with a'); + assert.strictEqual(parsed.get('EQUAL_SIGNS'), 'equals=='); + assert.strictEqual(parsed.get('RETAIN_INNER_QUOTES'), '{"foo": "bar"}'); + assert.strictEqual(parsed.get('RETAIN_INNER_QUOTES_AS_STRING'), '{"foo": "bar"}'); + assert.strictEqual(parsed.get('RETAIN_INNER_QUOTES_AS_BACKTICKS'), '{"foo": "bar\'s"}'); + assert.strictEqual(parsed.get('TRIM_SPACE_FROM_UNQUOTED'), 'some spaced out string'); + assert.strictEqual(parsed.get('USERNAME'), 'therealnerdybeast@example.tld'); + assert.strictEqual(parsed.get('SPACED_KEY'), 'parsed'); + const payload = parseEnvFile('BUFFER=true'); + assert.strictEqual(payload.get('BUFFER'), 'true'); + const expectedPayload = Object.entries({ SERVER: 'localhost', PASSWORD: 'password', DB: 'tests' }); + const RPayload = parseEnvFile('SERVER=localhost\rPASSWORD=password\rDB=tests\r'); + assert.deepStrictEqual([...RPayload], expectedPayload); + const NPayload = parseEnvFile('SERVER=localhost\nPASSWORD=password\nDB=tests\n'); + assert.deepStrictEqual([...NPayload], expectedPayload); + const RNPayload = parseEnvFile('SERVER=localhost\r\nPASSWORD=password\r\nDB=tests\r\n'); + assert.deepStrictEqual([...RNPayload], expectedPayload); + }); +}); diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index fd2fa4ef..6576cab3 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { Emitter } from '../../common/event.js'; -import { DisposableStore, dispose, IDisposable, markAsSingleton, ReferenceCollection, SafeDisposable, toDisposable } from '../../common/lifecycle.js'; +import { DisposableStore, dispose, IDisposable, markAsSingleton, ReferenceCollection, SafeDisposable, thenIfNotDisposed, toDisposable } from '../../common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite, throwIfDisposablesAreLeaked } from './utils.js'; class Disposable implements IDisposable { @@ -328,4 +328,30 @@ suite('No Leakage Utilities', () => { toDisposable(() => { }).dispose(); }); }); + + suite('thenIfNotDisposed', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + test('normal case', async () => { + let called = false; + store.add(thenIfNotDisposed(Promise.resolve(123), (result: number) => { + assert.strictEqual(result, 123); + called = true; + })); + + await new Promise(resolve => setTimeout(resolve, 0)); + assert.strictEqual(called, true); + }); + + test('disposed before promise resolves', async () => { + let called = false; + const disposable = thenIfNotDisposed(Promise.resolve(123), () => { + called = true; + }); + + disposable.dispose(); + await new Promise(resolve => setTimeout(resolve, 0)); + assert.strictEqual(called, false); + }); + }); }); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index 28fd291f..dcaa49b0 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -87,6 +87,13 @@ suite('MarkdownString', () => { assert.deepStrictEqual(mds.uris, dto.uris); }); + test('lift returns new instance', () => { + const instance = new MarkdownString('hello'); + const mds2 = MarkdownString.lift(instance).appendText('world'); + assert.strictEqual(mds2.value, 'helloworld'); + assert.strictEqual(instance.value, 'hello'); + }); + suite('appendCodeBlock', () => { function assertCodeBlock(lang: string, code: string, result: string) { const mds = new MarkdownString(); @@ -183,6 +190,5 @@ suite('MarkdownString', () => { }); }); - }); }); diff --git a/src/vs/base/test/common/numbers.test.ts b/src/vs/base/test/common/numbers.test.ts index 7916bf67..94c07090 100644 --- a/src/vs/base/test/common/numbers.test.ts +++ b/src/vs/base/test/common/numbers.test.ts @@ -54,8 +54,8 @@ suite('randomInt', () => { }); } - test(`should include min and max`, async () => { - let iterations = 100; + test('should include min and max', async () => { + let iterations = 125; const results = []; while (iterations-- > 0) { results.push(randomInt(max, min)); @@ -74,9 +74,9 @@ suite('randomInt', () => { }; suite('positive numbers', () => { - testRandomIntUtil(5, 2, 'max: 5, min: 2'); - testRandomIntUtil(5, 0, 'max: 5, min: 0'); - testRandomIntUtil(5, undefined, 'max: 5, min: undefined'); + testRandomIntUtil(4, 2, 'max: 4, min: 2'); + testRandomIntUtil(4, 0, 'max: 4, min: 0'); + testRandomIntUtil(4, undefined, 'max: 4, min: undefined'); testRandomIntUtil(1, 0, 'max: 0, min: 0'); }); @@ -137,14 +137,14 @@ suite('randomInt', () => { test('should throw if "min" is > "max" #5', () => { assert.throws(() => { - randomInt(-5, 0); - }, `"max"(-5) param should be greater than "min"(0)."`); + randomInt(-4, 0); + }, `"max"(-4) param should be greater than "min"(0)."`); }); test('should throw if "min" is > "max" #6', () => { assert.throws(() => { - randomInt(-5); - }, `"max"(-5) param should be greater than "min"(0)."`); + randomInt(-4); + }, `"max"(-4) param should be greater than "min"(0)."`); }); test('should throw if "max" is `NaN`', () => { @@ -155,7 +155,7 @@ suite('randomInt', () => { test('should throw if "min" is `NaN`', () => { assert.throws(() => { - randomInt(5, NaN); + randomInt(4, NaN); }, `"min" param is not a number."`); }); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 7c567df4..dca2a404 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 deleted file mode 100644 index a094636c..00000000 --- a/src/vs/base/worker/workerMain.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -(function () { - - function loadCode(moduleId: string): Promise { - const moduleUrl = new URL(`${moduleId}.js`, globalThis._VSCODE_FILE_ROOT); - return import(moduleUrl.href); - } - - interface MessageHandler { - onmessage(msg: any, ports: readonly MessagePort[]): void; - } - - // shape of vs/base/common/worker/simpleWorker.ts - interface SimpleWorkerModule { - create(postMessage: (msg: any, transfer?: Transferable[]) => void): MessageHandler; - } - - function setupWorkerServer(ws: SimpleWorkerModule) { - setTimeout(function () { - const messageHandler = ws.create((msg: any, transfer?: Transferable[]) => { - (globalThis).postMessage(msg, transfer); - }); - - self.onmessage = (e: MessageEvent) => messageHandler.onmessage(e.data, e.ports); - while (beforeReadyMessages.length > 0) { - self.onmessage(beforeReadyMessages.shift()!); - } - }, 0); - } - - let isFirstMessage = true; - const beforeReadyMessages: MessageEvent[] = []; - globalThis.onmessage = (message: MessageEvent) => { - if (!isFirstMessage) { - beforeReadyMessages.push(message); - return; - } - - isFirstMessage = false; - loadCode(message.data).then((ws) => { - setupWorkerServer(ws); - }, (err) => { - console.error(err); - }); - }; -})(); diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index faa711a3..ab967149 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -28,6 +28,7 @@ interface ISecretStorageCrypto { } class TransparentCrypto implements ISecretStorageCrypto { + async seal(data: string): Promise { return data; } @@ -44,6 +45,7 @@ const enum AESConstants { } class NetworkError extends Error { + constructor(inner: Error) { super(inner.message); this.name = inner.name; @@ -52,10 +54,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 +146,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 +159,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 +180,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, - ) { } + ) { + 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 +256,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 +500,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 f234324d..4feafb34 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,13 +3,12 @@ * 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, protocol, session, Session, systemPreferences, WebFrameMain } 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'; import { VSBuffer } from '../../base/common/buffer.js'; import { toErrorMessage } from '../../base/common/errorMessage.js'; -import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from '../../base/common/errors.js'; import { Event } from '../../base/common/event.js'; import { parse } from '../../base/common/jsonc.js'; import { getPathLabel } from '../../base/common/labels.js'; @@ -84,7 +83,7 @@ import { ElectronURLListener } from '../../platform/url/electron-main/electronUr import { IWebviewManagerService } from '../../platform/webview/common/webviewManagerService.js'; import { WebviewMainService } from '../../platform/webview/electron-main/webviewMainService.js'; import { isFolderToOpen, isWorkspaceToOpen, IWindowOpenable } from '../../platform/window/common/window.js'; -import { IWindowsMainService, OpenContext } from '../../platform/windows/electron-main/windows.js'; +import { getAllWindowsExcludingOffscreen, IWindowsMainService, OpenContext } from '../../platform/windows/electron-main/windows.js'; import { ICodeWindow } from '../../platform/window/electron-main/window.js'; import { WindowsMainService } from '../../platform/windows/electron-main/windowsMainService.js'; import { ActiveWindowManager } from '../../platform/windows/node/windowTracker.js'; @@ -118,6 +117,11 @@ 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'; +import ErrorTelemetry from '../../platform/telemetry/electron-main/errorTelemetry.js'; // in theory this is not allowed // ignore the eslint errors below @@ -154,7 +158,7 @@ export class CodeApplication extends Disposable { @IStateService private readonly stateService: IStateService, @IFileService private readonly fileService: IFileService, @IProductService private readonly productService: IProductService, - @IUserDataProfilesMainService private readonly userDataProfilesMainService: IUserDataProfilesMainService, + @IUserDataProfilesMainService private readonly userDataProfilesMainService: IUserDataProfilesMainService ) { super(); @@ -236,7 +240,7 @@ export class CodeApplication extends Disposable { } // Check to see if the request comes from one of the main windows (or shared process) and not from embedded content - const windows = BrowserWindow.getAllWindows(); + const windows = getAllWindowsExcludingOffscreen(); for (const window of windows) { if (frame.processId === window.webContents.mainFrame.processId) { return true; @@ -377,15 +381,6 @@ export class CodeApplication extends Disposable { private registerListeners(): void { - // We handle uncaught exceptions here to prevent electron from opening a dialog to the user - setUnexpectedErrorHandler(error => this.onUnexpectedError(error)); - process.on('uncaughtException', error => { - if (!isSigPipeError(error)) { - onUnexpectedError(error); - } - }); - process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason)); - // Dispose on shutdown Event.once(this.lifecycleMainService.onWillShutdown)(() => this.dispose()); @@ -530,35 +525,6 @@ export class CodeApplication extends Disposable { }); //#endregion - - // //#region Void IPC - // validatedIpcMain.handle('vscode:sendLLMMessage', async (event, data) => { - // try { - // await this.sendLLMMessage(data); - // } catch (error) { - // console.error('Error sending LLM message:', error); - // } - // }); - // //#endregion - } - - private onUnexpectedError(error: Error): void { - if (error) { - - // take only the message and stack property - const friendlyError = { - message: `[uncaught exception in main]: ${error.message}`, - stack: error.stack - }; - - // handle on client side - this.windowsMainService?.sendToFocused('vscode:reportError', JSON.stringify(friendlyError)); - } - - this.logService.error(`[uncaught exception in main]: ${error}`); - if (error.stack) { - this.logService.error(error.stack); - } } async startup(): Promise { @@ -617,6 +583,9 @@ export class CodeApplication extends Disposable { // Services const appInstantiationService = await this.initServices(machineId, sqmId, devDeviceId, sharedProcessReady); + // Error telemetry + appInstantiationService.invokeFunction(accessor => this._register(new ErrorTelemetry(accessor.get(ILogService), accessor.get(ITelemetryService)))); + // Auth Handler appInstantiationService.invokeFunction(accessor => accessor.get(IProxyAuthService)); @@ -1064,6 +1033,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)); @@ -1141,6 +1113,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)); @@ -1211,6 +1187,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); @@ -1244,6 +1224,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 3f819e16..84c4e128 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 47c3d28a..44a60515 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 7560b8fa..5e7ebeae 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 0c70b33f..f3f351eb 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 16e97934..e077d72c 100644 --- a/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts @@ -120,6 +120,10 @@ 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'; +import { ISharedWebContentExtractorService } from '../../../platform/webContentExtractor/common/webContentExtractor.js'; +import { SharedWebContentExtractorService } from '../../../platform/webContentExtractor/node/sharedWebContentExtractorService.js'; class SharedProcessMain extends Disposable implements IClientConnectionFilter { @@ -331,6 +335,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 @@ -371,13 +376,14 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { // Remote Tunnel services.set(IRemoteTunnelService, new SyncDescriptor(RemoteTunnelService)); + // Web Content Extractor + services.set(ISharedWebContentExtractorService, new SyncDescriptor(SharedWebContentExtractorService)); + return new InstantiationService(services); } 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); @@ -431,6 +437,10 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { // Remote Tunnel const remoteTunnelChannel = ProxyChannel.fromService(accessor.get(IRemoteTunnelService), this._store); this.server.registerChannel('remoteTunnel', remoteTunnelChannel); + + // Web Content Extractor + const webContentExtractorChannel = ProxyChannel.fromService(accessor.get(ISharedWebContentExtractorService), this._store); + this.server.registerChannel('sharedWebContentExtractor', webContentExtractorChannel); } private registerErrorHandler(logService: ILogService): void { diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index e91bea07..312df0c1 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 aff3b2da..99fb1f68 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -11,7 +11,7 @@ import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from '.. import { Disposable } from '../../base/common/lifecycle.js'; import { Schemas } from '../../base/common/network.js'; import { isAbsolute, join } from '../../base/common/path.js'; -import { isWindows } from '../../base/common/platform.js'; +import { isWindows, isMacintosh } from '../../base/common/platform.js'; import { cwd } from '../../base/common/process.js'; import { URI } from '../../base/common/uri.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; @@ -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 { @@ -162,9 +165,16 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, userDataProfilesService, uriIdentityService, logService)); // Policy - const policyService = isWindows && productService.win32RegValueName ? this._register(new NativePolicyService(logService, productService.win32RegValueName)) - : environmentService.policyFile ? this._register(new FilePolicyService(environmentService.policyFile, fileService, logService)) - : new NullPolicyService(); + let policyService: IPolicyService | undefined; + if (isWindows && productService.win32RegValueName) { + policyService = this._register(new NativePolicyService(logService, productService.win32RegValueName)); + } else if (isMacintosh && productService.darwinBundleIdentifier) { + policyService = this._register(new NativePolicyService(logService, productService.darwinBundleIdentifier)); + } else if (environmentService.policyFile) { + policyService = this._register(new FilePolicyService(environmentService.policyFile, fileService, logService)); + } else { + policyService = new NullPolicyService(); + } services.set(IPolicyService, policyService); // Configuration @@ -208,6 +218,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 +315,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 4adf4e3d..a056495b 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 792c15fe..5ca36c59 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 75acb7c2..9d17f5d9 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts @@ -41,7 +41,7 @@ enum CompositionClassName { export class NativeEditContext extends AbstractEditContext { // Text area used to handle paste events - public readonly textArea: FastDomNode; + private readonly _textArea: FastDomNode; public readonly domNode: FastDomNode; private readonly _editContext: EditContext; private readonly _screenReaderSupport: ScreenReaderSupport; @@ -74,14 +74,18 @@ export class NativeEditContext extends AbstractEditContext { this.domNode = new FastDomNode(document.createElement('div')); this.domNode.setClassName(`native-edit-context`); - this.textArea = new FastDomNode(document.createElement('textarea')); - this.textArea.setClassName('native-edit-context-textarea'); - this.textArea.setAttribute('tabindex', '-1'); + 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(); overflowGuardContainer.appendChild(this.domNode); - overflowGuardContainer.appendChild(this.textArea); + overflowGuardContainer.appendChild(this._textArea); this._parent = overflowGuardContainer.domNode; this._selectionChangeListener = this._register(new MutableDisposable()); @@ -147,7 +151,7 @@ export class NativeEditContext extends AbstractEditContext { // Emits ViewCompositionEndEvent which can be depended on by ViewEventHandlers this._context.viewModel.onCompositionEnd(); })); - this._register(addDisposableListener(this.textArea.domNode, 'paste', (e) => { + this._register(addDisposableListener(this._textArea.domNode, 'paste', (e) => { // Pretend here we touched the text area, as the `paste` event will most likely // result in a `selectionchange` event which we want to ignore this._screenReaderSupport.setIgnoreSelectionChangeTime('onPaste'); @@ -181,7 +185,7 @@ export class NativeEditContext extends AbstractEditContext { // Force blue the dom node so can write in pane with no native edit context after disposal this.domNode.domNode.blur(); this.domNode.domNode.remove(); - this.textArea.domNode.remove(); + this._textArea.domNode.remove(); super.dispose(); } @@ -208,6 +212,7 @@ 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; } @@ -248,7 +253,27 @@ export class NativeEditContext extends AbstractEditContext { return true; } - public onWillPaste(): void { + public executePaste(): boolean { + this._onWillPaste(); + try { + // pause focus tracking because we don't want to react to focus/blur + // events while pasting since we move the focus to the textarea + this._focusTracker.pause(); + + // Since we can not call execCommand('paste') on a dom node with edit context set + // we added a hidden text area that receives the paste execution + this._textArea.focus(); + const result = this._textArea.domNode.ownerDocument.execCommand('paste'); + this._textArea.domNode.textContent = ''; + this.domNode.focus(); + + return result; + } finally { + this._focusTracker.resume(); // resume focus tracking + } + } + + private _onWillPaste(): void { this._screenReaderSupport.setIgnoreSelectionChangeTime('onWillPaste'); } @@ -257,7 +282,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 { @@ -294,7 +319,7 @@ export class NativeEditContext extends AbstractEditContext { if (!editContextState) { return; } - this._editContext.updateText(0, Number.MAX_SAFE_INTEGER, editContextState.text); + this._editContext.updateText(0, Number.MAX_SAFE_INTEGER, editContextState.text ?? ' '); this._editContext.updateSelection(editContextState.selectionStartOffset, editContextState.selectionEndOffset); this._editContextPrimarySelection = editContextState.editContextPrimarySelection; } diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts index b3166de0..90f3e983 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 { @@ -15,14 +15,37 @@ export interface ITypeData { export class FocusTracker extends Disposable { private _isFocused: boolean = false; + private _isPaused: boolean = false; constructor( private readonly _domNode: HTMLElement, 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', () => { + if (this._isPaused) { + return; + } + // Here we don't trust the browser and instead we check + // that the active element is the one we are tracking + // (this happens when cmd+tab is used to switch apps) + this.refreshFocusState(); + })); + this._register(addDisposableListener(this._domNode, 'blur', () => { + if (this._isPaused) { + return; + } + this._handleFocusedChanged(false); + })); + } + + public pause(): void { + this._isPaused = true; + } + + public resume(): void { + this._isPaused = false; + this.refreshFocusState(); } private _handleFocusedChanged(focused: boolean): void { @@ -34,14 +57,14 @@ 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; + const shadowRoot = getShadowRoot(this._domNode); + const activeElement = shadowRoot ? shadowRoot.activeElement : getActiveElement(); + const focused = this._domNode === activeElement; this._handleFocusedChanged(focused); } diff --git a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts index 769bb020..93a03823 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/rect.ts b/src/vs/editor/browser/rect.ts index 66c1bd87..e6de59e4 100644 --- a/src/vs/editor/browser/rect.ts +++ b/src/vs/editor/browser/rect.ts @@ -62,16 +62,56 @@ export class Rect { } } - withMargin(margin: number): Rect { - return new Rect(this.left - margin, this.top - margin, this.right + margin, this.bottom + margin); + withMargin(margin: number): Rect; + withMargin(marginVertical: number, marginHorizontal: number): Rect; + withMargin(marginTop: number, marginRight: number, marginBottom: number, marginLeft: number): Rect; + withMargin(marginOrVerticalOrTop: number, rightOrHorizontal?: number, bottom?: number, left?: number): Rect { + let marginLeft, marginRight, marginTop, marginBottom; + + // Single margin value + if (rightOrHorizontal === undefined && bottom === undefined && left === undefined) { + marginLeft = marginRight = marginTop = marginBottom = marginOrVerticalOrTop; + } + // Vertical and horizontal margins + else if (bottom === undefined && left === undefined) { + marginLeft = marginRight = rightOrHorizontal!; + marginTop = marginBottom = marginOrVerticalOrTop; + } + // Individual margins for all sides + else { + marginLeft = left!; + marginRight = rightOrHorizontal!; + marginTop = marginOrVerticalOrTop; + marginBottom = bottom!; + } + + return new Rect( + this.left - marginLeft, + this.top - marginTop, + this.right + marginRight, + this.bottom + marginBottom, + ); } intersectVertical(range: OffsetRange): Rect { + const newTop = Math.max(this.top, range.start); + const newBottom = Math.min(this.bottom, range.endExclusive); return new Rect( this.left, - Math.max(this.top, range.start), + newTop, this.right, - Math.min(this.bottom, range.endExclusive), + Math.max(newTop, newBottom), + ); + } + + intersectHorizontal(range: OffsetRange): Rect { + const newLeft = Math.max(this.left, range.start); + const newRight = Math.min(this.right, range.endExclusive); + return new Rect( + newLeft, + this.top, + Math.max(newLeft, newRight), + this.bottom, ); } @@ -149,6 +189,10 @@ export class Rect { return new Rect(this.left, top, this.right, this.bottom); } + withLeft(left: number): Rect { + return new Rect(left, this.top, this.right, this.bottom); + } + translateX(delta: number): Rect { return new Rect(this.left + delta, this.top, this.right + delta, this.bottom); } @@ -188,4 +232,14 @@ export class Rect { getRightTop(): Point { return new Point(this.right, this.top); } + + toStyles() { + return { + position: 'absolute', + left: `${this.left}px`, + top: `${this.top}px`, + width: `${this.width}px`, + height: `${this.height}px`, + }; + } } diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index 6e01c738..f19765d9 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -6,14 +6,14 @@ import { timeout } from '../../../base/common/async.js'; import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; -import { logOnceWebWorkerWarning, IWorkerClient, Proxied, IWorkerDescriptor } from '../../../base/common/worker/simpleWorker.js'; -import { createWebWorker } from '../../../base/browser/defaultWorkerFactory.js'; +import { logOnceWebWorkerWarning, IWebWorkerClient, Proxied } from '../../../base/common/worker/webWorker.js'; +import { createWebWorker, IWebWorkerDescriptor } from '../../../base/browser/webWorkerFactory.js'; import { Position } from '../../common/core/position.js'; import { IRange, Range } from '../../common/core/range.js'; import { ITextModel } from '../../common/model.js'; import * as languages from '../../common/languages.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; -import { EditorSimpleWorker } from '../../common/services/editorSimpleWorker.js'; +import { EditorWorker } from '../../common/services/editorWebWorker.js'; import { DiffAlgorithmName, IEditorWorkerService, ILineChange, IUnicodeHighlightsResult } from '../../common/services/editorWorker.js'; import { IModelService } from '../../common/services/model.js'; import { ITextResourceConfigurationService } from '../../common/services/textResourceConfiguration.js'; @@ -59,7 +59,7 @@ export abstract class EditorWorkerService extends Disposable implements IEditorW private readonly _logService: ILogService; constructor( - workerDescriptor: IWorkerDescriptor, + workerDescriptor: IWebWorkerDescriptor, @IModelService modelService: IModelService, @ITextResourceConfigurationService configurationService: ITextResourceConfigurationService, @ILogService logService: ILogService, @@ -222,7 +222,7 @@ export abstract class EditorWorkerService extends Disposable implements IEditorW return worker.$computeDefaultDocumentColors(uri.toString()); } - private async _workerWithResources(resources: URI[], forceLargeModels: boolean = false): Promise> { + private async _workerWithResources(resources: URI[], forceLargeModels: boolean = false): Promise> { const worker = await this._workerManager.withWorker(); return await worker.workerWithSyncedResources(resources, forceLargeModels); } @@ -313,7 +313,7 @@ class WorkerManager extends Disposable { private _lastWorkerUsedTime: number; constructor( - private readonly _workerDescriptor: IWorkerDescriptor, + private readonly _workerDescriptor: IWebWorkerDescriptor, @IModelService modelService: IModelService ) { super(); @@ -375,7 +375,7 @@ class WorkerManager extends Disposable { } } -class SynchronousWorkerClient implements IWorkerClient { +class SynchronousWorkerClient implements IWebWorkerClient { private readonly _instance: T; public readonly proxy: Proxied; @@ -405,12 +405,12 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien private readonly _modelService: IModelService; private readonly _keepIdleModels: boolean; - private _worker: IWorkerClient | null; + private _worker: IWebWorkerClient | null; private _modelManager: WorkerTextModelSyncClient | null; private _disposed = false; constructor( - private readonly _workerDescriptor: IWorkerDescriptor, + private readonly _workerDescriptorOrWorker: IWebWorkerDescriptor | Worker, keepIdleModels: boolean, @IModelService modelService: IModelService, ) { @@ -426,10 +426,10 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien throw new Error(`Not implemented!`); } - private _getOrCreateWorker(): IWorkerClient { + private _getOrCreateWorker(): IWebWorkerClient { if (!this._worker) { try { - this._worker = this._register(createWebWorker(this._workerDescriptor)); + this._worker = this._register(createWebWorker(this._workerDescriptorOrWorker)); EditorWorkerHost.setChannel(this._worker, this._createEditorWorkerHost()); } catch (err) { logOnceWebWorkerWarning(err); @@ -439,7 +439,7 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien return this._worker; } - protected async _getProxy(): Promise> { + protected async _getProxy(): Promise> { try { const proxy = this._getOrCreateWorker().proxy; await proxy.$ping(); @@ -451,8 +451,8 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien } } - private _createFallbackLocalWorker(): SynchronousWorkerClient { - return new SynchronousWorkerClient(new EditorSimpleWorker(this._createEditorWorkerHost(), null)); + private _createFallbackLocalWorker(): SynchronousWorkerClient { + return new SynchronousWorkerClient(new EditorWorker(null)); } private _createEditorWorkerHost(): EditorWorkerHost { @@ -461,14 +461,14 @@ export class EditorWorkerClient extends Disposable implements IEditorWorkerClien }; } - private _getOrCreateModelManager(proxy: Proxied): WorkerTextModelSyncClient { + private _getOrCreateModelManager(proxy: Proxied): WorkerTextModelSyncClient { if (!this._modelManager) { this._modelManager = this._register(new WorkerTextModelSyncClient(proxy, this._modelService, this._keepIdleModels)); } return this._modelManager; } - public async workerWithSyncedResources(resources: URI[], forceLargeModels: boolean = false): Promise> { + public async workerWithSyncedResources(resources: URI[], forceLargeModels: boolean = false): Promise> { if (this._disposed) { return Promise.reject(canceled()); } diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index 3949c56e..523378c4 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) { @@ -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/view.ts b/src/vs/editor/browser/view.ts index e2a992cd..53acaf14 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; @@ -684,8 +696,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 +868,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 7e325a7f..fc0a05c5 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 = {}; @@ -506,8 +498,16 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE const hasTextFocus = this.hasTextFocus(); const detachedModel = this._detachModel(); this._attachModel(model); - if (hasTextFocus && this.hasModel()) { - this.focus(); + if (this.hasModel()) { + // we have a new model (with a new view)! + if (hasTextFocus) { + this.focus(); + } + } else { + // we have no model (and no view) anymore + // make sure the outside world knows we are not focused + this._editorTextFocus.setValue(false); + this._editorWidgetFocus.setValue(false); } this._removeDecorationTypes(); @@ -1033,7 +1033,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 +1450,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 +1692,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 +1878,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 +2035,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 +2047,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 +2311,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/components/diffEditorViewZones/renderLines.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts index ce0eea0b..ac4aee78 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts @@ -146,6 +146,23 @@ export class RenderOptions { setWidth, ); } + + public withScrollBeyondLastColumn(scrollBeyondLastColumn: number): RenderOptions { + return new RenderOptions( + this.tabSize, + this.fontInfo, + this.disableMonospaceOptimizations, + this.typicalHalfwidthCharacterWidth, + scrollBeyondLastColumn, + this.lineHeight, + this.lineDecorationsWidth, + this.stopRenderingLineAfter, + this.renderWhitespace, + this.renderControlCharacters, + this.fontLigatures, + this.setWidth, + ); + } } export interface RenderLinesResult { diff --git a/src/vs/editor/browser/widget/diffEditor/style.css b/src/vs/editor/browser/widget/diffEditor/style.css index 8c027c33..8a461de9 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 3703fa1d..bec96171 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 { PartialMarkdownImage } from './parsers/markdownImage.js'; import { ReadableStream } from '../../../../base/common/stream.js'; -import { CarriageReturn } from '../linesCodec/tokens/carriageReturn.js'; +import { LeftAngleBracket } from '../simpleCodec/tokens/angleBrackets.js'; +import { ExclamationMark } from '../simpleCodec/tokens/exclamationMark.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'; /** * 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 00000000..df5f3f02 --- /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 00000000..a3264f7c --- /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 00000000..e8163286 --- /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 00000000..f7875d95 --- /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 00000000..75e42e31 --- /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 b4c8947a..a4b15718 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 9e864177..e088a18f 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 88ad1298..c32542f2 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 00000000..70d264bd --- /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 5c6c1e46..16165cf6 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 2c4b89d9..76e9f0cd 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 00000000..ebc0179e --- /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 00000000..025edf70 --- /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 372e0b2e..ddca12a2 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 b67f4e10..d3509824 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 7f511c26..c0d775ff 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 fc3cefa7..2ca5598a 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 38aedd48..8b8d0991 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'; @@ -116,6 +117,12 @@ const editorConfiguration: IConfigurationNode = { markdownDescription: nls.localize('editor.experimental.treeSitterTelemetry', "Controls whether tree sitter parsing should be turned on and telemetry collected. Setting `editor.experimental.preferTreeSitter` for specific languages will take precedence."), tags: ['experimental', 'onExP'] }, + 'editor.experimental.preferTreeSitter.css': { + type: 'boolean', + default: false, + markdownDescription: nls.localize('editor.experimental.preferTreeSitter.css', "Controls whether tree sitter parsing should be turned on for css. This will take precedence over `editor.experimental.treeSitterTelemetry` for css."), + tags: ['experimental', 'onExP'] + }, 'editor.experimental.preferTreeSitter.typescript': { type: 'boolean', default: false, @@ -128,6 +135,12 @@ const editorConfiguration: IConfigurationNode = { markdownDescription: nls.localize('editor.experimental.preferTreeSitter.ini', "Controls whether tree sitter parsing should be turned on for ini. This will take precedence over `editor.experimental.treeSitterTelemetry` for ini."), tags: ['experimental', 'onExP'] }, + 'editor.experimental.preferTreeSitter.regex': { + type: 'boolean', + default: false, + markdownDescription: nls.localize('editor.experimental.preferTreeSitter.regex', "Controls whether tree sitter parsing should be turned on for regex. This will take precedence over `editor.experimental.treeSitterTelemetry` for regex."), + tags: ['experimental', 'onExP'] + }, 'editor.language.brackets': { type: ['array', 'null'], default: null, // We want to distinguish the empty array from not configured. @@ -318,3 +331,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 65d56c06..b0396646 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1944,7 +1944,7 @@ class EffectiveExperimentalEditContextEnabled extends ComputedEditorOption; onDidChangeBackgroundTokenization: Event<{ textModel: model.ITextModel }>; tokenizeEncodedInstrumented(lineNumber: number, textModel: model.ITextModel): { result: Uint32Array; captureTime: number; metadataTime: number } | undefined; + guessTokensForLinesContent(lineNumber: number, textModel: model.ITextModel, lines: string[]): Uint32Array[] | undefined; } /** @@ -431,6 +436,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/highlights/css.scm b/src/vs/editor/common/languages/highlights/css.scm new file mode 100644 index 00000000..6fb6e5c7 --- /dev/null +++ b/src/vs/editor/common/languages/highlights/css.scm @@ -0,0 +1,90 @@ +; Order matters! Place lower precedence first. + +[ + "{" + "}" + "(" + ")" + "[" + "]" +] @punctuation.css + +[ + "*=" +] @keyword.operator.css + +(comment) @comment.block.css + +; Selectors + +(selectors) @meta.selector.css + +(class_selector) @entity.other.attribute-name.class.css + +(tag_name) @entity.name.tag.css + +(pseudo_class_selector) @entity.other.attribute-name.pseudo-class.css + +(attribute_name) @entity.other.attribute-name.css + +; @ Rules + +[ + ("@import") + ("@charset") + ("@namespace") + ("@media") + ("@supports") + ("@keyframes") +] @keyword.control.at-rule.css + +(keyword_query) @support.constant.media.css + +(keyframes_name) @variable.parameter.keyframe-list.css + +; Functions + +(function_name) @support.function.css + +; Properties + +(property_name) @support.type.property-name.css + +; Other values + +(plain_value) @support.constant.property-value.css + +; Strings + +((string_value) @string.quoted.single.css + (#match? @string.quoted.single.css "^'.*'$")) + +((string_value) @string.quoted.double.css + (#match? @string.quoted.double.css "^\".*\"$")) + +; Numbers + +([ + (integer_value) + (float_value) +]) @constant.numeric.css + +(unit) @keyword.other.unit.css + +; Special values + +(declaration + ((property_name) @support.type.property-name.css + (#eq? @support.type.property-name.css "font")) + (plain_value) @support.constant.font-name.css) + +((color_value) @constant.other.color.rgb-value.hex.css + (#match? @constant.other.color.rgb-value.hex.css "^#.*")) + +; Special Functions + +(call_expression + ((function_name) @support.function.url.css + (#eq? @support.function.url.css "url")) + (arguments + (plain_value) @variable.parameter.url.css)) diff --git a/src/vs/editor/common/languages/highlights/regex.scm b/src/vs/editor/common/languages/highlights/regex.scm new file mode 100644 index 00000000..27b29813 --- /dev/null +++ b/src/vs/editor/common/languages/highlights/regex.scm @@ -0,0 +1,96 @@ +; Order matters! Place lower precedence first. +[ + "?" + "=" + "!" +] @keyword.operator.regexp + +[ + "(" + ")" +] @punctuation.definition.group.regexp + +[ + ">" + "{" + "}" +] @punctuation.regexp + +[ + "[" + "]" +] @punctuation.definition.character-class.regexp + +[ + "(?:" + "(?<" +] @punctuation.definition.group.assertion.regexp + +(lookaround_assertion ("!") @punctuation.definition.group.assertion.regexp) + +(named_capturing_group) @punctuation.definition.group.regexp + +(group_name) @variable.other.regexp + +[ + (control_letter_escape) + (non_boundary_assertion) +] @string.escape.regexp + +[ + (start_assertion) + (end_assertion) + (boundary_assertion) +] @keyword.control.anchor.regexp + +[ + ((identity_escape) @internal.regexp (#match? @internal.regexp "\\[^ux]")) +] @constant.character.escape.regexp + +( + ((identity_escape) @internal.regexp (#eq? @internal.regexp "\\u")) + . + (pattern_character) @constant.character.numeric.regexp + . + (pattern_character) @constant.character.numeric.regexp + . + (pattern_character) @constant.character.numeric.regexp + . + (pattern_character) @constant.character.numeric.regexp +) @constant.character.numeric.regexp + +( + ((identity_escape) @internal.regexp (#eq? @internal.regexp "\\x")) + . + (pattern_character) @constant.character.numeric.regexp + . + (pattern_character) @constant.character.numeric.regexp +) @constant.character.numeric.regexp + +[ + (character_class_escape) + (control_escape) +] @constant.other.character-class.regexp + +("|") @keyword.operator.or.regexp + +[ + "*" + "+" +] @keyword.operator.quantifier.regexp + +(count_quantifier) @keyword.operator.quantifier.regexp + +[ + (lazy) +] @keyword.operator.quantifier.regexp + +(optional ("?") @keyword.operator.quantifier.regexp) + +(character_class + [ + "^" @keyword.operator.negation.regexp + (class_range "-" @constant.other.character-class.range.regexp) + ]) + +(class_character) @constant.character-class.regexp diff --git a/src/vs/editor/common/languages/highlights/typescript.scm b/src/vs/editor/common/languages/highlights/typescript.scm index c536c716..860ef702 100644 --- a/src/vs/editor/common/languages/highlights/typescript.scm +++ b/src/vs/editor/common/languages/highlights/typescript.scm @@ -1,129 +1,203 @@ ; Order matters! Place lower precedence first. -; Adapted from https://github.com/zed-industries/zed/blob/main/crates/languages/src/typescript/highlights.scm ; 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 - (rest_pattern - (identifier) @variable.parameter)) + (_ + ([ + (identifier) + (shorthand_property_identifier_pattern) + ]) @variable.parameter.ts)) (optional_parameter - (identifier) @variable.parameter) + (identifier) @variable.parameter.ts) + +(optional_parameter + (_ + ([ + (identifier) + (shorthand_property_identifier_pattern) + ]) @variable.parameter.ts)) (catch_clause - parameter: (identifier) @variable.parameter) + 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 + +(_ + (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) -([ - (identifier) - (shorthand_property_identifier) - (shorthand_property_identifier_pattern)] @variable.other.constant - (#match? @variable.other.constant "^[A-Z][A-Z_]+$")) +(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 @@ -134,7 +208,7 @@ "," ":" "?" -] @punctuation.delimiter +] @punctuation.delimiter.ts [ "!" @@ -144,7 +218,7 @@ "&&" "||" "??" -] @keyword.operator.logical +] @keyword.operator.logical.ts (binary_expression ([ "-" @@ -153,18 +227,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 ([ "-=" @@ -178,15 +256,15 @@ "&&=" "||=" "??=" -]) @keyword.operator.assignment.compound) +]) @keyword.operator.assignment.compound.ts) [ "++" -] @keyword.operator.increment +] @keyword.operator.increment.ts [ "--" -] @keyword.operator.decrement +] @keyword.operator.decrement.ts [ "**" @@ -202,16 +280,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) [ "{" @@ -220,36 +310,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" @@ -273,10 +367,9 @@ "switch" "throw" "try" - "type" "while" "yield" -] @keyword.control +] @keyword.control.ts [ "abstract" @@ -290,7 +383,7 @@ "public" "readonly" "static" -] @storage.modifier +] @storage.modifier.ts [ "=>" @@ -304,7 +397,9 @@ "namespace" "set" "var" -] @storage.type +] @storage.type.ts + +("type") @storage.type.type.ts [ "module" @@ -314,67 +409,72 @@ "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 [ @@ -382,7 +482,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/languages/injections/typescript.scm b/src/vs/editor/common/languages/injections/typescript.scm new file mode 100644 index 00000000..5794b7c0 --- /dev/null +++ b/src/vs/editor/common/languages/injections/typescript.scm @@ -0,0 +1,2 @@ +((regex) @injection.content + (#set! injection.language "regex")) diff --git a/src/vs/editor/common/model/tokenStore.ts b/src/vs/editor/common/model/tokenStore.ts index 49264b40..4582e097 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,10 +289,12 @@ 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)) { + } else if (isLeaf(node.node) && (currentOffset + node.node.length > firstUnchangedOffsetAfterUpdate)) { // we have a partial postceeding node postcedingNodes.push({ length: currentOffset + node.node.length - firstUnchangedOffsetAfterUpdate, token: node.node.token, height: 0, tokenQuality: node.node.tokenQuality }); continue; @@ -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 d0fa8b42..b2ae5b3b 100644 --- a/src/vs/editor/common/model/treeSitterTokenStoreService.ts +++ b/src/vs/editor/common/model/treeSitterTokenStoreService.ts @@ -9,10 +9,12 @@ 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; @@ -42,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) { @@ -82,6 +53,38 @@ 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 }; + // Also mark tokens that are in the range of the change as needing a refresh. + storeInfo.store.markForRefresh(offset, change.rangeOffset + (change.text.length > change.rangeLength ? change.text.length : change.rangeLength)); + } 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); + } + } + } + rangeHasTokens(model: ITextModel, range: Range, minimumTokenQuality: TokenQuality): boolean { const tokens = this.tokens.get(model); if (!tokens) { diff --git a/src/vs/editor/common/model/treeSitterTokens.ts b/src/vs/editor/common/model/treeSitterTokens.ts index 1fe9c3cb..55c0d9bf 100644 --- a/src/vs/editor/common/model/treeSitterTokens.ts +++ b/src/vs/editor/common/model/treeSitterTokens.ts @@ -56,9 +56,9 @@ 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) { + if (rawTokens && rawTokens.length > 0) { return new LineTokens(rawTokens, content, this._languageIdCodec); } } @@ -88,11 +88,13 @@ 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); } } public override forceTokenization(lineNumber: number): void { - if (this._tokenizationSupport) { + if (this._tokenizationSupport && !this.hasAccurateTokensForLine(lineNumber)) { this._tokenizationSupport.tokenizeEncoded(lineNumber, this._textModel); } } @@ -110,10 +112,21 @@ export class TreeSitterTokens extends AbstractTokens { // TODO @alexr00 implement once we have custom parsing and don't just feed in the whole text model value return StandardTokenType.Other; } + public override tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null { - // TODO @alexr00 understand what this is for and implement + if (this._tokenizationSupport) { + const rawLineTokens = this._tokenizationSupport.guessTokensForLinesContent(lineNumber, this._textModel, lines); + const lineTokens: LineTokens[] = []; + if (rawLineTokens) { + for (let i = 0; i < rawLineTokens.length; i++) { + lineTokens.push(new LineTokens(rawLineTokens[i], lines[i], this._languageIdCodec)); + } + return lineTokens; + } + } return null; } + public override get hasTokens(): boolean { return this._tokenStore.hasTokens(this._textModel); } diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorWebWorker.ts similarity index 87% rename from src/vs/editor/common/services/editorSimpleWorker.ts rename to src/vs/editor/common/services/editorWebWorker.ts index 2432a8f6..c5e8ea18 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorWebWorker.ts @@ -6,7 +6,7 @@ import { stringDiff } from '../../../base/common/diff/diff.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; -import { IRequestHandler, IWorkerServer } from '../../../base/common/worker/simpleWorker.js'; +import { IWebWorkerServerRequestHandler } from '../../../base/common/worker/webWorker.js'; import { Position } from '../core/position.js'; import { IRange, Range } from '../core/range.js'; import { EndOfLineSequence, ITextModel } from '../model.js'; @@ -16,16 +16,13 @@ import { computeLinks } from '../languages/linkComputer.js'; import { BasicInplaceReplace } from '../languages/supports/inplaceReplaceSupport.js'; import { DiffAlgorithmName, IDiffComputationResult, ILineChange, IUnicodeHighlightsResult } from './editorWorker.js'; import { createMonacoBaseAPI } from './editorBaseApi.js'; -import { EditorWorkerHost } from './editorWorkerHost.js'; import { StopWatch } from '../../../base/common/stopwatch.js'; import { UnicodeTextModelHighlighter, UnicodeHighlighterOptions } from './unicodeTextModelHighlighter.js'; import { DiffComputer, IChange } from '../diff/legacyLinesDiffComputer.js'; import { ILinesDiffComputer, ILinesDiffComputerOptions } from '../diff/linesDiffComputer.js'; import { DetailedLineRangeMapping } from '../diff/rangeMapping.js'; import { linesDiffComputers } from '../diff/linesDiffComputers.js'; -import { createProxyObject, getAllMethodNames } from '../../../base/common/objects.js'; import { IDocumentDiffProviderOptions } from '../diff/documentDiffProvider.js'; -import { AppResourcePath, FileAccess } from '../../../base/common/network.js'; import { BugIndicatingError } from '../../../base/common/errors.js'; import { computeDefaultDocumentColors } from '../languages/defaultDocumentColorsComputer.js'; import { FindSectionHeaderOptions, SectionHeader, findSectionHeaders } from './findSectionHeaders.js'; @@ -67,31 +64,27 @@ export interface IWordRange { /** * @internal */ -export interface IForeignModuleFactory { - (ctx: IWorkerContext, createData: any): any; -} - -declare const require: any; - -/** - * @internal - */ -export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSyncChannelServer, IRequestHandler { +export class EditorWorker implements IDisposable, IWorkerTextModelSyncChannelServer, IWebWorkerServerRequestHandler { _requestHandlerBrand: any; private readonly _workerTextModelSyncServer = new WorkerTextModelSyncServer(); - constructor() { - } + constructor( + private readonly _foreignModule: any | null = null + ) { } dispose(): void { } + public async $ping() { + return 'pong'; + } + protected _getModel(uri: string): ICommonModel | undefined { return this._workerTextModelSyncServer.getModel(uri); } - protected _getModels(): ICommonModel[] { + public getModels(): ICommonModel[] { return this._workerTextModelSyncServer.getModels(); } @@ -132,7 +125,7 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync return null; } - const result = EditorSimpleWorker.computeDiff(original, modified, options, algorithm); + const result = EditorWorker.computeDiff(original, modified, options, algorithm); return result; } @@ -267,7 +260,7 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync } // make sure diff won't take too long - if (Math.max(text.length, original.length) > EditorSimpleWorker._diffLimit) { + if (Math.max(text.length, original.length) > EditorWorker._diffLimit) { result.push({ range, text }); continue; } @@ -336,7 +329,7 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync } // make sure diff won't take too long - if (Math.max(text.length, original.length) > EditorSimpleWorker._diffLimit) { + if (Math.max(text.length, original.length) > EditorWorker._diffLimit) { result.push({ range, text }); continue; } @@ -437,7 +430,7 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync continue; } seen.add(word); - if (seen.size > EditorSimpleWorker._suggestionsLimit) { + if (seen.size > EditorWorker._suggestionsLimit) { break outer; } } @@ -509,60 +502,9 @@ export class BaseEditorSimpleWorker implements IDisposable, IWorkerTextModelSync const result = BasicInplaceReplace.INSTANCE.navigateValueSet(range, selectionText, wordRange, word, up); return result; } -} - -/** - * @internal - */ -export class EditorSimpleWorker extends BaseEditorSimpleWorker { - - private _foreignModule: any = null; - - constructor( - private readonly _host: EditorWorkerHost, - private readonly _foreignModuleFactory: IForeignModuleFactory | null - ) { - super(); - } - - public async $ping() { - return 'pong'; - } // ---- BEGIN foreign module support -------------------------------------------------------------------------- - public $loadForeignModule(moduleId: string, createData: any, foreignHostMethods: string[]): Promise { - const proxyMethodRequest = (method: string, args: any[]): Promise => { - return this._host.$fhr(method, args); - }; - - const foreignHost = createProxyObject(foreignHostMethods, proxyMethodRequest); - - const ctx: IWorkerContext = { - host: foreignHost, - getMirrorModels: (): IMirrorModel[] => { - return this._getModels(); - } - }; - - if (this._foreignModuleFactory) { - this._foreignModule = this._foreignModuleFactory(ctx, createData); - // static foreing module - return Promise.resolve(getAllMethodNames(this._foreignModule)); - } - - return new Promise((resolve, reject) => { - - const onModuleCallback = (foreignModule: { create: IForeignModuleFactory }) => { - this._foreignModule = foreignModule.create(ctx, createData); - resolve(getAllMethodNames(this._foreignModule)); - }; - - const url = FileAccess.asBrowserUri(`${moduleId}.js` as AppResourcePath).toString(true); - import(`${url}`).then(onModuleCallback).catch(reject); - }); - } - // foreign method request public $fmr(method: string, args: any[]): Promise { if (!this._foreignModule || typeof this._foreignModule[method] !== 'function') { @@ -579,15 +521,6 @@ export class EditorSimpleWorker extends BaseEditorSimpleWorker { // ---- END foreign module support -------------------------------------------------------------------------- } -/** - * Defines the worker entry point. Must be exported and named `create`. - * @skipMangle - * @internal - */ -export function create(workerServer: IWorkerServer): IRequestHandler { - return new EditorSimpleWorker(EditorWorkerHost.getChannel(workerServer), null); -} - // This is only available in a Web Worker declare function importScripts(...urls: string[]): void; diff --git a/src/vs/editor/common/services/editorWebWorkerMain.ts b/src/vs/editor/common/services/editorWebWorkerMain.ts new file mode 100644 index 00000000..2094b0f7 --- /dev/null +++ b/src/vs/editor/common/services/editorWebWorkerMain.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 { bootstrapWebWorker } from '../../../base/common/worker/webWorkerBootstrap.js'; +import { EditorWorker } from './editorWebWorker.js'; + +bootstrapWebWorker(() => new EditorWorker(null)); diff --git a/src/vs/editor/common/services/editorWorker.ts b/src/vs/editor/common/services/editorWorker.ts index c7b03cd7..fc0f44fa 100644 --- a/src/vs/editor/common/services/editorWorker.ts +++ b/src/vs/editor/common/services/editorWorker.ts @@ -10,7 +10,7 @@ import { IChange } from '../diff/legacyLinesDiffComputer.js'; import { IColorInformation, IInplaceReplaceSupportResult, TextEdit } from '../languages.js'; import { UnicodeHighlighterOptions } from './unicodeTextModelHighlighter.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; -import type { BaseEditorSimpleWorker } from './editorSimpleWorker.js'; +import type { EditorWorker } from './editorWebWorker.js'; import { SectionHeader, FindSectionHeaderOptions } from './findSectionHeaders.js'; export const IEditorWorkerService = createDecorator('editorWorkerService'); @@ -23,7 +23,7 @@ export interface IEditorWorkerService { canComputeUnicodeHighlights(uri: URI): boolean; computedUnicodeHighlights(uri: URI, options: UnicodeHighlighterOptions, range?: IRange): Promise; - /** Implementation in {@link BaseEditorSimpleWorker.computeDiff} */ + /** Implementation in {@link EditorWorker.computeDiff} */ computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise; canComputeDirtyDiff(original: URI, modified: URI): boolean; diff --git a/src/vs/editor/common/services/editorWorkerBootstrap.ts b/src/vs/editor/common/services/editorWorkerBootstrap.ts deleted file mode 100644 index f754fe8f..00000000 --- a/src/vs/editor/common/services/editorWorkerBootstrap.ts +++ /dev/null @@ -1,51 +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 { IWorkerServer, SimpleWorkerServer } from '../../../base/common/worker/simpleWorker.js'; -import { EditorSimpleWorker } from './editorSimpleWorker.js'; -import { EditorWorkerHost } from './editorWorkerHost.js'; - -type MessageEvent = { - data: any; -}; - -declare const globalThis: { - postMessage: (message: any) => void; - onmessage: (event: MessageEvent) => void; -}; - -let initialized = false; - -export function initialize(factory: any) { - if (initialized) { - return; - } - initialized = true; - - const simpleWorker = new SimpleWorkerServer((msg) => { - globalThis.postMessage(msg); - }, (workerServer: IWorkerServer) => new EditorSimpleWorker(EditorWorkerHost.getChannel(workerServer), null)); - - globalThis.onmessage = (e: MessageEvent) => { - simpleWorker.onmessage(e.data); - }; -} - -globalThis.onmessage = (e: MessageEvent) => { - // Ignore first message in this case and initialize if not yet initialized - if (!initialized) { - initialize(null); - } -}; - -type CreateFunction = (ctx: C, data: D) => R; - -export function bootstrapSimpleEditorWorker(createFn: CreateFunction) { - globalThis.onmessage = () => { - initialize((ctx: C, createData: D) => { - return createFn.call(self, ctx, createData); - }); - }; -} diff --git a/src/vs/editor/common/services/editorWorkerHost.ts b/src/vs/editor/common/services/editorWorkerHost.ts index 54751e1c..6223c6b7 100644 --- a/src/vs/editor/common/services/editorWorkerHost.ts +++ b/src/vs/editor/common/services/editorWorkerHost.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkerServer, IWorkerClient } from '../../../base/common/worker/simpleWorker.js'; +import { IWebWorkerServer, IWebWorkerClient } from '../../../base/common/worker/webWorker.js'; export abstract class EditorWorkerHost { public static CHANNEL_NAME = 'editorWorkerHost'; - public static getChannel(workerServer: IWorkerServer): EditorWorkerHost { + public static getChannel(workerServer: IWebWorkerServer): EditorWorkerHost { return workerServer.getChannel(EditorWorkerHost.CHANNEL_NAME); } - public static setChannel(workerClient: IWorkerClient, obj: EditorWorkerHost): void { + public static setChannel(workerClient: IWebWorkerClient, obj: EditorWorkerHost): void { workerClient.setChannel(EditorWorkerHost.CHANNEL_NAME, obj); } diff --git a/src/vs/editor/common/services/textModelSync/textModelSync.impl.ts b/src/vs/editor/common/services/textModelSync/textModelSync.impl.ts index 77bb17be..ccab956b 100644 --- a/src/vs/editor/common/services/textModelSync/textModelSync.impl.ts +++ b/src/vs/editor/common/services/textModelSync/textModelSync.impl.ts @@ -6,14 +6,14 @@ import { IntervalTimer } from '../../../../base/common/async.js'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { IWorkerClient, IWorkerServer } from '../../../../base/common/worker/simpleWorker.js'; +import { IWebWorkerClient, IWebWorkerServer } from '../../../../base/common/worker/webWorker.js'; import { IPosition, Position } from '../../core/position.js'; import { IRange, Range } from '../../core/range.js'; import { ensureValidWordDefinition, getWordAtText, IWordAtPosition } from '../../core/wordHelper.js'; import { IDocumentColorComputerTarget } from '../../languages/defaultDocumentColorsComputer.js'; import { ILinkComputerTarget } from '../../languages/linkComputer.js'; import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from '../../model/mirrorTextModel.js'; -import { IMirrorModel, IWordRange } from '../editorSimpleWorker.js'; +import { IMirrorModel, IWordRange } from '../editorWebWorker.js'; import { IModelService } from '../model.js'; import { IRawModelData, IWorkerTextModelSyncChannelServer } from './textModelSync.protocol.js'; @@ -26,7 +26,7 @@ export const WORKER_TEXT_MODEL_SYNC_CHANNEL = 'workerTextModelSync'; export class WorkerTextModelSyncClient extends Disposable { - public static create(workerClient: IWorkerClient, modelService: IModelService): WorkerTextModelSyncClient { + public static create(workerClient: IWebWorkerClient, modelService: IModelService): WorkerTextModelSyncClient { return new WorkerTextModelSyncClient( workerClient.getChannel(WORKER_TEXT_MODEL_SYNC_CHANNEL), modelService @@ -136,7 +136,7 @@ export class WorkerTextModelSyncServer implements IWorkerTextModelSyncChannelSer this._models = Object.create(null); } - public bindToServer(workerServer: IWorkerServer): void { + public bindToServer(workerServer: IWebWorkerServer): void { workerServer.setChannel(WORKER_TEXT_MODEL_SYNC_CHANNEL, this); } diff --git a/src/vs/editor/common/services/textResourceConfiguration.ts b/src/vs/editor/common/services/textResourceConfiguration.ts index f2edf685..da2c0a8b 100644 --- a/src/vs/editor/common/services/textResourceConfiguration.ts +++ b/src/vs/editor/common/services/textResourceConfiguration.ts @@ -72,7 +72,7 @@ export interface ITextResourceConfigurationService { * @param configurationTarget Optional target into which the configuration has to be updated. * If not specified, target will be derived by checking where the configuration is defined. */ - updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise; + updateValue(resource: URI | undefined, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise; } diff --git a/src/vs/editor/common/services/textResourceConfigurationService.ts b/src/vs/editor/common/services/textResourceConfigurationService.ts index b8534a81..04420d7f 100644 --- a/src/vs/editor/common/services/textResourceConfigurationService.ts +++ b/src/vs/editor/common/services/textResourceConfigurationService.ts @@ -37,8 +37,8 @@ export class TextResourceConfigurationService extends Disposable implements ITex return this._getValue(resource, null, typeof arg2 === 'string' ? arg2 : undefined); } - updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise { - const language = this.getLanguage(resource, null); + updateValue(resource: URI | undefined, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise { + const language = resource ? this.getLanguage(resource, null) : null; const configurationValue = this.configurationService.inspect(key, { resource, overrideIdentifier: language }); if (configurationTarget === undefined) { configurationTarget = this.deriveConfigurationTarget(configurationValue, language); 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 00000000..b5b6e701 --- /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/textModelTreeSitter.ts b/src/vs/editor/common/services/treeSitter/textModelTreeSitter.ts new file mode 100644 index 00000000..5c7116a3 --- /dev/null +++ b/src/vs/editor/common/services/treeSitter/textModelTreeSitter.ts @@ -0,0 +1,781 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { ITreeSitterParseResult, ITextModelTreeSitter, RangeChange, TreeParseUpdateEvent, ITreeSitterImporter, ModelTreeUpdateEvent } from '../treeSitterParserService.js'; +import { Disposable, DisposableStore, dispose, IDisposable } from '../../../../base/common/lifecycle.js'; +import { ITextModel } from '../../model.js'; +import { IModelContentChange, IModelContentChangedEvent } from '../../textModelEvents.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { setTimeout0 } from '../../../../base/common/platform.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { CancellationToken, cancelOnDispose } from '../../../../base/common/cancellation.js'; +import { Range } from '../../core/range.js'; +import { LimitedQueue } from '../../../../base/common/async.js'; +import { TextLength } from '../../core/textLength.js'; +import { TreeSitterLanguages } from './treeSitterLanguages.js'; +import { AppResourcePath, FileAccess } from '../../../../base/common/network.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; +import { getClosestPreviousNodes, gotoNthChild, gotoParent, nextSiblingOrParentSibling } from './cursorUtils.js'; + +export interface TextModelTreeSitterItem { + dispose(): void; + textModelTreeSitter: TextModelTreeSitter; + disposables: DisposableStore; +} + +const enum TelemetryParseType { + Full = 'fullParse', + Incremental = 'incrementalParse' +} + +export class TextModelTreeSitter extends Disposable implements ITextModelTreeSitter { + private _onDidChangeParseResult: Emitter = this._register(new Emitter()); + public readonly onDidChangeParseResult: Event = this._onDidChangeParseResult.event; + private _rootTreeSitterTree: TreeSitterParseResult | undefined; + + private _query: Parser.Query | undefined; + // TODO: @alexr00 use a better data structure for this + private _injectionTreeSitterTrees: Map = new Map(); + private _versionId: number = 0; + + get parseResult(): ITreeSitterParseResult | undefined { return this._rootTreeSitterTree; } + + constructor( + readonly textModel: ITextModel, + private readonly _treeSitterLanguages: TreeSitterLanguages, + parseImmediately: boolean = true, + @ITreeSitterImporter private readonly _treeSitterImporter: ITreeSitterImporter, + @ILogService private readonly _logService: ILogService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IFileService private readonly _fileService: IFileService + ) { + super(); + if (parseImmediately) { + this._register(Event.runAndSubscribe(this.textModel.onDidChangeLanguage, (e => this._onDidChangeLanguage(e ? e.newLanguage : this.textModel.getLanguageId())))); + } else { + this._register(this.textModel.onDidChangeLanguage(e => this._onDidChangeLanguage(e ? e.newLanguage : this.textModel.getLanguageId()))); + } + } + + private readonly _parseSessionDisposables = this._register(new DisposableStore()); + private async _onDidChangeLanguage(languageId: string) { + this.parse(languageId); + } + + /** + * Be very careful when making changes to this method as it is easy to introduce race conditions. + */ + public async parse(languageId: string = this.textModel.getLanguageId()): Promise { + this._parseSessionDisposables.clear(); + this._rootTreeSitterTree = undefined; + + const token = cancelOnDispose(this._parseSessionDisposables); + let language: Parser.Language | undefined; + try { + language = await this._getLanguage(languageId, token); + } catch (e) { + if (isCancellationError(e)) { + return; + } + throw e; + } + + const Parser = await this._treeSitterImporter.getParserClass(); + if (token.isCancellationRequested) { + return; + } + + const treeSitterTree = this._parseSessionDisposables.add(new TreeSitterParseResult(new Parser(), languageId, language, this._logService, this._telemetryService)); + this._rootTreeSitterTree = treeSitterTree; + this._parseSessionDisposables.add(treeSitterTree.onDidUpdate(e => this._handleTreeUpdate(e))); + this._parseSessionDisposables.add(this.textModel.onDidChangeContent(e => this._onDidChangeContent(treeSitterTree, [e]))); + this._onDidChangeContent(treeSitterTree, undefined); + if (token.isCancellationRequested) { + return; + } + + return this._rootTreeSitterTree; + } + + private _getLanguage(languageId: string, token: CancellationToken): Promise { + const language = this._treeSitterLanguages.getOrInitLanguage(languageId); + if (language) { + return Promise.resolve(language); + } + const disposables: IDisposable[] = []; + + return new Promise((resolve, reject) => { + disposables.push(this._treeSitterLanguages.onDidAddLanguage(e => { + if (e.id === languageId) { + dispose(disposables); + resolve(e.language); + } + })); + token.onCancellationRequested(() => { + dispose(disposables); + reject(new CancellationError()); + }, undefined, disposables); + }); + } + + private async _handleTreeUpdate(e: TreeParseUpdateEvent, parentTreeResult?: ITreeSitterParseResult, parentLanguage?: string) { + if (e.ranges && (e.versionId >= this._versionId)) { + this._versionId = e.versionId; + const tree = parentTreeResult ?? this._rootTreeSitterTree!; + let injections: Map | undefined; + if (tree.tree) { + injections = await this._collectInjections(tree.tree); + // kick off check for injected languages + if (injections) { + this._processInjections(injections, tree, parentLanguage ?? this.textModel.getLanguageId(), e.includedModelChanges); + } + } + + this._onDidChangeParseResult.fire({ ranges: e.ranges, versionId: e.versionId, tree: this, languageId: this.textModel.getLanguageId(), hasInjections: !!injections && injections.size > 0 }); + } + } + + private _queries: string | undefined; + private async _ensureInjectionQueries() { + if (!this._queries) { + const injectionsQueriesLocation: AppResourcePath = `vs/editor/common/languages/injections/${this.textModel.getLanguageId()}.scm`; + const uri = FileAccess.asFileUri(injectionsQueriesLocation); + if (!(await this._fileService.exists(uri))) { + this._queries = ''; + } else if (this._fileService.hasProvider(uri)) { + const query = await this._fileService.readFile(uri); + this._queries = query.value.toString(); + } else { + this._queries = ''; + } + } + return this._queries; + } + + private async _getQuery() { + if (!this._query) { + const language = await this._treeSitterLanguages.getLanguage(this.textModel.getLanguageId()); + if (!language) { + return; + } + const queries = await this._ensureInjectionQueries(); + if (queries === '') { + return; + } + const Query = await this._treeSitterImporter.getQueryClass(); + this._query = new Query(language, queries); + } + return this._query; + } + + private async _collectInjections(tree: Parser.Tree): Promise | undefined> { + const query = await this._getQuery(); + if (!query) { + return; + } + + if (!tree?.rootNode) { + // need to check the root node here as `walk` will throw if not defined. + return; + } + + const cursor = tree.walk(); + const injections: Map = new Map(); + let hasNext = true; + + while (hasNext) { + hasNext = await this._processNode(cursor, query, injections); + // Yield periodically + await new Promise(resolve => setTimeout0(resolve)); + } + + return this._mergeAdjacentRanges(injections); + } + + private _processNode(cursor: Parser.TreeCursor, query: Parser.Query, injections: Map): boolean { + const node = cursor.currentNode; + const nodeLineCount = node.endPosition.row - node.startPosition.row; + + // We check the node line count to avoid processing large nodes in one go as that can cause performance issues. + if (nodeLineCount <= 1000) { + this._processCaptures(query, node, injections); + // Move to next sibling or up and over + return cursor.gotoNextSibling() || this.gotoNextSiblingOfAncestor(cursor); + } else { + // Node is too large, go to first child or next sibling + return cursor.gotoFirstChild() || cursor.gotoNextSibling() || this.gotoNextSiblingOfAncestor(cursor); + } + } + + private _processCaptures(query: Parser.Query, node: Parser.Node, injections: Map): void { + const captures = query.captures(node); + for (const capture of captures) { + const injectionLanguage = capture.setProperties?.['injection.language']; + if (injectionLanguage) { + const range = this._createRangeFromNode(capture.node); + if (!injections.has(injectionLanguage)) { + injections.set(injectionLanguage, []); + } + injections.get(injectionLanguage)?.push(range); + } + } + } + + private _createRangeFromNode(node: Parser.Node): Parser.Range { + return { + startIndex: node.startIndex, + endIndex: node.endIndex, + startPosition: { row: node.startPosition.row, column: node.startPosition.column }, + endPosition: { row: node.endPosition.row, column: node.endPosition.column } + }; + } + + private _mergeAdjacentRanges(injections: Map): Map { + for (const [languageId, ranges] of injections) { + if (ranges.length <= 1) { + continue; + } + + const mergedRanges: Parser.Range[] = []; + let current = ranges[0]; + + for (let i = 1; i < ranges.length; i++) { + const next = ranges[i]; + if (next.startIndex <= current.endIndex) { + current = this._mergeRanges(current, next); + } else { + mergedRanges.push(current); + current = next; + } + } + mergedRanges.push(current); + + injections.set(languageId, mergedRanges); + } + + return injections; + } + + private _mergeRanges(current: Parser.Range, next: Parser.Range): Parser.Range { + return { + startIndex: current.startIndex, + endIndex: Math.max(current.endIndex, next.endIndex), + startPosition: current.startPosition, + endPosition: next.endPosition.row > current.endPosition.row ? + next.endPosition : + current.endPosition + }; + } + + private async _processInjections( + injections: Map, + parentTree: ITreeSitterParseResult, + parentLanguage: string, + modelChanges: IModelContentChangedEvent[] | undefined + ): Promise { + for (const [languageId, ranges] of injections) { + const language = await this._treeSitterLanguages.getLanguage(languageId); + if (!language) { + continue; + } + + const treeSitterTree = await this._getOrCreateInjectedTree(languageId, language, parentTree, parentLanguage); + if (treeSitterTree) { + this._onDidChangeContent(treeSitterTree, modelChanges, ranges); + } + } + } + + private async _getOrCreateInjectedTree( + languageId: string, + language: Parser.Language, + parentTree: ITreeSitterParseResult, + parentLanguage: string + ): Promise { + let treeSitterTree = this._injectionTreeSitterTrees.get(languageId); + if (!treeSitterTree) { + const Parser = await this._treeSitterImporter.getParserClass(); + treeSitterTree = new TreeSitterParseResult(new Parser(), languageId, language, this._logService, this._telemetryService); + this._parseSessionDisposables.add(treeSitterTree.onDidUpdate(e => this._handleTreeUpdate(e, parentTree, parentLanguage))); + this._injectionTreeSitterTrees.set(languageId, treeSitterTree); + } + return treeSitterTree; + } + + private gotoNextSiblingOfAncestor(cursor: Parser.TreeCursor): boolean { + while (cursor.gotoParent()) { + if (cursor.gotoNextSibling()) { + return true; + } + } + return false; + } + + getInjection(offset: number, parentLanguage: string): ITreeSitterParseResult | undefined { + if (this._injectionTreeSitterTrees.size === 0) { + return undefined; + } + let hasFoundParentLanguage = parentLanguage === this.textModel.getLanguageId(); + + for (const [_, treeSitterTree] of this._injectionTreeSitterTrees) { + if (treeSitterTree.tree) { + if (hasFoundParentLanguage && treeSitterTree.ranges?.find(r => r.startIndex <= offset && r.endIndex >= offset)) { + return treeSitterTree; + } + if (!hasFoundParentLanguage && treeSitterTree.languageId === parentLanguage) { + hasFoundParentLanguage = true; + } + } + } + return undefined; + } + + private _onDidChangeContent(treeSitterTree: TreeSitterParseResult, change: IModelContentChangedEvent[] | undefined, ranges?: Parser.Range[]) { + treeSitterTree.onDidChangeContent(this.textModel, change, ranges); + } +} + +export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResult { + private _tree: Parser.Tree | undefined; + private _lastFullyParsed: Parser.Tree | undefined; + private _lastFullyParsedWithEdits: Parser.Tree | undefined; + private readonly _onDidUpdate: Emitter = new Emitter(); + public readonly onDidUpdate: Event = this._onDidUpdate.event; + private _versionId: number = 0; + private _editVersion: number = 0; + get versionId() { + return this._versionId; + } + private _isDisposed: boolean = false; + constructor(public readonly parser: Parser.Parser, + public readonly languageId: string, + public /** exposed for tests **/ readonly language: Parser.Language, + private readonly _logService: ILogService, + private readonly _telemetryService: ITelemetryService) { + this.parser.setLanguage(language); + } + dispose(): void { + this._isDisposed = true; + this._onDidUpdate.dispose(); + this._tree?.delete(); + this._lastFullyParsed?.delete(); + this._lastFullyParsedWithEdits?.delete(); + this.parser?.delete(); + } + get tree() { return this._lastFullyParsed; } + get isDisposed() { return this._isDisposed; } + + private findChangedNodes(newTree: Parser.Tree, oldTree: Parser.Tree): Parser.Range[] { + const newCursor = newTree.walk(); + const oldCursor = oldTree.walk(); + + const nodes: Parser.Range[] = []; + let next = true; + + do { + if (newCursor.currentNode.hasChanges) { + // Check if only one of the children has changes. + // If it's only one, then we go to that child. + // If it's more then, we need to go to each child + // If it's none, then we've found one of our ranges + const newChildren = newCursor.currentNode.children; + const indexChangedChildren: number[] = []; + const changedChildren = newChildren.filter((c, index) => { + if (c?.hasChanges || (oldCursor.currentNode.children.length <= index)) { + indexChangedChildren.push(index); + return true; + } + return false; + }); + // If we have changes and we *had* an error, the whole node should be refreshed. + if ((changedChildren.length === 0) || (newCursor.currentNode.hasError !== oldCursor.currentNode.hasError)) { + // walk up again until we get to the first one that's named as unnamed nodes can be too granular + while (newCursor.currentNode.parent && next && !newCursor.currentNode.isNamed) { + next = gotoParent(newCursor, oldCursor); + } + // Use the end position of the previous node and the start position of the current node + const newNode = newCursor.currentNode; + const closestPreviousNode = getClosestPreviousNodes(newCursor, newTree) ?? newNode; + nodes.push({ + startIndex: closestPreviousNode.startIndex, + endIndex: newNode.endIndex, + startPosition: closestPreviousNode.startPosition, + endPosition: newNode.endPosition + }); + next = nextSiblingOrParentSibling(newCursor, oldCursor); + } else if (changedChildren.length >= 1) { + next = gotoNthChild(newCursor, oldCursor, indexChangedChildren[0]); + } + } else { + next = nextSiblingOrParentSibling(newCursor, oldCursor); + } + } while (next); + + return nodes; + } + + private findTreeChanges(newTree: Parser.Tree, changedNodes: Parser.Range[], newRanges: Parser.Range[]): RangeChange[] { + let newRangeIndex = 0; + 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; + } + } + + 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() && cursor.currentNode.isNamed) { + 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.endIndex > node.startIndex) { + // Found the starting point of our nodes + nodesInRange.push(cursor.currentNode); + do { + child = cursor.gotoNextSibling(); + } while (child && (cursor.endIndex < node.endIndex)); + + nodesInRange.push(cursor.currentNode); + break; + } + child = cursor.gotoNextSibling(); + } + } else { + nodesInRange = [cursor.currentNode]; + } + + // Fill in gaps between nodes + // Reset the cursor to the first node in the range; + while (cursor.currentNode.id !== nodesInRange[0].id) { + cursor.gotoPreviousSibling(); + } + const previousNode = getClosestPreviousNodes(cursor, newTree); + const startPosition = previousNode ? previousNode.endPosition : nodesInRange[0].startPosition; + const startIndex = previousNode ? previousNode.endIndex : nodesInRange[0].startIndex; + const endPosition = nodesInRange[nodesInRange.length - 1].endPosition; + const endIndex = nodesInRange[nodesInRange.length - 1].endIndex; + + const newChange = { newRange: new Range(startPosition.row + 1, startPosition.column + 1, endPosition.row + 1, endPosition.column + 1), newRangeStartOffset: startIndex, newRangeEndOffset: endIndex }; + if ((newRangeIndex < newRanges.length) && rangesIntersect(newRanges[newRangeIndex], { startIndex, endIndex, startPosition, endPosition })) { + // combine the new change with the range + if (newRanges[newRangeIndex].startIndex < newChange.newRangeStartOffset) { + newChange.newRange = newChange.newRange.setStartPosition(newRanges[newRangeIndex].startPosition.row + 1, newRanges[newRangeIndex].startPosition.column + 1); + newChange.newRangeStartOffset = newRanges[newRangeIndex].startIndex; + } + if (newRanges[newRangeIndex].endIndex > newChange.newRangeEndOffset) { + newChange.newRange = newChange.newRange.setEndPosition(newRanges[newRangeIndex].endPosition.row + 1, newRanges[newRangeIndex].endPosition.column + 1); + newChange.newRangeEndOffset = newRanges[newRangeIndex].endIndex; + } + newRangeIndex++; + } else if (newRangeIndex < newRanges.length && newRanges[newRangeIndex].endIndex < newChange.newRangeStartOffset) { + // add the full range to the merged changes + mergedChanges.push({ + newRange: new Range(newRanges[newRangeIndex].startPosition.row + 1, newRanges[newRangeIndex].startPosition.column + 1, newRanges[newRangeIndex].endPosition.row + 1, newRanges[newRangeIndex].endPosition.column + 1), + newRangeStartOffset: newRanges[newRangeIndex].startIndex, + newRangeEndOffset: newRanges[newRangeIndex].endIndex + }); + } + + 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 this._constrainRanges(mergedChanges); + } + + private _constrainRanges(changes: RangeChange[]): RangeChange[] { + if (!this.ranges) { + return changes; + } + + const constrainedChanges: RangeChange[] = []; + let changesIndex = 0; + let rangesIndex = 0; + while (changesIndex < changes.length && rangesIndex < this.ranges.length) { + const change = changes[changesIndex]; + const range = this.ranges[rangesIndex]; + if (change.newRangeEndOffset < range.startIndex) { + // Change is before the range, move to the next change + changesIndex++; + } else if (change.newRangeStartOffset > range.endIndex) { + // Change is after the range, move to the next range + rangesIndex++; + } else { + // Change is within the range, constrain it + const newRangeStartOffset = Math.max(change.newRangeStartOffset, range.startIndex); + const newRangeEndOffset = Math.min(change.newRangeEndOffset, range.endIndex); + const newRange = change.newRange.intersectRanges(new Range(range.startPosition.row + 1, range.startPosition.column + 1, range.endPosition.row + 1, range.endPosition.column + 1))!; + constrainedChanges.push({ + newRange, + newRangeEndOffset, + newRangeStartOffset + }); + // Remove the intersected range from the current change + if (newRangeEndOffset < change.newRangeEndOffset) { + change.newRange = Range.fromPositions(newRange.getEndPosition(), change.newRange.getEndPosition()); + change.newRangeStartOffset = newRangeEndOffset + 1; + } else { + // Move to the next change + changesIndex++; + } + } + } + + return constrainedChanges; + } + + private _unfiredChanges: IModelContentChangedEvent[] | undefined; + private _onDidChangeContentQueue: LimitedQueue = new LimitedQueue(); + public onDidChangeContent(model: ITextModel, changes: IModelContentChangedEvent[] | undefined, ranges?: Parser.Range[]): void { + const version = model.getVersionId(); + if (version === this._editVersion) { + return; + } + + let newRanges: Parser.Range[] = []; + if (ranges) { + newRanges = this._setRanges(ranges); + } + + if (changes && changes.length > 0) { + if (this._unfiredChanges) { + this._unfiredChanges.push(...changes); + } else { + this._unfiredChanges = changes; + } + for (const change of changes) { + this._applyEdits(change.changes, version); + } + } else { + this._applyEdits([], version); + } + + this._onDidChangeContentQueue.queue(async () => { + if (this.isDisposed) { + // No need to continue the queue if we are disposed + return; + } + + const oldTree = this._lastFullyParsed; + let changedNodes: Parser.Range[] | undefined; + if (this._lastFullyParsedWithEdits && this._lastFullyParsed) { + changedNodes = this.findChangedNodes(this._lastFullyParsedWithEdits, this._lastFullyParsed); + } + + const completed = await this._parseAndUpdateTree(model, version); + if (completed) { + let ranges: RangeChange[] | undefined; + if (!changedNodes) { + if (this._ranges) { + ranges = this._ranges.map(r => ({ newRange: new Range(r.startPosition.row + 1, r.startPosition.column + 1, r.endPosition.row + 1, r.endPosition.column + 1), oldRangeLength: r.endIndex - r.startIndex, newRangeStartOffset: r.startIndex, newRangeEndOffset: r.endIndex })); + } else { + ranges = [{ newRange: model.getFullModelRange(), newRangeStartOffset: 0, newRangeEndOffset: model.getValueLength() }]; + } + } else if (oldTree && changedNodes) { + ranges = this.findTreeChanges(completed, changedNodes, newRanges); + } + const changes = this._unfiredChanges ?? []; + this._unfiredChanges = undefined; + this._onDidUpdate.fire({ language: this.languageId, ranges, versionId: version, tree: completed, includedModelChanges: changes }); + } + }); + } + + private _applyEdits(changes: IModelContentChange[], version: number) { + for (const change of changes) { + const originalTextLength = TextLength.ofRange(Range.lift(change.range)); + const newTextLength = TextLength.ofText(change.text); + const summedTextLengths = change.text.length === 0 ? newTextLength : originalTextLength.add(newTextLength); + const edit = { + startIndex: change.rangeOffset, + oldEndIndex: change.rangeOffset + change.rangeLength, + newEndIndex: change.rangeOffset + change.text.length, + startPosition: { row: change.range.startLineNumber - 1, column: change.range.startColumn - 1 }, + oldEndPosition: { row: change.range.endLineNumber - 1, column: change.range.endColumn - 1 }, + newEndPosition: { row: change.range.startLineNumber + summedTextLengths.lineCount - 1, column: summedTextLengths.lineCount ? summedTextLengths.columnCount : (change.range.endColumn + summedTextLengths.columnCount) } + }; + this._tree?.edit(edit); + this._lastFullyParsedWithEdits?.edit(edit); + } + this._editVersion = version; + } + + private async _parseAndUpdateTree(model: ITextModel, version: number): Promise { + const tree = await this._parse(model); + if (tree) { + this._tree?.delete(); + this._tree = tree; + this._lastFullyParsed?.delete(); + this._lastFullyParsed = tree.copy(); + this._lastFullyParsedWithEdits?.delete(); + this._lastFullyParsedWithEdits = tree.copy(); + this._versionId = version; + return tree; + } else if (!this._tree) { + // No tree means this is the initial parse and there were edits + // parse function doesn't handle this well and we can end up with an incorrect tree, so we reset + this.parser.reset(); + } + return undefined; + } + + private _parse(model: ITextModel): Promise { + let parseType: TelemetryParseType = TelemetryParseType.Full; + if (this.tree) { + parseType = TelemetryParseType.Incremental; + } + return this._parseAndYield(model, parseType); + } + + private async _parseAndYield(model: ITextModel, parseType: TelemetryParseType): Promise { + let time: number = 0; + let passes: number = 0; + const inProgressVersion = this._editVersion; + let newTree: Parser.Tree | null | undefined; + this._lastYieldTime = performance.now(); + + do { + const timer = performance.now(); + try { + newTree = this.parser.parse((index: number, position?: Parser.Point) => this._parseCallback(model, index), this._tree, { progressCallback: this._parseProgressCallback.bind(this), includedRanges: this._ranges }); + } catch (e) { + // parsing can fail when the timeout is reached, will resume upon next loop + } finally { + time += performance.now() - timer; + passes++; + } + + // So long as this isn't the initial parse, even if the model changes and edits are applied, the tree parsing will continue correctly after the await. + await new Promise(resolve => setTimeout0(resolve)); + + } while (!model.isDisposed() && !this.isDisposed && !newTree && inProgressVersion === model.getVersionId()); + this.sendParseTimeTelemetry(parseType, time, passes); + return (newTree && (inProgressVersion === model.getVersionId())) ? newTree : undefined; + } + + private _lastYieldTime: number = 0; + private _parseProgressCallback(state: Parser.ParseState) { + const now = performance.now(); + if (now - this._lastYieldTime > 50) { + this._lastYieldTime = now; + return true; + } + return false; + } + + private _parseCallback(textModel: ITextModel, index: number): string | undefined { + try { + return textModel.getTextBuffer().getNearestChunk(index); + } catch (e) { + this._logService.debug('Error getting chunk for tree-sitter parsing', e); + } + return undefined; + } + + private _ranges: Parser.Range[] | undefined; + private _setRanges(newRanges: Parser.Range[]): Parser.Range[] { + const unKnownRanges: Parser.Range[] = []; + // If we have existing ranges, find the parts of the new ranges that are not included in the existing ones + if (this._ranges) { + for (const newRange of newRanges) { + let isFullyIncluded = false; + + for (let i = 0; i < this._ranges.length; i++) { + const existingRange = this._ranges[i]; + + if (rangesEqual(existingRange, newRange) || rangesIntersect(existingRange, newRange)) { + isFullyIncluded = true; + break; + } + } + + if (!isFullyIncluded) { + unKnownRanges.push(newRange); + } + } + } else { + // No existing ranges, all new ranges are unknown + unKnownRanges.push(...newRanges); + } + + this._ranges = newRanges; + return unKnownRanges; + } + + get ranges(): Parser.Range[] | undefined { + return this._ranges; + } + + private sendParseTimeTelemetry(parseType: TelemetryParseType, time: number, passes: number): void { + this._logService.debug(`Tree parsing (${parseType}) took ${time} ms and ${passes} passes.`); + type ParseTimeClassification = { + owner: 'alexr00'; + comment: 'Used to understand how long it takes to parse a tree-sitter tree'; + languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The programming language ID.' }; + time: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The ms it took to parse' }; + passes: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The number of passes it took to parse' }; + }; + if (parseType === TelemetryParseType.Full) { + this._telemetryService.publicLog2<{ languageId: string; time: number; passes: number }, ParseTimeClassification>(`treeSitter.fullParse`, { languageId: this.languageId, time, passes }); + } else { + this._telemetryService.publicLog2<{ languageId: string; time: number; passes: number }, ParseTimeClassification>(`treeSitter.incrementalParse`, { languageId: this.languageId, time, passes }); + } + } +} + +function rangesEqual(a: Parser.Range, b: Parser.Range) { + return (a.startPosition.row === b.startPosition.row) + && (a.startPosition.column === b.startPosition.column) + && (a.endPosition.row === b.endPosition.row) + && (a.endPosition.column === b.endPosition.column) + && (a.startIndex === b.startIndex) + && (a.endIndex === b.endIndex); +} + +function rangesIntersect(a: Parser.Range, b: Parser.Range) { + return (a.startIndex <= b.startIndex && a.endIndex >= b.startIndex) || + (b.startIndex <= a.startIndex && b.endIndex >= a.startIndex); +} diff --git a/src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts b/src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts new file mode 100644 index 00000000..32287322 --- /dev/null +++ b/src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as Parser from '@vscode/tree-sitter-wasm'; +import { AppResourcePath, FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../../base/common/network.js'; +import { ITreeSitterImporter } from '../treeSitterParserService.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { canASAR } from '../../../../amdX.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { PromiseResult } from '../../../../base/common/observable.js'; + +export const MODULE_LOCATION_SUBPATH = `@vscode/tree-sitter-wasm/wasm`; + +export function getModuleLocation(environmentService: IEnvironmentService): AppResourcePath { + return `${(canASAR && environmentService.isBuilt) ? nodeModulesAsarUnpackedPath : nodeModulesPath}/${MODULE_LOCATION_SUBPATH}`; +} + +export class TreeSitterLanguages extends Disposable { + private _languages: AsyncCache = new AsyncCache(); + public /*exposed for tests*/ readonly _onDidAddLanguage: Emitter<{ id: string; language: Parser.Language }> = this._register(new Emitter()); + /** + * If you're looking for a specific language, make sure to check if it already exists with `getLanguage` as it will kick off the process to add it if it doesn't exist. + */ + public readonly onDidAddLanguage: Event<{ id: string; language: Parser.Language }> = this._onDidAddLanguage.event; + + constructor(private readonly _treeSitterImporter: ITreeSitterImporter, + private readonly _fileService: IFileService, + private readonly _environmentService: IEnvironmentService, + private readonly _registeredLanguages: Map, + ) { + super(); + } + + public getOrInitLanguage(languageId: string): Parser.Language | undefined { + if (this._languages.isCached(languageId)) { + return this._languages.getSyncIfCached(languageId); + } else { + // kick off adding the language, but don't wait + this._addLanguage(languageId); + return undefined; + } + } + + public async getLanguage(languageId: string): Promise { + if (this._languages.isCached(languageId)) { + return this._languages.getSyncIfCached(languageId); + } else { + await this._addLanguage(languageId); + return this._languages.get(languageId); + } + } + + private async _addLanguage(languageId: string): Promise { + const languagePromise = this._languages.get(languageId); + if (!languagePromise) { + this._languages.set(languageId, this._fetchLanguage(languageId)); + const language = await this._languages.get(languageId); + if (!language) { + return undefined; + } + this._onDidAddLanguage.fire({ id: languageId, language }); + } + } + + private async _fetchLanguage(languageId: string): Promise { + const grammarName = this._registeredLanguages.get(languageId); + const languageLocation = this._getLanguageLocation(languageId); + if (!grammarName || !languageLocation) { + return undefined; + } + const wasmPath: AppResourcePath = `${languageLocation}/${grammarName}.wasm`; + const languageFile = await (this._fileService.readFile(FileAccess.asFileUri(wasmPath))); + const Language = await this._treeSitterImporter.getLanguageClass(); + return Language.load(languageFile.value.buffer); + } + + private _getLanguageLocation(languageId: string): AppResourcePath | undefined { + const grammarName = this._registeredLanguages.get(languageId); + if (!grammarName) { + return undefined; + } + return getModuleLocation(this._environmentService); + } +} + +class AsyncCache { + private readonly _values = new Map>(); + + set(key: TKey, promise: Promise) { + this._values.set(key, new PromiseWithSyncAccess(promise)); + } + + get(key: TKey): Promise | undefined { + return this._values.get(key)?.promise; + } + + getSyncIfCached(key: TKey): T | undefined { + return this._values.get(key)?.result?.data; + } + + isCached(key: TKey): boolean { + return this._values.get(key)?.result !== undefined; + } +} + +class PromiseWithSyncAccess { + private _result: PromiseResult | undefined; + /** + * Returns undefined if the promise did not resolve yet. + */ + get result(): PromiseResult | undefined { + return this._result; + } + + constructor(public readonly promise: Promise) { + promise.then(result => { + this._result = new PromiseResult(result, undefined); + }).catch(e => { + this._result = new PromiseResult(undefined, e); + }); + } +} diff --git a/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts b/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts index 49c61dd2..174f3154 100644 --- a/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts +++ b/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts @@ -4,554 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import type * as Parser from '@vscode/tree-sitter-wasm'; -import { AppResourcePath, FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../../base/common/network.js'; -import { EDITOR_EXPERIMENTAL_PREFER_TREESITTER, ITreeSitterParserService, ITreeSitterParseResult, ITextModelTreeSitter, RangeChange, TreeUpdateEvent, TreeParseUpdateEvent, ITreeSitterImporter, TREESITTER_ALLOWED_SUPPORT } from '../treeSitterParserService.js'; +import { AppResourcePath, FileAccess } from '../../../../base/common/network.js'; +import { EDITOR_EXPERIMENTAL_PREFER_TREESITTER, ITreeSitterParserService, ITextModelTreeSitter, TreeUpdateEvent, ITreeSitterImporter, TREESITTER_ALLOWED_SUPPORT, ModelTreeUpdateEvent } from '../treeSitterParserService.js'; import { IModelService } from '../model.js'; -import { Disposable, DisposableMap, DisposableStore, dispose, IDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ITextModel } from '../../model.js'; import { IFileService } from '../../../../platform/files/common/files.js'; -import { IModelContentChange, IModelContentChangedEvent } from '../../textModelEvents.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { ILogService } from '../../../../platform/log/common/log.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { setTimeout0 } from '../../../../base/common/platform.js'; -import { canASAR } from '../../../../amdX.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { CancellationToken, cancelOnDispose } from '../../../../base/common/cancellation.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -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 { TextModelTreeSitter, TextModelTreeSitterItem } from './textModelTreeSitter.js'; +import { getModuleLocation, TreeSitterLanguages } from './treeSitterLanguages.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; const EDITOR_TREESITTER_TELEMETRY = 'editor.experimental.treeSitterTelemetry'; -const MODULE_LOCATION_SUBPATH = `@vscode/tree-sitter-wasm/wasm`; const FILENAME_TREESITTER_WASM = `tree-sitter.wasm`; -function getModuleLocation(environmentService: IEnvironmentService): AppResourcePath { - return `${(canASAR && environmentService.isBuilt) ? nodeModulesAsarUnpackedPath : nodeModulesPath}/${MODULE_LOCATION_SUBPATH}`; -} - -export class TextModelTreeSitter extends Disposable implements ITextModelTreeSitter { - private _onDidChangeParseResult: Emitter = this._register(new Emitter()); - public readonly onDidChangeParseResult: Event = this._onDidChangeParseResult.event; - private _parseResult: TreeSitterParseResult | undefined; - private _versionId: number = 0; - - get parseResult(): ITreeSitterParseResult | undefined { return this._parseResult; } - - constructor(readonly model: ITextModel, - private readonly _treeSitterLanguages: TreeSitterLanguages, - private readonly _treeSitterImporter: ITreeSitterImporter, - private readonly _logService: ILogService, - private readonly _telemetryService: ITelemetryService, - parseImmediately: boolean = true - ) { - super(); - if (parseImmediately) { - this._register(Event.runAndSubscribe(this.model.onDidChangeLanguage, (e => this._onDidChangeLanguage(e ? e.newLanguage : this.model.getLanguageId())))); - } else { - this._register(this.model.onDidChangeLanguage(e => this._onDidChangeLanguage(e ? e.newLanguage : this.model.getLanguageId()))); - } - } - - private readonly _parseSessionDisposables = this._register(new DisposableStore()); - /** - * Be very careful when making changes to this method as it is easy to introduce race conditions. - */ - private async _onDidChangeLanguage(languageId: string) { - this.parse(languageId); - } - - public async parse(languageId: string = this.model.getLanguageId()): Promise { - this._parseSessionDisposables.clear(); - this._parseResult = undefined; - - const token = cancelOnDispose(this._parseSessionDisposables); - let language: Parser.Language | undefined; - try { - language = await this._getLanguage(languageId, token); - } catch (e) { - if (isCancellationError(e)) { - return; - } - throw e; - } - - const Parser = await this._treeSitterImporter.getParserClass(); - if (token.isCancellationRequested) { - return; - } - - 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)) { - this._versionId = e.versionId; - this._onDidChangeParseResult.fire({ ranges: e.ranges, versionId: e.versionId }); - } - })); - this._parseSessionDisposables.add(this.model.onDidChangeContent(e => this._onDidChangeContent(treeSitterTree, e))); - this._onDidChangeContent(treeSitterTree, undefined); - if (token.isCancellationRequested) { - return; - } - - return this._parseResult; - } - - private _getLanguage(languageId: string, token: CancellationToken): Promise { - const language = this._treeSitterLanguages.getOrInitLanguage(languageId); - if (language) { - return Promise.resolve(language); - } - const disposables: IDisposable[] = []; - - return new Promise((resolve, reject) => { - disposables.push(this._treeSitterLanguages.onDidAddLanguage(e => { - if (e.id === languageId) { - dispose(disposables); - resolve(e.language); - } - })); - token.onCancellationRequested(() => { - dispose(disposables); - reject(new CancellationError()); - }, undefined, disposables); - }); - } - - private _onDidChangeContent(treeSitterTree: TreeSitterParseResult, change: IModelContentChangedEvent | undefined) { - return treeSitterTree.onDidChangeContent(this.model, change); - } -} - -const enum TelemetryParseType { - Full = 'fullParse', - 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; - private _lastFullyParsedWithEdits: Parser.Tree | undefined; - private readonly _onDidUpdate: Emitter = new Emitter(); - public readonly onDidUpdate: Event = this._onDidUpdate.event; - private _versionId: number = 0; - private _editVersion: number = 0; - get versionId() { - return this._versionId; - } - private _isDisposed: boolean = false; - constructor(public readonly parser: Parser.Parser, - public /** exposed for tests **/ readonly language: Parser.Language, - private readonly _logService: ILogService, - private readonly _telemetryService: ITelemetryService) { - this.parser.setLanguage(language); - } - dispose(): void { - this._isDisposed = true; - this._onDidUpdate.dispose(); - this._tree?.delete(); - this._lastFullyParsed?.delete(); - this._lastFullyParsedWithEdits?.delete(); - this.parser?.delete(); - } - get tree() { return this._lastFullyParsed; } - get isDisposed() { return this._isDisposed; } - - private findChangedNodes(newTree: Parser.Tree, oldTree: Parser.Tree): ChangedRange[] { - 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[] = []; - 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. - // If it's only one, then we go to that child. - // If it's more then, we need to go to each child - // If it's none, then we've found one of our ranges - const newChildren = newCursor.currentNode.children; - const indexChangedChildren: number[] = []; - const changedChildren = newChildren.filter((c, index) => { - if (c?.hasChanges) { - indexChangedChildren.push(index); - } - 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) { - // 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(); - } - - 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(); - } else if (changedChildren.length >= 1) { - next = gotoNthChild(indexChangedChildren[0]); - } - } else { - next = nextSiblingOrParentSibling(); - } - } 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; - } - } - - private calculateRangeChange(model: ITextModel, changedNodes: ChangedRange[] | undefined): RangeChange[] | undefined { - if (!changedNodes) { - return undefined; - } - - // 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; - } else { - ranges.push({ newRange: Range.fromPositions(node.newStartPosition, node.newEndPosition), oldRangeLength: node.oldEndIndex - node.oldStartIndex, newRangeStartOffset: node.newStartIndex, newRangeEndOffset: node.newEndIndex }); - } - } - - 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)); - } - } - return ranges; - } - - private _onDidChangeContentQueue: LimitedQueue = new LimitedQueue(); - public onDidChangeContent(model: ITextModel, changes: IModelContentChangedEvent | undefined): void { - const version = model.getVersionId(); - if (version === this._editVersion) { - return; - } - - this._applyEdits(changes?.changes ?? [], version); - - this._onDidChangeContentQueue.queue(async () => { - if (this.isDisposed) { - // No need to continue the queue if we are disposed - return; - } - - let ranges: RangeChange[] | undefined; - if (this._lastFullyParsedWithEdits && this._lastFullyParsed) { - ranges = this.calculateRangeChange(model, 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() }]; - } - this._onDidUpdate.fire({ ranges, versionId: version }); - } - }); - } - - private _applyEdits(changes: IModelContentChange[], version: number) { - for (const change of changes) { - const originalTextLength = TextLength.ofRange(Range.lift(change.range)); - const newTextLength = TextLength.ofText(change.text); - const summedTextLengths = change.text.length === 0 ? newTextLength : originalTextLength.add(newTextLength); - const edit = { - startIndex: change.rangeOffset, - oldEndIndex: change.rangeOffset + change.rangeLength, - newEndIndex: change.rangeOffset + change.text.length, - startPosition: { row: change.range.startLineNumber - 1, column: change.range.startColumn - 1 }, - oldEndPosition: { row: change.range.endLineNumber - 1, column: change.range.endColumn - 1 }, - newEndPosition: { row: change.range.startLineNumber + summedTextLengths.lineCount - 1, column: summedTextLengths.lineCount ? summedTextLengths.columnCount : (change.range.endColumn + summedTextLengths.columnCount) } - }; - this._tree?.edit(edit); - this._lastFullyParsedWithEdits?.edit(edit); - } - this._editVersion = version; - } - - private async _parseAndUpdateTree(model: ITextModel, version: number): Promise { - const tree = await this._parse(model); - if (tree) { - this._tree?.delete(); - this._tree = tree; - this._lastFullyParsed?.delete(); - this._lastFullyParsed = tree.copy(); - this._lastFullyParsedWithEdits?.delete(); - this._lastFullyParsedWithEdits = tree.copy(); - this._versionId = version; - return tree; - } else if (!this._tree) { - // No tree means this is the inial parse and there were edits - // parse function doesn't handle this well and we can end up with an incorrect tree, so we reset - this.parser.reset(); - } - return undefined; - } - - private _parse(model: ITextModel): Promise { - let parseType: TelemetryParseType = TelemetryParseType.Full; - if (this.tree) { - parseType = TelemetryParseType.Incremental; - } - return this._parseAndYield(model, parseType); - } - - private async _parseAndYield(model: ITextModel, parseType: TelemetryParseType): Promise { - const language = model.getLanguageId(); - let time: number = 0; - let passes: number = 0; - const inProgressVersion = this._editVersion; - let newTree: Parser.Tree | null | undefined; - this._lastYieldTime = performance.now(); - - do { - const timer = performance.now(); - try { - newTree = this.parser.parse((index: number, position?: Parser.Point) => this._parseCallback(model, index), this._tree, { progressCallback: this._parseProgressCallback.bind(this) }); - } catch (e) { - // parsing can fail when the timeout is reached, will resume upon next loop - } finally { - time += performance.now() - timer; - passes++; - } - - // So long as this isn't the initial parse, even if the model changes and edits are applied, the tree parsing will continue correctly after the await. - await new Promise(resolve => setTimeout0(resolve)); - - } while (!model.isDisposed() && !this.isDisposed && !newTree && inProgressVersion === model.getVersionId()); - this.sendParseTimeTelemetry(parseType, language, time, passes); - return (newTree && (inProgressVersion === model.getVersionId())) ? newTree : undefined; - } - - private _lastYieldTime: number = 0; - private _parseProgressCallback(state: Parser.ParseState) { - const now = performance.now(); - if (now - this._lastYieldTime > 50) { - this._lastYieldTime = now; - return true; - } - return false; - } - - private _parseCallback(textModel: ITextModel, index: number): string | undefined { - try { - return textModel.getTextBuffer().getNearestChunk(index); - } catch (e) { - this._logService.debug('Error getting chunk for tree-sitter parsing', e); - } - return undefined; - } - - private sendParseTimeTelemetry(parseType: TelemetryParseType, languageId: string, time: number, passes: number): void { - this._logService.debug(`Tree parsing (${parseType}) took ${time} ms and ${passes} passes.`); - type ParseTimeClassification = { - owner: 'alexr00'; - comment: 'Used to understand how long it takes to parse a tree-sitter tree'; - languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The programming language ID.' }; - time: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The ms it took to parse' }; - passes: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The number of passes it took to parse' }; - }; - if (parseType === TelemetryParseType.Full) { - this._telemetryService.publicLog2<{ languageId: string; time: number; passes: number }, ParseTimeClassification>(`treeSitter.fullParse`, { languageId, time, passes }); - } else { - this._telemetryService.publicLog2<{ languageId: string; time: number; passes: number }, ParseTimeClassification>(`treeSitter.incrementalParse`, { languageId, time, passes }); - } - } -} - -export class TreeSitterLanguages extends Disposable { - private _languages: AsyncCache = new AsyncCache(); - public /*exposed for tests*/ readonly _onDidAddLanguage: Emitter<{ id: string; language: Parser.Language }> = this._register(new Emitter()); - /** - * If you're looking for a specific language, make sure to check if it already exists with `getLanguage` as it will kick off the process to add it if it doesn't exist. - */ - public readonly onDidAddLanguage: Event<{ id: string; language: Parser.Language }> = this._onDidAddLanguage.event; - - constructor(private readonly _treeSitterImporter: ITreeSitterImporter, - private readonly _fileService: IFileService, - private readonly _environmentService: IEnvironmentService, - private readonly _registeredLanguages: Map, - ) { - super(); - } - - public getOrInitLanguage(languageId: string): Parser.Language | undefined { - if (this._languages.isCached(languageId)) { - return this._languages.getSyncIfCached(languageId); - } else { - // kick off adding the language, but don't wait - this._addLanguage(languageId); - return undefined; - } - } - - public async getLanguage(languageId: string): Promise { - if (this._languages.isCached(languageId)) { - return this._languages.getSyncIfCached(languageId); - } else { - await this._addLanguage(languageId); - return this._languages.get(languageId); - } - } - - private async _addLanguage(languageId: string): Promise { - const languagePromise = this._languages.get(languageId); - if (!languagePromise) { - this._languages.set(languageId, this._fetchLanguage(languageId)); - const language = await this._languages.get(languageId); - if (!language) { - return undefined; - } - this._onDidAddLanguage.fire({ id: languageId, language }); - } - } - - private async _fetchLanguage(languageId: string): Promise { - const grammarName = this._registeredLanguages.get(languageId); - const languageLocation = this._getLanguageLocation(languageId); - if (!grammarName || !languageLocation) { - return undefined; - } - const wasmPath: AppResourcePath = `${languageLocation}/${grammarName}.wasm`; - const languageFile = await (this._fileService.readFile(FileAccess.asFileUri(wasmPath))); - const Language = await this._treeSitterImporter.getLanguageClass(); - return Language.load(languageFile.value.buffer); - } - - private _getLanguageLocation(languageId: string): AppResourcePath | undefined { - const grammarName = this._registeredLanguages.get(languageId); - if (!grammarName) { - return undefined; - } - return getModuleLocation(this._environmentService); - } -} - -interface TextModelTreeSitterItem { - dispose(): void; - textModelTreeSitter: TextModelTreeSitter; - disposables: DisposableStore; -} - export class TreeSitterTextModelService extends Disposable implements ITreeSitterParserService { readonly _serviceBrand: undefined; private _init!: Promise; @@ -567,11 +35,10 @@ export class TreeSitterTextModelService extends Disposable implements ITreeSitte constructor(@IModelService private readonly _modelService: IModelService, @IFileService fileService: IFileService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @ILogService private readonly _logService: ILogService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @ITreeSitterImporter private readonly _treeSitterImporter: ITreeSitterImporter + @ITreeSitterImporter private readonly _treeSitterImporter: ITreeSitterImporter, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); this._treeSitterLanguages = this._register(new TreeSitterLanguages(this._treeSitterImporter, fileService, this._environmentService, this._registeredLanguages)); @@ -588,9 +55,9 @@ export class TreeSitterTextModelService extends Disposable implements ITreeSitte return this._treeSitterLanguages.getOrInitLanguage(languageId); } - getParseResult(textModel: ITextModel): ITreeSitterParseResult | undefined { + getParseResult(textModel: ITextModel): ITextModelTreeSitter | undefined { const textModelTreeSitter = this._textModelTreeSitters.get(textModel); - return textModelTreeSitter?.textModelTreeSitter.parseResult; + return textModelTreeSitter?.textModelTreeSitter; } /** @@ -702,10 +169,10 @@ export class TreeSitterTextModelService extends Disposable implements ITreeSitte } private _createTextModelTreeSitter(model: ITextModel, parseImmediately: boolean = true): ITextModelTreeSitter { - const textModelTreeSitter = new TextModelTreeSitter(model, this._treeSitterLanguages, this._treeSitterImporter, this._logService, this._telemetryService, parseImmediately); + const textModelTreeSitter = this._instantiationService.createInstance(TextModelTreeSitter, model, this._treeSitterLanguages, parseImmediately); const disposables = new DisposableStore(); disposables.add(textModelTreeSitter); - disposables.add(textModelTreeSitter.onDidChangeParseResult(change => this._onDidUpdateTree.fire({ textModel: model, ranges: change.ranges ?? [], versionId: change.versionId }))); + disposables.add(textModelTreeSitter.onDidChangeParseResult((e) => this._handleOnDidChangeParseResult(e, model))); this._textModelTreeSitters.set(model, { textModelTreeSitter, disposables, @@ -714,6 +181,10 @@ export class TreeSitterTextModelService extends Disposable implements ITreeSitte return textModelTreeSitter; } + private _handleOnDidChangeParseResult(change: ModelTreeUpdateEvent, model: ITextModel) { + this._onDidUpdateTree.fire({ textModel: model, ranges: change.ranges, versionId: change.versionId, tree: change.tree, languageId: change.languageId, hasInjections: change.hasInjections }); + } + private _addGrammar(languageId: string, grammarName: string) { if (!this._registeredLanguages.has(languageId)) { this._registeredLanguages.set(languageId, grammarName); @@ -722,45 +193,9 @@ export class TreeSitterTextModelService extends Disposable implements ITreeSitte private _removeGrammar(languageId: string) { if (this._registeredLanguages.has(languageId)) { - this._registeredLanguages.delete('typescript'); + this._registeredLanguages.delete(languageId); } } } -class PromiseWithSyncAccess { - private _result: PromiseResult | undefined; - /** - * Returns undefined if the promise did not resolve yet. - */ - get result(): PromiseResult | undefined { - return this._result; - } - constructor(public readonly promise: Promise) { - promise.then(result => { - this._result = new PromiseResult(result, undefined); - }).catch(e => { - this._result = new PromiseResult(undefined, e); - }); - } -} - -class AsyncCache { - private readonly _values = new Map>(); - - set(key: TKey, promise: Promise) { - this._values.set(key, new PromiseWithSyncAccess(promise)); - } - - get(key: TKey): Promise | undefined { - return this._values.get(key)?.promise; - } - - getSyncIfCached(key: TKey): T | undefined { - return this._values.get(key)?.result?.data; - } - - isCached(key: TKey): boolean { - return this._values.get(key)?.result !== undefined; - } -} diff --git a/src/vs/editor/common/services/treeSitterParserService.ts b/src/vs/editor/common/services/treeSitterParserService.ts index 5e96ebd1..a362e68f 100644 --- a/src/vs/editor/common/services/treeSitterParserService.ts +++ b/src/vs/editor/common/services/treeSitterParserService.ts @@ -9,9 +9,10 @@ import { ITextModel } from '../model.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { Range } from '../core/range.js'; import { importAMDNodeModule } from '../../../amdX.js'; +import { IModelContentChangedEvent } from '../textModelEvents.js'; export const EDITOR_EXPERIMENTAL_PREFER_TREESITTER = 'editor.experimental.preferTreeSitter'; -export const TREESITTER_ALLOWED_SUPPORT = ['typescript', 'ini']; +export const TREESITTER_ALLOWED_SUPPORT = ['css', 'typescript', 'ini', 'regex']; export const ITreeSitterParserService = createDecorator('treeSitterParserService'); @@ -23,20 +24,28 @@ export interface RangeWithOffsets { export interface RangeChange { newRange: Range; - oldRangeLength: number; newRangeStartOffset: number; newRangeEndOffset: number; } export interface TreeParseUpdateEvent { ranges: RangeChange[] | undefined; + language: string; versionId: number; + tree: Parser.Tree; + includedModelChanges: IModelContentChangedEvent[]; } -export interface TreeUpdateEvent { - textModel: ITextModel; +export interface ModelTreeUpdateEvent { ranges: RangeChange[]; versionId: number; + tree: ITextModelTreeSitter; + languageId: string; + hasInjections: boolean; +} + +export interface TreeUpdateEvent extends ModelTreeUpdateEvent { + textModel: ITextModel; } export interface ITreeSitterParserService { @@ -44,7 +53,7 @@ export interface ITreeSitterParserService { onDidAddLanguage: Event<{ id: string; language: Parser.Language }>; getOrInitLanguage(languageId: string): Parser.Language | undefined; getLanguage(languageId: string): Promise; - getParseResult(textModel: ITextModel): ITreeSitterParseResult | undefined; + getParseResult(textModel: ITextModel): ITextModelTreeSitter | undefined; getTree(content: string, languageId: string): Promise; getTreeSync(content: string, languageId: string): Parser.Tree | undefined; onDidUpdateTree: Event; @@ -57,6 +66,8 @@ export interface ITreeSitterParserService { export interface ITreeSitterParseResult { readonly tree: Parser.Tree | undefined; readonly language: Parser.Language; + readonly languageId: string; + readonly ranges: Parser.Range[] | undefined; versionId: number; } @@ -65,6 +76,9 @@ export interface ITextModelTreeSitter { * For testing purposes so that the time to parse can be measured. */ parse(languageId?: string): Promise; + textModel: ITextModel; + parseResult: ITreeSitterParseResult | undefined; + getInjection(offset: number, parentLanguage: string): ITreeSitterParseResult | undefined; dispose(): void; } diff --git a/src/vs/editor/common/tokens/lineTokens.ts b/src/vs/editor/common/tokens/lineTokens.ts index 72d94d12..ebe43f09 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 { onUnexpectedError } 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) { + onUnexpectedError(new Error('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 444bac5d..1aefa04a 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 1db59085..7fe34c92 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -36,7 +36,7 @@ import { ILineBreaksComputer, ILineBreaksComputerFactory, InjectedText } from '. import { ViewEventHandler } from '../viewEventHandler.js'; import { ICoordinatesConverter, InlineDecoration, IViewModel, IWhitespaceChangeAccessor, MinimapLinesRenderingData, OverviewRulerDecorationsGroup, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from '../viewModel.js'; import { ViewModelDecorations } from './viewModelDecorations.js'; -import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent } from '../viewModelEventDispatcher.js'; +import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent, WidgetFocusChangedEvent } from '../viewModelEventDispatcher.js'; import { IViewModelLines, ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js'; import { IThemeService } from '../../../platform/theme/common/themeService.js'; import { GlyphMarginLanesModel } from './glyphLanesModel.js'; @@ -153,9 +153,9 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent()); })); - this._register(this._themeService.onDidColorThemeChange((e) => { + this._register(this._themeService.onDidColorThemeChange((theme) => { this._invalidateDecorationsColorCache(); - this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent(e.theme)); + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent(theme)); })); this._updateConfigurationViewLineCountNow(); @@ -216,6 +216,10 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus)); } + public setHasWidgetFocus(hasWidgetFocus: boolean): void { + this._eventDispatcher.emitOutgoingEvent(new WidgetFocusChangedEvent(!hasWidgetFocus, hasWidgetFocus)); + } + public onCompositionStart(): void { this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent()); } diff --git a/src/vs/editor/common/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModelEventDispatcher.ts index 7fddb802..fc91875b 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 a5bc65bc..d3fe79f1 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, @@ -237,17 +237,9 @@ if (PasteAction) { let result: boolean; const experimentalEditContextEnabled = focusedEditor.getOption(EditorOption.effectiveExperimentalEditContextEnabled); if (experimentalEditContextEnabled) { - // Since we can not call execCommand('paste') on a dom node with edit context set - // we added a hidden text area that receives the paste execution - // see nativeEditContext.ts for more details const nativeEditContext = NativeEditContextRegistry.get(focusedEditor.getId()); if (nativeEditContext) { - const textArea = nativeEditContext.textArea; - nativeEditContext.onWillPaste(); - textArea.focus(); - result = focusedEditor.getContainerDomNode().ownerDocument.execCommand('paste'); - textArea.domNode.textContent = ''; - nativeEditContext.domNode.focus(); + result = nativeEditContext.executePaste(); } else { result = false; } diff --git a/src/vs/editor/contrib/codelens/browser/codelens.ts b/src/vs/editor/contrib/codelens/browser/codelens.ts index be24cae0..78aa18c6 100644 --- a/src/vs/editor/contrib/codelens/browser/codelens.ts +++ b/src/vs/editor/contrib/codelens/browser/codelens.ts @@ -22,6 +22,8 @@ export interface CodeLensItem { export class CodeLensModel { + static readonly Empty = new CodeLensModel(); + lenses: CodeLensItem[] = []; private _store: DisposableStore | undefined; @@ -67,6 +69,11 @@ export async function getCodeLensModel(registry: LanguageFeatureRegistry { // sort by lineNumber, provider-rank, and column if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { diff --git a/src/vs/editor/contrib/codelens/browser/codelensController.ts b/src/vs/editor/contrib/codelens/browser/codelensController.ts index 85cc79d7..bd54868e 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/codelens/browser/codelensWidget.css b/src/vs/editor/contrib/codelens/browser/codelensWidget.css index ab6a97ab..adc19b13 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensWidget.css +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.css @@ -5,7 +5,8 @@ .monaco-editor .codelens-decoration { overflow: hidden; - display: inline-block; + display: inline-flex !important; /* !important to override inline display:block style */ + align-items: center; text-overflow: ellipsis; white-space: nowrap; color: var(--vscode-editorCodeLens-foreground); diff --git a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts index 3a00c1d8..e6043ebb 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts @@ -15,7 +15,7 @@ import { DynamicCssRules } from '../../../browser/editorDom.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; -import { IEditorContribution } from '../../../common/editorCommon.js'; +import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { IModelDecoration, IModelDeltaDecoration } from '../../../common/model.js'; import { ModelDecorationOptions } from '../../../common/model/textModel.js'; import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; @@ -40,12 +40,12 @@ export class ColorDetector extends Disposable implements IEditorContribution { private _decorationsIds: string[] = []; private _colorDatas = new Map(); - private readonly _colorDecoratorIds = this._editor.createDecorationsCollection(); + private readonly _colorDecoratorIds: IEditorDecorationsCollection; private _isColorDecoratorsEnabled: boolean; private _defaultColorDecoratorsEnablement: 'auto' | 'always' | 'never'; - private readonly _ruleFactory = new DynamicCssRules(this._editor); + private readonly _ruleFactory: DynamicCssRules; private readonly _decoratorLimitReporter = new DecoratorLimitReporter(); @@ -56,6 +56,8 @@ export class ColorDetector extends Disposable implements IEditorContribution { @ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService, ) { super(); + this._colorDecoratorIds = this._editor.createDecorationsCollection(); + this._ruleFactory = new DynamicCssRules(this._editor); this._debounceInformation = languageFeatureDebounceService.for(_languageFeaturesService.colorProvider, 'Document Colors', { min: ColorDetector.RECOMPUTE_TIME }); this._register(_editor.onDidChangeModel(() => { this._isColorDecoratorsEnabled = this.isEnabled(); diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts index 8d11bdf3..8522f41e 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts @@ -43,8 +43,8 @@ export class ColorPickerHeader extends Disposable { this._originalColorNode.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || ''; this.backgroundColor = themeService.getColorTheme().getColor(editorHoverBackground) || Color.white; - this._register(themeService.onDidColorThemeChange(e => { - this.backgroundColor = e.theme.getColor(editorHoverBackground) || Color.white; + this._register(themeService.onDidColorThemeChange(theme => { + this.backgroundColor = theme.getColor(editorHoverBackground) || Color.white; })); this._register(dom.addDisposableListener(this._pickedColorNode, dom.EventType.CLICK, () => this.model.selectNextColorPresentation())); diff --git a/src/vs/editor/contrib/find/browser/findController.ts b/src/vs/editor/contrib/find/browser/findController.ts index 6b6102ea..9e00a1d5 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/folding/browser/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts index 97fa12bb..2acc59de 100644 --- a/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts +++ b/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts @@ -47,6 +47,9 @@ export class SyntaxRangeProvider implements RangeProvider { compute(cancellationToken: CancellationToken): Promise { return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => { + if (this.editorModel.isDisposed()) { + return null; + } if (ranges) { const res = sanitizeRanges(ranges, this.foldingRangesLimit); return res; diff --git a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts index 33c8ce33..3d253851 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts @@ -26,9 +26,9 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { ILabelService } from '../../../../platform/label/common/label.js'; import { IMarker, IRelatedInformation, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { contrastBorder, editorBackground, editorErrorBorder, editorErrorForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, oneOf, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; -import { IColorTheme, IThemeChangeEvent, IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; class MessageWidget { @@ -255,15 +255,11 @@ export class MarkerNavigationWidget extends PeekViewWidget { this._backgroundColor = Color.white; this._applyTheme(_themeService.getColorTheme()); - this._callOnDispose.add(_themeService.onDidColorThemeChange(this._ondDidColorThemeChange.bind(this))); + this._callOnDispose.add(_themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this.create(); } - private _ondDidColorThemeChange(e: IThemeChangeEvent) { - this._applyTheme(e.theme); - } - private _applyTheme(theme: IColorTheme) { this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); let colorId = editorMarkerNavigationError; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts index 6c7da647..9581b5cf 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts @@ -32,7 +32,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IWorkbenchAsyncDataTreeOptions, WorkbenchAsyncDataTree } from '../../../../../platform/list/browser/listService.js'; -import { IColorTheme, IThemeChangeEvent, IThemeService } from '../../../../../platform/theme/common/themeService.js'; +import { IColorTheme, IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { FileReferences, OneReference, ReferencesModel } from '../referencesModel.js'; import { ITreeDragAndDrop, ITreeDragOverReaction } from '../../../../../base/browser/ui/tree/tree.js'; import { DataTransfers, IDragAndDropData } from '../../../../../base/browser/dnd.js'; @@ -276,7 +276,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true, supportOnTitleClick: true }, _instantiationService); this._applyTheme(themeService.getColorTheme()); - this._callOnDispose.add(themeService.onDidColorThemeChange(this._onDidColorThemeChange.bind(this))); + this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); this._peekViewService.addExclusiveWidget(editor, this); this.create(); } @@ -298,10 +298,6 @@ export class ReferenceWidget extends peekView.PeekViewWidget { super.dispose(); } - private _onDidColorThemeChange(e: IThemeChangeEvent): void { - this._applyTheme(e.theme); - } - private _applyTheme(theme: IColorTheme) { const borderColor = theme.getColor(peekView.peekViewBorder) || Color.transparent; this.style({ diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index bc5d7237..1658944c 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 0a4e864d..959a99ea 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 2e91840d..91d80882 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 40411f02..700bc76b 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 e31394a5..43be28ea 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -6,6 +6,7 @@ import { mapFindFirst } from '../../../../../base/common/arraysFind.js'; import { itemsEquals } from '../../../../../base/common/equals.js'; import { BugIndicatingError, onUnexpectedError, onUnexpectedExternalError } from '../../../../../base/common/errors.js'; +import { Emitter } from '../../../../../base/common/event.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { IObservable, IObservableWithChange, IReader, ITransaction, autorun, constObservable, derived, derivedHandleChanges, derivedOpts, observableSignal, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from '../../../../../base/common/observable.js'; import { commonPrefixLength, firstNonWhitespaceIndex } from '../../../../../base/common/strings.js'; @@ -56,13 +57,16 @@ export class InlineCompletionsModel extends Disposable { private _isAcceptingPartially = false; public get isAcceptingPartially() { return this._isAcceptingPartially; } + private readonly _onDidAccept = new Emitter(); + public readonly onDidAccept = this._onDidAccept.event; + private readonly _editorObs = observableCodeEditor(this._editor); private readonly _suggestPreviewEnabled = this._editorObs.getOption(EditorOption.suggest).map(v => v.preview); 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, @@ -114,9 +118,52 @@ export class InlineCompletionsModel extends Disposable { this._inAcceptFlow.set(false, undefined); })); + this._register(autorun(reader => { + const jumpToReset = this.state.map(s => !s || s.kind === 'inlineEdit' && !s.cursorAtInlineEdit).read(reader); + if (jumpToReset) { + this._jumpedToId.set(undefined, undefined); + } + })); + const inlineEditSemanticId = this.inlineEditState.map(s => s?.inlineCompletion.semanticId); + + this._register(autorun(reader => { + const id = inlineEditSemanticId.read(reader); + if (id) { + this._editor.pushUndoStop(); + this._lastShownInlineCompletionInfo = { + alternateTextModelVersionId: this.textModel.getAlternativeVersionId(), + inlineCompletion: this.state.get()!.inlineCompletion!.inlineCompletion, + }; + } + })); + + this._didUndoInlineEdits.recomputeInitiallyAndOnChange(this._store); } + private _lastShownInlineCompletionInfo: { alternateTextModelVersionId: number; /* already freed! */ inlineCompletion: InlineCompletionItem } | undefined = undefined; + private _lastAcceptedInlineCompletionInfo: { textModelVersionIdAfter: number; /* already freed! */ inlineCompletion: InlineCompletionItem } | undefined = undefined; + private readonly _didUndoInlineEdits = derivedHandleChanges({ + owner: this, + createEmptyChangeSummary: () => ({ didUndo: false }), + handleChange: (ctx, changeSummary) => { + changeSummary.didUndo = ctx.didChange(this._textModelVersionId) && !!ctx.change?.isUndoing; + return true; + } + }, (reader, changeSummary) => { + const versionId = this._textModelVersionId.read(reader); + if (versionId !== null + && this._lastAcceptedInlineCompletionInfo + && this._lastAcceptedInlineCompletionInfo.textModelVersionIdAfter === versionId - 1 + && this._lastAcceptedInlineCompletionInfo.inlineCompletion.isInlineEdit + && changeSummary.didUndo + ) { + this._lastAcceptedInlineCompletionInfo = undefined; + return true; + } + return false; + }); + public debugGetSelectedSuggestItem(): IObservable { return this._selectedSuggestItem; } @@ -191,6 +238,7 @@ export class InlineCompletionsModel extends Disposable { return true; }, }, (reader, changeSummary) => { + this._source.clearOperationOnTextModelChange.read(reader); // Make sure the clear operation runs before the fetch operation this._noDelaySignal.read(reader); this.dontRefetchSignal.read(reader); this._onlyRequestInlineEditsSignal.read(reader); @@ -221,16 +269,38 @@ export class InlineCompletionsModel extends Disposable { return Promise.resolve(true); } - const context: InlineCompletionContext = { + if (this._didUndoInlineEdits.read(reader)) { + transaction(tx => { + this._source.clear(tx); + }); + return undefined; + } + + let context: InlineCompletionContext = { triggerKind: changeSummary.inlineCompletionTriggerKind, selectedSuggestionInfo: suggestItem?.toSelectedSuggestionInfo(), includeInlineCompletions: !changeSummary.onlyRequestInlineEdits, includeInlineEdits: this._inlineEditsEnabled.read(reader), }; + + if (context.triggerKind === InlineCompletionTriggerKind.Automatic) { + if (this.textModel.getAlternativeVersionId() === this._lastShownInlineCompletionInfo?.alternateTextModelVersionId) { + // When undoing back to a version where an inline edit/completion was shown, + // we want to show an inline edit (or completion) again if it was originally an inline edit (or completion). + context = { + ...context, + includeInlineCompletions: !this._lastShownInlineCompletionInfo.inlineCompletion.isInlineEdit, + includeInlineEdits: this._lastShownInlineCompletionInfo.inlineCompletion.isInlineEdit, + }; + } + } + const itemToPreserveCandidate = this.selectedInlineCompletion.get() ?? this._inlineCompletionItems.get()?.inlineEdit; const itemToPreserve = changeSummary.preserveCurrentCompletion || itemToPreserveCandidate?.forwardStable ? itemToPreserveCandidate : undefined; - return this._source.fetch(cursorPosition, context, itemToPreserve, changeSummary.shouldDebounce); + const userJumpedToActiveCompletion = this._jumpedToId.map(jumpedTo => !!jumpedTo && jumpedTo === this._inlineCompletionItems.get()?.inlineEdit?.semanticId); + + return this._source.fetch(cursorPosition, context, itemToPreserve, changeSummary.shouldDebounce, userJumpedToActiveCompletion); }); public async trigger(tx?: ITransaction, options?: { onlyFetchInlineEdits?: boolean; noDelay?: boolean }): Promise { @@ -282,7 +352,7 @@ export class InlineCompletionsModel extends Disposable { let inlineEdit: InlineCompletionWithUpdatedRange | undefined = undefined; const visibleCompletions: InlineCompletionWithUpdatedRange[] = []; for (const completion of c.inlineCompletions) { - if (!(completion.sourceInlineCompletion.isInlineEdit || completion.sourceInlineCompletion.showInlineEditMenu)) { + if (!completion.sourceInlineCompletion.isInlineEdit) { if (completion.isVisible(this.textModel, cursorPosition, reader)) { visibleCompletions.push(completion); } @@ -391,8 +461,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]; @@ -400,8 +469,6 @@ export class InlineCompletionsModel extends Disposable { return { kind: 'inlineEdit', inlineEdit, inlineCompletion: inlineEditResult, edits: e, cursorAtInlineEdit }; } - this._jumpedTo.set(false, undefined); - const suggestItem = this._selectedSuggestItem.read(reader); if (suggestItem) { const suggestCompletionEdit = singleTextRemoveCommonPrefix(suggestItem.toSingleTextEdit(), model); @@ -510,6 +577,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); + return (this._inlineEditsShowCollapsedEnabled.read(reader) || !isCurrentModelVersion) + && this._jumpedToId.read(reader) !== state.inlineCompletion.semanticId + && !this._inAcceptFlow.read(reader); + }); + private readonly _tabShouldIndent = derived(this, reader => { if (this._inAcceptFlow.read(reader)) { return false; @@ -546,8 +625,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,13 +637,13 @@ 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; } - if (this._jumpedTo.read(reader)) { + if (this._jumpedToId.read(reader) === s.inlineCompletion.semanticId) { return true; } if (this._tabShouldIndent.read(reader)) { @@ -651,6 +730,8 @@ export class InlineCompletionsModel extends Disposable { } } + this._onDidAccept.fire(); + // Reset before invoking the command, as the command might cause a follow up trigger (which we don't want to reset). this.stop(); @@ -662,6 +743,7 @@ export class InlineCompletionsModel extends Disposable { } this._inAcceptFlow.set(true, undefined); + this._lastAcceptedInlineCompletionInfo = { textModelVersionIdAfter: this.textModel.getVersionId(), inlineCompletion: completion }; } public async acceptNextWord(editor: ICodeEditor): Promise { @@ -771,7 +853,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(); @@ -864,7 +946,7 @@ export class InlineCompletionsModel extends Disposable { }; } - private readonly _jumpedTo = observableValue(this, false); + private readonly _jumpedToId = observableValue(this, undefined); private readonly _inAcceptFlow = observableValue(this, false); public readonly inAcceptFlow: IObservable = this._inAcceptFlow; @@ -873,7 +955,7 @@ export class InlineCompletionsModel extends Disposable { if (!s) { return; } transaction(tx => { - this._jumpedTo.set(true, tx); + this._jumpedToId.set(s.inlineCompletion.semanticId, tx); this.dontRefetchSignal.trigger(tx); const edit = s.inlineCompletion.toSingleTextEdit(undefined); this._editor.setPosition(edit.range.getStartPosition(), 'inlineCompletions.jump'); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts index d9d9f0e0..afaad779 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; @@ -63,11 +62,15 @@ export class InlineCompletionsSource extends Disposable { ) { super(); - this._register(this._textModel.onDidChangeContent((e) => { - this._updateOperation.clear(); - })); + this.clearOperationOnTextModelChange.recomputeInitiallyAndOnChange(this._store); } + public readonly clearOperationOnTextModelChange = derived(this, reader => { + this._versionId.read(reader); + this._updateOperation.clear(); + return undefined; // always constant + }); + private _log(entry: { sourceId: string; kind: 'start'; requestId: number; context: unknown } & IRecordableEditorLogEntry | { sourceId: string; kind: 'end'; error: any; durationMs: number; result: unknown; requestId: number } & IRecordableLogEntry @@ -81,7 +84,7 @@ export class InlineCompletionsSource extends Disposable { private readonly _loadingCount = observableValue(this, 0); public readonly loading = this._loadingCount.map(this, v => v > 0); - public fetch(position: Position, context: InlineCompletionContext, activeInlineCompletion: InlineCompletionWithUpdatedRange | undefined, withDebounce: boolean): Promise { + public fetch(position: Position, context: InlineCompletionContext, activeInlineCompletion: InlineCompletionWithUpdatedRange | undefined, withDebounce: boolean, userJumpedToActiveCompletion: IObservable): Promise { const request = new UpdateRequest(position, context, this._textModel.getVersionId()); const target = context.selectedSuggestionInfo ? this.suggestWidgetInlineCompletions : this.inlineCompletions; @@ -145,20 +148,25 @@ 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() }); } } - if (source.token.isCancellationRequested || this._store.isDisposed || this._textModel.getVersionId() !== request.versionId) { + if (source.token.isCancellationRequested || this._store.isDisposed || this._textModel.getVersionId() !== request.versionId || userJumpedToActiveCompletion.get() /* In the meantime the user showed interest for the active completion so dont hide it */) { updatedCompletions.dispose(); return false; } // Reuse Inline Edit if possible - if (activeInlineCompletion && activeInlineCompletion.isInlineEdit && (activeInlineCompletion.canBeReused(this._textModel, position) || updatedCompletions.has(activeInlineCompletion.inlineCompletion) /* Inline Edit wins over completions if it's already been shown*/)) { + if (activeInlineCompletion && activeInlineCompletion.isInlineEdit && activeInlineCompletion.updatedEditModelVersion === this._textModel.getVersionId() && ( + activeInlineCompletion.canBeReused(this._textModel, position) + || updatedCompletions.has(activeInlineCompletion.inlineCompletion) /* Inline Edit wins over completions if it's already been shown*/ + || updatedCompletions.isEmpty() /* Incoming completion is empty, keep the current one alive */ + )) { + activeInlineCompletion.reuse(); updatedCompletions.dispose(); return false; } @@ -318,25 +326,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 +343,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 +376,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 @@ -481,13 +416,17 @@ export class InlineCompletionWithUpdatedRange extends Disposable { && !!matchesSubString(originalValueAfter, filterTextAfter); } + public reuse(): void { + this._updatedEditObj.reuse(); + } + public canBeReused(model: ITextModel, position: Position): boolean { if (!this.updatedEdit.get()) { return false; } if (this.sourceInlineCompletion.isInlineEdit) { - return this._updatedEdit.lastChangePartOfInlineEdit; + return this._updatedEditObj.lastChangePartOfInlineEdit; } const updatedRange = this._updatedRange.read(undefined); @@ -498,16 +437,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 +535,6 @@ class UpdatedEdit extends Disposable { this._innerEdits = this._applyTextModelChanges(change, this._innerEdits); } - if (this._hasInvalidationTimePassed()) { - return undefined; - } - if (this._innerEdits.length === 0) { return undefined; } @@ -548,16 +550,18 @@ class UpdatedEdit extends Disposable { constructor( offsetEdit: OffsetEdit, - textModel: ITextModel, + private readonly _textModel: ITextModel, private readonly _modelVersion: IObservableWithChange, isInlineEdit: boolean, ) { 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)); - const replacedText = textModel.getValueInRange(replacedRange); + const replacedRange = Range.fromPositions(this._textModel.getPositionAt(edit.replaceRange.start), this._textModel.getPositionAt(edit.replaceRange.endExclusive)); + const replacedText = this._textModel.getValueInRange(replacedRange); return new SingleUpdatedNextEdit(edit, replacedText); } @@ -576,9 +580,15 @@ class UpdatedEdit extends Disposable { return []; // change is invalid, so we will have to drop the completion } + const currentModelVersion = this._modelVersion.get(); + this._lastChangePartOfInlineEdit = edits.some(edit => edit.lastChangeUpdatedEdit); if (this._lastChangePartOfInlineEdit) { - this._cancelInvalidationTimer(); + this._inlineEditModelVersion = currentModelVersion ?? -1; + } + + if (currentModelVersion === null || this._inlineEditModelVersion + 20 < currentModelVersion) { + return []; // the completion has been ignored for a while, remove it } edits = edits.filter(innerEdit => !innerEdit.edit!.isEmpty); @@ -589,12 +599,8 @@ class UpdatedEdit extends Disposable { return edits; } - private _cancelInvalidationTimer() { - this._invalidationTime = undefined; - } - - private _hasInvalidationTimePassed(): boolean { - return !!this._invalidationTime && this._invalidationTime < Date.now(); + reuse(): void { + this._inlineEditModelVersion = this._modelVersion.get() ?? -1; } } @@ -737,50 +743,59 @@ class SingleUpdatedNextEdit extends SingleUpdatedEdit { const emptyRange = new Range(1, 1, 1, 1); -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; +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)); } - private readonly _contextKeyValue = observableContextKey(this._contextKey, this._contextKeyService).recomputeInitiallyAndOnChange(this._store); - - constructor( - private readonly _contextKey: string, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @ICommandService private readonly _commandService: ICommandService, - ) { - super(); + // 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); } - public readonly isEnabled = this._contextKeyValue.map(v => v !== 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)); - public log(data: T): boolean { - const commandId = this._contextKeyValue.get(); - if (!commandId) { - return false; + // 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), ''); } - this._commandService.executeCommand(commandId, data); - return true; } + + return edit; } -export function observableContextKey(key: string, contextKeyService: IContextKeyService): IObservable { - return observableFromEvent(contextKeyService.onDidChangeContext, () => contextKeyService.getContextKeyValue(key)); +function reshapeMultiLineInsertion(edit: SingleOffsetEdit, textModel: ITextModel): SingleOffsetEdit { + if (!edit.replaceRange.isEmpty) { + throw new BugIndicatingError('Unexpected original range'); + } + + if (edit.replaceRange.start === 0) { + return edit; + } + + 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)); + } + + 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 63e45b69..13e6d6ed 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 0f7691cb..49c79efe 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -228,6 +228,12 @@ export class InlineCompletionProviderResult implements IDisposable { return this.hashs.has(item.hash()); } + // TODO: This is not complete as it does not take the textmodel into account + isEmpty(): boolean { + return this.completions.length === 0 + || this.completions.every(c => c.range.isEmpty() && c.insertText.length === 0); + } + dispose(): void { for (const result of this.providerResults) { result.removeRef(); @@ -370,9 +376,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 +389,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 00000000..8a307df3 --- /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.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts index 06d68f71..d592eca0 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); } })); @@ -229,7 +230,7 @@ export class GhostTextView extends Disposable { + 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, } @@ -258,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 ); @@ -277,7 +280,9 @@ export class GhostTextView extends Disposable { } } -class GhostTextAttachedData { } +class GhostTextAttachedData { + constructor(public readonly owner: GhostTextView) { } +} interface WidgetDomElement { ghostTextViewWarningWidgetData?: { @@ -380,6 +385,8 @@ export class AdditionalLinesWidget extends Disposable { this._store ); + private hasBeenAccepted = false; + constructor( private readonly _editor: ICodeEditor, private readonly _lines: IObservable<{ @@ -393,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(); @@ -475,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 04afc6a8..bcf120f4 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 6624abea..4e0a4c88 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,72 @@ 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 })), - 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 */ } - ) - ] : []) + { hoverDelegate: nativeHoverDelegate /* unable to show hover inside another hover */ } + ) : undefined; + + return hoverContent([ + title, + gotoAndAccept, + reject, + toggleCollapsedMode, + settings, + + extensionCommands.length ? separator() : undefined, + ...extensionCommands, + + actionBarFooter ? separator() : undefined, + actionBarFooter ]); } @@ -188,6 +222,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 e737ff7a..4a3c4f22 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 @@ -5,41 +5,120 @@ import { n, trackFocus } from '../../../../../../../base/browser/dom.js'; import { renderIcon } from '../../../../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { timeout } from '../../../../../../../base/common/async.js'; import { Codicon } from '../../../../../../../base/common/codicons.js'; -import { Disposable, DisposableStore, toDisposable } from '../../../../../../../base/common/lifecycle.js'; -import { IObservable, ISettableObservable, constObservable, derived, observableFromEvent, observableValue, runOnChange } from '../../../../../../../base/common/observable.js'; +import { BugIndicatingError } from '../../../../../../../base/common/errors.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../../../../base/common/lifecycle.js'; +import { IObservable, ISettableObservable, autorun, autorunWithStore, constObservable, derived, observableFromEvent, observableValue, runOnChange } from '../../../../../../../base/common/observable.js'; import { debouncedObservable } from '../../../../../../../base/common/observableInternal/utils.js'; import { IAccessibilityService } from '../../../../../../../platform/accessibility/common/accessibility.js'; import { IHoverService } from '../../../../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../../../../platform/storage/common/storage.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; +import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; +import { IEditorMouseEvent } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; +import { Point } from '../../../../../../browser/point.js'; import { Rect } from '../../../../../../browser/rect.js'; import { HoverService } from '../../../../../../browser/services/hoverService/hoverService.js'; import { HoverWidget } from '../../../../../../browser/services/hoverService/hoverWidget.js'; -import { EditorOption } from '../../../../../../common/config/editorOptions.js'; +import { EditorOption, RenderLineNumbersType } 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 { inlineEditIndicatorBackground, inlineEditIndicatorPrimaryBackground, inlineEditIndicatorPrimaryForeground, inlineEditIndicatorSecondaryBackground, inlineEditIndicatorSecondaryForeground, inlineEditIndicatorsuccessfulBackground, inlineEditIndicatorsuccessfulForeground } from '../theme.js'; -import { InlineEditTabAction, mapOutFalsy, rectToProps } from '../utils/utils.js'; +import { InlineEditHost } from '../inlineEditsModel.js'; +import { IInlineEditModel, InlineEditTabAction } from '../inlineEditsViewInterface.js'; +import { getEditorBlendedColor, inlineEditIndicatorBackground, inlineEditIndicatorPrimaryBackground, inlineEditIndicatorPrimaryBorder, inlineEditIndicatorPrimaryForeground, inlineEditIndicatorSecondaryBackground, inlineEditIndicatorSecondaryBorder, inlineEditIndicatorSecondaryForeground, inlineEditIndicatorsuccessfulBackground, inlineEditIndicatorsuccessfulBorder, inlineEditIndicatorsuccessfulForeground } from '../theme.js'; +import { mapOutFalsy, rectToProps } from '../utils/utils.js'; import { GutterIndicatorMenuContent } from './gutterIndicatorMenu.js'; +// Represents the user's familiarity with the inline edits feature. +enum UserKind { + FirstTime = 'firstTime', + SecondTime = 'secondTime', + Active = 'active' +} + 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 _activeCompletionId = derived(reader => { + const layout = this._layout.read(reader); + if (!layout) { return undefined; } + const model = this._model.read(reader); + if (!model) { return undefined; } + return model.inlineEdit.inlineCompletion.id; + }); + + private readonly _gutterIndicatorStyles: IObservable<{ background: string; foreground: string; border: string }>; + private readonly _isHoveredOverInlineEditDebounced: IObservable; + + private readonly _newUserAnimationDisposable = this._register(new MutableDisposable()); + private readonly _firstToSecondTimeUserDisposable = this._register(new MutableDisposable()); + private readonly _secondTimeToActiveUserDisposable = this._register(new MutableDisposable()); + + private get _newUserType(): UserKind { + return this._storageService.get('inlineEditsGutterIndicatorUserKind', StorageScope.APPLICATION, UserKind.FirstTime) as UserKind; + } + private set _newUserType(value: UserKind) { + switch (value) { + case UserKind.FirstTime: + throw new BugIndicatingError('UserKind should not be set to first time'); + case UserKind.SecondTime: + this._firstToSecondTimeUserDisposable.clear(); + break; + case UserKind.Active: + this._newUserAnimationDisposable.clear(); + this._firstToSecondTimeUserDisposable.clear(); + this._secondTimeToActiveUserDisposable.clear(); + break; + } + + this._storageService.store('inlineEditsGutterIndicatorUserKind', value, StorageScope.APPLICATION, StorageTarget.USER); + } + constructor( private readonly _editorObs: ObservableCodeEditor, private readonly _originalRange: IObservable, private readonly _verticalOffset: IObservable, - private readonly _host: IInlineEditsViewHost, + private readonly _host: IObservable, + private readonly _model: IObservable, private readonly _isHoveringOverInlineEdit: IObservable, private readonly _focusIsInMenu: ISettableObservable, @IHoverService private readonly _hoverService: HoverService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IAccessibilityService accessibilityService: IAccessibilityService, + @IStorageService private readonly _storageService: IStorageService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @IThemeService themeService: IThemeService, ) { super(); + this._gutterIndicatorStyles = this._tabAction.map((v, reader) => { + switch (v) { + case InlineEditTabAction.Inactive: return { + background: getEditorBlendedColor(inlineEditIndicatorSecondaryBackground, themeService).read(reader).toString(), + foreground: getEditorBlendedColor(inlineEditIndicatorSecondaryForeground, themeService).read(reader).toString(), + border: getEditorBlendedColor(inlineEditIndicatorSecondaryBorder, themeService).read(reader).toString(), + }; + case InlineEditTabAction.Jump: return { + background: getEditorBlendedColor(inlineEditIndicatorPrimaryBackground, themeService).read(reader).toString(), + foreground: getEditorBlendedColor(inlineEditIndicatorPrimaryForeground, themeService).read(reader).toString(), + border: getEditorBlendedColor(inlineEditIndicatorPrimaryBorder, themeService).read(reader).toString() + }; + case InlineEditTabAction.Accept: return { + background: getEditorBlendedColor(inlineEditIndicatorsuccessfulBackground, themeService).read(reader).toString(), + foreground: getEditorBlendedColor(inlineEditIndicatorsuccessfulForeground, themeService).read(reader).toString(), + border: getEditorBlendedColor(inlineEditIndicatorsuccessfulBorder, themeService).read(reader).toString() + }; + } + }); + this._register(this._editorObs.createOverlayWidget({ domNode: this._indicator.element, position: constObservable(null), @@ -47,22 +126,144 @@ export class InlineEditsGutterIndicator extends Disposable { minContentWidthInPx: constObservable(0), })); - if (!accessibilityService.isMotionReduced()) { - const debouncedIsHovering = debouncedObservable(this._isHoveringOverInlineEdit, 100); - this._register(runOnChange(debouncedIsHovering, (isHovering) => { - if (!isHovering) { - return; - } - 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 }); - })); + this._register(this._editorObs.editor.onMouseMove((e: IEditorMouseEvent) => { + const el = this._iconRef.element; + const rect = el.getBoundingClientRect(); + const rectangularArea = Rect.fromLeftTopWidthHeight(rect.left, rect.top, rect.width, rect.height); + const point = new Point(e.event.posx, e.event.posy); + this._isHoveredOverIcon.set(rectangularArea.containsPoint(point), undefined); + })); + + this._register(this._editorObs.editor.onDidScrollChange(() => { + this._isHoveredOverIcon.set(false, undefined); + })); + + this._isHoveredOverInlineEditDebounced = debouncedObservable(this._isHoveringOverInlineEdit, 100); + + // pulse animation when hovering inline edit + this._register(runOnChange(this._isHoveredOverInlineEditDebounced, (isHovering) => { + if (isHovering) { + this._triggerAnimation(); + } + })); + + if (this._newUserType === UserKind.Active) { + this._register(this.setupNewUserExperience()); } + + this._register(autorun(reader => { + this._indicator.readEffect(reader); + if (this._indicator.element) { + this._editorObs.editor.applyFontInfo(this._indicator.element); + } + })); + + this._register(autorunWithStore((reader, store) => { + const host = this._host.read(reader); + if (!host) { return; } + store.add(host.onDidAccept(() => { + this._storageService.store('inlineEditsGutterIndicatorUserKind', UserKind.Active, StorageScope.APPLICATION, StorageTarget.USER); + })); + })); + } + + private setupNewUserExperience(): IDisposable { + if (this._newUserType === UserKind.Active) { + return Disposable.None; + } + + const disposableStore = new DisposableStore(); + + let userHasHoveredOverIcon = false; + let inlineEditHasBeenAccepted = false; + let firstTimeUserAnimationCount = 0; + let secondTimeUserAnimationCount = 0; + + // pulse animation for new users + disposableStore.add(runOnChange(this._activeCompletionId, async (id) => { + if (id === undefined) { return; } + const userType = this._newUserType; + + // Animation + switch (userType) { + case UserKind.FirstTime: { + for (let i = 0; i < 3 && this._activeCompletionId.get() === id; i++) { + await this._triggerAnimation(); + await timeout(500); + } + break; + } + case UserKind.SecondTime: { + this._triggerAnimation(); + break; + } + } + + // User Kind Transition + switch (userType) { + case UserKind.FirstTime: { + if (++firstTimeUserAnimationCount >= 5 || userHasHoveredOverIcon) { + this._newUserType = UserKind.SecondTime; + } + break; + } + case UserKind.SecondTime: { + if (++secondTimeUserAnimationCount >= 5 && inlineEditHasBeenAccepted) { + this._newUserType = UserKind.Active; + } + break; + } + } + })); + + // Remember when the user has hovered over the icon + disposableStore.add(runOnChange(this._isHoveredOverIconDebounced, async (isHovered) => { + if (isHovered) { + userHasHoveredOverIcon = true; + } + })); + + // Remember when the user has accepted an inline edit + disposableStore.add(autorunWithStore((reader, store) => { + const host = this._host.read(reader); + if (!host) { return; } + store.add(host.onDidAccept(() => { + inlineEditHasBeenAccepted = true; + })); + })); + + return disposableStore; + } + + private _triggerAnimation(): Promise { + if (this._accessibilityService.isMotionReduced()) { + return new Animation(null, null).finished; + } + // 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 }); */ + + // PULSE ANIMATION: + const animation = this._iconRef.element.animate([ + { + outline: `2px solid ${this._gutterIndicatorStyles.map(v => v.border).get()}`, + outlineOffset: '-1px', + offset: 0 + }, + { + outline: `2px solid transparent`, + outlineOffset: '10px', + offset: 1 + }, + ], { duration: 500 }); + + return animation.finished; } private readonly _originalRangeObs = mapOutFalsy(this._originalRange); @@ -81,40 +282,120 @@ export class InlineEditsGutterIndicator extends Disposable { ? observableFromEvent(this._stickyScrollController.onDidChangeStickyScrollHeight, () => this._stickyScrollController!.stickyScrollWidgetHeight) : constObservable(0); + private readonly _lineNumberToRender = derived(this, reader => { + if (this._verticalOffset.read(reader) !== 0) { + return ''; + } + + const lineNumber = this._originalRange.read(reader)?.startLineNumber; + const lineNumberOptions = this._editorObs.getOption(EditorOption.lineNumbers).read(reader); + + if (lineNumber === undefined || lineNumberOptions.renderType === RenderLineNumbersType.Off) { + return ''; + } + + if (lineNumberOptions.renderType === RenderLineNumbersType.Interval) { + const cursorPosition = this._editorObs.cursorPosition.read(reader); + if (lineNumber % 10 === 0 || cursorPosition && cursorPosition.lineNumber === lineNumber) { + return lineNumber.toString(); + } + return ''; + } + + if (lineNumberOptions.renderType === RenderLineNumbersType.Relative) { + const cursorPosition = this._editorObs.cursorPosition.read(reader); + if (!cursorPosition) { + return ''; + } + const relativeLineNumber = Math.abs(lineNumber - cursorPosition.lineNumber); + if (relativeLineNumber === 0) { + return lineNumber.toString(); + } + return relativeLineNumber.toString(); + } + + if (lineNumberOptions.renderType === RenderLineNumbersType.Custom) { + if (lineNumberOptions.renderFn) { + return lineNumberOptions.renderFn(lineNumber); + } + return ''; + } + + return lineNumber.toString(); + }); + private readonly _layout = derived(this, reader => { const s = this._state.read(reader); if (!s) { return undefined; } const layout = this._editorObs.layoutInfo.read(reader); + const lineHeight = this._editorObs.getOption(EditorOption.lineHeight).read(reader); const bottomPadding = 1; + const leftPadding = 1; + const rightPadding = 1; + + // Entire editor area without sticky scroll const fullViewPort = Rect.fromLeftTopRightBottom(0, 0, layout.width, layout.height - bottomPadding); const viewPortWithStickyScroll = fullViewPort.withTop(this._stickyScrollHeight.read(reader)); + // The glyph margin area across all relevant lines const targetVertRange = s.lineOffsetRange.read(reader); + const targetRect = Rect.fromRanges(OffsetRange.fromTo(leftPadding + layout.glyphMarginLeft, layout.decorationsLeft + layout.decorationsWidth - rightPadding), targetVertRange); - const space = 1; - - const targetRect = Rect.fromRanges(OffsetRange.fromTo(space + layout.glyphMarginLeft, layout.lineNumbersLeft + layout.lineNumbersWidth + 4), targetVertRange); - - - const lineHeight = this._editorObs.getOption(EditorOption.lineHeight).read(reader); + // The gutter view container (pill) const pillOffset = this._verticalOffset.read(reader); - const pillRect = targetRect.withHeight(lineHeight).withWidth(22).translateY(pillOffset); + let pillRect = targetRect.withHeight(lineHeight).withWidth(22).translateY(pillOffset); const pillRectMoved = pillRect.moveToBeContainedIn(viewPortWithStickyScroll); const rect = targetRect; - const iconRect = (targetRect.containsRect(pillRectMoved)) + // Move pill to be in viewport if it is not + pillRect = (targetRect.containsRect(pillRectMoved)) ? pillRectMoved : pillRectMoved.moveToBeContainedIn(fullViewPort.intersect(targetRect.union(fullViewPort.withHeight(lineHeight)))!); //viewPortWithStickyScroll.intersect(rect)!; + // docked = pill was already in the viewport + const docked = rect.containsRect(pillRect) && viewPortWithStickyScroll.containsRect(pillRect); + let iconDirecion = targetRect.containsRect(pillRect) ? + 'right' as const + : pillRect.top > targetRect.top ? + 'top' as const : + 'bottom' as const; + + // Grow icon the the whole glyph margin area if it is docked + let lineNumberRect = pillRect.withWidth(0); + let iconRect = pillRect; + if (docked && pillRect.top === targetRect.top + pillOffset) { + pillRect = pillRect.withWidth(layout.decorationsLeft + layout.decorationsWidth - layout.glyphMarginLeft - leftPadding - rightPadding); + lineNumberRect = pillRect.intersectHorizontal(new OffsetRange(0, Math.max(layout.lineNumbersLeft + layout.lineNumbersWidth - leftPadding - 1, 0))); + iconRect = iconRect.translateX(lineNumberRect.width); + } + + 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), + pillRect, + lineHeight, + lineNumberRect, }; }); @@ -122,6 +403,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 +413,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,12 +428,13 @@ 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; if (h) { this._hoverVisible.set(true, undefined); + disposableStore.add(this._editorObs.editor.onDidScrollChange(() => h.dispose())); disposableStore.add(h.onDispose(() => { this._hoverVisible.set(false, undefined); disposableStore.dispose(); @@ -161,15 +444,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, @@ -191,51 +480,49 @@ export class InlineEditsGutterIndicator extends Disposable { ref: this._iconRef, onmouseenter: () => { // TODO show hover when hovering ghost text etc. - this._isHoveredOverIcon.set(true, undefined); this._showHover(); }, - onmouseleave: () => { this._isHoveredOverIcon.set(false, undefined); }, style: { 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._gutterIndicatorStyles.map(v => v.background), + ['--vscodeIconForeground' as any]: this._gutterIndicatorStyles.map(v => v.foreground), + border: this._gutterIndicatorStyles.map(v => `1px solid ${v.border}`), + boxSizing: 'border-box', borderRadius: '4px', display: 'flex', justifyContent: 'center', - transition: 'background-color 0.2s ease-in-out', - ...rectToProps(reader => layout.read(reader).iconRect), + transition: 'background-color 0.2s ease-in-out, width 0.2s ease-in-out', + ...rectToProps(reader => layout.read(reader).pillRect), } }, [ n.div({ + className: 'line-number', style: { - rotate: layout.map(l => { - switch (l.arrowDirection) { - case 'right': return '0deg'; - case 'bottom': return '90deg'; - case 'top': return '-90deg'; - } - }), + lineHeight: layout.map(l => `${l.lineHeight}px`), + display: layout.map(l => l.lineNumberRect.width > 0 ? 'flex' : 'none'), + alignItems: 'center', + justifyContent: 'flex-end', + width: layout.map(l => l.lineNumberRect.width), + height: '100%', + color: this._gutterIndicatorStyles.map(v => v.foreground), + } + }, + this._lineNumberToRender + ), + n.div({ + style: { + rotate: layout.map(i => `${i.rotation}deg`), transition: 'rotate 0.2s ease-in-out', display: 'flex', alignItems: 'center', justifyContent: 'center', + height: '100%', + width: layout.map(l => `${l.iconRect.width}px`), } }, [ - 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 7a31b034..0aac9633 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 00000000..0a07f09a --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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 { IInlineEditHost, 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; + + 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.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 InlineEditHost implements IInlineEditHost { + readonly onDidAccept: Event; + readonly inAcceptFlow: IObservable; + readonly inPartialAcceptFlow: IObservable; + + constructor( + private readonly _model: InlineCompletionsModel, + ) { + this.onDidAccept = this._model.onDidAccept; + this.inAcceptFlow = this._model.inAcceptFlow; + this.inPartialAcceptFlow = this._model.inPartialAcceptFlow; + } +} + +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 fe29ffa0..7931a86e 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,56 @@ 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 { InlineEditWithChanges } from './inlineEditWithChanges.js'; -import { IInlineEditsViewHost } from './inlineEditsViewInterface.js'; +import { GhostTextIndicator, InlineEditHost, InlineEditModel } from './inlineEditsModel.js'; +import { IInlineEditModel, InlineEditTabAction } from './inlineEditsViewInterface.js'; +import { InlineEditsCollapsedView } from './inlineEditsViews/inlineEditsCollapsedView.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 _host: 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 +78,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(); + } }) ); })); @@ -79,8 +90,11 @@ export class InlineEditsView extends Disposable { this._wordReplacementViews.recomputeInitiallyAndOnChange(this._store); this._indicatorCyclicDependencyCircuitBreaker.set(true, undefined); + this._constructorDone.set(true, undefined); // TODO: remove and use correct initialization order } + private readonly _constructorDone = observableValue(this, false); + private readonly _uiState = derived<{ state: ReturnType; diff: DetailedLineRangeMapping[]; @@ -89,35 +103,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 || !this._constructorDone.read(reader)) { 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 +143,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)) { - state = { kind: 'hidden' }; + if (model.showCollapsed.read(reader) && !this._indicator.read(reader)?.isHoverVisible.read(reader)) { + state = { kind: 'collapsed' }; } return { state, diff, - edit, + edit: inlineEdit, newText, - newTextLineCount: edit.modifiedLineRange.length, + newTextLineCount: inlineEdit.modifiedLineRange.length, originalDisplayRange: originalDisplayRange, }; }); @@ -150,48 +165,17 @@ 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) => { + protected readonly _indicator = derivedWithStore(this, (reader, store) => { if (!this._indicatorCyclicDependencyCircuitBreaker.read(reader)) { return undefined; } 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 +185,30 @@ 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, + this._host, + modelWithGhostTextSupport, + this._inlineEditsIsHovered, + this._focusIsInMenu, + )); }); private readonly _inlineEditsIsHovered = derived(this, reader => { @@ -245,24 +230,23 @@ 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,13 +256,13 @@ export class InlineEditsView extends Disposable { startColumn: s.state.column, text: s.state.text, }) : undefined), - this._host, + this._tabAction, )); private readonly _inlineDiffViewState = derived(this, reader => { const e = this._uiState.read(reader); if (!e || !e.state) { return undefined; } - if (e.state.kind === 'wordReplacements' || e.state.kind === 'lineReplacement' || e.state.kind === 'insertionMultiLine' || e.state.kind === 'hidden') { + if (e.state.kind === 'wordReplacements' || e.state.kind === 'lineReplacement' || e.state.kind === 'insertionMultiLine' || e.state.kind === 'collapsed') { return undefined; } return { @@ -289,10 +273,15 @@ export class InlineEditsView extends Disposable { }; }); + protected readonly _inlineCollapsedView = this._register(this._instantiationService.createInstance(InlineEditsCollapsedView, + this._editor, + this._model.map((m, reader) => this._uiState.read(reader)?.state?.kind === 'collapsed' ? m?.inlineEdit : undefined) + )); + 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, this._tabAction)); }); protected readonly _lineReplacementView = this._register(this._instantiationService.createInstance(InlineEditsLineReplacementView, @@ -303,32 +292,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 (this._host.get()?.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,70 +323,56 @@ 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, this._previewTextModel, 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 }; + case 'collapsed': return { kind: 'collapsed' as const }; } const inner = diff.flatMap(d => d.innerChanges ?? []); @@ -408,7 +380,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 +401,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 +416,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 ffa4cdba..dae49315 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,34 @@ 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 IInlineEditHost { + inAcceptFlow: IObservable; + inPartialAcceptFlow: IObservable; +} + +export interface IInlineEditModel { + displayName: string; + action: Command | undefined; + extensionCommands: Command[]; + inlineEdit: InlineEditWithChanges; tabAction: IObservable; - extensionCommands: IObservable; + showCollapsed: 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 53% 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 9b5fcbb5..39b6acae 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, InlineEditHost, 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,47 @@ 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 _inlineEditHost = derived(this, reader => { + const model = this._model.read(reader); + if (!model) { return undefined; } + return new InlineEditHost(model); + }); + + 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 +96,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._inlineEditHost, this._inlineEditModel, this._ghostTextIndicator, this._focusIsInMenu)); } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts new file mode 100644 index 00000000..2acbdd9d --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { n } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; +import { Disposable } from '../../../../../../../base/common/lifecycle.js'; +import { constObservable, derived, IObservable } from '../../../../../../../base/common/observable.js'; +import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; +import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; +import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; +import { Point } from '../../../../../../browser/point.js'; +import { singleTextRemoveCommonPrefix } from '../../../model/singleTextEditHelpers.js'; +import { IInlineEditsView } from '../inlineEditsViewInterface.js'; +import { InlineEditWithChanges } from '../inlineEditWithChanges.js'; +import { inlineEditIndicatorPrimaryBorder } from '../theme.js'; +import { PathBuilder } from '../utils/utils.js'; + +export class InlineEditsCollapsedView extends Disposable implements IInlineEditsView { + + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + + private readonly _editorObs: ObservableCodeEditor; + + constructor( + private readonly _editor: ICodeEditor, + private readonly _edit: IObservable, + ) { + super(); + + this._editorObs = observableCodeEditor(this._editor); + + const firstEdit = this._edit.map(inlineEdit => inlineEdit?.edit.edits[0] ?? null); + + const startPosition = firstEdit.map(edit => edit ? singleTextRemoveCommonPrefix(edit, this._editor.getModel()!).range.getStartPosition() : null); + const observedStartPoint = this._editorObs.observePosition(startPosition, this._store); + const startPoint = derived(reader => { + const point = observedStartPoint.read(reader); + if (!point) { return null; } + + const contentLeft = this._editorObs.layoutInfoContentLeft.read(reader); + const scrollLeft = this._editorObs.scrollLeft.read(reader); + return new Point(contentLeft + point.x - scrollLeft, point.y); + }); + + const overlayElement = n.div({ + class: 'inline-edits-collapsed-view', + style: { + position: 'absolute', + overflow: 'visible', + top: '0px', + left: '0px', + zIndex: '0', + display: 'block', + }, + }, [ + [this.getCollapsedIndicator(startPoint)], + ]).keepUpdated(this._store).element; + + this._register(this._editorObs.createOverlayWidget({ + domNode: overlayElement, + position: constObservable(null), + allowEditorOverflow: false, + minContentWidthInPx: constObservable(0), + })); + } + + private getCollapsedIndicator(startPoint: IObservable) { + const contentLeft = this._editorObs.layoutInfoContentLeft; + const startPointTranslated = startPoint.map((p, reader) => p ? p.deltaX(-contentLeft.read(reader)) : null); + const iconPath = this.createIconPath(startPointTranslated); + + return n.svg({ + class: 'collapsedView', + style: { + position: 'absolute', + top: 0, + left: contentLeft, + width: this._editorObs.contentWidth, + height: this._editorObs.editor.getContentHeight(), + overflow: 'hidden', + pointerEvents: 'none', + } + }, [ + n.svgElem('path', { + class: 'collapsedViewPath', + d: iconPath, + fill: asCssVariable(inlineEditIndicatorPrimaryBorder), + }), + ]); + } + + private createIconPath(indicatorPoint: IObservable): IObservable { + const width = 6; + const triangleHeight = 3; + const baseHeight = 1; + + return indicatorPoint.map(point => { + if (!point) { return new PathBuilder().build(); } + const baseTopLeft = point.deltaX(-width / 2).deltaY(-baseHeight); + const baseTopRight = baseTopLeft.deltaX(width); + const baseBottomLeft = baseTopLeft.deltaY(baseHeight); + const baseBottomRight = baseTopRight.deltaY(baseHeight); + const triangleBottomCenter = baseBottomLeft.deltaX(width / 2).deltaY(triangleHeight); + return new PathBuilder() + .moveTo(baseTopLeft) + .lineTo(baseTopRight) + .lineTo(baseBottomRight) + .lineTo(triangleBottomCenter) + .lineTo(baseBottomLeft) + .lineTo(baseTopLeft) + .build(); + }); + } + + readonly isHovered = constObservable(false); +} 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 f8c6bcf3..fcd48453 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 @@ -7,24 +7,39 @@ import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, derivedObservableWithCache, IObservable } from '../../../../../../../base/common/observable.js'; +import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; -import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; +import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; +import { Rect } from '../../../../../../browser/rect.js'; +import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { OffsetRange } from '../../../../../../common/core/offsetRange.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'; +import { getPrefixTrim, mapOutFalsy, maxContentWidthInRange } from '../utils/utils.js'; + +const HORIZONTAL_PADDING = 0; +const VERTICAL_PADDING = 0; +const BORDER_WIDTH = 1; +const WIDGET_SEPARATOR_WIDTH = 1; +const BORDER_RADIUS = 4; 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 +47,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), @@ -43,27 +74,13 @@ export class InlineEditsDeletionView extends Disposable implements IInlineEditsV minContentWidthInPx: derived(reader => { const info = this._editorLayoutInfo.read(reader); if (info === null) { return 0; } - return info.code1.x - info.codeStart1.x; + return info.codeRect.width; }), })); } 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) { @@ -99,70 +116,78 @@ export class InlineEditsDeletionView extends Disposable implements IInlineEditsV const editorLayout = this._editorObs.layoutInfo.read(reader); const horizontalScrollOffset = this._editorObs.scrollLeft.read(reader); + const w = this._editorObs.getOption(EditorOption.fontInfo).map(f => f.typicalHalfwidthCharacterWidth).read(reader); - const left = editorLayout.contentLeft + this._editorMaxContentWidthInRange.read(reader) - horizontalScrollOffset; + const right = editorLayout.contentLeft + Math.max(this._editorMaxContentWidthInRange.read(reader), w) - horizontalScrollOffset; const range = inlineEdit.originalLineRange; const selectionTop = this._originalVerticalStartPosition.read(reader) ?? this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); const selectionBottom = this._originalVerticalEndPosition.read(reader) ?? this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); - const codeLeft = editorLayout.contentLeft + this._maxPrefixTrim.read(reader).prefixLeftOffset; + const left = editorLayout.contentLeft + this._maxPrefixTrim.read(reader).prefixLeftOffset - horizontalScrollOffset; - if (left <= codeLeft) { + if (right <= left) { return null; } - const code1 = new Point(left, selectionTop); - const codeStart1 = new Point(codeLeft, selectionTop); - const code2 = new Point(left, selectionBottom); - const codeStart2 = new Point(codeLeft, selectionBottom); - const codeHeight = selectionBottom - selectionTop; + const codeRect = Rect.fromLeftTopRightBottom(left, selectionTop, right, selectionBottom).withMargin(VERTICAL_PADDING, HORIZONTAL_PADDING); return { - code1, - codeStart1, - code2, - codeStart2, - codeHeight, - horizontalScrollOffset, - padding: 3, - borderRadius: 4, + codeRect, + contentLeft: editorLayout.contentLeft, }; }).recomputeInitiallyAndOnChange(this._store); - private readonly _foregroundSvg = n.svg({ - transform: 'translate(-0.5 -0.5)', - style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, + private readonly _originalOverlay = n.div({ + style: { pointerEvents: 'none', } }, derived(reader => { const layoutInfoObs = mapOutFalsy(this._editorLayoutInfo).read(reader); if (!layoutInfoObs) { return undefined; } - const layoutInfo = layoutInfoObs.read(reader); + // Create an overlay which hides the left hand side of the original overlay when it overflows to the left + // such that there is a smooth transition at the edge of content left + const overlayhider = layoutInfoObs.map(layoutInfo => Rect.fromLeftTopRightBottom( + layoutInfo.contentLeft - BORDER_RADIUS - BORDER_WIDTH, + layoutInfo.codeRect.top, + layoutInfo.contentLeft, + layoutInfo.codeRect.bottom + )); - // TODO: look into why 1px offset is needed - const rectangleOverlay = createRectangle( - { - topLeft: layoutInfo.codeStart1, - width: layoutInfo.code1.x - layoutInfo.codeStart1.x + 1, - height: layoutInfo.code2.y - layoutInfo.code1.y + 1, - }, - layoutInfo.padding, - layoutInfo.borderRadius, - { hideLeft: layoutInfo.horizontalScrollOffset !== 0 } - ); + const overlayRect = derived(reader => { + const rect = layoutInfoObs.read(reader).codeRect; + const overlayHider = overlayhider.read(reader); + return rect.intersectHorizontal(new OffsetRange(overlayHider.left, Number.MAX_SAFE_INTEGER)); + }); - const originalBorderColor = getOriginalBorderColor(this._host.tabAction).read(reader); + const separatorRect = overlayRect.map(rect => rect.withMargin(WIDGET_SEPARATOR_WIDTH, WIDGET_SEPARATOR_WIDTH)); return [ - n.svgElem('path', { - class: 'originalOverlay', - d: rectangleOverlay, + n.div({ + class: 'originalSeparatorDeletion', style: { - fill: asCssVariable(originalBackgroundColor), - stroke: originalBorderColor, - strokeWidth: '1px', + ...separatorRect.read(reader).toStyles(), + borderRadius: `${BORDER_RADIUS}px`, + border: `${BORDER_WIDTH + WIDGET_SEPARATOR_WIDTH}px solid ${asCssVariable(editorBackground)}`, + boxSizing: 'border-box', } }), + n.div({ + class: 'originalOverlayDeletion', + style: { + ...overlayRect.read(reader).toStyles(), + borderRadius: `${BORDER_RADIUS}px`, + border: getOriginalBorderColor(this._tabAction).map(bc => `${BORDER_WIDTH}px solid ${asCssVariable(bc)}`), + boxSizing: 'border-box', + backgroundColor: asCssVariable(originalBackgroundColor), + } + }), + n.div({ + class: 'originalOverlayHiderDeletion', + style: { + ...overlayhider.read(reader).toStyles(), + backgroundColor: asCssVariable(editorBackground), + } + }) ]; })).keepUpdated(this._store); @@ -177,7 +202,7 @@ export class InlineEditsDeletionView extends Disposable implements IInlineEditsV display: this._display, }, }, [ - [this._foregroundSvg], + [this._originalOverlay], ]).keepUpdated(this._store); readonly isHovered = constObservable(false); 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 38fe0c00..e79107bb 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 @@ -8,13 +8,15 @@ import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, derivedWithStore, IObservable, observableValue } from '../../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; +import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.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'; import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; import { ILanguageService } from '../../../../../../common/languages/language.js'; @@ -23,12 +25,16 @@ 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 { getModifiedBorderColor, modifiedChangedLineBackgroundColor } from '../theme.js'; -import { createRectangle, getPrefixTrim, mapOutFalsy } from '../utils/utils.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; +import { getModifiedBorderColor, modifiedBackgroundColor } from '../theme.js'; +import { getPrefixTrim, mapOutFalsy } from '../utils/utils.js'; + +const BORDER_WIDTH = 1; +const WIDGET_SEPARATOR_WIDTH = 1; +const BORDER_RADIUS = 4; 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; @@ -48,23 +54,49 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits return { lineNumber: state.lineNumber, column: state.startColumn, text: state.text }; }); + private readonly _trimVertically = derived(this, reader => { + const text = this._state.read(reader)?.text; + if (!text || text.trim() === '') { + return { topOffset: 0, bottomOffset: 0, linesTop: 0, linesBottom: 0 }; + } + + // Adjust for leading/trailing newlines + const lineHeight = this._editor.getOption(EditorOption.lineHeight); + const eol = this._editor.getModel()!.getEOL(); + let linesTop = 0; + let linesBottom = 0; + + let i = 0; + for (; i < text.length && text.startsWith(eol, i); i += eol.length) { + linesTop += 1; + } + + for (let j = text.length; j > i && text.endsWith(eol, j); j -= eol.length) { + linesBottom += 1; + } + + return { topOffset: linesTop * lineHeight, bottomOffset: linesBottom * lineHeight, linesTop, linesBottom }; + }); + private readonly _maxPrefixTrim = derived(reader => { const state = this._state.read(reader); if (!state) { return { prefixLeftOffset: 0, prefixTrim: 0 }; - } + const textModel = this._editor.getModel()!; const eol = textModel.getEOL(); - const startsWithEol = state.text.startsWith(eol); - const originalRange = new LineRange(state.lineNumber, state.lineNumber + (startsWithEol ? 0 : 1)); - let modifiedLines = state.text.split(eol); - if (startsWithEol) { - modifiedLines = modifiedLines.splice(1); - } else { + + const trimVertically = this._trimVertically.read(reader); + + const lines = state.text.split(eol); + const modifiedLines = lines.slice(trimVertically.linesTop, lines.length - trimVertically.linesBottom); + if (trimVertically.linesTop === 0) { modifiedLines[0] = textModel.getLineContent(state.lineNumber) + modifiedLines[0]; } + const originalRange = new LineRange(state.lineNumber, state.lineNumber + (trimVertically.linesTop > 0 ? 0 : 1)); + return getPrefixTrim([], originalRange, modifiedLines, this._editor); }); @@ -86,18 +118,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,18 +128,35 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits startColumn: number; text: string; } | undefined>, - private readonly _host: IInlineEditsViewHost, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + private readonly _tabAction: IObservable, + @IInstantiationService instantiationService: IInstantiationService, @ILanguageService private readonly _languageService: ILanguageService, ) { super(); + this._editorObs = observableCodeEditor(this._editor); + + this._ghostTextView = this._register(instantiationService.createInstance(GhostTextView, + this._editor, + { + ghostText: this._ghostText, + minReservedLineCount: constObservable(0), + targetTextModel: this._editorObs.model.map(model => model ?? undefined), + warning: constObservable(undefined), + }, + observableValue(this, { syntaxHighlightingEnabled: true, extraClasses: ['inline-edit'] }), + true, + true + )); + + this.isHovered = this._ghostTextView.isHovered; + this._register(this._ghostTextView.onDidClick((e) => { this._onDidClick.fire(e); })); this._register(this._editorObs.createOverlayWidget({ - domNode: this._nonOverflowView.element, + domNode: this._view.element, position: constObservable(null), allowEditorOverflow: false, minContentWidthInPx: derived(reader => { @@ -144,7 +183,7 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits const text = textBeforeInsertion + state.text + textAfterInsertion; const lines = text.split(eol); - const renderOptions = RenderOptions.fromEditor(this._editor).withSetWidth(false); + const renderOptions = RenderOptions.fromEditor(this._editor).withSetWidth(false).withScrollBeyondLastColumn(0); const lineWidths = lines.map(line => { const t = textModel.tokenization.tokenizeLinesAt(state.lineNumber, [line])?.[0]; let tokens: LineTokens; @@ -154,7 +193,7 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits tokens = LineTokens.createEmpty(line, this._languageService.languageIdCodec); } - return renderLines(new LineSource([tokens]), renderOptions, [], $('div'), true).minWidthInPx - 20; // TODO: always too much padding included, why? + return renderLines(new LineSource([tokens]), renderOptions, [], $('div'), true).minWidthInPx; }); // Take the max value that we observed. @@ -162,31 +201,7 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits return Math.max(...lineWidths); }); - private readonly _trimVertically = derived(this, reader => { - const text = this._state.read(reader)?.text; - if (!text || text.trim() === '') { - return { top: 0, bottom: 0 }; - } - - // Adjust for leading/trailing newlines - const lineHeight = this._editor.getOption(EditorOption.lineHeight); - const eol = this._editor.getModel()!.getEOL(); - let topTrim = 0; - let bottomTrim = 0; - - let i = 0; - for (; i < text.length && text.startsWith(eol, i); i += eol.length) { - topTrim += lineHeight; - } - - for (let j = text.length; j > i && text.endsWith(eol, j); j -= eol.length) { - bottomTrim += lineHeight; - } - - return { top: topTrim, bottom: bottomTrim }; - }); - - public readonly startLineOffset = this._trimVertically.map(v => v.top); + public readonly startLineOffset = this._trimVertically.map(v => v.topOffset); public readonly originalLines = this._state.map(s => s ? new LineRange( s.lineNumber, @@ -215,7 +230,7 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits return null; } - const { top: topTrim, bottom: bottomTrim } = this._trimVertically.read(reader); + const { topOffset: topTrim, bottomOffset: bottomTrim } = this._trimVertically.read(reader); const scrollTop = this._editorObs.scrollTop.read(reader); const height = this._ghostTextView.height.read(reader) - topTrim - bottomTrim; @@ -226,51 +241,61 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits return { overlay, + startsAtContentLeft: prefixLeftOffset === 0, contentLeft: editorLayout.contentLeft, minContentWidthRequired: prefixLeftOffset + overlay.width + verticalScrollbarWidth, - borderRadius: 4, - padding: 3 }; }).recomputeInitiallyAndOnChange(this._store); - private readonly _foregroundSvg = n.svg({ - transform: 'translate(-0.5 -0.5)', - style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, + private readonly _modifiedOverlay = n.div({ + style: { pointerEvents: 'none', } }, derived(reader => { const overlayLayoutObs = mapOutFalsy(this._overlayLayout).read(reader); 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); + // Create an overlay which hides the left hand side of the original overlay when it overflows to the left + // such that there is a smooth transition at the edge of content left + const overlayHider = overlayLayoutObs.map(layoutInfo => Rect.fromLeftTopRightBottom( + layoutInfo.contentLeft - BORDER_RADIUS - BORDER_WIDTH, + layoutInfo.overlay.top, + layoutInfo.contentLeft, + layoutInfo.overlay.bottom + )).read(reader); - const rectangleOverlay = createRectangle( - { - topLeft: croppedOverlay.getLeftTop(), - width: croppedOverlay.width + 1, - height: croppedOverlay.height + 1, - }, - layoutInfo.padding, - layoutInfo.borderRadius, - { hideLeft: croppedOverlay.left !== overlay.left } - ); - - const modifiedBorderColor = getModifiedBorderColor(this._host.tabAction).read(reader); + const overlayRect = overlayLayoutObs.map(l => l.overlay.withMargin(0, BORDER_WIDTH, 0, l.startsAtContentLeft ? 0 : BORDER_WIDTH).intersectHorizontal(new OffsetRange(overlayHider.left, Number.MAX_SAFE_INTEGER))); + const underlayRect = overlayRect.map(rect => rect.withMargin(WIDGET_SEPARATOR_WIDTH, WIDGET_SEPARATOR_WIDTH)); return [ - n.svgElem('path', { - class: 'originalOverlay', - d: rectangleOverlay, + n.div({ + class: 'originalUnderlayInsertion', style: { - fill: asCssVariable(modifiedChangedLineBackgroundColor), - stroke: modifiedBorderColor, - strokeWidth: '1px', + ...underlayRect.read(reader).toStyles(), + borderRadius: BORDER_RADIUS, + border: `${BORDER_WIDTH + WIDGET_SEPARATOR_WIDTH}px solid ${asCssVariable(editorBackground)}`, + boxSizing: 'border-box', } }), + n.div({ + class: 'originalOverlayInsertion', + style: { + ...overlayRect.read(reader).toStyles(), + borderRadius: BORDER_RADIUS, + border: getModifiedBorderColor(this._tabAction).map(bc => `${BORDER_WIDTH}px solid ${asCssVariable(bc)}`), + boxSizing: 'border-box', + backgroundColor: asCssVariable(modifiedBackgroundColor), + } + }), + n.div({ + class: 'originalOverlayHiderInsertion', + style: { + ...overlayHider.toStyles(), + backgroundColor: asCssVariable(editorBackground), + } + }) ]; })).keepUpdated(this._store); - private readonly _nonOverflowView = n.div({ + private readonly _view = n.div({ class: 'inline-edits-view', style: { position: 'absolute', @@ -281,8 +306,6 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits display: this._display, }, }, [ - [this._foregroundSvg], + [this._modifiedOverlay], ]).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 123fb77c..7d79399b 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 @@ -10,6 +10,7 @@ import { Disposable, toDisposable } from '../../../../../../../base/common/lifec import { autorun, autorunDelta, constObservable, derived, IObservable } from '../../../../../../../base/common/observable.js'; import { editorBackground, scrollbarShadow } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; +import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; import { IEditorMouseEvent, IViewZoneChangeAccessor } from '../../../../../../browser/editorBrowser.js'; import { EditorMouseEvent } from '../../../../../../browser/editorDom.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; @@ -25,10 +26,9 @@ 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 { getModifiedBorderColor, modifiedChangedLineBackgroundColor } from '../theme.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; +import { getEditorBlendedColor, getModifiedBorderColor, getOriginalBorderColor, modifiedChangedLineBackgroundColor, originalBackgroundColor } from '../theme.js'; import { getPrefixTrim, mapOutFalsy, rectToProps } from '../utils/utils.js'; -import { rangesToBubbleRanges, Replacement } from './inlineEditsWordReplacementView.js'; export class InlineEditsLineReplacementView extends Disposable implements IInlineEditsView { @@ -82,7 +82,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin } // TODO: All lines should be rendered at once for one dom element - const result = renderLines(new LineSource([tokens]), RenderOptions.fromEditor(this._editor.editor).withSetWidth(false), decorations, line, true); + const result = renderLines(new LineSource([tokens]), RenderOptions.fromEditor(this._editor.editor).withSetWidth(false).withScrollBeyondLastColumn(0), decorations, line, true); this._editor.getOption(EditorOption.fontInfo).read(reader); // update when font info changes requiredWidth = Math.max(requiredWidth, result.minWidthInPx); @@ -90,7 +90,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin lines.push(line); } - return { lines, requiredWidth: requiredWidth - 10 }; // TODO: Width is always too large, why? + return { lines, requiredWidth: requiredWidth }; }); @@ -111,7 +111,6 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin const scrollLeft = this._editor.scrollLeft.read(reader); const scrollTop = this._editor.scrollTop.read(reader); const editorLeftOffset = contentLeft - scrollLeft; - const PADDING = 4; const textModel = this._editor.editor.getModel()!; @@ -128,18 +127,18 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin editorLeftOffset + prefixLeftOffset, topOfOriginalLines, maxLineWidth, - bottomOfOriginalLines - topOfOriginalLines + PADDING + bottomOfOriginalLines - topOfOriginalLines ); const modifiedLinesOverlay = Rect.fromLeftTopWidthHeight( originalLinesOverlay.left, - originalLinesOverlay.bottom + PADDING, + originalLinesOverlay.bottom, originalLinesOverlay.width, edit.modifiedRange.length * lineHeight ); - const background = Rect.hull([originalLinesOverlay, modifiedLinesOverlay]).withMargin(PADDING); + const background = Rect.hull([originalLinesOverlay, modifiedLinesOverlay]); const lowerBackground = background.intersectVertical(new OffsetRange(originalLinesOverlay.bottom, Number.MAX_SAFE_INTEGER)); - const lowerText = new Rect(lowerBackground.left + PADDING, lowerBackground.top + PADDING, lowerBackground.right, lowerBackground.bottom); + const lowerText = new Rect(lowerBackground.left, lowerBackground.top, lowerBackground.right, lowerBackground.bottom); return { originalLinesOverlay, @@ -147,13 +146,12 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin background, lowerBackground, lowerText, - padding: PADDING, - minContentWidthRequired: prefixLeftOffset + maxLineWidth + PADDING * 2 + verticalScrollbarWidth, + minContentWidthRequired: prefixLeftOffset + maxLineWidth + verticalScrollbarWidth, }; }); private readonly _viewZoneInfo = derived<{ height: number; lineNumber: number } | undefined>(reader => { - const shouldShowViewZone = this._editor.getOption(EditorOption.inlineSuggest).map(o => o.edits.codeShifting).read(reader); + const shouldShowViewZone = this._editor.getOption(EditorOption.inlineSuggest).map(o => o.edits.allowCodeShifting === 'always').read(reader); if (!shouldShowViewZone) { return undefined; } @@ -164,7 +162,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin return undefined; } - const viewZoneHeight = layout.lowerBackground.height + 2 * layout.padding; + const viewZoneHeight = layout.lowerBackground.height; const viewZoneLineNumber = edit.originalRange.endLineNumberExclusive; return { height: viewZoneHeight, lineNumber: viewZoneLineNumber }; }); @@ -180,24 +178,19 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin } const layoutProps = layout.read(reader); - const scrollLeft = this._editor.scrollLeft.read(reader); - let contentLeft = this._editor.layoutInfoContentLeft.read(reader); - let contentWidth = this._editor.contentWidth.read(reader); + const contentLeft = this._editor.layoutInfoContentLeft.read(reader); + const contentWidth = this._editor.contentWidth.read(reader); const contentHeight = this._editor.editor.getContentHeight(); - if (scrollLeft === 0) { - contentLeft -= layoutProps.padding; - contentWidth += layoutProps.padding; - } - const lineHeight = this._editor.getOption(EditorOption.lineHeight).read(reader); modifiedLineElements.lines.forEach(l => { - l.style.width = `${layout.read(reader).lowerText.width}px`; + l.style.width = `${layoutProps.lowerText.width}px`; l.style.height = `${lineHeight}px`; l.style.position = 'relative'; }); - const modifiedBorderColor = getModifiedBorderColor(this._host.tabAction).read(reader); + const modifiedBorderColor = getModifiedBorderColor(this._tabAction).read(reader); + const originalBorderColor = getOriginalBorderColor(this._tabAction).read(reader); return [ n.div({ @@ -212,23 +205,28 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin } }, [ n.div({ + class: 'originalOverlayLineReplacement', style: { position: 'absolute', - top: layoutProps.lowerBackground.top - layoutProps.padding, - left: layoutProps.lowerBackground.left - contentLeft, - width: layoutProps.lowerBackground.width, - height: layoutProps.padding * 2, - background: asCssVariable(editorBackground), - }, + ...rectToProps(reader => layout.read(reader).background.translateX(-contentLeft)), + borderRadius: '4px', + + border: getEditorBlendedColor(originalBorderColor, this._themeService).map(c => `1px solid ${c.toString()}`), + pointerEvents: 'none', + boxSizing: 'border-box', + background: asCssVariable(originalBackgroundColor), + } }), n.div({ + class: 'modifiedOverlayLineReplacement', style: { position: 'absolute', ...rectToProps(reader => layout.read(reader).lowerBackground.translateX(-contentLeft)), borderRadius: '4px', background: asCssVariable(editorBackground), boxShadow: `${asCssVariable(scrollbarShadow)} 0 6px 6px -6px`, - borderTop: `1px solid ${modifiedBorderColor}`, + border: `1px solid ${asCssVariable(modifiedBorderColor)}`, + boxSizing: 'border-box', overflow: 'hidden', cursor: 'pointer', pointerEvents: 'auto', @@ -240,38 +238,26 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin }, [ n.div({ style: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', + position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', background: asCssVariable(modifiedChangedLineBackgroundColor), }, }) ]), n.div({ + class: 'modifiedLinesLineReplacement', style: { position: 'absolute', - padding: '0px', boxSizing: 'border-box', ...rectToProps(reader => layout.read(reader).lowerText.translateX(-contentLeft)), fontFamily: this._editor.getOption(EditorOption.fontFamily), fontSize: this._editor.getOption(EditorOption.fontSize), fontWeight: this._editor.getOption(EditorOption.fontWeight), pointerEvents: 'none', + whiteSpace: 'nowrap', + borderRadius: '4px', + overflow: 'hidden', } }, [...modifiedLineElements.lines]), - n.div({ - style: { - position: 'absolute', - ...rectToProps(reader => layout.read(reader).background.translateX(-contentLeft)), - borderRadius: '4px', - - border: `1px solid ${modifiedBorderColor}`, - pointerEvents: 'none', - boxSizing: 'border-box', - } - }, []), ]) ]; }) @@ -287,8 +273,9 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin modifiedLines: string[]; replacements: Replacement[]; } | undefined>, - private readonly _host: IInlineEditsViewHost, - @ILanguageService private readonly _languageService: ILanguageService + private readonly _tabAction: IObservable, + @ILanguageService private readonly _languageService: ILanguageService, + @IThemeService private readonly _themeService: IThemeService, ) { super(); @@ -367,3 +354,23 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin } } } + +function rangesToBubbleRanges(ranges: Range[]): Range[] { + const result: Range[] = []; + while (ranges.length) { + let range = ranges.shift()!; + if (range.startLineNumber !== range.endLineNumber) { + ranges.push(new Range(range.startLineNumber + 1, 1, range.endLineNumber, range.endColumn)); + range = new Range(range.startLineNumber, range.startColumn, range.startLineNumber, Number.MAX_SAFE_INTEGER); // TODO: this is not correct + } + + result.push(range); + } + return result; + +} + +export interface Replacement { + originalRange: Range; + modifiedRange: Range; +} 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 a2e6f5fa..26e186b6 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 @@ -9,7 +9,8 @@ import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { IObservable, IReader, autorun, constObservable, derived, derivedObservableWithCache, observableFromEvent } from '../../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; -import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; +import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js'; +import { asCssVariable, asCssVariableWithDefault } from '../../../../../../../platform/theme/common/colorUtils.js'; import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; @@ -23,31 +24,37 @@ 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'; +import { getEditorBlendedColor, getModifiedBorderColor, getOriginalBorderColor, modifiedBackgroundColor, originalBackgroundColor } from '../theme.js'; +import { PathBuilder, getContentRenderWidth, getOffsetForPos, mapOutFalsy, maxContentWidthInRange } from '../utils/utils.js'; -const PADDING = 4; +const HORIZONTAL_PADDING = 0; +const VERTICAL_PADDING = 0; const ENABLE_OVERFLOW = false; +const BORDER_WIDTH = 1; +const WIDGET_SEPARATOR_WIDTH = 1; +const BORDER_RADIUS = 4; +const ORIGINAL_END_PADDING = 20; +const MODIFIED_END_PADDING = 12; + export class InlineEditsSideBySideView extends Disposable implements IInlineEditsView { // This is an approximation and should be improved by using the real parameters used bellow - static fitsInsideViewport(editor: ICodeEditor, edit: InlineEditWithChanges, originalDisplayRange: LineRange, reader: IReader): boolean { + static fitsInsideViewport(editor: ICodeEditor, textModel: ITextModel, edit: InlineEditWithChanges, originalDisplayRange: LineRange, reader: IReader): boolean { const editorObs = observableCodeEditor(editor); const editorWidth = editorObs.layoutInfoWidth.read(reader); const editorContentLeft = editorObs.layoutInfoContentLeft.read(reader); const editorVerticalScrollbar = editor.getLayoutInfo().verticalScrollbarWidth; - const w = editor.getOption(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; const minimapWidth = editorObs.layoutInfoMinimap.read(reader).minimapLeft !== 0 ? editorObs.layoutInfoMinimap.read(reader).minimapWidth : 0; const maxOriginalContent = maxContentWidthInRange(editorObs, originalDisplayRange, undefined/* do not reconsider on each layout info change */); - const maxModifiedContent = edit.lineEdit.newLines.reduce((max, line) => Math.max(max, line.length * w), 0); - const endOfEditorPadding = 20; // padding after last line of editor - const editorsPadding = edit.modifiedLineRange.length <= edit.originalLineRange.length ? PADDING * 3 + endOfEditorPadding : 60 + endOfEditorPadding * 2; + const maxModifiedContent = edit.lineEdit.newLines.reduce((max, line) => Math.max(max, getContentRenderWidth(line, editor, textModel)), 0); + const originalPadding = ORIGINAL_END_PADDING; // padding after last line of original editor + const modifiedPadding = MODIFIED_END_PADDING + 2 * BORDER_WIDTH; // padding after last line of modified editor - return maxOriginalContent + maxModifiedContent + editorsPadding < editorWidth - editorContentLeft - editorVerticalScrollbar - minimapWidth; + return maxOriginalContent + maxModifiedContent + originalPadding + modifiedPadding < editorWidth - editorContentLeft - editorVerticalScrollbar - minimapWidth; } private readonly _editorObs = observableCodeEditor(this._editor); @@ -60,28 +67,15 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit private readonly _edit: IObservable, private readonly _previewTextModel: ITextModel, private readonly _uiState: IObservable<{ - edit: InlineEditWithChanges; newTextLineCount: number; originalDisplayRange: LineRange; } | undefined>, - private readonly _host: IInlineEditsViewHost, + private readonly _tabAction: IObservable, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IThemeService private readonly _themeService: IThemeService, ) { super(); - this._register(this._editorObs.createOverlayWidget({ - domNode: this._overflowView.element, - position: constObservable({ - preference: { - top: 0, - left: 0 - } - }), - allowEditorOverflow: true, - minContentWidthInPx: constObservable(0), - })); - this._register(this._editorObs.createOverlayWidget({ domNode: this._nonOverflowView.element, position: constObservable(null), @@ -100,12 +94,13 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit if (!layoutInfo) { return; } - const editorRect = layoutInfo.editRect.deltaTop(layoutInfo.padding).deltaBottom(-layoutInfo.padding); + const editorRect = layoutInfo.editRect.withMargin(-VERTICAL_PADDING, -HORIZONTAL_PADDING); this.previewEditor.layout({ height: editorRect.height, width: layoutInfo.previewEditorWidth + 15 /* Make sure editor does not scroll horizontally */ }); this._editorContainer.element.style.top = `${editorRect.top}px`; this._editorContainer.element.style.left = `${editorRect.left}px`; - this._editorContainer.element.style.width = `${layoutInfo.previewEditorWidth}px`; // Set width to clip view zone + this._editorContainer.element.style.width = `${layoutInfo.previewEditorWidth + HORIZONTAL_PADDING}px`; // Set width to clip view zone + //this._editorContainer.element.style.borderRadius = `0 ${BORDER_RADIUS}px ${BORDER_RADIUS}px 0`; })); this._register(autorun(reader => { @@ -116,6 +111,8 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit this._previewEditorObs.editor.setScrollLeft(layoutInfo.desiredPreviewEditorScrollLeft); })); + + this._updatePreviewEditor.recomputeInitiallyAndOnChange(this._store); } private readonly _display = derived(this, reader => !!this._uiState.read(reader) ? 'block' : 'none'); @@ -123,7 +120,7 @@ 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 @@ -194,19 +191,17 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit // because of the auto run initial // Before removing these, verify with a non-monospace font family this._display.read(reader); - if (this._overflowView) { - this._overflowView.element.style.display = this._display.read(reader); - } if (this._nonOverflowView) { this._nonOverflowView.element.style.display = this._display.read(reader); } const uiState = this._uiState.read(reader); - if (!uiState) { + const edit = this._edit.read(reader); + if (!uiState || !edit) { return; } - const range = uiState.edit.originalLineRange; + const range = edit.originalLineRange; const hiddenAreas: Range[] = []; if (range.startLineNumber > 1) { @@ -235,8 +230,7 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit })); } }); - - }).recomputeInitiallyAndOnChange(this._store); + }); private readonly _previewEditorWidth = derived(this, reader => { const edit = this._edit.read(reader); @@ -319,52 +313,48 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit editorContentAreaWidth + horizontalScrollOffset ) ); - const previewEditorLeftInTextArea = Math.min(editorContentMaxWidthInRange + 20, maxPreviewEditorLeft); + const previewEditorLeftInTextArea = Math.min(editorContentMaxWidthInRange + ORIGINAL_END_PADDING, maxPreviewEditorLeft); - const maxContentWidth = editorContentMaxWidthInRange + 20 + previewContentWidth + 70; + const maxContentWidth = editorContentMaxWidthInRange + ORIGINAL_END_PADDING + previewContentWidth + 70; const dist = maxPreviewEditorLeft - previewEditorLeftInTextArea; let desiredPreviewEditorScrollLeft; - let left; + let codeRight; if (previewEditorLeftInTextArea > horizontalScrollOffset) { desiredPreviewEditorScrollLeft = 0; - left = editorLayout.contentLeft + previewEditorLeftInTextArea - horizontalScrollOffset; + codeRight = editorLayout.contentLeft + previewEditorLeftInTextArea - horizontalScrollOffset; } else { desiredPreviewEditorScrollLeft = horizontalScrollOffset - previewEditorLeftInTextArea; - left = editorLayout.contentLeft; + codeRight = editorLayout.contentLeft; } const selectionTop = this._originalVerticalStartPosition.read(reader) ?? this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); const selectionBottom = this._originalVerticalEndPosition.read(reader) ?? this._editor.getBottomForLineNumber(range.endLineNumberExclusive - 1) - this._editorObs.scrollTop.read(reader); // TODO: const { prefixLeftOffset } = getPrefixTrim(inlineEdit.edit.edits.map(e => e.range), inlineEdit.originalLineRange, [], this._editor); - const codeLeft = editorLayout.contentLeft; + const codeLeft = editorLayout.contentLeft - horizontalScrollOffset; - let codeRect = Rect.fromLeftTopRightBottom(codeLeft, selectionTop, left, selectionBottom); + let codeRect = Rect.fromLeftTopRightBottom(codeLeft, selectionTop, codeRight, selectionBottom); + const isInsertion = codeRect.height === 0; + if (!isInsertion) { + codeRect = codeRect.withMargin(VERTICAL_PADDING, HORIZONTAL_PADDING); + } const editHeight = this._editor.getOption(EditorOption.lineHeight) * inlineEdit.modifiedLineRange.length; const codeHeight = selectionBottom - selectionTop; const previewEditorHeight = Math.max(codeHeight, editHeight); - const editIsSameHeight = codeRect.height === previewEditorHeight; - const codeEditDistRange = editIsSameHeight - ? new OffsetRange(4, 61) - : new OffsetRange(60, 61); - const clipped = dist === 0; - const codeEditDist = editIsSameHeight ? PADDING : codeEditDistRange.clip(dist); // TODO: Is there a better way to specify the distance? - const previewEditorWidth = Math.min(previewContentWidth, remainingWidthRightOfEditor + editorLayout.width - editorLayout.contentLeft - codeEditDist); + const codeEditDist = 0; + const previewEditorWidth = Math.min(previewContentWidth + MODIFIED_END_PADDING, remainingWidthRightOfEditor + editorLayout.width - editorLayout.contentLeft - codeEditDist); - let editRect = Rect.fromLeftTopWidthHeight(left + codeEditDist, selectionTop, previewEditorWidth, previewEditorHeight); - - const isInsertion = codeRect.height === 0; + let editRect = Rect.fromLeftTopWidthHeight(codeRect.right + codeEditDist, selectionTop, previewEditorWidth, previewEditorHeight); if (!isInsertion) { - codeRect = codeRect.withMargin(PADDING).deltaRight(-PADDING); - editRect = editRect.withMargin(PADDING).deltaLeft(PADDING); + editRect = editRect.withMargin(VERTICAL_PADDING, HORIZONTAL_PADDING).translateX(HORIZONTAL_PADDING + BORDER_WIDTH); } else { // Align top of edit with insertion line - editRect = editRect.withMargin(PADDING).translateY(PADDING); + editRect = editRect.withMargin(VERTICAL_PADDING, HORIZONTAL_PADDING).translateY(VERTICAL_PADDING); } // debugView(debugLogRects({ codeRect, editRect }, this._editor.getDomNode()!), reader); @@ -373,13 +363,13 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit codeRect, editRect, codeScrollLeft: horizontalScrollOffset, + contentLeft: editorLayout.contentLeft, + isInsertion, maxContentWidth, shouldShowShadow: clipped, desiredPreviewEditorScrollLeft, previewEditorWidth, - padding: PADDING, - borderRadius: PADDING }; }); @@ -406,27 +396,6 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit return true; }); - private readonly _extendedModifiedPath = derived(reader => { - const layoutInfo = this._previewEditorLayoutInfo.read(reader); - if (!layoutInfo) { return undefined; } - - const path = new PathBuilder() - .moveTo(layoutInfo.codeRect.getRightBottom()) - .lineTo(layoutInfo.codeRect.getRightTop()) - .lineTo(layoutInfo.editRect.getLeftTop()) - .lineTo(layoutInfo.editRect.getRightTop().deltaX(-layoutInfo.borderRadius)) - .curveTo(layoutInfo.editRect.getRightTop(), layoutInfo.editRect.getRightTop().deltaY(layoutInfo.borderRadius)) - .lineTo(layoutInfo.editRect.getRightBottom().deltaY(-layoutInfo.borderRadius)) - .curveTo(layoutInfo.editRect.getRightBottom(), layoutInfo.editRect.getRightBottom().deltaX(-layoutInfo.borderRadius)) - .lineTo(layoutInfo.editRect.getLeftBottom()); - - if (layoutInfo.editRect.bottom !== layoutInfo.codeRect.bottom) { - path.curveTo2(layoutInfo.editRect.getLeftBottom().deltaX(-20), layoutInfo.codeRect.getRightBottom().deltaX(20), layoutInfo.codeRect.getRightBottom().deltaX(0)); - } - path.lineTo(layoutInfo.codeRect.getRightBottom()); - return path.build(); - }); - private readonly _originalBackgroundColor = observableFromEvent(this, this._themeService.onDidColorThemeChange, () => { return this._themeService.getColorTheme().getColor(originalBackgroundColor) ?? Color.transparent; }); @@ -455,98 +424,153 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit .build(); }), style: { - fill: 'var(--vscode-editor-background, transparent)', + fill: asCssVariableWithDefault(editorBackground, 'transparent'), } }), ]).keepUpdated(this._store); - private readonly _modifiedBackgroundSvg = n.svg({ - transform: 'translate(-0.5 -0.5)', - style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, - }, [ - n.svgElem('path', { - class: 'extendedModifiedBackgroundCoverUp', - d: this._extendedModifiedPath, - style: { - fill: 'var(--vscode-editor-background, transparent)', - strokeWidth: '0px', - } - }), - ]).keepUpdated(this._store); - - private readonly _foregroundBackgroundSvg = n.svg({ - transform: 'translate(-0.5 -0.5)', - style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, - }, [ - n.svgElem('path', { - class: 'extendedModifiedBackgroundCoverUp', - d: this._extendedModifiedPath, - style: { - fill: 'var(--vscode-inlineEdit-modifiedChangedLineBackground, transparent)', - strokeWidth: '1px', - } - }), - ]).keepUpdated(this._store); - - private readonly _middleBorderWithShadow = n.div({ - class: ['middleBorderWithShadow'], - style: { - position: 'absolute', - display: this._previewEditorLayoutInfo.map(i => i?.shouldShowShadow ? 'block' : 'none'), - width: '6px', - boxShadow: 'var(--vscode-scrollbar-shadow) -6px 0 6px -6px inset', - left: this._previewEditorLayoutInfo.map(i => i ? i.codeRect.right - 6 : 0), - top: this._previewEditorLayoutInfo.map(i => i ? i.codeRect.top : 0), - height: this._previewEditorLayoutInfo.map(i => i ? i.codeRect.height : 0), - }, - }, []).keepUpdated(this._store); - - private readonly _foregroundSvg = n.svg({ - transform: 'translate(-0.5 -0.5)', - style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, + private readonly _originalOverlay = n.div({ + style: { pointerEvents: 'none', display: this._previewEditorLayoutInfo.map(layoutInfo => layoutInfo?.isInsertion ? 'none' : 'block') }, }, derived(reader => { 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 borderStyling = getOriginalBorderColor(this._tabAction).map(bc => `${BORDER_WIDTH}px solid ${asCssVariable(bc)}`); + const borderStylingSeparator = `${BORDER_WIDTH + WIDGET_SEPARATOR_WIDTH}px solid ${asCssVariable(editorBackground)}`; + + const hasBorderLeft = layoutInfoObs.read(reader).codeScrollLeft !== 0; + const isModifiedLower = layoutInfoObs.map(layoutInfo => layoutInfo.codeRect.bottom < layoutInfo.editRect.bottom); + const transitionRectSize = BORDER_RADIUS * 2 + BORDER_WIDTH * 2; + + // Create an overlay which hides the left hand side of the original overlay when it overflows to the left + // such that there is a smooth transition at the edge of content left + const overlayHider = layoutInfoObs.map(layoutInfo => Rect.fromLeftTopRightBottom( + layoutInfo.contentLeft - BORDER_RADIUS - BORDER_WIDTH, + layoutInfo.codeRect.top, + layoutInfo.contentLeft, + layoutInfo.codeRect.bottom + transitionRectSize + )).read(reader); + + const intersectionLine = new OffsetRange(overlayHider.left, Number.MAX_SAFE_INTEGER); + const overlayRect = layoutInfoObs.map(layoutInfo => layoutInfo.codeRect.intersectHorizontal(intersectionLine)); + const separatorRect = overlayRect.map(overlayRect => overlayRect.withMargin(WIDGET_SEPARATOR_WIDTH, 0, WIDGET_SEPARATOR_WIDTH, WIDGET_SEPARATOR_WIDTH).intersectHorizontal(intersectionLine)); + + const transitionRect = overlayRect.map(overlayRect => Rect.fromLeftTopWidthHeight(overlayRect.right - transitionRectSize + BORDER_WIDTH, overlayRect.bottom - BORDER_WIDTH, transitionRectSize, transitionRectSize).intersectHorizontal(intersectionLine)); return [ - n.svgElem('path', { - class: 'originalOverlay', - d: layoutInfoObs.map(layoutInfo => createRectangle( - { topLeft: layoutInfo.codeRect.getLeftTop(), width: layoutInfo.codeRect.width, height: layoutInfo.codeRect.height }, - 0, - { topLeft: layoutInfo.borderRadius, bottomLeft: layoutInfo.borderRadius, topRight: 0, bottomRight: 0 }, - { hideRight: true, hideLeft: layoutInfo.codeScrollLeft !== 0 } - )), + n.div({ + class: 'originalSeparatorSideBySide', style: { - fill: asCssVariable(originalBackgroundColor), - stroke: originalBorderColor, - strokeWidth: '1px', + ...separatorRect.read(reader).toStyles(), + boxSizing: 'border-box', + borderRadius: `${BORDER_RADIUS}px 0 0 ${BORDER_RADIUS}px`, + borderTop: borderStylingSeparator, + borderBottom: borderStylingSeparator, + borderLeft: hasBorderLeft ? 'none' : borderStylingSeparator, } }), - n.svgElem('path', { - class: 'extendedModifiedOverlay', - d: this._extendedModifiedPath, + n.div({ + class: 'originalOverlaySideBySide', style: { - fill: asCssVariable(modifiedBackgroundColor), - stroke: modifiedBorderColor, - strokeWidth: '1px', + ...overlayRect.read(reader).toStyles(), + boxSizing: 'border-box', + borderRadius: `${BORDER_RADIUS}px 0 0 ${BORDER_RADIUS}px`, + borderTop: borderStyling, + borderBottom: borderStyling, + borderLeft: hasBorderLeft ? 'none' : borderStyling, + backgroundColor: asCssVariable(originalBackgroundColor), } }), - n.svgElem('path', { - class: 'middleBorder', - d: layoutInfoObs.map(layoutInfo => new PathBuilder() - .moveTo(layoutInfo.codeRect.getRightTop()) - .lineTo(layoutInfo.codeRect.getRightBottom()) - .build() - ), + + n.div({ + class: 'originalCornerCutoutSideBySide', style: { - display: layoutInfoObs.map(i => i.shouldShowShadow ? 'none' : 'block'), - stroke: modifiedBorderColor, - strokeWidth: '1px' + pointerEvents: 'none', + display: isModifiedLower.map(isLower => isLower ? 'block' : 'none'), + ...transitionRect.read(reader).toStyles(), + } + }, [ + n.div({ + class: 'originalCornerCutoutBackground', + style: { + position: 'absolute', top: '0px', left: '0px', width: '100%', height: '100%', + backgroundColor: getEditorBlendedColor(originalBackgroundColor, this._themeService).map(c => c.toString()), + } + }), + n.div({ + class: 'originalCornerCutoutBorder', + style: { + position: 'absolute', top: '0px', left: '0px', width: '100%', height: '100%', + boxSizing: 'border-box', + borderTop: borderStyling, + borderRight: borderStyling, + borderRadius: `0 100% 0 0`, + backgroundColor: asCssVariable(editorBackground) + } + }) + ]), + n.div({ + class: 'originalOverlaySideBySideHider', + style: { + ...overlayHider.toStyles(), + backgroundColor: asCssVariable(editorBackground), + } + }), + ]; + })).keepUpdated(this._store); + + private readonly _modifiedOverlay = n.div({ + style: { pointerEvents: 'none', } + }, derived(reader => { + const layoutInfoObs = mapOutFalsy(this._previewEditorLayoutInfo).read(reader); + if (!layoutInfoObs) { return undefined; } + + const isModifiedLower = layoutInfoObs.map(layoutInfo => layoutInfo.codeRect.bottom < layoutInfo.editRect.bottom); + + const borderRadius = isModifiedLower.map(isLower => `0 ${BORDER_RADIUS}px ${BORDER_RADIUS}px ${isLower ? BORDER_RADIUS : 0}px`); + const borderStyling = getEditorBlendedColor(getModifiedBorderColor(this._tabAction), this._themeService).map(c => `1px solid ${c.toString()}`); + const borderStylingSeparator = `${BORDER_WIDTH + WIDGET_SEPARATOR_WIDTH}px solid ${asCssVariable(editorBackground)}`; + + const overlayRect = layoutInfoObs.map(layoutInfo => layoutInfo.editRect.withMargin(0, BORDER_WIDTH)); + const separatorRect = overlayRect.map(overlayRect => overlayRect.withMargin(WIDGET_SEPARATOR_WIDTH, WIDGET_SEPARATOR_WIDTH, WIDGET_SEPARATOR_WIDTH, 0)); + + const insertionRect = derived(reader => { + const overlay = overlayRect.read(reader); + const layoutinfo = layoutInfoObs.read(reader); + if (!layoutinfo.isInsertion || layoutinfo.contentLeft >= overlay.left) { + return Rect.fromLeftTopWidthHeight(overlay.left, overlay.top, 0, 0); + } + return new Rect(layoutinfo.contentLeft, overlay.top, overlay.left, overlay.top + BORDER_WIDTH * 2); + }); + + return [ + n.div({ + class: 'modifiedInsertionSideBySide', + style: { + ...insertionRect.read(reader).toStyles(), + backgroundColor: getModifiedBorderColor(this._tabAction).map(c => asCssVariable(c)), + } + }), + n.div({ + class: 'modifiedSeparatorSideBySide', + style: { + ...separatorRect.read(reader).toStyles(), + borderRadius, + borderTop: borderStylingSeparator, + borderBottom: borderStylingSeparator, + borderRight: borderStylingSeparator, + boxSizing: 'border-box', + } + }), + n.div({ + class: 'modifiedOverlaySideBySide', + style: { + ...overlayRect.read(reader).toStyles(), + borderRadius, + border: borderStyling, + boxSizing: 'border-box', + backgroundColor: asCssVariable(modifiedBackgroundColor), } }) ]; @@ -564,17 +588,6 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit }, }, [ this._backgroundSvg, - derived(this, reader => this._shouldOverflow.read(reader) ? [] : [this._modifiedBackgroundSvg, this._foregroundBackgroundSvg, this._editorContainer, this._foregroundSvg, this._middleBorderWithShadow]), - ]).keepUpdated(this._store); - - private readonly _overflowView = n.div({ - class: 'inline-edits-view', - style: { - overflow: 'visible', - zIndex: '20', - display: this._display, - }, - }, [ - derived(this, reader => this._shouldOverflow.read(reader) ? [this._modifiedBackgroundSvg, this._foregroundBackgroundSvg, this._editorContainer, this._foregroundSvg, this._middleBorderWithShadow] : []), + derived(this, reader => this._shouldOverflow.read(reader) ? [] : [this._editorContainer, this._originalOverlay, this._modifiedOverlay]), ]).keepUpdated(this._store); } 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 d3ee68c6..55ee6e9b 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 @@ -8,15 +8,16 @@ import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, IObservable } from '../../../../../../../base/common/observable.js'; +import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { Point } from '../../../../../../browser/point.js'; 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 { @@ -58,7 +59,7 @@ export class InlineEditsWordInsertView extends Disposable implements IInlineEdit return []; } - const modifiedBorderColor = getModifiedBorderColor(this._tabAction).read(reader); + const modifiedBorderColor = asCssVariable(getModifiedBorderColor(this._tabAction).read(reader)); return [ n.div({ 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 86d42cb4..2702159e 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,8 +7,8 @@ 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 { editorBackground, editorHoverForeground, scrollbarShadow } from '../../../../../../../platform/theme/common/colorRegistry.js'; +import { constObservable, derived, IObservable, observableValue } from '../../../../../../../base/common/observable.js'; +import { editorBackground, editorHoverForeground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { Point } from '../../../../../../browser/point.js'; @@ -17,13 +17,12 @@ import { LineSource, renderLines, RenderOptions } from '../../../../../../browse import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { SingleOffsetEdit } from '../../../../../../common/core/offsetEdit.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Range } from '../../../../../../common/core/range.js'; 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 { getModifiedBorderColor, modifiedChangedTextOverlayColor, originalChangedTextOverlayColor, replacementViewBackground } from '../theme.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; +import { getModifiedBorderColor, getOriginalBorderColor, modifiedChangedTextOverlayColor, originalChangedTextOverlayColor } from '../theme.js'; import { mapOutFalsy, rectToProps } from '../utils/utils.js'; export class InlineEditsWordReplacementView extends Disposable implements IInlineEditsView { @@ -46,8 +45,7 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin private readonly _editor: ObservableCodeEditor, /** Must be single-line in both sides */ private readonly _edit: SingleTextEdit, - private readonly _innerEdits: SingleTextEdit[], - private readonly _host: IInlineEditsViewHost, + protected readonly _tabAction: IObservable, @ILanguageService private readonly _languageService: ILanguageService, ) { super(); @@ -73,70 +71,40 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin } else { tokens = LineTokens.createEmpty(this._edit.text, this._languageService.languageIdCodec); } - renderLines(new LineSource([tokens]), RenderOptions.fromEditor(this._editor.editor).withSetWidth(false), [], this._line, true); - }); - - private readonly _editLocations = this._innerEdits.map(edit => { - const start = this._editor.observePosition(constObservable(edit.range.getStartPosition()), this._store); - const end = this._editor.observePosition(constObservable(edit.range.getEndPosition()), this._store); - return { start, end, edit }; + const res = renderLines(new LineSource([tokens]), RenderOptions.fromEditor(this._editor.editor).withSetWidth(false).withScrollBeyondLastColumn(0), [], this._line, true); + this._line.style.width = `${res.minWidthInPx}px`; }); private readonly _layout = derived(this, reader => { this._renderTextEffect.read(reader); const widgetStart = this._start.read(reader); - const widgetEnd = this._end.read(reader);// + const widgetEnd = this._end.read(reader); - if (!widgetStart || !widgetEnd || widgetStart.x > widgetEnd.x) { + // TODO@hediet better about widgetStart and widgetEnd in a single transaction! + if (!widgetStart || !widgetEnd || widgetStart.x > widgetEnd.x || widgetStart.y > widgetEnd.y) { return undefined; } - const contentLeft = this._editor.layoutInfoContentLeft.read(reader); const lineHeight = this._editor.getOption(EditorOption.lineHeight).read(reader); const scrollLeft = this._editor.scrollLeft.read(reader); const w = this._editor.getOption(EditorOption.fontInfo).read(reader).typicalHalfwidthCharacterWidth; - const modifiedLeftOffset = 20; - const modifiedTopOffset = 5; + const modifiedLeftOffset = 3 * w; + const modifiedTopOffset = 4; const modifiedOffset = new Point(modifiedLeftOffset, modifiedTopOffset); - const PADDING = 4; - const originalLine = Rect.fromPoints(widgetStart, widgetEnd).withHeight(lineHeight).translateX(contentLeft - scrollLeft); - const modifiedLine = Rect.fromPointSize(originalLine.getLeftBottom().add(modifiedOffset), new Point(this._edit.text.length * w + 5, originalLine.height)); - const background = Rect.hull([originalLine, modifiedLine]).withMargin(PADDING); + const originalLine = Rect.fromPoints(widgetStart, widgetEnd).withHeight(lineHeight).translateX(-scrollLeft); + const modifiedLine = Rect.fromPointSize(originalLine.getLeftBottom().add(modifiedOffset), new Point(this._edit.text.length * w, originalLine.height)); - let textLengthDelta = 0; - const innerEdits = []; - for (const editLocation of this._editLocations) { - const editStart = editLocation.start.read(reader); - const editEnd = editLocation.end.read(reader); - const edit = editLocation.edit; - - if (!editStart || !editEnd || editStart.x > editEnd.x) { - return undefined; - } - - const original = Rect.fromLeftTopWidthHeight(editStart.x + contentLeft - scrollLeft, editStart.y, editEnd.x - editStart.x, lineHeight); - const modified = Rect.fromLeftTopWidthHeight(original.left + modifiedLeftOffset + textLengthDelta * w, original.bottom + modifiedTopOffset, edit.text.length * w + 5, original.height); - - textLengthDelta += edit.text.length - (edit.range.endColumn - edit.range.startColumn); - - innerEdits.push({ original, modified }); - } - - const lowerBackground = background.intersectVertical(new OffsetRange(originalLine.bottom, Number.MAX_SAFE_INTEGER)); - const lowerText = new Rect(lowerBackground.left + modifiedLeftOffset + 6, lowerBackground.top + modifiedTopOffset, lowerBackground.right, lowerBackground.bottom); // TODO: left seems slightly off? zooming? + const lowerBackground = modifiedLine.withLeft(originalLine.left); // debugView(debugLogRects({ lowerBackground }, this._editor.editor.getContainerDomNode()), reader); return { originalLine, modifiedLine, - background, - innerEdits, lowerBackground, - lowerText, - padding: PADDING + lineHeight, }; }); @@ -149,20 +117,11 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin return []; } - const layoutProps = layout.read(reader); - const scrollLeft = this._editor.scrollLeft.read(reader); - let contentLeft = this._editor.layoutInfoContentLeft.read(reader); - let contentWidth = this._editor.contentWidth.read(reader); - const contentHeight = this._editor.editor.getContentHeight(); + const contentLeft = this._editor.layoutInfoContentLeft.read(reader); + const borderWidth = 1; - if (scrollLeft === 0) { - contentLeft -= layoutProps.padding; - contentWidth += layoutProps.padding; - } - - 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 originalBorderColor = getOriginalBorderColor(this._tabAction).map(c => asCssVariable(c)).read(reader); + const modifiedBorderColor = getModifiedBorderColor(this._tabAction).map(c => asCssVariable(c)).read(reader); return [ n.div({ @@ -170,8 +129,8 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin position: 'absolute', top: 0, left: contentLeft, - width: contentWidth, - height: contentHeight, + width: this._editor.contentWidth, + height: this._editor.editor.getContentHeight(), overflow: 'hidden', pointerEvents: 'none', } @@ -179,10 +138,9 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin n.div({ style: { position: 'absolute', - ...rectToProps(reader => layout.read(reader).lowerBackground.translateX(-contentLeft)), - borderRadius: '4px', + ...rectToProps(reader => layout.read(reader).lowerBackground.withMargin(borderWidth, 2 * borderWidth, borderWidth, 0)), background: asCssVariable(editorBackground), - boxShadow: `${asCssVariable(scrollbarShadow)} 0 6px 6px -6px`, + //boxShadow: `${asCssVariable(scrollbarShadow)} 0 6px 6px -6px`, cursor: 'pointer', pointerEvents: 'auto', }, @@ -197,72 +155,53 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin n.div({ style: { position: 'absolute', - padding: '0px', - boxSizing: 'border-box', - ...rectToProps(reader => layout.read(reader).lowerText.translateX(-contentLeft)), + ...rectToProps(reader => layout.read(reader).modifiedLine.withMargin(1, 2)), fontFamily: this._editor.getOption(EditorOption.fontFamily), fontSize: this._editor.getOption(EditorOption.fontSize), fontWeight: this._editor.getOption(EditorOption.fontWeight), + pointerEvents: 'none', - } - }, [this._line]), - ...edits.map(edit => n.div({ - style: { - position: 'absolute', - top: edit.modified.top, - left: edit.modified.left, - width: edit.modified.width, - height: edit.modified.height, + boxSizing: 'border-box', borderRadius: '4px', + border: `${borderWidth}px solid ${modifiedBorderColor}`, background: asCssVariable(modifiedChangedTextOverlayColor), - pointerEvents: 'none', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + + outline: `2px solid ${asCssVariable(editorBackground)}`, } - }), []), - ...edits.map(edit => n.div({ - style: { - position: 'absolute', - top: edit.original.top, - left: edit.original.left, - width: edit.original.width, - height: edit.original.height, - borderRadius: '4px', - boxSizing: 'border-box', - background: asCssVariable(originalChangedTextOverlayColor), - pointerEvents: 'none', - } - }, [])), + }, [this._line]), n.div({ style: { position: 'absolute', - ...rectToProps(reader => layout.read(reader).background.translateX(-contentLeft)), - borderRadius: '4px', - - border: `1px solid ${modifiedBorderColor}`, - //background: 'rgba(122, 122, 122, 0.12)', looks better - background: asCssVariable(replacementViewBackground), - pointerEvents: 'none', + ...rectToProps(reader => layout.read(reader).originalLine.withMargin(1)), boxSizing: 'border-box', + borderRadius: '4px', + border: `${borderWidth}px solid ${originalBorderColor}`, + background: asCssVariable(originalChangedTextOverlayColor), + pointerEvents: 'none', } }, []), n.svg({ width: 11, - height: 13, - viewBox: '0 0 11 13', + height: 14, + viewBox: '0 0 11 14', fill: 'none', style: { position: 'absolute', - left: derived(reader => layout.read(reader).modifiedLine.translateX(-contentLeft).left - 15), - top: derived(reader => layout.read(reader).modifiedLine.top), + left: layout.map(l => l.modifiedLine.left - 16), + top: layout.map(l => l.modifiedLine.top + Math.round((l.lineHeight - 14 - 5) / 2)), } }, [ n.svgElem('path', { - d: 'M1 0C1 2.98966 1 4.92087 1 7.49952C1 8.60409 1.89543 9.5 3 9.5H10.5', + d: 'M1 0C1 2.98966 1 5.92087 1 8.49952C1 9.60409 1.89543 10.5 3 10.5H10.5', stroke: asCssVariable(editorHoverForeground), }), n.svgElem('path', { - d: 'M6 6.5L9.99999 9.49998L6 12.5', + d: 'M6 7.5L9.99999 10.49998L6 13.5', stroke: asCssVariable(editorHoverForeground), }) ]), @@ -272,23 +211,3 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin }) ]).keepUpdated(this._store); } - -export function rangesToBubbleRanges(ranges: Range[]): Range[] { - const result: Range[] = []; - while (ranges.length) { - let range = ranges.shift()!; - if (range.startLineNumber !== range.endLineNumber) { - ranges.push(new Range(range.startLineNumber + 1, 1, range.endLineNumber, range.endColumn)); - range = new Range(range.startLineNumber, range.startColumn, range.startLineNumber, Number.MAX_SAFE_INTEGER); // TODO: this is not correct - } - - result.push(range); - } - return result; - -} - -export interface Replacement { - originalRange: Range; - modifiedRange: Range; -} 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 414f4555..63b43821 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,8 @@ 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 hasOneInnerChange = diff.diff.length === 1 && diff.diff[0].innerChanges?.length === 1; const showEmptyDecorations = true; @@ -178,7 +123,7 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE }); for (const m of diff.diff) { - const showFullLineDecorations = diff.mode !== 'sideBySide'; + const showFullLineDecorations = diff.mode !== 'sideBySide' && diff.mode !== 'deletion' && diff.mode !== 'insertionInline'; if (showFullLineDecorations) { if (!m.original.isEmpty) { originalDecorations.push({ @@ -216,7 +161,7 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE 'inlineCompletions-char-delete', i.originalRange.isSingleLine() && diff.mode === 'insertionInline' && 'single-line-inline', i.originalRange.isEmpty() && 'empty', - ((i.originalRange.isEmpty() || diff.mode === 'deletion' && replacedText === '\n') && showEmptyDecorations && !useInlineDiff) && 'diff-range-empty' + ((i.originalRange.isEmpty() && hasOneInnerChange || diff.mode === 'deletion' && replacedText === '\n') && showEmptyDecorations && !useInlineDiff) && 'diff-range-empty' ), inlineClassName: useInlineDiff ? classNames('strike-through', 'inlineCompletions') : null, zIndex: 1 @@ -226,7 +171,7 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE if (m.modified.contains(i.modifiedRange.startLineNumber)) { modifiedDecorations.push({ range: i.modifiedRange, - options: (i.modifiedRange.isEmpty() && showEmptyDecorations && !useInlineDiff) + options: (i.modifiedRange.isEmpty() && showEmptyDecorations && !useInlineDiff && hasOneInnerChange) ? diffAddDecorationEmpty : diffAddDecoration }); @@ -263,7 +208,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 +225,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 85cc06a8..65646980 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.ts @@ -4,35 +4,38 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from '../../../../../../base/common/color.js'; +import { BugIndicatingError } from '../../../../../../base/common/errors.js'; import { IObservable } from '../../../../../../base/common/observable.js'; +import { observableFromEventOpts } from '../../../../../../base/common/observableInternal/utils.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 { diffRemoved, diffInsertedLine, diffInserted, buttonBackground, buttonForeground, buttonSecondaryBackground, buttonSecondaryForeground, editorBackground } from '../../../../../../platform/theme/common/colorRegistry.js'; +import { registerColor, transparent, darken, ColorIdentifier } from '../../../../../../platform/theme/common/colorUtils.js'; +import { IThemeService } from '../../../../../../platform/theme/common/themeService.js'; +import { InlineEditTabAction } from './inlineEditsViewInterface.js'; export const originalBackgroundColor = registerColor( 'inlineEdit.originalBackground', - Color.transparent, + transparent(diffRemoved, 0.2), localize('inlineEdit.originalBackground', 'Background color for the original text in inline edits.'), true ); export const modifiedBackgroundColor = registerColor( 'inlineEdit.modifiedBackground', - Color.transparent, + transparent(diffInserted, 0.3), localize('inlineEdit.modifiedBackground', 'Background color for the modified text in inline edits.'), true ); export const originalChangedLineBackgroundColor = registerColor( 'inlineEdit.originalChangedLineBackground', - Color.transparent, + transparent(diffRemoved, 0.8), localize('inlineEdit.originalChangedLineBackground', 'Background color for the changed lines in the original text of inline edits.'), true ); export const originalChangedTextOverlayColor = registerColor( 'inlineEdit.originalChangedTextBackground', - diffRemoved, + transparent(diffRemoved, 0.8), localize('inlineEdit.originalChangedTextBackground', 'Overlay color for the changed text in the original text of inline edits.'), true ); @@ -40,8 +43,8 @@ export const originalChangedTextOverlayColor = registerColor( export const modifiedChangedLineBackgroundColor = registerColor( 'inlineEdit.modifiedChangedLineBackground', { - light: transparent(diffInsertedLine, 0.5), - dark: transparent(diffInsertedLine, 0.5), + light: transparent(diffInsertedLine, 0.7), + dark: transparent(diffInsertedLine, 0.7), hcDark: diffInsertedLine, hcLight: diffInsertedLine }, @@ -51,22 +54,11 @@ export const modifiedChangedLineBackgroundColor = registerColor( export const modifiedChangedTextOverlayColor = registerColor( 'inlineEdit.modifiedChangedTextBackground', - diffInserted, + transparent(diffInserted, 0.7), localize('inlineEdit.modifiedChangedTextBackground', 'Overlay color for the changed text in the modified text of inline edits.'), true ); -export const replacementViewBackground = registerColor( - 'inlineEdit.wordReplacementView.background', - { - light: transparent(editorHoverStatusBarBackground, 0.1), - dark: transparent(editorHoverStatusBarBackground, 0.1), - hcLight: transparent(editorHoverStatusBarBackground, 0.1), - hcDark: transparent(editorHoverStatusBarBackground, 0.1), - }, - localize('inlineEdit.wordReplacementView.background', 'Background color for the inline edit word replacement view.') -); - // ------- GUTTER INDICATOR ------- export const inlineEditIndicatorPrimaryForeground = registerColor( @@ -74,9 +66,19 @@ export const inlineEditIndicatorPrimaryForeground = registerColor( buttonForeground, localize('inlineEdit.gutterIndicator.primaryForeground', 'Foreground color for the primary inline edit gutter indicator.') ); +export const inlineEditIndicatorPrimaryBorder = registerColor( + 'inlineEdit.gutterIndicator.primaryBorder', + buttonBackground, + localize('inlineEdit.gutterIndicator.primaryBorder', 'Border color for the primary inline edit gutter indicator.') +); export const inlineEditIndicatorPrimaryBackground = registerColor( 'inlineEdit.gutterIndicator.primaryBackground', - buttonBackground, + { + light: transparent(inlineEditIndicatorPrimaryBorder, 0.5), + dark: transparent(inlineEditIndicatorPrimaryBorder, 0.4), + hcDark: transparent(inlineEditIndicatorPrimaryBorder, 0.4), + hcLight: transparent(inlineEditIndicatorPrimaryBorder, 0.5), + }, localize('inlineEdit.gutterIndicator.primaryBackground', 'Background color for the primary inline edit gutter indicator.') ); @@ -85,9 +87,14 @@ export const inlineEditIndicatorSecondaryForeground = registerColor( buttonSecondaryForeground, localize('inlineEdit.gutterIndicator.secondaryForeground', 'Foreground color for the secondary inline edit gutter indicator.') ); +export const inlineEditIndicatorSecondaryBorder = registerColor( + 'inlineEdit.gutterIndicator.secondaryBorder', + buttonSecondaryBackground, + localize('inlineEdit.gutterIndicator.secondaryBorder', 'Border color for the secondary inline edit gutter indicator.') +); export const inlineEditIndicatorSecondaryBackground = registerColor( 'inlineEdit.gutterIndicator.secondaryBackground', - buttonSecondaryBackground, + inlineEditIndicatorSecondaryBorder, localize('inlineEdit.gutterIndicator.secondaryBackground', 'Background color for the secondary inline edit gutter indicator.') ); @@ -96,9 +103,14 @@ export const inlineEditIndicatorsuccessfulForeground = registerColor( buttonForeground, localize('inlineEdit.gutterIndicator.successfulForeground', 'Foreground color for the successful inline edit gutter indicator.') ); +export const inlineEditIndicatorsuccessfulBorder = registerColor( + 'inlineEdit.gutterIndicator.successfulBorder', + buttonBackground, + localize('inlineEdit.gutterIndicator.successfulBorder', 'Border color for the successful inline edit gutter indicator.') +); export const inlineEditIndicatorsuccessfulBackground = registerColor( 'inlineEdit.gutterIndicator.successfulBackground', - { light: '#2e825c', dark: '#2e825c', hcLight: '#2e825c', hcDark: '#2e825c' }, + inlineEditIndicatorsuccessfulBorder, localize('inlineEdit.gutterIndicator.successfulBackground', 'Background color for the successful inline edit gutter indicator.') ); @@ -118,10 +130,10 @@ export const inlineEditIndicatorBackground = registerColor( const originalBorder = registerColor( 'inlineEdit.originalBorder', { - light: editorHoverBorder, - dark: editorHoverBorder, - hcDark: editorHoverBorder, - hcLight: editorHoverBorder + light: diffRemoved, + dark: diffRemoved, + hcDark: diffRemoved, + hcLight: diffRemoved }, localize('inlineEdit.originalBorder', 'Border color for the original text in inline edits.') ); @@ -129,40 +141,70 @@ const originalBorder = registerColor( const modifiedBorder = registerColor( 'inlineEdit.modifiedBorder', { - light: editorHoverBorder, - dark: editorHoverBorder, - hcDark: editorHoverBorder, - hcLight: editorHoverBorder + light: darken(diffInserted, 0.6), + dark: diffInserted, + hcDark: diffInserted, + hcLight: diffInserted }, localize('inlineEdit.modifiedBorder', 'Border color for the modified text in inline edits.') ); const tabWillAcceptModifiedBorder = registerColor( - 'inlineEdit.tabWillAcceptBorder', + 'inlineEdit.tabWillAcceptModifiedBorder', { - light: darken(modifiedBorder, 0.25), - dark: lighten(modifiedBorder, 0.25), - hcDark: lighten(modifiedBorder, 0.5), - hcLight: darken(modifiedBorder, 0.5) + light: darken(modifiedBorder, 0), + dark: darken(modifiedBorder, 0), + hcDark: darken(modifiedBorder, 0), + hcLight: darken(modifiedBorder, 0) }, - localize('inlineEdit.tabWillAcceptBorder', 'Border color for the inline edits widget when tab will accept it.') + localize('inlineEdit.tabWillAcceptModifiedBorder', 'Modified border color for the inline edits widget when tab will accept it.') ); const tabWillAcceptOriginalBorder = registerColor( - 'inlineEdit.tabWillAcceptBorder', + 'inlineEdit.tabWillAcceptOriginalBorder', { - light: darken(originalBorder, 0.25), - dark: lighten(originalBorder, 0.25), - hcDark: lighten(originalBorder, 0.5), - hcLight: darken(originalBorder, 0.5) + light: darken(originalBorder, 0), + dark: darken(originalBorder, 0), + hcDark: darken(originalBorder, 0), + hcLight: darken(originalBorder, 0) }, - localize('inlineEdit.tabWillAcceptOriginalBorder', 'Border color for the inline edits widget over the original text when tab will accept it.') + localize('inlineEdit.tabWillAcceptOriginalBorder', 'Original border color for the inline edits widget over the original text when tab will accept it.') ); export function getModifiedBorderColor(tabAction: IObservable): IObservable { - return tabAction.map(a => asCssVariable(a === InlineEditTabAction.Accept ? tabWillAcceptModifiedBorder : modifiedBorder)); + return tabAction.map(a => a === InlineEditTabAction.Accept ? tabWillAcceptModifiedBorder : modifiedBorder); } export function getOriginalBorderColor(tabAction: IObservable): IObservable { - return tabAction.map(a => asCssVariable(a === InlineEditTabAction.Accept ? tabWillAcceptOriginalBorder : originalBorder)); + return tabAction.map(a => a === InlineEditTabAction.Accept ? tabWillAcceptOriginalBorder : originalBorder); +} + +export function getEditorBlendedColor(colorIdentifier: ColorIdentifier | IObservable, themeService: IThemeService): IObservable { + let color: IObservable; + if (typeof colorIdentifier === 'string') { + color = observeColor(colorIdentifier, themeService); + } else { + color = colorIdentifier.map((identifier, reader) => observeColor(identifier, themeService).read(reader)); + } + + const backgroundColor = observeColor(editorBackground, themeService); + + return color.map((c, reader) => c.makeOpaque(backgroundColor.read(reader))); +} + +export function observeColor(colorIdentifier: ColorIdentifier, themeService: IThemeService): IObservable { + return observableFromEventOpts( + { + owner: { observeColor: colorIdentifier }, + equalsFn: (a: Color, b: Color) => a.equals(b), + }, + themeService.onDidColorThemeChange, + () => { + const color = themeService.getColorTheme().getColor(colorIdentifier); + if (!color) { + throw new BugIndicatingError(`Missing color: ${colorIdentifier}`); + } + return color; + } + ); } 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 9427e0b2..8aac0066 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 @@ -24,14 +24,9 @@ import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; import { SingleTextEdit, TextEdit } from '../../../../../../common/core/textEdit.js'; import { RangeMapping } from '../../../../../../common/diff/rangeMapping.js'; +import { ITextModel } from '../../../../../../common/model.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); @@ -89,10 +84,9 @@ export function getPrefixTrim(diffRanges: Range[], originalLinesRange: LineRange if (startLineIndent >= prefixTrim + 1) { // We can use the editor to get the offset prefixLeftOffset = editor.getOffsetForColumn(originalLinesRange.startLineNumber, prefixTrim + 1); - } else if (startLineIndent !== 1) { - // We need to approximate the offset as the editor does not contain the modified lines yet - const startLineIndentOffset = editor.getOffsetForColumn(originalLinesRange.startLineNumber, startLineIndent); - prefixLeftOffset = startLineIndentOffset / (startLineIndent - 1) * prefixTrim; + } else if (modifiedLines.length > 0) { + // Content is not in the editor, we can use the content width to calculate the offset + prefixLeftOffset = getContentRenderWidth(modifiedLines[0].slice(0, prefixTrim), editor, textModel); } else { // unable to approximate the offset return { prefixTrim: 0, prefixLeftOffset: 0 }; @@ -101,6 +95,15 @@ export function getPrefixTrim(diffRanges: Range[], originalLinesRange: LineRange return { prefixTrim, prefixLeftOffset }; } +export function getContentRenderWidth(content: string, editor: ICodeEditor, textModel: ITextModel) { + const w = editor.getOption(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; + const tabSize = textModel.getOptions().tabSize * w; + + const numTabs = content.split('\t').length - 1; + const numNoneTabs = content.length - numTabs; + return numNoneTabs * w + numTabs * tabSize; +} + export class StatusBarViewItem extends MenuEntryActionViewItem { protected readonly _updateLabelListener = this._register(this._contextKeyService.onDidChangeContext(() => { this.updateLabel(); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css index bbc23c3b..bff0b88c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css @@ -76,71 +76,19 @@ } } - .inline-edits-view { - &.toolbarDropdownVisible, .editorContainer.showHover:hover { - .toolbar { - display: block; + .inline-edits-view .editorContainer { + .preview .monaco-editor { + .view-overlays .current-line-exact { + border: none; + } + + .current-line-margin { + border: none; } } - .editorContainer { - color: var(--vscode-editorHoverWidget-foreground); - - .toolbar { - display: none; - border-top: 1px solid rgba(69, 69, 69, 0.5); - background-color: var(--vscode-editorHoverWidget-statusBarBackground); - - a { - color: var(--vscode-foreground); - } - - a:hover { - color: var(--vscode-foreground); - } - - .keybinding { - display: flex; - margin-left: 4px; - opacity: 0.6; - } - - .keybinding .monaco-keybinding-key { - font-size: 8px; - padding: 2px 3px; - } - - .availableSuggestionCount a { - display: flex; - min-width: 19px; - justify-content: center; - } - - .inlineSuggestionStatusBarItemLabel { - margin-right: 2px; - } - - } - - .preview { - .monaco-editor { - .view-overlays .current-line-exact { - border: none; - } - - .current-line-margin { - border: none; - } - } - - .monaco-editor-background { - background-color: var(--vscode-inlineEdit-modifiedChangedLineBackground) - } - } - - .inline-edits-view-zone.diagonal-fill { - opacity: 0.5; - } + .inline-edits-view-zone.diagonal-fill { + opacity: 0.5; } } @@ -175,25 +123,23 @@ } .inlineCompletions-char-delete.single-line-inline { /* Editor Decoration */ - border-radius: 4px; border: 1px solid var(--vscode-editorHoverWidget-border); margin: -2px 0 0 -2px; } .inlineCompletions-char-insert.single-line-inline { /* Inline Decoration */ - padding: 1px 0; - border-top: 1px solid var(--vscode-editorHoverWidget-border); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ - border-bottom: 1px solid var(--vscode-editorHoverWidget-border); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ + border-top: 1px solid var(--vscode-inlineEdit-modifiedBorder); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ + border-bottom: 1px solid var(--vscode-inlineEdit-modifiedBorder); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ } .inlineCompletions-char-insert.single-line-inline.start { border-top-left-radius: 4px; border-bottom-left-radius: 4px; - border-left: 1px solid var(--vscode-editorHoverWidget-border); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ + border-left: 1px solid var(--vscode-inlineEdit-modifiedBorder); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ } .inlineCompletions-char-insert.single-line-inline.end { border-top-right-radius: 4px; border-bottom-right-radius: 4px; - border-right: 1px solid var(--vscode-editorHoverWidget-border); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ + border-right: 1px solid var(--vscode-inlineEdit-modifiedBorder); /* TODO: Do not set border inline but create overlaywidget (like deletion view) */ } .inlineCompletions-char-delete.single-line-inline.empty, @@ -213,7 +159,6 @@ .inlineCompletions-original-bubble{ background: var(--vscode-inlineEdit-originalChangedTextBackground); - border-radius: 4px; } .inlineCompletions-modified-bubble, @@ -222,16 +167,6 @@ display: inline-block; } - .inlineCompletions-modified-bubble.start { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - } - - .inlineCompletions-modified-bubble.end { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - } - .inline-edit.ghost-text, .inline-edit.ghost-text-decoration, .inline-edit.ghost-text-decoration-preview, diff --git a/src/vs/editor/contrib/links/browser/getLinks.ts b/src/vs/editor/contrib/links/browser/getLinks.ts index be8a31af..fed24b5b 100644 --- a/src/vs/editor/contrib/links/browser/getLinks.ts +++ b/src/vs/editor/contrib/links/browser/getLinks.ts @@ -74,7 +74,7 @@ export class LinksList { readonly links: Link[]; - private readonly _disposables = new DisposableStore(); + private readonly _disposables: DisposableStore | undefined = new DisposableStore(); constructor(tuples: [ILinksList, LinkProvider][]) { @@ -85,6 +85,7 @@ export class LinksList { links = LinksList._union(links, newLinks); // register disposables if (isDisposable(list)) { + this._disposables ??= new DisposableStore(); this._disposables.add(list); } } @@ -92,7 +93,7 @@ export class LinksList { } dispose(): void { - this._disposables.dispose(); + this._disposables?.dispose(); this.links.length = 0; } diff --git a/src/vs/editor/contrib/links/browser/links.ts b/src/vs/editor/contrib/links/browser/links.ts index a07122f5..ef1b67ad 100644 --- a/src/vs/editor/contrib/links/browser/links.ts +++ b/src/vs/editor/contrib/links/browser/links.ts @@ -287,10 +287,6 @@ export class LinkDetector extends Disposable implements IEditorContribution { return null; } - public getAllLinkOccurrences(): LinkOccurrence[] { - return Object.values(this.currentOccurrences); - } - private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent | null): boolean { return Boolean( (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) diff --git a/src/vs/editor/contrib/multicursor/browser/multicursor.ts b/src/vs/editor/contrib/multicursor/browser/multicursor.ts index c3bcf55d..abc43c1f 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 a6e7a743..b4f95f71 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts @@ -9,7 +9,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IMatch } from '../../../../base/common/filters.js'; import { IPreparedQuery, pieceToQuery, prepareQuery, scoreFuzzy2 } from '../../../../base/common/fuzzyScorer.js'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { format, trim } from '../../../../base/common/strings.js'; import { IRange, Range } from '../../../common/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; @@ -169,21 +169,21 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit const symbolsPromise = this.getDocumentSymbols(model, token); // Set initial picks and update on type - let picksCts: CancellationTokenSource | undefined = undefined; + const picksCts = disposables.add(new MutableDisposable()); const updatePickerItems = async (positionToEnclose: Position | undefined) => { // Cancel any previous ask for picks and busy - picksCts?.dispose(true); + picksCts?.value?.cancel(); picker.busy = false; // Create new cancellation source for this run - picksCts = new CancellationTokenSource(token); + picksCts.value = new CancellationTokenSource(); // Collect symbol picks picker.busy = true; try { const query = prepareQuery(picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim()); - const items = await this.doGetSymbolPicks(symbolsPromise, query, undefined, picksCts.token, model); + const items = await this.doGetSymbolPicks(symbolsPromise, query, undefined, picksCts.value.token, model); if (token.isCancellationRequested) { return; } diff --git a/src/vs/editor/contrib/rename/browser/renameWidget.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts index baf0f0b8..e5a7bc71 100644 --- a/src/vs/editor/contrib/rename/browser/renameWidget.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -181,7 +181,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable } })); - this._disposables.add(_themeService.onDidColorThemeChange(e => this._updateStyles(e.theme))); + this._disposables.add(_themeService.onDidColorThemeChange(this._updateStyles, this)); } dispose(): void { diff --git a/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts b/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts index 01b57413..8fda7ce5 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/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 38e44194..0c4c59ff 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -584,7 +584,10 @@ export class StickyScrollController extends Disposable implements IEditorContrib } findScrollWidgetState(): StickyScrollWidgetState { - const lineHeight: number = this._editor.getOption(EditorOption.lineHeight); + if (!this._editor.hasModel()) { + return StickyScrollWidgetState.Empty; + } + const textModel = this._editor.getModel(); const maxNumberStickyLines = Math.min(this._maxStickyLines, this._editor.getOption(EditorOption.stickyScroll).maxLineCount); const scrollTop: number = this._editor.getScrollTop(); let lastLineRelativePosition: number = 0; @@ -597,26 +600,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 bottomOfBeginningLine = this._editor.getBottomForLineNumber(start) - scrollTop; - const topOfEndLine = this._editor.getTopForLineNumber(end) - scrollTop; + const isValidRange = textModel.isValidRange({ startLineNumber: start, endLineNumber: end, startColumn: 1, endColumn: 1 }); + if (isValidRange && end - start > 0) { + const topOfElement = range.top; + const bottomOfElement = topOfElement + range.height; + 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 77cd911f..4fbe34be 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, ) { } } @@ -69,12 +70,10 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi private readConfiguration() { this._sessionStore.clear(); - const options = this._editor.getOption(EditorOption.stickyScroll); if (!options.enabled) { return; } - this._sessionStore.add(this._editor.onDidChangeModel(() => { // We should not show an old model for a different file, it will always be wrong. // So we clear the model here immediately and then trigger an update. @@ -102,7 +101,6 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi private updateStickyModelProvider() { this._stickyModelProvider?.dispose(); this._stickyModelProvider = null; - const editor = this._editor; if (editor.hasModel()) { this._stickyModelProvider = new StickyModelProvider( @@ -122,7 +120,6 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi } private async updateStickyModel(token: CancellationToken): Promise { - if (!this._editor.hasModel() || !this._stickyModelProvider || this._editor.getModel().isTooLargeForTokenization()) { this._model = null; return; @@ -132,7 +129,6 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi // the computation was canceled, so do not overwrite the model return; } - this._model = model; } @@ -150,6 +146,7 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi outlineModel: StickyElement, result: StickyLineCandidate[], depth: number, + top: number, lastStartLineNumber: number ): void { if (outlineModel.children.length === 0) { @@ -172,16 +169,18 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi if (!child) { return; } - if (child.range) { - const childStartLine = child.range.startLineNumber; - const childEndLine = child.range.endLineNumber; + const childRange = child.range; + if (childRange) { + const childStartLine = childRange.startLineNumber; + const childEndLine = childRange.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 +190,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 fada2e0c..9ec68fb9 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); @@ -226,7 +230,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } // For existing sticky lines update the top and z-index for (const stickyLine of this._renderedStickyLines) { - this._updateTopAndZIndexOfStickyLine(stickyLine); + this._updatePosition(stickyLine); } // For new sticky lines const layoutInfo = this._editor.getLayoutInfo(); @@ -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,30 +365,35 @@ 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); - return this._updateTopAndZIndexOfStickyLine(renderedLine); + const renderedLine = new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping, lineHTMLNode.scrollWidth, lineHeight); + return this._updatePosition(renderedLine); } - private _updateTopAndZIndexOfStickyLine(stickyLine: RenderedStickyLine): RenderedStickyLine { + private _updatePosition(stickyLine: RenderedStickyLine): RenderedStickyLine { const index = stickyLine.index; const lineHTMLNode = stickyLine.lineDomNode; const lineNumberHTMLNode = stickyLine.lineNumberDomNode; const isLastLine = index === this._lineNumbers.length - 1; - - const lastLineZIndex = '0'; - const intermediateLineZIndex = '1'; - lineHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; - lineNumberHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; - - const lastLineTop = `${index * this._lineHeight + this._lastLineRelativePosition + (stickyLine.foldingIcon?.isCollapsed ? 1 : 0)}px`; - const intermediateLineTop = `${index * this._lineHeight}px`; - lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; - lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; + if (isLastLine) { + const zIndex = '0'; + lineHTMLNode.style.zIndex = zIndex; + lineNumberHTMLNode.style.zIndex = zIndex; + const top = `${index * this._lineHeight + this._lastLineRelativePosition + (stickyLine.foldingIcon?.isCollapsed ? 1 : 0)}px`; + lineHTMLNode.style.top = top; + lineNumberHTMLNode.style.top = top; + } else { + const zIndex = '1'; + lineHTMLNode.style.zIndex = zIndex; + lineNumberHTMLNode.style.zIndex = zIndex; + const top = `${index * this._lineHeight}px`; + lineHTMLNode.style.top = top; + lineNumberHTMLNode.style.top = top; + } return stickyLine; } @@ -512,6 +521,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 7ac05581..07493f90 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/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index bf6ce4b6..fcf97af3 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; } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index fbaa5530..f22622e5 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 @@ -230,23 +231,25 @@ export class SuggestWidget implements IDisposable { mouseSupport: false, multipleSelectionSupport: false, accessibilityProvider: { - getRole: () => 'option', + getRole: () => 'listitem', getWidgetAriaLabel: () => nls.localize('suggest', "Suggest"), getWidgetRole: () => 'listbox', getAriaLabel: (item: CompletionItem) => { let label = item.textLabel; + const kindLabel = CompletionItemKinds.toLabel(item.completion.kind); if (typeof item.completion.label !== 'string') { const { detail, description } = item.completion.label; if (detail && description) { - label = nls.localize('label.full', '{0} {1}, {2}', label, detail, description); + label = nls.localize('label.full', '{0} {1}, {2}, {3}', label, detail, description, kindLabel); } else if (detail) { - label = nls.localize('label.detail', '{0} {1}', label, detail); + label = nls.localize('label.detail', '{0} {1}, {2}', label, detail, kindLabel); } else if (description) { - label = nls.localize('label.desc', '{0}, {1}', label, description); + label = nls.localize('label.desc', '{0}, {1}, {2}', label, description, kindLabel); } + } else { + label = nls.localize('label', '{0}, {1}', label, kindLabel); } - if (!item.isResolved || !this._isDetailsVisible()) { return label; } @@ -270,7 +273,7 @@ export class SuggestWidget implements IDisposable { const applyStatusBarStyle = () => this.element.domNode.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).showStatusBar); applyStatusBarStyle(); - this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t.theme))); + this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t))); this._onThemeChange(_themeService.getColorTheme()); this._disposables.add(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); @@ -1041,3 +1044,4 @@ export class SuggestContentWidget implements IContentWidget { this._position = position; } } + diff --git a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts index 0057e3cf..5c50c041 100644 --- a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts @@ -13,7 +13,7 @@ import { IRange } from '../../../../common/core/range.js'; import { DEFAULT_WORD_REGEXP } from '../../../../common/core/wordHelper.js'; import * as languages from '../../../../common/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { BaseEditorSimpleWorker } from '../../../../common/services/editorSimpleWorker.js'; +import { EditorWorker } from '../../../../common/services/editorWebWorker.js'; import { EditorWorkerService } from '../../../../browser/services/editorWorkerService.js'; import { IModelService } from '../../../../common/services/model.js'; import { ITextResourceConfigurationService } from '../../../../common/services/textResourceConfiguration.js'; @@ -62,7 +62,7 @@ suite('suggest, word distance', function () { const service = new class extends EditorWorkerService { - private _worker = new BaseEditorSimpleWorker(); + private _worker = new EditorWorker(); constructor() { super(null!, modelService, new class extends mock() { }, new NullLogService(), new TestLanguageConfigurationService(), new LanguageFeaturesService()); diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index e39eaa2e..3a80a6d4 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -12,7 +12,7 @@ import * as platform from '../../../../base/common/platform.js'; import { InvisibleCharacters, isBasicASCII } from '../../../../base/common/strings.js'; import './unicodeHighlighter.css'; import { IActiveCodeEditor, ICodeEditor } from '../../../browser/editorBrowser.js'; -import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; +import { EditorAction, EditorContributionInstantiation, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { InUntrustedWorkspace, inUntrustedWorkspace, EditorOption, InternalUnicodeHighlightOptions, unicodeHighlightConfigKeys } from '../../../common/config/editorOptions.js'; import { Range } from '../../../common/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; @@ -32,6 +32,7 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js'; +import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; export const warningIcon = registerIcon('extensions-warning-message', Codicon.warning, nls.localize('warningIcon', 'Icon shown with a warning message in the extensions editor.')); @@ -119,7 +120,7 @@ export class UnicodeHighlighter extends Disposable implements IEditorContributio actions: [ { label: data.command.shortLabel, - href: `command:${data.command.id}` + href: `command:${data.command.desc.id}` } ], onClose: () => { @@ -618,14 +619,15 @@ export class DisableHighlightingInStringsAction extends EditorAction implements } } -export class DisableHighlightingOfAmbiguousCharactersAction extends EditorAction implements IDisableUnicodeHighlightAction { +export class DisableHighlightingOfAmbiguousCharactersAction extends Action2 implements IDisableUnicodeHighlightAction { public static ID = 'editor.action.unicodeHighlight.disableHighlightingOfAmbiguousCharacters'; public readonly shortLabel = nls.localize('unicodeHighlight.disableHighlightingOfAmbiguousCharacters.shortLabel', 'Disable Ambiguous Highlight'); constructor() { super({ id: DisableHighlightingOfAmbiguousCharactersAction.ID, - label: nls.localize2('action.unicodeHighlight.disableHighlightingOfAmbiguousCharacters', "Disable highlighting of ambiguous characters"), - precondition: undefined + title: nls.localize2('action.unicodeHighlight.disableHighlightingOfAmbiguousCharacters', "Disable highlighting of ambiguous characters"), + precondition: undefined, + f1: false, }); } @@ -641,14 +643,15 @@ export class DisableHighlightingOfAmbiguousCharactersAction extends EditorAction } } -export class DisableHighlightingOfInvisibleCharactersAction extends EditorAction implements IDisableUnicodeHighlightAction { +export class DisableHighlightingOfInvisibleCharactersAction extends Action2 implements IDisableUnicodeHighlightAction { public static ID = 'editor.action.unicodeHighlight.disableHighlightingOfInvisibleCharacters'; public readonly shortLabel = nls.localize('unicodeHighlight.disableHighlightingOfInvisibleCharacters.shortLabel', 'Disable Invisible Highlight'); constructor() { super({ id: DisableHighlightingOfInvisibleCharactersAction.ID, - label: nls.localize2('action.unicodeHighlight.disableHighlightingOfInvisibleCharacters', "Disable highlighting of invisible characters"), - precondition: undefined + title: nls.localize2('action.unicodeHighlight.disableHighlightingOfInvisibleCharacters', "Disable highlighting of invisible characters"), + precondition: undefined, + f1: false, }); } @@ -664,14 +667,15 @@ export class DisableHighlightingOfInvisibleCharactersAction extends EditorAction } } -export class DisableHighlightingOfNonBasicAsciiCharactersAction extends EditorAction implements IDisableUnicodeHighlightAction { +export class DisableHighlightingOfNonBasicAsciiCharactersAction extends Action2 implements IDisableUnicodeHighlightAction { public static ID = 'editor.action.unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters'; public readonly shortLabel = nls.localize('unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters.shortLabel', 'Disable Non ASCII Highlight'); constructor() { super({ id: DisableHighlightingOfNonBasicAsciiCharactersAction.ID, - label: nls.localize2('action.unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters', "Disable highlighting of non basic ASCII characters"), - precondition: undefined + title: nls.localize2('action.unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters', "Disable highlighting of non basic ASCII characters"), + precondition: undefined, + f1: false, }); } @@ -694,17 +698,18 @@ interface ShowExcludeOptionsArgs { inString: boolean; } -export class ShowExcludeOptions extends EditorAction { +export class ShowExcludeOptions extends Action2 { public static ID = 'editor.action.unicodeHighlight.showExcludeOptions'; constructor() { super({ id: ShowExcludeOptions.ID, - label: nls.localize2('action.unicodeHighlight.showExcludeOptions', "Show Exclude Options"), - precondition: undefined + title: nls.localize2('action.unicodeHighlight.showExcludeOptions', "Show Exclude Options"), + precondition: undefined, + f1: false, }); } - public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor, args: any): Promise { + public async run(accessor: ServicesAccessor | undefined, args: any): Promise { const { codePoint, reason, inString, inComment } = args as ShowExcludeOptionsArgs; const char = String.fromCodePoint(codePoint); @@ -751,16 +756,19 @@ export class ShowExcludeOptions extends EditorAction { options.push({ label: action.label, run: async () => action.runAction(configurationService) }); } + function getTitle(options: Action2) { + return typeof options.desc.title === 'string' ? options.desc.title : options.desc.title.value; + } + if (reason.kind === UnicodeHighlighterReasonKind.Ambiguous) { const action = new DisableHighlightingOfAmbiguousCharactersAction(); - options.push({ label: action.label, run: async () => action.runAction(configurationService) }); + options.push({ label: getTitle(action), run: async () => action.runAction(configurationService) }); } else if (reason.kind === UnicodeHighlighterReasonKind.Invisible) { const action = new DisableHighlightingOfInvisibleCharactersAction(); - options.push({ label: action.label, run: async () => action.runAction(configurationService) }); - } - else if (reason.kind === UnicodeHighlighterReasonKind.NonBasicAscii) { + options.push({ label: getTitle(action), run: async () => action.runAction(configurationService) }); + } else if (reason.kind === UnicodeHighlighterReasonKind.NonBasicAscii) { const action = new DisableHighlightingOfNonBasicAsciiCharactersAction(); - options.push({ label: action.label, run: async () => action.runAction(configurationService) }); + options.push({ label: getTitle(action), run: async () => action.runAction(configurationService) }); } else { expectNever(reason); } @@ -815,9 +823,9 @@ function expectNever(value: never) { throw new Error(`Unexpected value: ${value}`); } -registerEditorAction(DisableHighlightingOfAmbiguousCharactersAction); -registerEditorAction(DisableHighlightingOfInvisibleCharactersAction); -registerEditorAction(DisableHighlightingOfNonBasicAsciiCharactersAction); -registerEditorAction(ShowExcludeOptions); +registerAction2(DisableHighlightingOfAmbiguousCharactersAction); +registerAction2(DisableHighlightingOfInvisibleCharactersAction); +registerAction2(DisableHighlightingOfNonBasicAsciiCharactersAction); +registerAction2(ShowExcludeOptions); registerEditorContribution(UnicodeHighlighter.ID, UnicodeHighlighter, EditorContributionInstantiation.AfterFirstRender); HoverParticipantRegistry.register(UnicodeHighlighterHoverParticipant); diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index 611fcb7e..2a65960d 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/editor.worker.start.ts b/src/vs/editor/editor.worker.start.ts new file mode 100644 index 00000000..9906249a --- /dev/null +++ b/src/vs/editor/editor.worker.start.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { initialize } from '../base/common/worker/webWorkerBootstrap.js'; +import { EditorWorker, IWorkerContext } from './common/services/editorWebWorker.js'; +import { EditorWorkerHost } from './common/services/editorWorkerHost.js'; + +/** + * Used by `monaco-editor` to hook up web worker rpc. + * @skipMangle + * @internal + */ +export function start(client: TClient): IWorkerContext { + const webWorkerServer = initialize(() => new EditorWorker(client)); + const editorWorkerHost = EditorWorkerHost.getChannel(webWorkerServer); + const host = new Proxy({}, { + get(target, prop, receiver) { + if (typeof prop !== 'string') { + throw new Error(`Not supported`); + } + return (...args: any[]) => { + return editorWorkerHost.$fhr(prop, args); + }; + } + }); + + return { + host: host as THost, + getMirrorModels: () => { + return webWorkerServer.requestHandler.getModels(); + } + }; +} diff --git a/src/vs/editor/standalone/browser/standalone-tokens.css b/src/vs/editor/standalone/browser/standalone-tokens.css index 1fc85078..1d65e13d 100644 --- a/src/vs/editor/standalone/browser/standalone-tokens.css +++ b/src/vs/editor/standalone/browser/standalone-tokens.css @@ -37,16 +37,16 @@ clip-path: inset(50%); } -.monaco-editor, .monaco-diff-editor .synthetic-focus, -.monaco-editor, .monaco-diff-editor [tabindex="0"]:focus, -.monaco-editor, .monaco-diff-editor [tabindex="-1"]:focus, -.monaco-editor, .monaco-diff-editor button:focus, -.monaco-editor, .monaco-diff-editor input[type=button]:focus, -.monaco-editor, .monaco-diff-editor input[type=checkbox]:focus, -.monaco-editor, .monaco-diff-editor input[type=search]:focus, -.monaco-editor, .monaco-diff-editor input[type=text]:focus, -.monaco-editor, .monaco-diff-editor select:focus, -.monaco-editor, .monaco-diff-editor textarea:focus { +.monaco-editor .synthetic-focus, .monaco-diff-editor .synthetic-focus, +.monaco-editor [tabindex="0"]:focus, .monaco-diff-editor [tabindex="0"]:focus, +.monaco-editor [tabindex="-1"]:focus, .monaco-diff-editor [tabindex="-1"]:focus, +.monaco-editor button:focus, .monaco-diff-editor button:focus, +.monaco-editor input[type=button]:focus, .monaco-diff-editor input[type=button]:focus, +.monaco-editor input[type=checkbox]:focus, .monaco-diff-editor input[type=checkbox]:focus, +.monaco-editor input[type=search]:focus, .monaco-diff-editor input[type=search]:focus, +.monaco-editor input[type=text]:focus, .monaco-diff-editor input[type=text]:focus, +.monaco-editor select:focus, .monaco-diff-editor select:focus, +.monaco-editor textarea:focus, .monaco-diff-editor textarea:focus { outline-width: 1px; outline-style: solid; outline-offset: -1px; diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 92df7c57..5b123211 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -12,7 +12,7 @@ import { FontMeasurements } from '../../browser/config/fontMeasurements.js'; import { ICodeEditor } from '../../browser/editorBrowser.js'; import { EditorCommand, ServicesAccessor } from '../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../browser/services/codeEditorService.js'; -import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from './standaloneWebWorker.js'; +import { IInternalWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from './standaloneWebWorker.js'; import { ApplyUpdateResult, ConfigurationChangedEvent, EditorOptions } from '../../common/config/editorOptions.js'; import { EditorZoom } from '../../common/config/editorZoom.js'; import { BareFontInfo, FontInfo } from '../../common/config/fontInfo.js'; @@ -331,7 +331,7 @@ export function onDidChangeModelLanguage(listener: (e: { readonly model: ITextMo * Create a new web worker that has model syncing capabilities built in. * Specify an AMD module to load that will `create` an object that will be proxied. */ -export function createWebWorker(opts: IWebWorkerOptions): MonacoWebWorker { +export function createWebWorker(opts: IInternalWebWorkerOptions): MonacoWebWorker { return actualCreateWebWorker(StandaloneServices.get(IModelService), opts); } diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index a19d7603..ddf887c8 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -98,7 +98,7 @@ import { mainWindow } from '../../../base/browser/window.js'; import { ResourceMap } from '../../../base/common/map.js'; import { ITreeSitterParserService } from '../../common/services/treeSitterParserService.js'; import { StandaloneTreeSitterParserService } from './standaloneTreeSitterService.js'; -import { IWorkerDescriptor } from '../../../base/common/worker/simpleWorker.js'; +import { IWebWorkerDescriptor } from '../../../base/browser/webWorkerFactory.js'; class SimpleModel implements IResolvedTextEditorModel { @@ -1077,8 +1077,7 @@ class StandaloneContextMenuService extends ContextMenuService { } } -export const standaloneEditorWorkerDescriptor: IWorkerDescriptor = { - moduleId: 'vs/editor/common/services/editorSimpleWorker', +const standaloneEditorWorkerDescriptor: IWebWorkerDescriptor = { esmModuleLocation: undefined, label: 'editorWorkerService' }; diff --git a/src/vs/editor/standalone/browser/standaloneThemeService.ts b/src/vs/editor/standalone/browser/standaloneThemeService.ts index 4446b693..8a697f65 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeService.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeService.ts @@ -16,7 +16,7 @@ import { hc_black, hc_light, vs, vs_dark } from '../common/themes.js'; import { IEnvironmentService } from '../../../platform/environment/common/environment.js'; import { Registry } from '../../../platform/registry/common/platform.js'; import { asCssVariableName, ColorIdentifier, Extensions, IColorRegistry } from '../../../platform/theme/common/colorRegistry.js'; -import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IProductIconTheme, IThemingRegistry, ITokenStyle, IThemeChangeEvent } from '../../../platform/theme/common/themeService.js'; +import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IProductIconTheme, IThemingRegistry, ITokenStyle } from '../../../platform/theme/common/themeService.js'; import { IDisposable, Disposable } from '../../../base/common/lifecycle.js'; import { ColorScheme, isDark, isHighContrast } from '../../../platform/theme/common/theme.js'; import { getIconsStyleSheet, UnthemedProductIconTheme } from '../../../platform/theme/browser/iconsStyleSheet.js'; @@ -213,7 +213,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe declare readonly _serviceBrand: undefined; - private readonly _onColorThemeChange = this._register(new Emitter()); + private readonly _onColorThemeChange = this._register(new Emitter()); public readonly onDidColorThemeChange = this._onColorThemeChange.event; private readonly _onFileIconThemeChange = this._register(new Emitter()); @@ -403,7 +403,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe this._updateCSS(); TokenizationRegistry.setColorMap(colorMap); - this._onColorThemeChange.fire({ theme: this._theme }); + this._onColorThemeChange.fire(this._theme); } private _updateCSS(): void { diff --git a/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts b/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts index 307507b5..f9e0c0bf 100644 --- a/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts +++ b/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts @@ -6,7 +6,7 @@ import type * as Parser from '@vscode/tree-sitter-wasm'; import { Event } from '../../../base/common/event.js'; import { ITextModel } from '../../common/model.js'; -import { ITextModelTreeSitter, ITreeSitterParseResult, ITreeSitterParserService, TreeUpdateEvent } from '../../common/services/treeSitterParserService.js'; +import { ITextModelTreeSitter, ITreeSitterParserService, TreeUpdateEvent } from '../../common/services/treeSitterParserService.js'; /** * The monaco build doesn't like the dynamic import of tree sitter in the real service. @@ -32,7 +32,7 @@ export class StandaloneTreeSitterParserService implements ITreeSitterParserServi getOrInitLanguage(_languageId: string): Parser.Language | undefined { return undefined; } - getParseResult(textModel: ITextModel): ITreeSitterParseResult | undefined { + getParseResult(textModel: ITextModel): ITextModelTreeSitter | undefined { return undefined; } } diff --git a/src/vs/editor/standalone/browser/standaloneWebWorker.ts b/src/vs/editor/standalone/browser/standaloneWebWorker.ts index bd557c7b..f8aa5966 100644 --- a/src/vs/editor/standalone/browser/standaloneWebWorker.ts +++ b/src/vs/editor/standalone/browser/standaloneWebWorker.ts @@ -3,18 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getAllMethodNames } from '../../../base/common/objects.js'; import { URI } from '../../../base/common/uri.js'; -import { IWorkerDescriptor } from '../../../base/common/worker/simpleWorker.js'; import { EditorWorkerClient } from '../../browser/services/editorWorkerService.js'; import { IModelService } from '../../common/services/model.js'; -import { standaloneEditorWorkerDescriptor } from './standaloneServices.js'; /** * Create a new web worker that has model syncing capabilities built in. * Specify an AMD module to load that will `create` an object that will be proxied. */ -export function createWebWorker(modelService: IModelService, opts: IWebWorkerOptions): MonacoWebWorker { +export function createWebWorker(modelService: IModelService, opts: IInternalWebWorkerOptions): MonacoWebWorker { return new MonacoWebWorkerImpl(modelService, opts); } @@ -37,20 +34,11 @@ export interface MonacoWebWorker { withSyncedResources(resources: URI[]): Promise; } -export interface IWebWorkerOptions { +export interface IInternalWebWorkerOptions { /** - * The AMD moduleId to load. - * It should export a function `create` that should return the exported proxy. + * The worker. */ - moduleId: string; - /** - * The data to send over when calling create on the module. - */ - createData?: any; - /** - * A label to be used to identify the web worker for debugging purposes. - */ - label?: string; + worker: Worker; /** * An object that can be used by the web worker to make calls back to the main thread. */ @@ -64,22 +52,24 @@ export interface IWebWorkerOptions { class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWorker { - private readonly _foreignModuleId: string; private readonly _foreignModuleHost: { [method: string]: Function } | null; - private _foreignModuleCreateData: any | null; - private _foreignProxy: Promise | null; + private _foreignProxy: Promise; - constructor(modelService: IModelService, opts: IWebWorkerOptions) { - const workerDescriptor: IWorkerDescriptor = { - moduleId: standaloneEditorWorkerDescriptor.moduleId, - esmModuleLocation: standaloneEditorWorkerDescriptor.esmModuleLocation, - label: opts.label, - }; - super(workerDescriptor, opts.keepIdleModels || false, modelService); - this._foreignModuleId = opts.moduleId; - this._foreignModuleCreateData = opts.createData || null; + constructor(modelService: IModelService, opts: IInternalWebWorkerOptions) { + super(opts.worker, opts.keepIdleModels || false, modelService); this._foreignModuleHost = opts.host || null; - this._foreignProxy = null; + this._foreignProxy = this._getProxy().then(proxy => { + return new Proxy({}, { + get(target, prop, receiver) { + if (typeof prop !== 'string') { + throw new Error(`Not supported`); + } + return (...args: any[]) => { + return proxy.$fmr(prop, args); + }; + } + }) as T; + }); } // foreign host request @@ -95,38 +85,8 @@ class MonacoWebWorkerImpl extends EditorWorkerClient implement } } - private _getForeignProxy(): Promise { - if (!this._foreignProxy) { - this._foreignProxy = this._getProxy().then((proxy) => { - const foreignHostMethods = this._foreignModuleHost ? getAllMethodNames(this._foreignModuleHost) : []; - return proxy.$loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData, foreignHostMethods).then((foreignMethods) => { - this._foreignModuleCreateData = null; - - const proxyMethodRequest = (method: string, args: any[]): Promise => { - return proxy.$fmr(method, args); - }; - - const createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise): () => Promise => { - return function () { - const args = Array.prototype.slice.call(arguments, 0); - return proxyMethodRequest(method, args); - }; - }; - - const foreignProxy = {} as any as T; - for (const foreignMethod of foreignMethods) { - (foreignProxy)[foreignMethod] = createProxyMethod(foreignMethod, proxyMethodRequest); - } - - return foreignProxy; - }); - }); - } - return this._foreignProxy; - } - public getProxy(): Promise { - return this._getForeignProxy(); + return this._foreignProxy; } public withSyncedResources(resources: URI[]): Promise { diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index def6b624..0fa82cf7 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -17,7 +17,7 @@ import { IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from import { UnthemedProductIconTheme } from '../../../../platform/theme/browser/iconsStyleSheet.js'; import { ColorIdentifier } from '../../../../platform/theme/common/colorRegistry.js'; import { ColorScheme } from '../../../../platform/theme/common/theme.js'; -import { IFileIconTheme, IProductIconTheme, IThemeChangeEvent, ITokenStyle } from '../../../../platform/theme/common/themeService.js'; +import { IColorTheme, IFileIconTheme, IProductIconTheme, ITokenStyle } from '../../../../platform/theme/common/themeService.js'; suite('TokenizationSupport2Adapter', () => { @@ -92,7 +92,7 @@ suite('TokenizationSupport2Adapter', () => { public getProductIconTheme(): IProductIconTheme { return this._builtInProductIconTheme; } - public readonly onDidColorThemeChange = new Emitter().event; + public readonly onDidColorThemeChange = new Emitter().event; public readonly onDidFileIconThemeChange = new Emitter().event; public readonly onDidProductIconThemeChange = new Emitter().event; } diff --git a/src/vs/editor/test/browser/services/treeSitterParserService.test.ts b/src/vs/editor/test/browser/services/treeSitterParserService.test.ts index 29f3d8eb..5ebb74c6 100644 --- a/src/vs/editor/test/browser/services/treeSitterParserService.test.ts +++ b/src/vs/editor/test/browser/services/treeSitterParserService.test.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { TextModelTreeSitter, TreeSitterLanguages } from '../../../common/services/treeSitter/treeSitterParserService.js'; import type * as Parser from '@vscode/tree-sitter-wasm'; import { createTextModel } from '../../common/testTextModel.js'; import { timeout } from '../../../../base/common/async.js'; @@ -13,6 +12,8 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { LogService } from '../../../../platform/log/common/logService.js'; import { mock } from '../../../../base/test/common/mock.js'; import { ITreeSitterImporter } from '../../../common/services/treeSitterParserService.js'; +import { TextModelTreeSitter } from '../../../common/services/treeSitter/textModelTreeSitter.js'; +import { TreeSitterLanguages } from '../../../common/services/treeSitter/treeSitterLanguages.js'; class MockParser implements Parser.Parser { language: Parser.Language | null = null; @@ -167,9 +168,9 @@ suite('TreeSitterParserService', function () { } } - const treeSitterParser: TreeSitterLanguages = store.add(new MockTreeSitterLanguages(treeSitterImporter, {} as any, { isBuilt: false } as any, new Map())); + const treeSitterLanguages: TreeSitterLanguages = store.add(new MockTreeSitterLanguages(treeSitterImporter, {} as any, { isBuilt: false } as any, new Map())); const textModel = store.add(createTextModel('console.log("Hello, world!");', 'javascript')); - const textModelTreeSitter = store.add(new TextModelTreeSitter(textModel, treeSitterParser, treeSitterImporter, logService, telemetryService)); + const textModelTreeSitter = store.add(new TextModelTreeSitter(textModel, treeSitterLanguages, false, treeSitterImporter, logService, telemetryService, { exists: async () => false } as any)); textModel.setLanguage('typescript'); await timeout(300); assert.strictEqual((textModelTreeSitter.parseResult?.language as MockLanguage).languageId, 'typescript'); diff --git a/src/vs/editor/test/common/codecs/markdownDecoder.test.ts b/src/vs/editor/test/common/codecs/markdownDecoder.test.ts index bff4b428..d0ea73d7 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 { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.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 { 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 { LeftParenthesis, RightParenthesis } from '../../../common/codecs/simpleCodec/tokens/parentheses.js'; +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(), - ); + suite('• general', () => { + test('• base cases', 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)'), - ], - ); - }); + 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'), + ], + ); + }); - 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)), - ], - ); - }); - - 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 = ''; - - if (stopCharacter === CarriageReturn) { - characterName = '\\r'; - } - if (stopCharacter === NewLine) { - characterName = '\\n'; - } - - assert( - characterName !== '', - 'The "characterName" must be set, got "empty line".', + suite('• links', () => { + suite('• broken', () => { + test('• invalid', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), ); - test(`stop character - "${characterName}"`, 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)', + ]; + + 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)), + ], + ); + }); + + 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'; + } + + 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)`, - ]; + 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(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)), - ], + 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)), + ], + ); + }); + } + }); + + /** + * 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'), + ], + ); + }); }); - /** - * 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".', + suite('• broken', () => { + test('• invalid', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), ); - test(`stop character - "${characterName}"`, 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 )', + ]; + + 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 = ''; + + 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)`, - ]; + 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 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)), - ], + 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), 'Z{n*1vIIMR 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

    -MaX$a-DglX1>+H#MSz)NHkiUSu+2d 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

    S3a9FGD*WG?Dkd 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%0A)SCuNqbC4o_>)H2z0 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

    DbO05AlQ=vy{4~?{@hM8bIj& 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%0DE72vF+KrEw4l1d$48u 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 !this.equalsEditorInputLabel(label, this.tabLabels.at(index))) // editor labels changed ) { this.redraw({ forceRevealActiveTab: true }); didChange = true; @@ -803,25 +801,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 +837,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 +1082,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 b373fd8b..691a716c 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 a3b16b2d..d8620a08 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 c026e158..e927fbca 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 85356ca8..103e37a3 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 d0893c26..edb6062b 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/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index f0dda3b4..3a02bb87 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 389569cf..ab770c95 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 { - const colorValue = e.theme.getColor(color.id)?.toString(); + const listener = this.themeService.onDidColorThemeChange(theme => { + const colorValue = theme.getColor(color.id)?.toString(); if (isBackground) { container.style.backgroundColor = colorValue ?? ''; diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index ba3d0d62..2e6ce3bc 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'; @@ -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,31 +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') || - 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 - } - } - ))); + private readonly hoverDelegate: WorkbenchHoverDelegate; private readonly compactEntriesDisposable = this._register(new MutableDisposable()); private readonly styleOverrides = new Set(); @@ -180,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(); } @@ -324,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); } @@ -405,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 @@ -752,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; @@ -764,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 @@ -772,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 @@ -802,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) { @@ -906,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 { @@ -920,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 2df48c0a..e1a7a7e2 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; @@ -242,7 +257,7 @@ } .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-left > .window-appicon:not(.codicon) { - background-image: url('../../../media/void-icon-sm.png'); + background-image: url('../../../media/code-icon.svg'); background-repeat: no-repeat; background-position: center center; background-size: 16px; @@ -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/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 718037ce..87fa51d5 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/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 87acf448..e8dedb87 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -550,14 +550,18 @@ export abstract class ViewPane extends Pane implements IView { } this.iconContainerHover = this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), this.iconContainer, calculatedTitle)); - this.iconContainer.setAttribute('aria-label', this._getAriaLabel(calculatedTitle)); + this.iconContainer.setAttribute('aria-label', this._getAriaLabel(calculatedTitle, this._titleDescription)); } - private _getAriaLabel(title: string): string { + private _getAriaLabel(title: string, description: string | undefined): string { const viewHasAccessibilityHelpContent = this.viewDescriptorService.getViewDescriptorById(this.id)?.accessibilityHelpContent; const accessibleViewHasShownForView = this.accessibleViewInformationService?.hasShownAccessibleView(this.id); if (!viewHasAccessibilityHelpContent || accessibleViewHasShownForView) { - return title; + if (description) { + return `${title} - ${description}`; + } else { + return title; + } } return nls.localize('viewAccessibilityHelp', 'Use Alt+F1 for accessibility help {0}', title); @@ -570,17 +574,21 @@ export abstract class ViewPane extends Pane implements IView { this.titleContainerHover?.update(calculatedTitle); } - const ariaLabel = this._getAriaLabel(calculatedTitle); - if (this.iconContainer) { - this.iconContainerHover?.update(calculatedTitle); - this.iconContainer.setAttribute('aria-label', ariaLabel); - } - this.ariaHeaderLabel = this.getAriaHeaderLabel(ariaLabel); + this.updateAriaHeaderLabel(calculatedTitle, this._titleDescription); this._title = title; this._onDidChangeTitleArea.fire(); } + private updateAriaHeaderLabel(title: string, description: string | undefined) { + const ariaLabel = this._getAriaLabel(title, description); + if (this.iconContainer) { + this.iconContainerHover?.update(title); + this.iconContainer.setAttribute('aria-label', ariaLabel); + } + this.ariaHeaderLabel = this.getAriaHeaderLabel(ariaLabel); + } + private setTitleDescription(description: string | undefined) { if (this.titleDescriptionContainer) { this.titleDescriptionContainer.textContent = description ?? ''; @@ -594,7 +602,7 @@ export abstract class ViewPane extends Pane implements IView { protected updateTitleDescription(description?: string | undefined): void { this.setTitleDescription(description); - + this.updateAriaHeaderLabel(this._title, description); this._titleDescription = description; this._onDidChangeTitleArea.fire(); } diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index b4f069c6..eaa4e9aa 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addDisposableListener, Dimension, DragAndDropObserver, EventType, getWindow, isAncestor } from '../../../../base/browser/dom.js'; +import { $, addDisposableListener, Dimension, DragAndDropObserver, EventType, getWindow, isAncestor } from '../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { EventType as TouchEventType, Gesture } from '../../../../base/browser/touch.js'; import { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js'; @@ -104,9 +104,9 @@ class ViewPaneDropOverlay extends Themable { } private create(): void { + // Container - this.container = document.createElement('div'); - this.container.id = ViewPaneDropOverlay.OVERLAY_ID; + this.container = $('div', { id: ViewPaneDropOverlay.OVERLAY_ID }); this.container.style.top = '0px'; // Parent @@ -118,8 +118,7 @@ class ViewPaneDropOverlay extends Themable { })); // Overlay - this.overlay = document.createElement('div'); - this.overlay.classList.add('pane-overlay-indicator'); + this.overlay = $('.pane-overlay-indicator'); this.container.appendChild(this.overlay); // Overlay Event Handling diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 39492ef3..e82a1134 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -509,7 +509,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'type': 'string', 'enum': ['left', 'right'], 'default': 'left', - 'description': localize('sideBarLocation', "Controls the location of the primary side bar and activity bar. They can either show on the left or right of the workbench. The Void side bar will show on the opposite side of the workbench.") // Void side bar + 'description': localize('sideBarLocation', "Controls the location of the primary side bar and activity bar. They can either show on the left or right of the workbench. The Void side bar will show on the opposite side of the workbench.") }, 'workbench.panel.showLabels': { 'type': 'boolean', diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 8ed7a669..8f2c0a16 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 778f956a..360b534b 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 5b20850a..3a4cb208 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/release.ts b/src/vs/workbench/common/release.ts deleted file mode 100644 index 12e9ba2f..00000000 --- a/src/vs/workbench/common/release.ts +++ /dev/null @@ -1,15 +0,0 @@ -// added by VSCodium -import { language } from '../../../vs/base/common/platform.js'; - -const DEFAULT_LABEL = 'Release:'; -const LABELS: { [key: string]: string } = { - 'en': DEFAULT_LABEL, - 'fr': 'Révision :', - 'ru': 'Релиз:', - 'zh-hans': '发布版本:', - 'zh-hant': '發布版本:', -}; - -export function getReleaseString(): string { - return LABELS[language] ?? DEFAULT_LABEL; -} diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index bba032c6..52e7ba54 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -801,11 +801,11 @@ export const WINDOW_ACTIVE_BORDER = registerColor('window.activeBorder', { light: null, hcDark: contrastBorder, hcLight: contrastBorder -}, localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the macOS and Linux desktop client when using the custom title bar.")); +}, localize('windowActiveBorder', "The color used for the border of the window when it is active on macOS or Linux. Requires custom title bar style and custom or hidden window controls on Linux.")); export const WINDOW_INACTIVE_BORDER = registerColor('window.inactiveBorder', { dark: null, light: null, hcDark: contrastBorder, hcLight: contrastBorder -}, localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the macOS and Linux desktop client when using the custom title bar.")); +}, localize('windowInactiveBorder', "The color used for the border of the window when it is inactive on macOS or Linux. Requires custom title bar style and custom or hidden window controls on Linux.")); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 6a031163..0f1c1bbc 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -47,6 +47,7 @@ export const enum ViewDimUnfocusedOpacityProperties { export const enum AccessibilityVerbositySettingId { Terminal = 'accessibility.verbosity.terminal', DiffEditor = 'accessibility.verbosity.diffEditor', + MergeEditor = 'accessibility.verbosity.mergeEditor', Chat = 'accessibility.verbosity.panelChat', InlineChat = 'accessibility.verbosity.inlineChat', TerminalChat = 'accessibility.verbosity.terminalChat', @@ -640,6 +641,34 @@ const configuration: IConfigurationNode = { }, }, }, + 'accessibility.signals.editsUndone': { + ...signalFeatureBase, + 'description': localize('accessibility.signals.editsUndone', "Plays a signal - sound (audio cue) and/or announcement (alert) - when edits have been undone."), + 'properties': { + 'sound': { + 'description': localize('accessibility.signals.editsUndone.sound', "Plays a sound when edits have been undone."), + ...soundFeatureBase + }, + 'announcement': { + 'description': localize('accessibility.signals.editsUndone.announcement', "Announces when edits have been undone."), + ...announcementFeatureBase + }, + }, + }, + 'accessibility.signals.editsKept': { + ...signalFeatureBase, + 'description': localize('accessibility.signals.editsKept', "Plays a signal - sound (audio cue) and/or announcement (alert) - when edits are kept."), + 'properties': { + 'sound': { + 'description': localize('accessibility.signals.editsKept.sound', "Plays a sound when edits are kept."), + ...soundFeatureBase + }, + 'announcement': { + 'description': localize('accessibility.signals.editsKept.announcement', "Announces when edits are kept."), + ...announcementFeatureBase + }, + }, + }, 'accessibility.signals.save': { 'type': 'object', 'tags': ['accessibility'], diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index fde082ed..6e000778 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/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index f587bd7c..6e9adf5d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -330,7 +330,7 @@ class AccessibleViewAcceptInlineCompletionAction extends Action2 { return; } const model = InlineCompletionsController.get(editor)?.model.get(); - const state = model?.inlineCompletionState.get(); + const state = model?.state.get(); if (!model || !state) { return; } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 0afedce8..75da6466 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -96,7 +96,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.create(); this._peekViewService.addExclusiveWidget(editor, this); this._applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(e => this._applyTheme(e.theme))); + this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); this._disposables.add(this._previewDisposable); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 6de18445..c9e3ad06 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.Ask), 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,14 +44,25 @@ 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'); } } -export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'quickChat' | 'editsView', keybindingService: IKeybindingService): string { +export class AgentChatAccessibilityHelp implements IAccessibleViewImplementation { + readonly priority = 120; + readonly name = 'agentView'; + readonly type = AccessibleViewType.Help; + readonly when = ContextKeyExpr.and(ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent), ChatContextKeys.inChatInput); + getProvider(accessor: ServicesAccessor) { + const codeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor() || accessor.get(ICodeEditorService).getFocusedCodeEditor(); + return getChatAccessibilityHelpProvider(accessor, codeEditor ?? undefined, 'agentView'); + } +} + +export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'quickChat' | 'editsView' | 'agentView', keybindingService: IKeybindingService): string { const content = []; if (type === 'panelChat' || type === 'quickChat') { if (type === 'quickChat') { @@ -73,23 +83,31 @@ export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'qui content.push(localize('workbench.action.chat.newChat', 'To create a new chat session, invoke the New Chat command{0}.', '')); } } - if (type === 'editsView') { - content.push(localize('chatEditing.overview', 'The chat editing view is used to apply edits across files.')); + if (type === 'editsView' || type === 'agentView') { + if (type === 'agentView') { + content.push(localize('chatAgent.overview', 'The chat agent view is used to apply edits across files in your workspace, enable running commands in the terminal, and more.')); + } else { + content.push(localize('chatEditing.overview', 'The chat editing view is used to apply edits across files.')); + } content.push(localize('chatEditing.format', 'It is comprised of an input box and a file working set (Shift+Tab).')); 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.helpfulCommands', 'When in the edits view, some helpful commands include:')); + content.push(localize('chatEditing.acceptHunk', 'In the editor, Keep{0}, Undo{1}, or Toggle the Diff{2} for the current Change.', '', '', '')); + content.push(localize('chatEditing.undoKeepSounds', 'Sounds will play when a change is accepted or undone. The sounds can be disabled with accessibility.signals.editsKept and accessibility.signals.editsUndone.')); + if (type === 'agentView') { + content.push(localize('chatAgent.userActionRequired', 'An alert will indicate when user action is required. For example, if the agent wants to run something in the terminal, you will hear Action Required: Run Command in Terminal.')); + content.push(localize('chatAgent.runCommand', 'To take the action, use the accept tool command{0}.', '')); + } + content.push(localize('chatEditing.helpfulCommands', '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}.', '')); content.push(localize('chatEditing.removeFileFromWorkingSet', '- Remove File from Working Set{0}.', '')); - content.push(localize('chatEditing.acceptFile', '- Accept{0} and Discard File{1}.', '', '')); + content.push(localize('chatEditing.acceptFile', '- Keep{0} and Undo File{1}.', '', '')); content.push(localize('chatEditing.saveAllFiles', '- Save All Files{0}.', '')); - content.push(localize('chatEditing.acceptAllFiles', '- Accept All Edits{0}.', '')); - content.push(localize('chatEditing.discardAllFiles', '- Discard All Edits{0}.', '')); + content.push(localize('chatEditing.acceptAllFiles', '- Keep All Edits{0}.', '')); + content.push(localize('chatEditing.discardAllFiles', '- Undo 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 { @@ -106,7 +124,7 @@ export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'qui return content.join('\n'); } -export function getChatAccessibilityHelpProvider(accessor: ServicesAccessor, editor: ICodeEditor | undefined, type: 'panelChat' | 'inlineChat' | 'quickChat' | 'editsView') { +export function getChatAccessibilityHelpProvider(accessor: ServicesAccessor, editor: ICodeEditor | undefined, type: 'panelChat' | 'inlineChat' | 'quickChat' | 'editsView' | 'agentView'): AccessibleContentProvider | undefined { const widgetService = accessor.get(IChatWidgetService); const keybindingService = accessor.get(IKeybindingService); const inputEditor: ICodeEditor | undefined = type === 'panelChat' || type === 'editsView' || type === 'quickChat' ? widgetService.lastFocusedWidget?.inputEditor : editor; @@ -123,7 +141,7 @@ export function getChatAccessibilityHelpProvider(accessor: ServicesAccessor, edi inputEditor.getSupportedActions(); const helpText = getAccessibilityHelpText(type, keybindingService); return new AccessibleContentProvider( - type === 'panelChat' ? AccessibleViewProviderId.PanelChat : type === 'inlineChat' ? AccessibleViewProviderId.InlineChat : AccessibleViewProviderId.QuickChat, + type === 'panelChat' ? AccessibleViewProviderId.PanelChat : type === 'inlineChat' ? AccessibleViewProviderId.InlineChat : type === 'agentView' ? AccessibleViewProviderId.AgentChat : AccessibleViewProviderId.QuickChat, { type: AccessibleViewType.Help }, () => helpText, () => { diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 365ad73a..13274ccc 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -3,13 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toAction } from '../../../../../base/common/actions.js'; +import { toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../../base/common/actions.js'; 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'; @@ -21,55 +23,52 @@ import { IActionViewItemService } from '../../../../../platform/actions/browser/ 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, 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 { INotificationService } from '../../../../../platform/notification/common/notification.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import product from '../../../../../platform/product/common/product.js'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js'; +import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.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 { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { IChatEditingSession, WorkingSetEntryState } from '../../common/chatEditingService.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, validateChatMode } 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 { /** - * The query for quick chat. + * The query for chat. */ query: string; /** @@ -84,11 +83,14 @@ export interface IChatViewOpenOptions { * Any previous chat requests and responses that should be shown in the chat view. */ previousRequests?: IChatViewOpenRequestEntry[]; - /** * Whether a screenshot of the focused window should be taken and attached */ attachScreenshot?: boolean; + /** + * The mode to open the chat in. + */ + mode?: ChatMode; } export interface IChatViewOpenRequestEntry { @@ -96,14 +98,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 +106,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, @@ -143,6 +138,9 @@ export function registerChatActions() { if (!chatWidget) { return; } + if (opts?.mode && validateChatMode(opts.mode)) { + chatWidget.input.setChatMode(opts.mode); + } if (opts?.previousRequests?.length && chatWidget.viewModel) { for (const { request, response } of opts.previousRequests) { chatService.addCompleteRequest(chatWidget.viewModel.sessionId, request, undefined, 0, { message: response }); @@ -249,8 +247,27 @@ export function registerChatActions() { const quickInputService = accessor.get(IQuickInputService); const viewsService = accessor.get(IViewsService); const editorService = accessor.get(IEditorService); + const dialogService = accessor.get(IDialogService); - const showPicker = () => { + const view = await viewsService.openView(ChatViewId); + if (!view) { + return; + } + + const chatSessionId = view.widget.viewModel?.model.sessionId; + if (!chatSessionId) { + return; + } + + const editingSession = view.widget.viewModel?.model.editingSession; + if (editingSession) { + const phrase = localize('switchChat.confirmPhrase', "Switching chats will end your current edit session."); + if (!await handleCurrentEditingSession(editingSession, phrase, dialogService)) { + return; + } + } + + const showPicker = async () => { const openInEditorButton: IQuickInputButton = { iconClass: ThemeIcon.asClassName(Codicon.file), tooltip: localize('interactiveSession.history.editor', "Open in Editor"), @@ -268,8 +285,8 @@ export function registerChatActions() { chat: IChatDetail; } - const getPicks = () => { - const items = chatService.getHistory(); + const getPicks = async () => { + const items = await chatService.getHistory(); items.sort((a, b) => (b.lastMessageDate ?? 0) - (a.lastMessageDate ?? 0)); let lastDate: string | undefined = undefined; @@ -300,7 +317,7 @@ export function registerChatActions() { const store = new DisposableStore(); const picker = store.add(quickInputService.createQuickPick({ useSeparators: true })); picker.placeholder = localize('interactiveSession.history.pick', "Switch to chat"); - const picks = getPicks(); + const picks = await getPicks(); picker.items = picks; store.add(picker.onDidTriggerItemButton(async context => { if (context.button === openInEditorButton) { @@ -309,7 +326,7 @@ export function registerChatActions() { picker.hide(); } else if (context.button === deleteButton) { chatService.removeHistoryEntry(context.item.chat.sessionId); - picker.items = getPicks(); + picker.items = await getPicks(); } else if (context.button === renameButton) { const title = await quickInputService.input({ title: localize('newChatTitle', "New chat title"), value: context.item.chat.title }); if (title) { @@ -317,15 +334,14 @@ export function registerChatActions() { } // The quick input hides the picker, it gets disposed, so we kick it off from scratch - showPicker(); + await showPicker(); } })); store.add(picker.onDidAccept(async () => { try { const item = picker.selectedItems[0]; const sessionId = item.chat.sessionId; - const view = await viewsService.openView(ChatViewId) as ChatViewPane; - view.loadSession(sessionId); + await view.loadSession(sessionId); } finally { picker.hide(); } @@ -334,7 +350,7 @@ export function registerChatActions() { picker.show(); }; - showPicker(); + await showPicker(); } }); @@ -366,7 +382,7 @@ export function registerChatActions() { category: CHAT_CATEGORY, menu: { id: MenuId.ChatInput, - when: ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + when: ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask), group: 'navigation', order: 1 } @@ -428,22 +444,22 @@ export function registerChatActions() { } async run(accessor: ServicesAccessor, ...args: any[]) { const editorGroupsService = accessor.get(IEditorGroupsService); - const viewsService = accessor.get(IViewsService); - const chatService = accessor.get(IChatService); - chatService.clearAllHistoryEntries(); + const instantiationService = accessor.get(IInstantiationService); + const widgetService = accessor.get(IChatWidgetService); - const chatView = viewsService.getViewWithId(ChatViewId) as ChatViewPane | undefined; - if (chatView) { - chatView.widget.clear(); - } + await chatService.clearAllHistoryEntries(); + + widgetService.getAllWidgets().forEach(widget => { + widget.clear(); + }); // Clear all chat editors. Have to go this route because the chat editor may be in the background and // not have a ChatEditorInput. editorGroupsService.groups.forEach(group => { group.editors.forEach(editor => { if (editor instanceof ChatEditorInput) { - clearChatEditor(accessor, editor); + instantiationService.invokeFunction(clearChatEditor, editor); } }); }); @@ -514,7 +530,7 @@ export function registerChatActions() { } }); - const nonEnterpriseCopilotUsers = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.notEquals(`config.${defaultChat.providerSetting}`, defaultChat.enterpriseProviderId)); + const nonEnterpriseCopilotUsers = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.notEquals(`config.${defaultChat.completionsAdvancedSetting}.authProvider`, defaultChat.enterpriseProviderId)); registerAction2(class extends Action2 { constructor() { super({ @@ -522,7 +538,13 @@ export function registerChatActions() { title: localize2('manageCopilot', "Manage Copilot"), category: CHAT_CATEGORY, f1: true, - precondition: nonEnterpriseCopilotUsers, + precondition: ContextKeyExpr.and( + ContextKeyExpr.or( + ChatContextKeys.Entitlement.limited, + ChatContextKeys.Entitlement.pro + ), + nonEnterpriseCopilotUsers + ), menu: { id: MenuId.ChatTitleBarMenu, group: 'y_manage', @@ -546,6 +568,7 @@ export function registerChatActions() { title: localize2('showCopilotUsageExtensions', "Show Extensions using Copilot"), f1: true, category: EXTENSIONS_CATEGORY, + precondition: ChatContextKeys.enabled }); } @@ -561,7 +584,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', @@ -572,7 +595,7 @@ export function registerChatActions() { override async run(accessor: ServicesAccessor): Promise { const commandService = accessor.get(ICommandService); - commandService.executeCommand('github.copilot.toggleStatusMenu'); + commandService.executeCommand(defaultChat.completionsMenuCommand); } }); @@ -586,20 +609,21 @@ 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 telemetryService = accessor.get(ITelemetryService); 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"); @@ -614,7 +638,11 @@ export function registerChatActions() { buttons: [ { label: localize('upgradePro', "Upgrade to Copilot Pro"), - run: () => commandService.executeCommand('workbench.action.chat.upgradePlan') + run: () => { + const commandId = 'workbench.action.chat.upgradePlan'; + telemetryService.publicLog2('workbenchActionExecuted', { id: commandId, from: 'chat-dialog' }); + commandService.executeCommand(commandId); + } }, ], custom: { @@ -645,21 +673,20 @@ 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 ?? '', }; -// Void commented this out - copilot head -// Add next to the command center if command center is disabled +// // Add next to the command center if command center is disabled +// Void commented this out with /* */ - copilot head /* MenuRegistry.appendMenuItem(MenuId.CommandCenter, { submenu: MenuId.ChatTitleBarMenu, title: localize('title4', "Copilot"), icon: Codicon.copilot, - // Void commented this out - copilot head - when: ContextKeyExpr.false(), - // when: ContextKeyExpr.and( - // ChatContextKeys.supported, - // ContextKeyExpr.has('config.chat.commandCenter.enabled') - // ), + when: ContextKeyExpr.and( + ChatContextKeys.supported, + ContextKeyExpr.has('config.chat.commandCenter.enabled') + ), order: 10001 // to the right of command center }); @@ -669,13 +696,11 @@ MenuRegistry.appendMenuItem(MenuId.TitleBar, { title: localize('title4', "Copilot"), group: 'navigation', icon: Codicon.copilot, - when: ContextKeyExpr.false(), - // Void commented this out - copilot head - // when: ContextKeyExpr.and( - // ChatContextKeys.supported, - // ContextKeyExpr.has('config.chat.commandCenter.enabled'), - // ContextKeyExpr.has('config.window.commandCenter').negate(), - // ), + when: ContextKeyExpr.and( + ChatContextKeys.supported, + ContextKeyExpr.has('config.chat.commandCenter.enabled'), + ContextKeyExpr.has('config.window.commandCenter').negate(), + ), order: 1 }); */ @@ -690,21 +715,33 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction { } }); +registerAction2(class ResetTrustedToolsAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.resetTrustedTools', + title: localize2('resetTrustedTools', "Reset Tool Confirmations"), + category: CHAT_CATEGORY, + f1: true, + }); + } + override run(accessor: ServicesAccessor): void { + accessor.get(ILanguageModelToolsService).resetToolAutoConfirmation(); + accessor.get(INotificationService).info(localize('resetTrustedToolsSuccess', "Tool confirmation preferences have been reset.")); + } +}); + export class CopilotTitleBarMenuRendering extends Disposable implements IWorkbenchContribution { - static readonly ID = 'copilot.titleBarMenuRendering'; + static readonly ID = 'workbench.contrib.copilotTitleBarMenuRendering'; 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 +753,32 @@ 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 chatHidden = chatEntitlementService.sentiment === ChatSentiment.Disabled; + const { chatQuotaExceeded, completionsQuotaExceeded } = chatEntitlementService.quotas; + const signedOut = chatEntitlementService.entitlement === ChatEntitlement.Unknown; + const setupFromDialog = configurationService.getValue('chat.setupFromDialog'); - let primaryActionId: string; - let primaryActionTitle: string; - let primaryActionIcon: ThemeIcon; - if (!chatExtensionInstalled) { + let primaryActionId = TOGGLE_CHAT_ACTION_ID; + let primaryActionTitle = localize('toggleChat', "Toggle Chat"); + let primaryActionIcon = Codicon.copilot; + if (!chatExtensionInstalled && (!setupFromDialog || chatHidden)) { primaryActionId = CHAT_SETUP_ACTION_ID; - primaryActionTitle = CHAT_SETUP_ACTION_LABEL.value; - 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; + primaryActionTitle = localize('triggerChatSetup', "Use AI Features with Copilot for free..."); + } else if (chatExtensionInstalled && signedOut) { + primaryActionId = setupFromDialog ? CHAT_SETUP_ACTION_ID : 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; } return instantiationService.createInstance(DropdownWithPrimaryActionViewItem, instantiationService.createInstance(MenuItemAction, { id: primaryActionId, @@ -748,9 +786,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.setupFromDialog')) )); // Reduces flicker a bit on reload/restart @@ -758,12 +797,70 @@ 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; +} + +/** + * Returns whether we can continue clearing/switching chat sessions, false to cancel. + */ +export async function handleCurrentEditingSession(currentEditingSession: IChatEditingSession, phrase: string | undefined, dialogService: IDialogService): Promise { + if (shouldShowClearEditingSessionConfirmation(currentEditingSession)) { + return showClearEditingSessionConfirmation(currentEditingSession, dialogService, { messageOverride: phrase }); + } + + return true; +} + +export interface IClearEditingSessionConfirmationOptions { + titleOverride?: string; + messageOverride?: string; +} + +export async function showClearEditingSessionConfirmation(editingSession: IChatEditingSession, dialogService: IDialogService, options?: IClearEditingSessionConfirmationOptions): Promise { + const defaultPhrase = localize('chat.startEditing.confirmation.pending.message.default', "Starting a new chat will end your current edit session."); + const defaultTitle = localize('chat.startEditing.confirmation.title', "Start new chat?"); + const phrase = options?.messageOverride ?? defaultPhrase; + const title = options?.titleOverride ?? defaultTitle; + + const currentEdits = editingSession.entries.get(); + const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === WorkingSetEntryState.Modified); + + const { result } = await dialogService.prompt({ + title, + message: phrase + ' ' + localize('chat.startEditing.confirmation.pending.message.2', "Do you want to keep pending edits to {0} files?", undecidedEdits.length), + type: 'info', + cancelButton: true, + buttons: [ + { + label: localize('chat.startEditing.confirmation.acceptEdits', "Keep & Continue"), + run: async () => { + await editingSession.accept(); + return true; + } + }, + { + label: localize('chat.startEditing.confirmation.discardEdits', "Undo & Continue"), + run: async () => { + await editingSession.reject(); + return true; + } + } + ], + }); + + return Boolean(result); +} + +export function shouldShowClearEditingSessionConfirmation(editingSession: IChatEditingSession): boolean { + const currentEdits = editingSession.entries.get(); + const currentEditCount = currentEdits.length; + + if (currentEditCount) { + const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === WorkingSetEntryState.Modified); + return !!undecidedEdits.length; + } + + return false; } 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 985dc1b9..1b8d7a77 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts @@ -5,14 +5,19 @@ import { CHAT_CATEGORY } from '../chatActions.js'; import { localize2 } from '../../../../../../nls.js'; +import { IChatService } from '../../../common/chatService.js'; +import { ChatContextKeys } from '../../../common/chatContextKeys.js'; import { Action2 } from '../../../../../../platform/actions/common/actions.js'; import { IPromptsService } from '../../../common/promptSyntax/service/types.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 { IViewsService } from '../../../../../services/views/common/viewsService.js'; +import { IDialogService } from '../../../../../../platform/dialogs/common/dialogs.js'; import { ServicesAccessor } from '../../../../../../editor/browser/editorExtensions.js'; -import { ISelectPromptOptions, askToSelectPrompt } from './dialogs/askToSelectPrompt.js'; +import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; import { IQuickInputService } from '../../../../../../platform/quickinput/common/quickInput.js'; +import { ISelectPromptOptions, askToSelectPrompt } from './dialogs/askToSelectPrompt/askToSelectPrompt.js'; /** * Action ID for the `Attach Prompt` action. @@ -23,7 +28,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' | 'viewsService' + ISelectPromptOptions, 'resource' | 'widget' > { } /** @@ -35,6 +40,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, }); } @@ -43,10 +49,14 @@ export class AttachPromptAction extends Action2 { accessor: ServicesAccessor, options: IChatAttachPromptActionOptions, ): Promise { + const fileService = accessor.get(IFileService); + const chatService = accessor.get(IChatService); const labelService = accessor.get(ILabelService); const viewsService = accessor.get(IViewsService); const openerService = accessor.get(IOpenerService); + const dialogService = accessor.get(IDialogService); const promptsService = accessor.get(IPromptsService); + const commandService = accessor.get(ICommandService); const quickInputService = accessor.get(IQuickInputService); // find all prompt files in the user workspace @@ -55,9 +65,13 @@ export class AttachPromptAction extends Action2 { await askToSelectPrompt({ ...options, promptFiles, - labelService, + chatService, + fileService, viewsService, + labelService, + dialogService, openerService, + commandService, 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 deleted file mode 100644 index 79d7cecc..00000000 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts +++ /dev/null @@ -1,331 +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 { assert } from '../../../../../../../base/common/assert.js'; -import { IChatWidget, showChatView, showEditsView } from '../../../chat.js'; -import { IChatAttachPromptActionOptions } from '../chatAttachPromptAction.js'; -import { IPromptPath } from '../../../../common/promptSyntax/service/types.js'; -import { DisposableStore } from '../../../../../../../base/common/lifecycle.js'; -import { dirname, extUri } from '../../../../../../../base/common/resources.js'; -import { DOCUMENTATION_URL } from '../../../../common/promptSyntax/constants.js'; -import { isLinux, isWindows } from '../../../../../../../base/common/platform.js'; -import { ILabelService } from '../../../../../../../platform/label/common/label.js'; -import { IOpenerService } from '../../../../../../../platform/opener/common/opener.js'; -import { IViewsService } from '../../../../../../services/views/common/viewsService.js'; -import { assertDefined, WithUriValue } from '../../../../../../../base/common/types.js'; -import { getCleanPromptName } from '../../../../../../../platform/prompts/common/constants.js'; -import { IQuickInputService, IQuickPickItem } from '../../../../../../../platform/quickinput/common/quickInput.js'; - -/** - * Options for the {@link askToSelectPrompt} function. - */ -export interface ISelectPromptOptions { - /** - * Prompt resource `URI` to attach to the chat input, if any. - * If provided the resource will be pre-selected in the prompt picker dialog, - * otherwise the dialog will show the prompts list without any pre-selection. - */ - readonly resource?: URI; - - /** - * Target chat widget reference to attach the prompt to. If not provided, the command - * attaches the prompt to a `chat panel` widget by default (either the last focused, - * or a new one). If the `alt` (`option` on mac) key was pressed when the prompt is - * selected, the `edits` widget is used instead (likewise, either the last focused, - * or a new one). - */ - readonly widget?: IChatWidget; - - /** - * List of prompt files to show in the selection dialog. - */ - readonly promptFiles: readonly IPromptPath[]; - - readonly labelService: ILabelService; - readonly viewsService: IViewsService; - readonly openerService: IOpenerService; - readonly quickInputService: IQuickInputService; -} - -/** - * A special quick pick item that links to the documentation. - */ -const DOCS_OPTION: WithUriValue = { - type: 'item', - label: localize( - 'commands.prompts.use.select-dialog.docs-label', - 'Learn how to create reusable prompts', - ), - description: DOCUMENTATION_URL, - tooltip: DOCUMENTATION_URL, - value: URI.parse(DOCUMENTATION_URL), -}; - -/** - * Shows the prompt selection dialog to the user that allows to select a prompt file(s). - * - * If {@link ISelectPromptOptions.resource resource} is provided, the dialog will have - * the resource pre-selected in the prompts list. - */ -export const askToSelectPrompt = async ( - options: ISelectPromptOptions, -): Promise => { - const { promptFiles, resource, quickInputService, labelService } = options; - - const fileOptions = promptFiles.map((promptFile) => { - return createPickItem(promptFile, labelService); - }); - - /** - * Add a link to the documentation to the end of prompts list. - */ - fileOptions.push(DOCS_OPTION); - - // if a resource is provided, create an `activeItem` for it to pre-select - // it in the UI, and sort the list so the active item appears at the top - let activeItem: WithUriValue | undefined; - if (resource) { - activeItem = fileOptions.find((file) => { - return extUri.isEqual(file.value, resource); - }); - - // if no item for the `resource` was found, it means that the resource is not - // in the list of prompt files, so add a new item for it; this ensures that - // the currently active prompt file is always available in the selection dialog, - // even if it is not included in the prompts list otherwise(from location setting) - if (!activeItem) { - activeItem = createPickItem({ - uri: resource, - // "user" prompts are always registered in the prompts list, hence it - // should be safe to assume that `resource` is not "user" prompt here - type: 'local', - }, labelService); - fileOptions.push(activeItem); - } - - fileOptions.sort((file1, file2) => { - if (extUri.isEqual(file1.value, resource)) { - return -1; - } - - if (extUri.isEqual(file2.value, resource)) { - return 1; - } - - return 0; - }); - } - - /** - * If still no active item present, fall back to the first item in the list. - * This can happen only if command was invoked not from a focused prompt file - * (hence the `resource` is not provided in the options). - * - * Fixes the two main cases: - * - when no prompt files found it, pre-selects the documentation link - * - when there is only a single prompt file, pre-selects it - */ - if (!activeItem) { - activeItem = fileOptions[0]; - } - - // otherwise show the prompt file selection dialog - const { openerService } = options; - - const quickPick = quickInputService.createQuickPick>(); - quickPick.activeItems = activeItem ? [activeItem] : []; - quickPick.placeholder = createPlaceholderText(options); - quickPick.canAcceptInBackground = true; - quickPick.matchOnDescription = true; - quickPick.items = fileOptions; - - return await new Promise(resolve => { - const disposables = new DisposableStore(); - - let lastActiveWidget = options.widget; - disposables.add({ - dispose() { - quickPick.dispose(); - resolve(); - - // if something was attached, focus on the target chat input - lastActiveWidget?.focusInput(); - }, - }); - - disposables.add(quickPick.onDidAccept(async (event) => { - const { selectedItems } = quickPick; - const { alt, ctrlCmd } = quickPick.keyMods; - - // sanity check to confirm our expectations - assert( - selectedItems.length === 1, - `Only one item can be accepted, got '${selectedItems.length}'.`, - ); - - // whether user selected the docs link option - const docsSelected = (selectedItems[0] === DOCS_OPTION); - - // if `super` key was pressed, open the selected prompt file(s) - if (ctrlCmd || docsSelected) { - return await openFiles(selectedItems, openerService); - } - - // otherwise attach the selected prompt to a chat input - lastActiveWidget = await attachFiles(selectedItems, options, alt); - - // if user submitted their selection, close the dialog - if (!event.inBackground) { - disposables.dispose(); - } - })); - - disposables.add(quickPick.onDidHide( - disposables.dispose.bind(disposables), - )); - - quickPick.show(); - }); -}; - -/** - * Creates a quick pick item for a prompt. - */ -const createPickItem = ( - promptFile: IPromptPath, - labelService: ILabelService, -): WithUriValue => { - const { uri, type } = promptFile; - const fileWithoutExtension = getCleanPromptName(uri); - - // if a "user" prompt, don't show its filesystem path in - // the user interface, but do that for all the "local" ones - const description = (type === 'user') - ? localize( - 'user-prompt.capitalized', - 'User prompt', - ) - : labelService.getUriLabel(dirname(uri), { relative: true }); - - const tooltip = (type === 'user') - ? description - : uri.fsPath; - - return { - type: 'item', - label: fileWithoutExtension, - description, - tooltip, - value: uri, - id: uri.toString(), - }; -}; - -/** - * Creates a placeholder text to show in the prompt selection dialog. - */ -const createPlaceholderText = (options: ISelectPromptOptions): string => { - const { widget } = options; - - let text = localize( - 'commands.prompts.use.select-dialog.placeholder', - 'Select a prompt to use', - ); - - // if no widget reference is provided, add the note about `options` - // and `cmd` modifiers users can use to alter the command behavior - if (!widget) { - const altOptionkey = (isWindows || isLinux) ? 'Alt' : 'Option'; - - const altOptionModifierNote = localize( - 'commands.prompts.use.select-dialog.alt-modifier-note', - '{0}-key to use in Edits', - altOptionkey, - ); - - const cmdCtrlkey = (isWindows || isLinux) ? 'Ctrl' : 'Cmd'; - const superModifierNote = localize( - 'commands.prompts.use.select-dialog.super-modifier-note', - '{0}-key to open in editor', - cmdCtrlkey, - ); - - text += localize( - 'commands.prompts.use.select-dialog.modifier-notes', - ' (hold {0} or {1})', - altOptionModifierNote, - superModifierNote, - ); - } - - return text; -}; - -/** - * Opens provided files in the editor. - */ -const openFiles = async ( - files: readonly WithUriValue[], - openerService: IOpenerService, -) => { - for (const file of files) { - await openerService.open(file.value); - } -}; - -/** - * Attaches provided files to a chat input. - */ -const attachFiles = async ( - files: readonly WithUriValue[], - options: ISelectPromptOptions, - altOption: boolean, -): Promise => { - const widget = await getChatWidgetObject(options, altOption); - - for (const file of files) { - widget - .attachmentModel - .promptInstructions - .add(file.value); - } - - return widget; -}; - -/** - * Gets a chat widget based on the provided {@link IChatAttachPromptActionOptions.widget widget} - * reference. If no widget reference is provided, the function will reveal a `chat panel` by default - * (either a last focused, or a new one), but if the {@link altOption} is set to `true`, a `chat edits` - * panel will be revealed instead (likewise either a last focused, or a new one). - * - * @throws if failed to reveal a chat widget. - */ -const getChatWidgetObject = async ( - options: IChatAttachPromptActionOptions, - altOption: boolean, -): Promise => { - const { widget, viewsService } = options; - - // if no widget reference is present, the command was triggered from outside of - // an active chat input, so we reveal a chat widget window based on the `alt` - // key modifier state when a prompt was selected from the picker UI dialog - if (!widget) { - const widget = (altOption) - ? await showEditsView(viewsService) - : await showChatView(viewsService); - - assertDefined( - widget, - 'Revealed chat widget must be defined.', - ); - - return widget; - } - - return widget; -}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/askToSelectPrompt.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/askToSelectPrompt.ts new file mode 100644 index 00000000..e0e0db79 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/askToSelectPrompt.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. + *--------------------------------------------------------------------------------------------*/ + +import { DOCS_OPTION } from './constants.js'; +import { IChatWidget } from '../../../../chat.js'; +import { attachPrompts } from './utils/attachPrompts.js'; +import { handleButtonClick } from './utils/handleButtonClick.js'; +import { URI } from '../../../../../../../../base/common/uri.js'; +import { IChatService } from '../../../../../common/chatService.js'; +import { assert } from '../../../../../../../../base/common/assert.js'; +import { createPromptPickItem } from './utils/createPromptPickItem.js'; +import { createPlaceholderText } from './utils/createPlaceholderText.js'; +import { extUri } from '../../../../../../../../base/common/resources.js'; +import { WithUriValue } from '../../../../../../../../base/common/types.js'; +import { IPromptPath } from '../../../../../common/promptSyntax/service/types.js'; +import { DisposableStore } from '../../../../../../../../base/common/lifecycle.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 { IViewsService } from '../../../../../../../services/views/common/viewsService.js'; +import { IDialogService } from '../../../../../../../../platform/dialogs/common/dialogs.js'; +import { ICommandService } from '../../../../../../../../platform/commands/common/commands.js'; +import { IQuickInputService, IQuickPickItem } from '../../../../../../../../platform/quickinput/common/quickInput.js'; + +/** + * Options for the {@link askToSelectPrompt} function. + */ +export interface ISelectPromptOptions { + /** + * Prompt resource `URI` to attach to the chat input, if any. + * If provided the resource will be pre-selected in the prompt picker dialog, + * otherwise the dialog will show the prompts list without any pre-selection. + */ + readonly resource?: URI; + + /** + * Target chat widget reference to attach the prompt to. If not provided, the command + * attaches the prompt to a `chat panel` widget by default (either the last focused, + * or a new one). If the `alt` (`option` on mac) key was pressed when the prompt is + * selected, the `edits` widget is used instead (likewise, either the last focused, + * or a new one). + */ + readonly widget?: IChatWidget; + + /** + * List of prompt files to show in the selection dialog. + */ + readonly promptFiles: readonly IPromptPath[]; + + readonly fileService: IFileService; + readonly chatService: IChatService; + readonly labelService: ILabelService; + readonly viewsService: IViewsService; + readonly openerService: IOpenerService; + readonly dialogService: IDialogService; + readonly commandService: ICommandService; + readonly quickInputService: IQuickInputService; +} + +/** + * Shows the prompt selection dialog to the user that allows to select a prompt file(s). + * + * If {@link ISelectPromptOptions.resource resource} is provided, the dialog will have + * the resource pre-selected in the prompts list. + */ +export const askToSelectPrompt = async ( + options: ISelectPromptOptions, +): Promise => { + const { promptFiles, resource, quickInputService, labelService } = options; + + const fileOptions = promptFiles.map((promptFile) => { + return createPromptPickItem(promptFile, labelService); + }); + + /** + * Add a link to the documentation to the end of prompts list. + */ + fileOptions.push(DOCS_OPTION); + + // if a resource is provided, create an `activeItem` for it to pre-select + // it in the UI, and sort the list so the active item appears at the top + let activeItem: WithUriValue | undefined; + if (resource) { + activeItem = fileOptions.find((file) => { + return extUri.isEqual(file.value, resource); + }); + + // if no item for the `resource` was found, it means that the resource is not + // in the list of prompt files, so add a new item for it; this ensures that + // the currently active prompt file is always available in the selection dialog, + // even if it is not included in the prompts list otherwise(from location setting) + if (!activeItem) { + activeItem = createPromptPickItem({ + uri: resource, + // "user" prompts are always registered in the prompts list, hence it + // should be safe to assume that `resource` is not "user" prompt here + type: 'local', + }, labelService); + fileOptions.push(activeItem); + } + + fileOptions.sort((file1, file2) => { + if (extUri.isEqual(file1.value, resource)) { + return -1; + } + + if (extUri.isEqual(file2.value, resource)) { + return 1; + } + + return 0; + }); + } + + /** + * If still no active item present, fall back to the first item in the list. + * This can happen only if command was invoked not from a focused prompt file + * (hence the `resource` is not provided in the options). + * + * Fixes the two main cases: + * - when no prompt files found it, pre-selects the documentation link + * - when there is only a single prompt file, pre-selects it + */ + if (!activeItem) { + activeItem = fileOptions[0]; + } + + // otherwise show the prompt file selection dialog + const quickPick = quickInputService.createQuickPick>(); + quickPick.activeItems = activeItem ? [activeItem] : []; + quickPick.placeholder = createPlaceholderText(options); + quickPick.canAcceptInBackground = true; + quickPick.matchOnDescription = true; + quickPick.items = fileOptions; + + const { openerService } = options; + return await new Promise(resolve => { + const disposables = new DisposableStore(); + + let lastActiveWidget = options.widget; + + // then the dialog is hidden or disposed for other reason, + // dispose everything and resolve the main promise + disposables.add({ + dispose() { + quickPick.dispose(); + resolve(); + // if something was attached (lastActiveWidget is set), focus on the target chat input + lastActiveWidget?.focusInput(); + }, + }); + + // handle the prompt `accept` event + disposables.add(quickPick.onDidAccept(async (event) => { + const { selectedItems } = quickPick; + + // sanity check to confirm our expectations + assert( + selectedItems.length === 1, + `Only one item can be accepted, got '${selectedItems.length}'.`, + ); + + const selectedOption = selectedItems[0]; + + // whether user selected the docs link option + const docsSelected = (selectedOption === DOCS_OPTION); + + // if documentation item was selected, open its link in a browser + if (docsSelected) { + // note that opening a file in editor also hides(disposes) the dialog + await openerService.open(selectedOption.value); + return; + } + + // otherwise attach the selected prompt to a chat input + lastActiveWidget = await attachPrompts(selectedItems, options, quickPick.keyMods); + + // if user submitted their selection, close the dialog + if (!event.inBackground) { + disposables.dispose(); + } + })); + + // handle the `button click` event on a list item (edit, delete, etc.) + disposables.add(quickPick.onDidTriggerItemButton( + handleButtonClick.bind(null, { quickPick, ...options }), + )); + + // when the dialog is hidden, dispose everything + disposables.add(quickPick.onDidHide( + disposables.dispose.bind(disposables), + )); + + // finally, reveal the dialog + quickPick.show(); + }); +}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/constants.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/constants.ts new file mode 100644 index 00000000..a7b1de1c --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/constants.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Codicon } from '../../../../../../../../base/common/codicons.js'; +import { WithUriValue } from '../../../../../../../../base/common/types.js'; +import { ThemeIcon } from '../../../../../../../../base/common/themables.js'; +import { DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js'; +import { isLinux, isWindows } from '../../../../../../../../base/common/platform.js'; +import { IQuickInputButton, IQuickPickItem } from '../../../../../../../../platform/quickinput/common/quickInput.js'; + +/** + * Name of the `"super"` key based on the current OS. + */ +export const SUPER_KEY_NAME = (isWindows || isLinux) ? 'Ctrl' : '⌘'; + +/** + * Name of the `alt`/`options` key based on the current OS. + */ +export const ALT_KEY_NAME = (isWindows || isLinux) ? 'Alt' : '⌥'; + +/** + * A special quick pick item that links to the documentation. + */ +export const DOCS_OPTION: WithUriValue = Object.freeze({ + 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), +}); + +/** + * Button that opens a prompt file in the editor. + */ +export const EDIT_BUTTON: IQuickInputButton = Object.freeze({ + tooltip: localize( + 'commands.prompts.use.select-dialog.open-button.tooltip', + "edit ({0}-key + enter)", + SUPER_KEY_NAME, + ), + iconClass: ThemeIcon.asClassName(Codicon.edit), +}); + +/** + * Button that deletes a prompt file. + */ +export const DELETE_BUTTON: IQuickInputButton = Object.freeze({ + tooltip: localize('delete', "delete"), + iconClass: ThemeIcon.asClassName(Codicon.trash), +}); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/attachPrompts.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/attachPrompts.ts new file mode 100644 index 00000000..2a1df480 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/attachPrompts.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ISelectPromptOptions } from '../askToSelectPrompt.js'; +import { IChatWidget, showChatView, showEditsView } from '../../../../../chat.js'; +import { IChatAttachPromptActionOptions } from '../../../chatAttachPromptAction.js'; +import { assertDefined, WithUriValue } from '../../../../../../../../../base/common/types.js'; +import { ACTION_ID_NEW_CHAT, ACTION_ID_NEW_EDIT_SESSION } from '../../../../chatClearActions.js'; +import { IKeyMods, IQuickPickItem } from '../../../../../../../../../platform/quickinput/common/quickInput.js'; + +/** + * Attaches provided prompts to a chat input. + */ +export const attachPrompts = async ( + files: readonly WithUriValue[], + options: ISelectPromptOptions, + keyMods: IKeyMods, +): Promise => { + const widget = await getChatWidgetObject(options, keyMods); + + 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: ISelectPromptOptions, + keyMods: IKeyMods, +): Promise => { + const { widget } = options; + const { alt, ctrlCmd } = keyMods; + + // if `ctrl/cmd` key was pressed, create a new chat session + if (ctrlCmd) { + return await openNewChat(options, alt); + } + + // if no widget reference is present, the command was triggered from outside of + // an active chat input, so we reveal a chat widget window based on the `alt` + // key modifier state when a prompt was selected from the picker UI dialog + if (!widget) { + return await showExistingChat(options, alt); + } + + return widget; +}; + +/** + * Opens a new chat session based on the `unified chat view` mode + * enablement, and provided `edits` flag. + */ +const openNewChat = async ( + options: ISelectPromptOptions, + edits: boolean, +): Promise => { + const { commandService, chatService, viewsService } = options; + + // the `unified chat view` mode does not have a separate `edits` view + // therefore we always open a new default chat session in this mode + if (chatService.unifiedViewEnabled === true) { + await commandService.executeCommand(ACTION_ID_NEW_CHAT); + const widget = await showChatView(viewsService); + + assertDefined( + widget, + 'Chat widget must be defined.', + ); + + return widget; + } + + // in non-unified chat view mode, we can open either an `edits` view + // or an `ask` chat view based on the `edits` flag + (edits === true) + ? await commandService.executeCommand(ACTION_ID_NEW_EDIT_SESSION) + : await commandService.executeCommand(ACTION_ID_NEW_CHAT); + + const widget = (edits === true) + ? await showEditsView(viewsService) + : await showChatView(viewsService); + + assertDefined( + widget, + 'Chat widget must be defined.', + ); + + return widget; +}; + +/** + * Shows an existing chat view based on the `unified chat view` mode + * enablement, and provided `edits` flag. + */ +const showExistingChat = async ( + options: ISelectPromptOptions, + edits: boolean, +): Promise => { + const { chatService, viewsService } = options; + + // there is no "edits" view when in the unified view mode + const widget = (edits && (chatService.unifiedViewEnabled === false)) + ? await showEditsView(viewsService) + : await showChatView(viewsService); + + assertDefined( + widget, + 'Revealed chat widget must be defined.', + ); + + return widget; +}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPlaceholderText.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPlaceholderText.ts new file mode 100644 index 00000000..fcf0d6e5 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPlaceholderText.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ALT_KEY_NAME, SUPER_KEY_NAME } from '../constants.js'; +import { ISelectPromptOptions } from '../askToSelectPrompt.js'; + +/** + * Creates a placeholder text to show in the prompt selection dialog. + */ +export const createPlaceholderText = ( + options: ISelectPromptOptions, +): string => { + const { widget, chatService } = options; + + let text = localize( + 'commands.prompts.use.select-dialog.placeholder', + 'Select a prompt to use', + ); + + // if no widget reference is provided, add the note about `options` + // and `cmd` modifiers users can leverage to alter the command behavior + if (widget === undefined) { + const superModifierNote = localize( + 'commands.prompts.use.select-dialog.super-modifier-note', + '{0}-key to use in new chat', + SUPER_KEY_NAME, + ); + + const altOptionModifierNote = localize( + 'commands.prompts.use.select-dialog.alt-modifier-note', + ' or {0}-key to use in Copilot Edits', + ALT_KEY_NAME, + ); + + // "open in-edits" action does not really fit the unified chat view mode + const openInEditsNote = (chatService.unifiedViewEnabled === true) + ? '' + : altOptionModifierNote; + + text += localize( + 'commands.prompts.use.select-dialog.modifier-notes', + ' (hold {0}{1})', + superModifierNote, + openInEditsNote, + ); + } + + return text; +}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPromptPickItem.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPromptPickItem.ts new file mode 100644 index 00000000..5203dddc --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/createPromptPickItem.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { DELETE_BUTTON, EDIT_BUTTON } from '../constants.js'; +import { dirname } from '../../../../../../../../../base/common/resources.js'; +import { WithUriValue } from '../../../../../../../../../base/common/types.js'; +import { IPromptPath } from '../../../../../../common/promptSyntax/service/types.js'; +import { ILabelService } from '../../../../../../../../../platform/label/common/label.js'; +import { getCleanPromptName } from '../../../../../../../../../platform/prompts/common/constants.js'; +import { IQuickPickItem } from '../../../../../../../../../platform/quickinput/common/quickInput.js'; + +/** + * Creates a quick pick item for a prompt. + */ +export const createPromptPickItem = ( + 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 { + id: uri.toString(), + type: 'item', + label: fileWithoutExtension, + description, + tooltip, + value: uri, + buttons: [EDIT_BUTTON, DELETE_BUTTON], + }; +}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/handleButtonClick.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/handleButtonClick.ts new file mode 100644 index 00000000..51768640 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt/utils/handleButtonClick.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { DELETE_BUTTON, EDIT_BUTTON } from '../constants.js'; +import { assert } from '../../../../../../../../../base/common/assert.js'; +import { WithUriValue } from '../../../../../../../../../base/common/types.js'; +import { IFileService } from '../../../../../../../../../platform/files/common/files.js'; +import { IOpenerService } from '../../../../../../../../../platform/opener/common/opener.js'; +import { IDialogService } from '../../../../../../../../../platform/dialogs/common/dialogs.js'; +import { getCleanPromptName } from '../../../../../../../../../platform/prompts/common/constants.js'; +import { IQuickPick, IQuickPickItem, IQuickPickItemButtonEvent } from '../../../../../../../../../platform/quickinput/common/quickInput.js'; + +/** + * Options for the {@link handleButtonClick} function. + */ +interface IHandleButtonClickOptions { + quickPick: IQuickPick>; + fileService: IFileService; + openerService: IOpenerService; + dialogService: IDialogService; +} + +/** + * Handler for a button click event on a prompt file item in the prompt selection dialog. + */ +export async function handleButtonClick( + options: IHandleButtonClickOptions, + context: IQuickPickItemButtonEvent>, +) { + const { quickPick, openerService, fileService, dialogService } = options; + const { item, button } = context; + const { value } = item; + + // `edit` button was pressed, open the prompt file in editor + if (button === EDIT_BUTTON) { + return await openerService.open(value); + } + + // `delete` button was pressed, delete the prompt file + if (button === DELETE_BUTTON) { + // sanity check to confirm our expectations + assert( + (quickPick.activeItems.length < 2), + `Expected maximum one active item, got '${quickPick.activeItems.length}'.`, + ); + + const activeItem: WithUriValue | undefined = quickPick.activeItems[0]; + + // sanity checks - prompt file exists and is not a folder + const info = await fileService.stat(value); + assert( + info.isDirectory === false, + `'${value.fsPath}' points to a folder.`, + ); + + // don't close the main prompt selection dialog by the confirmation dialog + const previousIgnoreFocusOut = quickPick.ignoreFocusOut; + quickPick.ignoreFocusOut = true; + + const filename = getCleanPromptName(value); + const { confirmed } = await dialogService.confirm({ + message: localize( + 'commands.prompts.use.select-dialog.delete-prompt.confirm.message', + "Are you sure you want to delete '{0}'?", + filename, + ), + }); + + // restore the previous value of the `ignoreFocusOut` property + quickPick.ignoreFocusOut = previousIgnoreFocusOut; + + // if prompt deletion was not confirmed, nothing to do + if (!confirmed) { + return; + } + + // prompt deletion was confirmed so delete the prompt file + await fileService.del(value); + + // remove the deleted prompt from the selection dialog list + let removedIndex = -1; + quickPick.items = quickPick.items.filter((option, index) => { + if (option === item) { + removedIndex = index; + + return false; + } + + return true; + }); + + // if the deleted item was active item, find a new item to set as active + if (activeItem && (activeItem === item)) { + assert( + removedIndex >= 0, + 'Removed item index must be a valid index.', + ); + + // we set the previous item as new active, or the next item + // if removed prompt item was in the beginning of the list + const newActiveItemIndex = Math.max(removedIndex - 1, 0); + const newActiveItem: WithUriValue | undefined = quickPick.items[newActiveItemIndex]; + + quickPick.activeItems = newActiveItem ? [newActiveItem] : []; + } + + return; + } + + throw new Error(`Unknown button '${JSON.stringify(button)}'.`); +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index cdd73683..bb5b6194 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { raceTimeout } from '../../../../../base/common/async.js'; import { Codicon } from '../../../../../base/common/codicons.js'; +import { Event } from '../../../../../base/common/event.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; -import { localize, localize2 } from '../../../../../nls.js'; +import { localize2 } from '../../../../../nls.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; @@ -15,15 +17,16 @@ import { KeybindingWeight } from '../../../../../platform/keybinding/common/keyb import { ActiveEditorContext } from '../../../../common/contextkeys.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { isChatViewTitleActionContext } from '../../common/chatActions.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; -import { hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingSession, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; +import { hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingSession } from '../../common/chatEditingService.js'; +import { IChatService } from '../../common/chatService.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, handleCurrentEditingSession, IChatViewOpenOptions } from './chatActions.js'; import { clearChatEditor } from './chatClear.js'; export const ACTION_ID_NEW_CHAT = `workbench.action.chat.newChat`; @@ -78,7 +81,7 @@ export function registerNewChatActions() { title: localize2('chat.newChat.label', "New Chat"), category: CHAT_CATEGORY, icon: Codicon.plus, - precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession)), + precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), ChatContextKeyExprs.unifiedChatEnabled.negate()), f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -90,11 +93,16 @@ export function registerNewChatActions() { }, menu: [{ id: MenuId.ChatContext, - group: 'z_clear' + group: 'z_clear', + when: ContextKeyExpr.and( + ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + ChatContextKeys.inUnifiedChat.negate()), }, { id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', ChatViewId), + when: ContextKeyExpr.and( + ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + ChatContextKeys.inUnifiedChat.negate()), group: 'navigation', order: -1 }] @@ -125,7 +133,7 @@ export function registerNewChatActions() { constructor() { super({ id: ACTION_ID_NEW_EDIT_SESSION, - title: localize2('chat.newEdits.label', "New Edit Session"), + title: localize2('chat.newEdits.label', "New Chat"), category: CHAT_CATEGORY, icon: Codicon.plus, precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), @@ -136,7 +144,7 @@ export function registerNewChatActions() { }, { id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', EditsViewId), + when: ChatContextKeyExprs.inEditsOrUnified, group: 'navigation', order: -1 }], @@ -146,93 +154,37 @@ export function registerNewChatActions() { mac: { primary: KeyMod.WinCtrl | KeyCode.KeyL }, - when: ContextKeyExpr.and(ChatContextKeys.inChatSession, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)) + when: ChatContextKeys.inChatSession } }); } - /** - * - * @returns false if the user had edits and did not action the dialog to take action on them, true otherwise - */ - private async _handleCurrentEditingSession(currentEditingSession: IChatEditingSession, dialogService: IDialogService): Promise { - const currentEdits = currentEditingSession?.entries.get(); - const currentEditCount = currentEdits?.length; - if (currentEditingSession && currentEditCount) { - const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === WorkingSetEntryState.Modified); - if (undecidedEdits.length) { - const { result } = await dialogService.prompt({ - title: localize('chat.startEditing.confirmation.title', "Start new editing session?"), - message: localize('chat.startEditing.confirmation.pending.message.2', "Starting a new editing session will end your current session. Do you want to accept pending edits to {0} files?", undecidedEdits.length), - type: 'info', - cancelButton: true, - buttons: [ - { - label: localize('chat.startEditing.confirmation.acceptEdits', "Accept & Continue"), - run: async () => { - await currentEditingSession.accept(); - return true; - } - }, - { - label: localize('chat.startEditing.confirmation.discardEdits', "Discard & Continue"), - run: async () => { - await currentEditingSession.reject(); - return true; - } - } - ], - }); - - return Boolean(result); - } - } - - return true; - } - - async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]) { + async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, widget: IChatWidget, ...args: any[]) { 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 agentService = accessor.get(IChatAgentService); + const chatService = accessor.get(IChatService); - if (!(await this._handleCurrentEditingSession(editingSession, dialogService))) { + if (!(await handleCurrentEditingSession(editingSession, undefined, dialogService))) { return; } - const isChatViewTitleAction = isChatViewTitleActionContext(context); - - let widget: IChatWidget | undefined; - if (isChatViewTitleAction) { - // Is running in the Chat view title - widget = widgetService.getWidgetBySessionId(context.sessionId); - } else { - // Is running from f1 or keybinding - const chatView = await viewsService.openView(EditsViewId) as ChatViewPane; - widget = chatView.widget; - } - announceChatCleared(accessibilitySignalService); - if (!widget) { - return; - } - - await editingSession.stop(true); + await editingSession.stop(); widget.clear(); + await waitForChatSessionCleared(editingSession.chatSessionId, chatService); widget.attachmentModel.clear(); + widget.input.relatedFiles?.clear(); widget.focusInput(); if (!context) { return; } - if (!isChatViewTitleAction && typeof context.agentMode === 'boolean') { - agentService.toggleToolsAgentMode(context.agentMode); + if (typeof context.agentMode === 'boolean') { + widget.input.setChatMode(context.agentMode ? ChatMode.Agent : ChatMode.Edit); } if (context.inputValue) { @@ -245,7 +197,7 @@ export function registerNewChatActions() { } }); - registerAction2(class GlobalEditsDoneAction extends Action2 { + registerAction2(class GlobalEditsDoneAction extends EditingSessionAction { constructor() { super({ id: ChatDoneActionId, @@ -255,21 +207,19 @@ 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 }] }); } - async run(accessor: ServicesAccessor, ...args: any[]) { + override async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, widget: IChatWidget, ...args: any[]) { const context = args[0]; const accessibilitySignalService = accessor.get(IAccessibilitySignalService); - const widgetService = accessor.get(IChatWidgetService); if (isChatViewTitleActionContext(context)) { // Is running in the Chat view title announceChatCleared(accessibilitySignalService); - const widget = widgetService.getWidgetBySessionId(context.sessionId); if (widget) { widget.clear(); widget.attachmentModel.clear(); @@ -277,11 +227,6 @@ export function registerNewChatActions() { } } else { // Is running from f1 or keybinding - const viewsService = accessor.get(IViewsService); - - const chatView = await viewsService.openView(EditsViewId) as ChatViewPane; - const widget = chatView.widget; - announceChatCleared(accessibilitySignalService); widget.clear(); widget.attachmentModel.clear(); @@ -294,14 +239,14 @@ export function registerNewChatActions() { constructor() { super({ id: 'workbench.action.chat.undoEdit', - title: localize2('chat.undoEdit.label', "Undo Last Edit"), + title: localize2('chat.undoEdit.label', "Undo Last Request"), category: CHAT_CATEGORY, icon: Codicon.discard, precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanUndo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), f1: true, menu: [{ id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', EditsViewId), + when: ChatContextKeyExprs.inEditsOrUnified, group: 'navigation', order: -3 }] @@ -317,14 +262,14 @@ export function registerNewChatActions() { constructor() { super({ id: 'workbench.action.chat.redoEdit', - title: localize2('chat.redoEdit.label', "Redo Last Edit"), + title: localize2('chat.redoEdit.label', "Redo Last Request"), category: CHAT_CATEGORY, icon: Codicon.redo, precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), f1: true, menu: [{ id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', EditsViewId), + when: ChatContextKeyExprs.inEditsOrUnified, group: 'navigation', order: -2 }] @@ -344,20 +289,26 @@ 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 }, { id: MenuId.ChatTitleBarMenu, group: 'a_open', - order: 2 + order: 2, + when: ChatContextKeyExprs.unifiedChatEnabled.negate() }, { id: MenuId.ChatEditingEditorContent, when: ctxIsGlobalEditingSession, @@ -375,9 +326,30 @@ 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; + const chatView = await viewsService.openView(EditsViewId) + ?? await viewsService.openView(ChatViewId); + + if (!chatView?.widget) { + return; + } + + if (!chatView.widget.viewModel) { + await Event.toPromise( + Event.filter(chatView.widget.onDidChangeViewModel, () => !!chatView.widget.viewModel) + ); + } + + if (opts?.query) { + if (opts.isPartialQuery) { + chatView.widget.setInput(opts.query); + } else { + chatView.widget.acceptInput(opts.query); + } + } + chatView.widget.focusInput(); } }); @@ -386,3 +358,15 @@ export function registerNewChatActions() { function announceChatCleared(accessibilitySignalService: IAccessibilitySignalService): void { accessibilitySignalService.playSignal(AccessibilitySignal.clear); } + +export async function waitForChatSessionCleared(sessionId: string, chatService: IChatService): Promise { + if (!chatService.getSession(sessionId)) { + return; + } + + // The ChatWidget just signals cancellation to its host viewpane or editor. Clearing the session is now async, we need to wait for it to finish. + // This is expected to always happen. + await raceTimeout(Event.toPromise( + Event.filter(chatService.onDidDisposeSession, e => e.sessionId === sessionId), + ), 2000); +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index aa90fc63..7e6a74fb 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 4f18fc10..a627d291 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -29,6 +29,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com 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 { ILogService } from '../../../../../platform/log/common/log.js'; import { AnythingQuickAccessProviderRunOptions } from '../../../../../platform/quickinput/common/quickAccess.js'; import { IQuickInputService, IQuickPickItem, IQuickPickItemWithResource, IQuickPickSeparator, QuickPickItem } from '../../../../../platform/quickinput/common/quickInput.js'; import { ActiveEditorContext, TextCompareEditorActiveContext } from '../../../../common/contextkeys.js'; @@ -48,12 +49,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'; @@ -68,9 +70,12 @@ import { ATTACH_PROMPT_ACTION_ID, AttachPromptAction, IChatAttachPromptActionOpt export function registerChatContextActions() { registerAction2(AttachContextAction); registerAction2(AttachFileToChatAction); + registerAction2(AttachFolderToChatAction); registerAction2(AttachSelectionToChatAction); registerAction2(AttachFileToEditingSessionAction); + registerAction2(AttachFolderToEditingSessionAction); registerAction2(AttachSelectionToEditingSessionAction); + registerAction2(AttachSearchResultAction); } /** @@ -253,8 +258,8 @@ interface IReusablePromptQuickPickItem extends IQuickPickItem { 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]]; @@ -280,7 +285,7 @@ abstract class AttachFileAction extends Action2 { } } -class AttachFileToChatAction extends AttachFileAction { +class AttachFileToChatAction extends AttachResourceAction { static readonly ID = 'workbench.action.chat.attachFile'; @@ -290,18 +295,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), SearchContext.SearchResultHeaderFocused.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 showChatView(accessor.get(IViewsService)))?.focusInput(); @@ -312,6 +317,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'; @@ -322,12 +353,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 - }] }); } @@ -373,7 +398,7 @@ class AttachSelectionToChatAction extends Action2 { } } -class AttachFileToEditingSessionAction extends AttachFileAction { +class AttachFileToEditingSessionAction extends AttachResourceAction { static readonly ID = 'workbench.action.edits.attachFile'; @@ -383,18 +408,22 @@ 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(), + SearchContext.SearchResultHeaderFocused.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(); @@ -405,6 +434,87 @@ class AttachFileToEditingSessionAction extends AttachFileAction { } } +export class AttachSearchResultAction extends Action2 { + static readonly Name = 'searchResults'; + static readonly ID = 'workbench.action.chat.insertSearchResults'; + + constructor() { + super({ + id: AttachSearchResultAction.ID, + title: localize2('chat.insertSearchResults', 'Add Search Results to Chat'), + category: CHAT_CATEGORY, + f1: false, + menu: [{ + id: MenuId.SearchContext, + group: 'z_chat', + order: 3, + when: ContextKeyExpr.and( + ChatContextKeys.enabled, + SearchContext.SearchResultHeaderFocused), + }] + }); + } + async run(accessor: ServicesAccessor, ...args: any[]) { + const logService = accessor.get(ILogService); + const widget = (await showChatView(accessor.get(IViewsService))); + + if (!widget) { + logService.trace('InsertSearchResultAction: no chat view available'); + return; + } + + const editor = widget.inputEditor; + const originalRange = editor.getSelection() ?? editor.getModel()?.getFullModelRange().collapseToEnd(); + + if (!originalRange) { + logService.trace('InsertSearchResultAction: no selection'); + return; + } + + let insertText = `#${AttachSearchResultAction.Name}`; + const varRange = new Range(originalRange.startLineNumber, originalRange.startColumn, originalRange.endLineNumber, originalRange.startColumn + insertText.length); + // check character before the start of the range. If it's not a space, add a space + const model = editor.getModel(); + if (model && model.getValueInRange(new Range(originalRange.startLineNumber, originalRange.startColumn - 1, originalRange.startLineNumber, originalRange.startColumn)) !== ' ') { + insertText = ' ' + insertText; + } + const success = editor.executeEdits('chatInsertSearch', [{ range: varRange, text: insertText + ' ' }]); + if (!success) { + logService.trace(`InsertSearchResultAction: failed to insert "${insertText}"`); + return; + } + } +} + +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'; @@ -415,7 +525,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() + ) }); } @@ -440,38 +554,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); @@ -608,7 +710,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(); @@ -616,7 +724,7 @@ export class AttachContextAction extends Action2 { toAttach.push(convertBufferToScreenshotVariable(blob)); } } else if (isPromptInstructionsQuickPickItem(pick)) { - const options: IChatAttachPromptActionOptions = { widget, viewsService }; + const options: IChatAttachPromptActionOptions = { widget }; await commandService.executeCommand(ATTACH_PROMPT_ACTION_ID, options); } else { // Anything else is an attachment @@ -689,7 +797,7 @@ export class AttachContextAction extends Action2 { 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'))) { @@ -794,7 +902,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', @@ -973,11 +1081,16 @@ registerAction2(class AttachFilesAction extends AttachContextAction { 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 } @@ -991,11 +1104,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 4d9afa63..11420ee0 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts @@ -8,14 +8,16 @@ 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 { IChatService } from '../../common/chatService.js'; import { IChatWidgetService } from '../chat.js'; export function registerChatDeveloperActions() { registerAction2(LogChatInputHistoryAction); + registerAction2(LogChatIndexAction); } class LogChatInputHistoryAction extends Action2 { - static readonly ID = 'workbench.action.chat.logInputHistory'; constructor() { @@ -24,7 +26,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 }); } @@ -33,3 +36,22 @@ class LogChatInputHistoryAction extends Action2 { chatWidgetService.lastFocusedWidget?.logInputHistory(); } } + +class LogChatIndexAction extends Action2 { + static readonly ID = 'workbench.action.chat.logChatIndex'; + + constructor() { + super({ + id: LogChatIndexAction.ID, + title: localize2('workbench.action.chat.logChatIndex.label', "Log Chat Index"), + icon: Codicon.attach, + category: Categories.Developer, + f1: true + }); + } + + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + const chatService = accessor.get(IChatService); + chatService.logChatIndex(); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index d6d800ab..0260214b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -5,25 +5,28 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; -import { URI } from '../../../../../base/common/uri.js'; +import { ThemeIcon } from '../../../../../base/common/themables.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 { IConfigurationService } from '../../../../../platform/configuration/common/configuration.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 { IChatEditingService, IChatEditingSession, WorkingSetEntryState } from '../../common/chatEditingService.js'; -import { chatAgentLeader, extractAgentAndCommand } from '../../common/chatParserTypes.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; +import { WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { chatVariableLeader } from '../../common/chatParserTypes.js'; import { IChatService } from '../../common/chatService.js'; +import { ChatAgentLocation, ChatConfiguration, ChatMode, validateChatMode } from '../../common/constants.js'; +import { ILanguageModelToolsService } from '../../common/languageModelToolsService.js'; import { EditsViewId, IChatWidget, IChatWidgetService } from '../chat.js'; -import { discardAllEditsWithConfirmation, EditingSessionAction } from '../chatEditing/chatEditingActions.js'; +import { getEditingSessionContext } from '../chatEditing/chatEditingActions.js'; import { ChatViewPane } from '../chatViewPane.js'; -import { CHAT_CATEGORY } from './chatActions.js'; -import { ChatDoneActionId } from './chatClearActions.js'; +import { CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js'; +import { ACTION_ID_NEW_CHAT, ChatDoneActionId, waitForChatSessionCleared } from './chatClearActions.js'; export interface IVoiceChatExecuteActionContext { readonly disableTimeout?: boolean; @@ -56,7 +59,7 @@ export class ChatSubmitAction extends SubmitAction { // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), whenNotInProgressOrPaused, - ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask), ); super({ @@ -75,14 +78,15 @@ export class ChatSubmitAction extends SubmitAction { { id: MenuId.ChatExecuteSecondary, group: 'group_1', - order: 1 + order: 1, + when: ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask) }, { id: MenuId.ChatExecute, order: 4, when: ContextKeyExpr.and( whenNotInProgressOrPaused, - ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask), ), group: 'navigation', }, @@ -93,32 +97,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"), 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"), keybinding: { when: ContextKeyExpr.and( ChatContextKeys.inChatInput, - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + ChatContextKeyExprs.inEditsOrUnified), primary: KeyMod.CtrlCmd | KeyCode.Period, weight: KeybindingWeight.EditorContrib }, @@ -126,34 +129,59 @@ 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 configurationService = accessor.get(IConfigurationService); 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)) { - // User cancelled + const context = getEditingSessionContext(accessor, args); + if (!context?.chatWidget) { + return; + } + + const arg = args.at(0) as IToggleChatModeArgs | undefined; + const chatSession = context.chatWidget.viewModel?.model; + const requestCount = chatSession?.getRequests().length ?? 0; + const switchToMode = validateChatMode(arg?.mode) ?? this.getNextMode(context.chatWidget, requestCount, configurationService); + const needToClearEdits = (!chatService.unifiedViewEnabled || (!configurationService.getValue(ChatConfiguration.Edits2Enabled) && (context.chatWidget.input.currentMode === ChatMode.Edit || switchToMode === ChatMode.Edit))) && requestCount > 0; + + if (switchToMode === context.chatWidget.input.currentMode) { + return; + } + + if (needToClearEdits) { + // If not in unified view, or not using edits2 and switching into or out of edit mode, ask to discard the session + const phrase = localize('switchMode.confirmPhrase', "Switching chat modes will end your current edit session."); + if (!context.editingSession) { return; } - } else { - const chatSession = chatService.getSession(currentEditingSession.chatSessionId); - if (chatSession?.getRequests().length) { + + const currentEdits = context.editingSession.entries.get(); + const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === WorkingSetEntryState.Modified); + if (undecidedEdits.length > 0) { + if (!await handleCurrentEditingSession(context.editingSession, phrase, dialogService)) { + return; + } + } else { 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 end your current edit session. Would you like to continue?"), primaryButton: localize('agent.newSession.confirm', "Yes"), type: 'info' }); @@ -163,10 +191,26 @@ export class ToggleAgentModeAction extends EditingSessionAction { } } - const arg = args[0] as IToggleAgentModeArgs | undefined; - agentService.toggleToolsAgentMode(typeof arg?.agentMode === 'boolean' ? arg.agentMode : undefined); + context.chatWidget.input.setChatMode(switchToMode); - await commandService.executeCommand(ChatDoneActionId); + if (needToClearEdits) { + const clearAction = chatService.unifiedViewEnabled ? ACTION_ID_NEW_CHAT : ChatDoneActionId; + await commandService.executeCommand(clearAction); + } + } + + private getNextMode(chatWidget: IChatWidget, requestCount: number, configurationService: IConfigurationService): ChatMode { + const modes = [ChatMode.Agent]; + if (configurationService.getValue(ChatConfiguration.Edits2Enabled) || requestCount === 0) { + modes.push(ChatMode.Edit); + } + if (chatWidget.location === ChatAgentLocation.Panel) { + modes.push(ChatMode.Ask); + } + + const modeIndex = modes.indexOf(chatWidget.input.currentMode); + const newMode = modes[(modeIndex + 1) % modes.length]; + return newMode; } } @@ -192,8 +236,8 @@ 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', @@ -210,6 +254,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'; @@ -219,7 +304,7 @@ export class ChatEditingSessionSubmitAction extends SubmitAction { // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), whenNotInProgressOrPaused, - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + ChatContextKeys.chatMode.notEqualsTo(ChatMode.Ask), ); super({ @@ -238,7 +323,7 @@ export class ChatEditingSessionSubmitAction extends SubmitAction { { id: MenuId.ChatExecuteSecondary, group: 'group_1', - when: ContextKeyExpr.and(whenNotInProgressOrPaused, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + when: ContextKeyExpr.and(whenNotInProgressOrPaused, ChatContextKeys.chatMode.notEqualsTo(ChatMode.Ask),), order: 1 }, { @@ -249,8 +334,7 @@ export class ChatEditingSessionSubmitAction extends SubmitAction { ContextKeyExpr.and(ChatContextKeys.isRequestPaused, ChatContextKeys.inputHasText), ChatContextKeys.requestInProgress.negate(), ), - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), - ), + ChatContextKeys.chatMode.notEqualsTo(ChatMode.Ask),), group: 'navigation', }, ] @@ -267,10 +351,7 @@ class SubmitWithoutDispatchingAction extends Action2 { // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), whenNotInProgressOrPaused, - ContextKeyExpr.and(ContextKeyExpr.or( - ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), - ChatContextKeys.location.isEqualTo(ChatAgentLocation.Editor), - )), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask), ); super({ @@ -288,8 +369,9 @@ class SubmitWithoutDispatchingAction extends Action2 { { id: MenuId.ChatExecuteSecondary, group: 'group_1', - order: 2 - } // need 'when'? + order: 2, + when: ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask), + } ] }); } @@ -303,47 +385,26 @@ 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'; +export class ChatSubmitWithCodebaseAction extends Action2 { + static readonly ID = 'workbench.action.chat.submitWithCodebase'; 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(), whenNotInProgressOrPaused, ); super({ - id: ChatSubmitSecondaryAgentAction.ID, - title: localize2({ key: 'actions.chat.submitSecondaryAgent', comment: ['Send input from the chat input box to the secondary agent'] }, "Submit to Secondary Agent"), + id: ChatSubmitWithCodebaseAction.ID, + title: localize2('actions.chat.submitWithCodebase', "Send with {0}", `${chatVariableLeader}codebase`), precondition, menu: { id: MenuId.ChatExecuteSecondary, group: 'group_1', order: 3, - when: ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Panel) + when: ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Panel), }, keybinding: { when: ChatContextKeys.inChatInput, @@ -355,11 +416,6 @@ export class ChatSubmitSecondaryAgentAction extends Action2 { run(accessor: ServicesAccessor, ...args: any[]) { const context: IChatExecuteActionContext | undefined = args[0]; - const agentService = accessor.get(IChatAgentService); - const secondaryAgent = agentService.getSecondaryAgent(); - if (!secondaryAgent) { - return; - } const widgetService = accessor.get(IChatWidgetService); const widget = context?.widget ?? widgetService.lastFocusedWidget; @@ -367,16 +423,25 @@ export class ChatSubmitSecondaryAgentAction extends Action2 { return; } - if (extractAgentAndCommand(widget.parsedInput).agentPart) { - widget.acceptInput(); - } else { - widget.lastSelectedAgent = secondaryAgent; - widget.acceptInputWithPrefix(`${chatAgentLeader}${secondaryAgent.name}`); + const languageModelToolsService = accessor.get(ILanguageModelToolsService); + const codebaseTool = languageModelToolsService.getToolByName('codebase'); + if (!codebaseTool) { + return; } + + widget.input.attachmentModel.addContext({ + id: codebaseTool.id, + name: codebaseTool.displayName ?? '', + fullName: codebaseTool.displayName ?? '', + value: undefined, + icon: ThemeIcon.isThemeIcon(codebaseTool.icon) ? codebaseTool.icon : undefined, + isTool: true + }); + widget.acceptInput(); } } -class SendToChatEditingAction extends EditingSessionAction { +class SendToChatEditingAction extends Action2 { constructor() { const precondition = ContextKeyExpr.and( // if the input has prompt instructions attached, allow submitting requests even @@ -384,6 +449,7 @@ class SendToChatEditingAction extends EditingSessionAction { ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), ChatContextKeys.inputHasAgent.negate(), whenNotInProgressOrPaused, + ChatContextKeyExprs.inNonUnifiedPanel ); super({ @@ -400,7 +466,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: { @@ -410,20 +477,29 @@ 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 { widget: editingWidget } = await viewsService.openView(EditsViewId) as ChatViewPane; + if (!editingWidget.viewModel?.sessionId) { + return; + } + const currentEditingSession = editingWidget.viewModel.model.editingSession; + if (!currentEditingSession) { + return; + } const currentEditCount = currentEditingSession?.entries.get().length; if (currentEditCount) { @@ -440,23 +516,12 @@ class SendToChatEditingAction extends EditingSessionAction { return; } - await currentEditingSession?.stop(true); + await currentEditingSession.stop(true); + editingWidget.clear(); } - const { widget: editingWidget } = await viewsService.openView(EditsViewId) as ChatViewPane; - if (!editingWidget.viewModel?.sessionId) { - return; - } - const chatEditingSession = chatEditingService.getEditingSession(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()); @@ -500,12 +565,16 @@ class SendToNewChatAction extends Action2 { const context: IChatExecuteActionContext | undefined = args[0]; const widgetService = accessor.get(IChatWidgetService); + const chatService = accessor.get(IChatService); const widget = context?.widget ?? widgetService.lastFocusedWidget; if (!widget) { return; } widget.clear(); + if (widget.viewModel) { + await waitForChatSessionCleared(widget.viewModel.sessionId, chatService); + } widget.acceptInput(context?.inputValue); } } @@ -556,8 +625,9 @@ export function registerChatExecuteActions() { registerAction2(SubmitWithoutDispatchingAction); registerAction2(CancelAction); registerAction2(SendToNewChatAction); - registerAction2(ChatSubmitSecondaryAgentAction); + registerAction2(ChatSubmitWithCodebaseAction); registerAction2(SendToChatEditingAction); - registerAction2(ToggleAgentModeAction); + registerAction2(ToggleChatModeAction); registerAction2(ToggleRequestPausedAction); + registerAction2(SwitchToNextModelAction); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts b/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts index 45ee1c6f..b7a0a661 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts @@ -74,7 +74,10 @@ export class ChatGettingStartedContribution extends Disposable implements IWorkb // Open Copilot view showCopilotView(this.viewsService, this.layoutService); - ensureSideBarChatViewSize(this.viewDescriptorService, this.layoutService, this.viewsService); + const setupFromDialog = this.configurationService.getValue('chat.setupFromDialog'); + if (!setupFromDialog) { + ensureSideBarChatViewSize(this.viewDescriptorService, this.layoutService, this.viewsService); + } // Only do this once this.storageService.store(ChatGettingStartedContribution.hideWelcomeView, true, StorageScope.APPLICATION, StorageTarget.MACHINE); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts index e732c01e..c0c33942 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts @@ -8,17 +8,19 @@ import { Action2, MenuId, registerAction2 } from '../../../../../platform/action import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { ActiveEditorContext } from '../../../../common/contextkeys.js'; -import { CHAT_CATEGORY } from './chatActions.js'; -import { ChatViewId, IChatWidgetService } from '../chat.js'; -import { ChatEditor, IChatEditorOptions } from '../chatEditor.js'; -import { ChatEditorInput } from '../chatEditorInput.js'; -import { ChatViewPane } from '../chatViewPane.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; 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 { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { IChatService } from '../../common/chatService.js'; +import { ChatAgentLocation } from '../../common/constants.js'; +import { ChatViewId, IChatWidgetService } from '../chat.js'; +import { ChatEditor, IChatEditorOptions } from '../chatEditor.js'; +import { ChatEditorInput } from '../chatEditorInput.js'; +import { ChatViewPane } from '../chatViewPane.js'; +import { CHAT_CATEGORY } from './chatActions.js'; +import { waitForChatSessionCleared } from './chatClearActions.js'; enum MoveToNewLocation { Editor = 'Editor', @@ -97,22 +99,20 @@ export function registerMoveActions() { async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNewLocation, _sessionId?: string) { const widgetService = accessor.get(IChatWidgetService); const editorService = accessor.get(IEditorService); + const chatService = accessor.get(IChatService); 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(); + await waitForChatSessionCleared(sessionId, chatService); const options: IChatEditorOptions = { target: { sessionId }, pinned: true, viewState: viewState }; await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options }, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP); @@ -129,7 +129,7 @@ async function moveToSidebar(accessor: ServicesAccessor): Promise { if (chatEditor instanceof ChatEditor && chatEditorInput instanceof ChatEditorInput && chatEditorInput.sessionId) { await editorService.closeEditor({ editor: chatEditor.input, groupId: editorGroupService.activeGroup.id }); view = await viewsService.openView(ChatViewId) as ChatViewPane; - view.loadSession(chatEditorInput.sessionId, chatEditor.getViewState()); + await view.loadSession(chatEditorInput.sessionId, chatEditor.getViewState()); } else { view = await viewsService.openView(ChatViewId) as ChatViewPane; } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index 7d59054a..f5f9343b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -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 c4595b8c..71bfc203 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -29,17 +29,19 @@ 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'; export const MarkUnhelpfulActionId = 'workbench.action.chat.markUnhelpful'; +const enableFeedbackConfig = 'config.telemetry.feedback.enabled'; export function registerChatTitleActions() { registerAction2(class MarkHelpfulAction extends Action2 { @@ -55,12 +57,12 @@ export function registerChatTitleActions() { id: MenuId.ChatMessageFooter, group: 'navigation', order: 1, - when: ContextKeyExpr.and(ChatContextKeys.isResponse, ChatContextKeys.responseHasError.negate()) + when: ContextKeyExpr.and(ChatContextKeys.isResponse, ChatContextKeys.responseHasError.negate(), ContextKeyExpr.has(enableFeedbackConfig)) }, { id: MENU_INLINE_CHAT_WIDGET_SECONDARY, group: 'navigation', order: 1, - when: ContextKeyExpr.and(ChatContextKeys.isResponse, ChatContextKeys.responseHasError.negate()) + when: ContextKeyExpr.and(ChatContextKeys.isResponse, ChatContextKeys.responseHasError.negate(), ContextKeyExpr.has(enableFeedbackConfig)) }] }); } @@ -102,12 +104,12 @@ export function registerChatTitleActions() { id: MenuId.ChatMessageFooter, group: 'navigation', order: 2, - when: ContextKeyExpr.and(ChatContextKeys.isResponse) + when: ContextKeyExpr.and(ChatContextKeys.isResponse, ContextKeyExpr.has(enableFeedbackConfig)) }, { id: MENU_INLINE_CHAT_WIDGET_SECONDARY, group: 'navigation', order: 2, - when: ContextKeyExpr.and(ChatContextKeys.isResponse, ChatContextKeys.responseHasError.negate()) + when: ContextKeyExpr.and(ChatContextKeys.isResponse, ChatContextKeys.responseHasError.negate(), ContextKeyExpr.has(enableFeedbackConfig)) }] }); } @@ -154,12 +156,12 @@ export function registerChatTitleActions() { id: MenuId.ChatMessageFooter, group: 'navigation', order: 3, - when: ContextKeyExpr.and(ChatContextKeys.responseSupportsIssueReporting, ChatContextKeys.isResponse) + when: ContextKeyExpr.and(ChatContextKeys.responseSupportsIssueReporting, ChatContextKeys.isResponse, ContextKeyExpr.has(enableFeedbackConfig)) }, { id: MENU_INLINE_CHAT_WIDGET_SECONDARY, group: 'navigation', order: 3, - when: ContextKeyExpr.and(ChatContextKeys.responseSupportsIssueReporting, ChatContextKeys.isResponse) + when: ContextKeyExpr.and(ChatContextKeys.responseSupportsIssueReporting, ChatContextKeys.isResponse, ContextKeyExpr.has(enableFeedbackConfig)) }] }); } @@ -229,11 +231,12 @@ 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); - const currentEditingSession = chatEditingService.getEditingSession(chatModel.sessionId); + const currentEditingSession = widget?.viewModel?.model.editingSession; if (!currentEditingSession) { return; } @@ -268,8 +271,14 @@ 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 }); + const languageModelId = widget?.input.currentLanguageModel; + const userSelectedTools = widget?.input.currentMode === ChatMode.Agent ? widget.input.selectedToolsModel.tools.get().map(tool => tool.id) : undefined; + chatService.resendRequest(request!, { + userSelectedModelId: languageModelId, + userSelectedTools, + attempt: (request?.attempt ?? -1) + 1, + mode: widget?.input.currentMode, + }); } }); @@ -354,20 +363,20 @@ export function registerChatTitleActions() { f1: false, category: CHAT_CATEGORY, icon: Codicon.x, - precondition: ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + precondition: ContextKeyExpr.and(ChatContextKeys.chatMode.isEqualTo(ChatMode.Ask), ChatContextKeyExprs.unifiedChatEnabled.negate()), 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.Ask), 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.Ask), ChatContextKeys.isRequest, ChatContextKeyExprs.unifiedChatEnabled.negate()) } }); } @@ -408,12 +417,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 +501,7 @@ export function registerChatTitleActions() { await chatService.adoptRequest(editingSession.chatSessionId, request); this._collectWorkingSetAdditions(request, workingSetAdditions); } - workingSetAdditions.forEach(uri => editingSession.addFileToWorkingSet(uri)); + await Promise.all(Array.from(workingSetAdditions, async 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 56d1b4e5..ce4d1e6a 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts @@ -3,18 +3,47 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { assertNever } from '../../../../../base/common/assert.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; +import { diffSets } from '../../../../../base/common/collections.js'; +import { Event } from '../../../../../base/common/event.js'; +import { Iterable } from '../../../../../base/common/iterator.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; +import { DisposableStore } from '../../../../../base/common/lifecycle.js'; +import { ThemeIcon } from '../../../../../base/common/themables.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 { ICommandService } from '../../../../../platform/commands/common/commands.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 { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; +import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; +import { IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js'; +import { AddConfigurationAction } from '../../../mcp/browser/mcpCommands.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, ToolDataSource } from '../../common/languageModelToolsService.js'; +import { IChatWidget, IChatWidgetService } from '../chat.js'; import { CHAT_CATEGORY } from './chatActions.js'; + +type SelectedToolData = { + enabled: number; + total: number; +}; +type SelectedToolClassification = { + owner: 'connor4312'; + comment: 'Details the capabilities of the MCP server'; + enabled: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of enabled chat tools' }; + total: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of total chat tools' }; +}; + export const AcceptToolConfirmationActionId = 'workbench.action.chat.acceptTool'; class AcceptToolConfirmation extends Action2 { @@ -51,6 +80,296 @@ 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: ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent), + menu: { + when: 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); + const telemetryService = accessor.get(ITelemetryService); + const commandService = accessor.get(ICommandService); + const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); + + 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[]; source: ToolDataSource }; + type ToolPick = IQuickPickItem & { picked: boolean; tool: IToolData; parent: BucketPick }; + type AddPick = IQuickPickItem & { pickable: false; run: () => void }; + type MyPick = ToolPick | BucketPick | AddPick; + + const addMcpPick: AddPick = { type: 'item', label: localize('addServer', "Add MCP Server..."), iconClass: ThemeIcon.asClassName(Codicon.add), pickable: false, run: () => commandService.executeCommand(AddConfigurationAction.ID) }; + const addExpPick: AddPick = { type: 'item', label: localize('addExtension', "Install Extension..."), iconClass: ThemeIcon.asClassName(Codicon.add), pickable: false, run: () => extensionWorkbenchService.openSearch('@tag:language-model-tools') }; + const addPick: AddPick = { + type: 'item', label: localize('addAny', "Add More Tools..."), iconClass: ThemeIcon.asClassName(Codicon.add), pickable: false, run: async () => { + const pick = await quickPickService.pick( + [addMcpPick, addExpPick], + { + canPickMany: false, + title: localize('noTools', "Add tools to chat") + } + ); + pick?.run(); + } + }; + + const defaultBucket: BucketPick = { + type: 'item', + children: [], + label: localize('defaultBucketLabel', "Other Tools"), + source: { type: 'internal' }, + 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.supportsToolPicker) { + continue; + } + + let bucket: BucketPick; + + if (tool.source.type === 'mcp') { + const mcpServer = mcpServerByTool.get(tool.id); + if (!mcpServer) { + continue; + } + bucket = toolBuckets.get(mcpServer.definition.id) ?? { + type: 'item', + label: localize('mcplabel', "MCP Server: {0}", mcpServer?.definition.label), + status: localize('mcpstatus', "From {0} ({1})", mcpServer.collection.label, McpConnectionState.toString(mcpServer.connectionState.get())), + ordinal: BucketOrdinal.Mcp, + source: tool.source, + picked: false, + children: [] + }; + toolBuckets.set(mcpServer.definition.id, bucket); + } else if (tool.source.type === 'extension') { + const extensionId = tool.source.extensionId; + const ext = extensionService.extensions.find(value => ExtensionIdentifier.equals(value.identifier, extensionId)); + if (!ext) { + continue; + } + + bucket = toolBuckets.get(ExtensionIdentifier.toKey(extensionId)) ?? { + type: 'item', + label: ext.displayName ?? ext.name, + ordinal: BucketOrdinal.Extension, + picked: false, + source: tool.source, + children: [] + }; + toolBuckets.set(ExtensionIdentifier.toKey(ext.identifier), bucket); + } else if (tool.source.type === 'internal') { + bucket = defaultBucket; + } else { + assertNever(tool.source); + } + + const picked = nowSelectedTools.has(tool); + + bucket.children.push({ + tool, + parent: bucket, + type: 'item', + label: tool.displayName, + description: tool.userDescription, + picked, + indented: true, + }); + + 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); + } + function isAddPick(obj: any): obj is AddPick { + return Boolean((obj as AddPick).run); + } + + const store = new DisposableStore(); + + 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); + } + + const picker = store.add(quickPickService.createQuickPick({ useSeparators: true })); + picker.placeholder = localize('placeholder', "Select tools that are available to chat"); + picker.canSelectMany = true; + picker.keepScrollPosition = true; + picker.matchOnDescription = true; + + if (picks.length === 0) { + picker.placeholder = localize('noTools', "Add tools to chat"); + picker.canSelectMany = false; + picks.push( + addMcpPick, + addExpPick, + ); + } else { + picks.push( + { type: 'separator' }, + addPick, + ); + } + + + 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.selectedItems = items; + + const disableBuckets: ToolDataSource[] = []; + const disableTools: IToolData[] = []; + for (const item of picks) { + if (item.type === 'item' && !item.picked) { + if (isBucketPick(item)) { + disableBuckets.push(item.source); + } else if (isToolPick(item) && item.parent.picked) { + disableTools.push(item.tool); + } + } + } + + widget.input.selectedToolsModel.update(disableBuckets, disableTools); + } finally { + ignoreEvent = false; + } + }; + + _update(); + picker.items = picks; + picker.show(); + + store.add(picker.onDidChangeSelection(selectedPicks => { + if (ignoreEvent) { + return; + } + + const addPick = selectedPicks.find(isAddPick); + if (addPick) { + addPick.run(); + picker.hide(); + 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(); + })); + + store.add(picker.onDidAccept(() => { + picker.activeItems.find(isAddPick)?.run(); + })); + + await Promise.race([Event.toPromise(Event.any(picker.onDidAccept, picker.onDidHide))]); + telemetryService.publicLog2('chat/selectedTools', { + enabled: widget.input.selectedToolsModel.tools.get().length, + total: Iterable.length(toolsService.getTools()), + }); + 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 00000000..52bc3680 --- /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 4b07d7ba..ac4fcefc 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); diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 0e38b614..db1a22b9 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,39 +18,47 @@ 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 { DEFAULT_SOURCE_FOLDER as PROMPT_FILES_DEFAULT_SOURCE_FOLDER, 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 { allDiscoverySources, discoverySourceLabel, mcpConfigurationSection, mcpDiscoverySection, mcpEnabledSection, 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, ChatConfiguration, ChatMode } 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'; -import { EditsChatAccessibilityHelp, PanelChatAccessibilityHelp, QuickChatAccessibilityHelp } from './actions/chatAccessibilityHelp.js'; +import { AgentChatAccessibilityHelp, EditsChatAccessibilityHelp, PanelChatAccessibilityHelp, QuickChatAccessibilityHelp } from './actions/chatAccessibilityHelp.js'; import { CopilotTitleBarMenuRendering, registerChatActions } from './actions/chatActions.js'; import { ACTION_ID_NEW_CHAT, registerNewChatActions } from './actions/chatClearActions.js'; import { CodeBlockActionRendering, registerChatCodeBlockActions, registerChatCodeCompareBlockActions } from './actions/chatCodeblockActions.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,15 +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'; -import product from '../../../../platform/product/common/product.js'; -import { ChatEditingNotebookFileSystemProviderContrib } from './chatEditing/chatEditingNotebookFileSystemProvider.js'; // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -193,13 +199,96 @@ configurationRegistry.registerConfiguration({ description: nls.localize('chat.renderRelatedFiles', "Controls whether related files should be rendered in the chat input."), default: false }, - 'chat.experimental.statusIndicator.enabled': { // TODO@bpasero remove this eventually + 'chat.setupFromDialog': { // 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."), + 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'] }, - [PromptsConfig.CONFIG_KEY]: { + 'chat.focusWindowOnConfirmation': { + type: 'boolean', + description: nls.localize('chat.focusWindowOnConfirmation', "Controls whether the Copilot window should be focused when a confirmation is needed."), + default: true, + }, + 'chat.tools.autoApprove': { + default: false, + description: nls.localize('chat.tools.autoApprove', "Controls whether tool use should be automatically approved ('YOLO mode')."), + type: 'boolean', + tags: ['experimental'], + policy: { + name: 'ChatToolsAutoApprove', + minimumVersion: '1.99', + previewFeature: true, + defaultValue: false + } + }, + [mcpEnabledSection]: { + type: 'boolean', + description: nls.localize('chat.mcp.enabled', "Enables integration with Model Context Protocol servers to provide additional tools and functionality."), + default: true, + tags: ['preview'], + policy: { + name: 'ChatMCP', + minimumVersion: '1.99', + previewFeature: true, + defaultValue: false + } + }, + [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.unifiedChatView', "Enables the unified view with Ask, Edit, and Agent modes in one view."), + default: true, + tags: ['preview'], + }, + [ChatConfiguration.UseFileStorage]: { + type: 'boolean', + description: nls.localize('chat.useFileStorage', "Enables storing chat sessions on disk instead of in the storage service. Enabling this does a one-time per-workspace migration of existing sessions to the new format."), + default: true, + tags: ['experimental'], + }, + [ChatConfiguration.Edits2Enabled]: { + type: 'boolean', + description: nls.localize('chat.edits2Enabled', "Enable the new Edits mode that is based on tool-calling. When this is enabled, models that don't support tool-calling are unavailable for Edits mode."), + default: true, + tags: ['onExp'], + }, + [ChatConfiguration.ExtensionToolsEnabled]: { + type: 'boolean', + description: nls.localize('chat.extensionToolsEnabled', "Enable using tools contributed by third-party extensions in Copilot Chat agent mode."), + default: true, + policy: { + name: 'ChatAgentExtensionTools', + minimumVersion: '1.99', + description: nls.localize('chat.extensionToolsPolicy', "Enable using tools contributed by third-party extensions in Copilot Chat agent mode."), + previewFeature: true, + defaultValue: false + } + }, + [mcpDiscoverySection]: { + oneOf: [ + { type: 'boolean' }, + { + type: 'object', + default: Object.fromEntries(allDiscoverySources.map(k => [k, true])), + properties: Object.fromEntries(allDiscoverySources.map(k => [ + k, + { type: 'boolean', description: nls.localize('mcp.discovery.source', "Enables discovery of {0} servers", discoverySourceLabel[k]) } + ])), + } + ], + default: true, + markdownDescription: nls.localize('mpc.discovery.enabled', "Configures discovery of Model Context Protocol servers on the machine. It may be set to `true` or `false` to disable or enable all sources, and an mapping sources you wish to enable."), + }, + [PromptsConfig.KEY]: { type: 'boolean', title: nls.localize( 'chat.reusablePrompts.config.enabled.title', @@ -215,8 +304,15 @@ configurationRegistry.registerConfiguration({ restricted: true, disallowConfigurationDefault: true, tags: ['experimental', 'prompts', 'reusable prompts', 'prompt snippets', 'instructions'], + policy: { + name: 'ChatPromptFiles', + minimumVersion: '1.99', + description: nls.localize('chat.promptFiles.policy', "Enables reusable prompt files in Chat, Edits, and Inline Chat sessions."), + previewFeature: true, + defaultValue: false + } }, - [PromptsConfig.LOCATIONS_CONFIG_KEY]: { + [PromptsConfig.LOCATIONS_KEY]: { type: 'object', title: nls.localize( 'chat.reusablePrompts.config.locations.title', @@ -229,20 +325,18 @@ configurationRegistry.registerConfiguration({ DOCUMENTATION_URL, ), default: { - [PromptsConfig.DEFAULT_SOURCE_FOLDER]: true, + [PROMPT_FILES_DEFAULT_SOURCE_FOLDER]: true, }, - required: [PromptsConfig.DEFAULT_SOURCE_FOLDER], additionalProperties: { type: 'boolean' }, unevaluatedProperties: { type: 'boolean' }, restricted: true, - disallowConfigurationDefault: true, tags: ['experimental', 'prompts', 'reusable prompts', 'prompt snippets', 'instructions'], examples: [ { - [PromptsConfig.DEFAULT_SOURCE_FOLDER]: true, + [PROMPT_FILES_DEFAULT_SOURCE_FOLDER]: true, }, { - [PromptsConfig.DEFAULT_SOURCE_FOLDER]: true, + [PROMPT_FILES_DEFAULT_SOURCE_FOLDER]: true, '/Users/vscode/repos/prompts': true, }, ], @@ -299,52 +393,64 @@ 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(); + if (enabled || typeof enabled !== 'boolean') { + // If enabled, or experiments not available, fall back to registering the setting + this.registerEnablementSetting(); expDisabledKey.set(false); - } else if (enabled === false) { + } else { + // If disabled, deregister the setting this.deregisterSetting(); expDisabledKey.set(true); } }); + + this.registerMaxRequestsSetting(); } - private registerSetting() { + private registerEnablementSetting() { if (this.registeredNode) { return; } - this.registeredNode = { + this.registeredNode = configurationRegistry.registerConfiguration({ id: 'chatAgent', title: nls.localize('interactiveSessionConfigurationTitle', "Chat"), type: 'object', properties: { - 'chat.agent.enabled': { + [ChatConfiguration.AgentEnabled]: { type: 'boolean', - description: nls.localize('chat.agent.enabled.description', "Enable agent mode for {0}. When this is enabled, a dropdown appears in the {0} view to toggle agent mode.", 'Copilot Edits'), + description: nls.localize('chat.agent.enabled.description', "Enable agent mode for {0}. When this is enabled, a dropdown appears in the view to toggle agent mode.", 'Copilot Chat'), default: this.productService.quality !== 'stable', - tags: ['experimental', 'onExp'], + tags: ['onExp'], + policy: { + name: 'ChatAgentMode', + minimumVersion: '1.99', + previewFeature: false, + defaultValue: false + } }, } - }; - configurationRegistry.registerConfiguration(this.registeredNode); + }); } private deregisterSetting() { @@ -353,12 +459,40 @@ 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, + }, + } + }; + configurationRegistry.updateConfigurations({ remove: lastNode ? [lastNode] : [], add: [node] }); + lastNode = node; + }); + }; + this._register(Event.runAndSubscribe(Event.debounce(this.entitlementService.onDidChangeEntitlement, () => { }, 1000), () => registerMaxRequestsSetting())); + } } AccessibleViewRegistry.register(new ChatResponseAccessibleView()); AccessibleViewRegistry.register(new PanelChatAccessibilityHelp()); AccessibleViewRegistry.register(new QuickChatAccessibilityHelp()); AccessibleViewRegistry.register(new EditsChatAccessibilityHelp()); +AccessibleViewRegistry.register(new AgentChatAccessibilityHelp()); registerEditorFeature(ChatInputBoxContentProvider); @@ -388,7 +522,8 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { detail: '', sortText: 'z1_help', executeImmediately: true, - locations: [ChatAgentLocation.Panel] + locations: [ChatAgentLocation.Panel], + modes: [ChatMode.Ask] }, async (prompt, progress) => { const defaultAgent = chatAgentService.getDefaultAgent(ChatAgentLocation.Panel); const agents = chatAgentService.getAgents(); @@ -405,7 +540,7 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { // Report agent list const agentText = (await Promise.all(agents - .filter(a => a.id !== defaultAgent?.id) + .filter(a => a.id !== defaultAgent?.id && !a.isCore) .filter(a => a.locations.includes(ChatAgentLocation.Panel)) .map(async a => { const description = a.description ? `- ${a.description}` : ''; @@ -475,6 +610,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(); @@ -513,8 +649,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 250b763f..45a3078e 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'; @@ -38,7 +39,7 @@ export interface IChatWidgetService { readonly onDidAddWidget: Event; - + getAllWidgets(): ReadonlyArray; getWidgetByInputUri(uri: URI): IChatWidget | undefined; getWidgetBySessionId(sessionId: string): IChatWidget | undefined; getWidgetsByLocations(location: ChatAgentLocation): ReadonlyArray; @@ -155,17 +156,15 @@ 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 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 +190,7 @@ export interface IChatWidgetViewOptions { editorOverflowWidgetsDomNode?: HTMLElement; enableImplicitContext?: boolean; enableWorkingSet?: 'explicit' | 'implicit'; + supportsChangingModes?: boolean; } export interface IChatViewViewContext { @@ -226,6 +226,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; @@ -237,7 +240,6 @@ export interface IChatWidget { logInputHistory(): void; acceptInput(query?: string, options?: IChatAcceptInputOptions): Promise; rerunLastRequest(): Promise; - acceptInputWithPrefix(prefix: string): void; setInputPlaceholder(placeholder: string): void; resetInputPlaceholder(): void; focusLastMessage(): void; diff --git a/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts b/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts index 80adb520..957cba60 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts @@ -17,7 +17,6 @@ export class ChatAccessibilityProvider implements IListAccessibilityProvider !('value' in v))?.length ?? 0; + + const toolInvocation = element.response.value.filter(v => v.kind === 'toolInvocation').filter(v => !v.isComplete); + let toolInvocationHint = ''; + if (toolInvocation.length) { + const titles = toolInvocation.map(v => v.confirmationMessages?.title).filter(v => !!v); + if (titles.length) { + toolInvocationHint = localize('toolInvocationsHint', "Action required: {0} ", titles.join(', ')); + } + } + const tableCount = marked.lexer(element.response.toString()).filter(token => token.type === 'table')?.length ?? 0; + let tableCountHint = ''; + switch (tableCount) { + case 0: + break; + case 1: + tableCountHint = localize('singleTableHint', "1 table "); + break; + default: + tableCountHint = localize('multiTableHint', "{0} tables ", tableCount); + break; + } + + const fileTreeCount = element.response.value.filter(v => v.kind === 'treeData').length ?? 0; let fileTreeCountHint = ''; switch (fileTreeCount) { case 0: break; case 1: - fileTreeCountHint = localize('singleFileTreeHint', "1 file tree"); + fileTreeCountHint = localize('singleFileTreeHint', "1 file tree "); break; default: - fileTreeCountHint = localize('multiFileTreeHint', "{0} file trees", fileTreeCount); + fileTreeCountHint = localize('multiFileTreeHint', "{0} file trees ", fileTreeCount); break; } const codeBlockCount = marked.lexer(element.response.toString()).filter(token => token.type === 'code')?.length ?? 0; switch (codeBlockCount) { case 0: - label = accessibleViewHint ? localize('noCodeBlocksHint', "{0} {1} {2}", fileTreeCountHint, element.response.toString(), accessibleViewHint) : localize('noCodeBlocks', "{0} {1}", fileTreeCountHint, element.response.toString()); + label = accessibleViewHint ? localize('noCodeBlocksHint', "{0}{1}{2}{3} {4}", toolInvocationHint, fileTreeCountHint, tableCountHint, element.response.toString(), accessibleViewHint) : localize('noCodeBlocks', "{0} {1}", fileTreeCountHint, element.response.toString()); break; case 1: - label = accessibleViewHint ? localize('singleCodeBlockHint', "{0} 1 code block: {1} {2}", fileTreeCountHint, element.response.toString(), accessibleViewHint) : localize('singleCodeBlock', "{0} 1 code block: {1}", fileTreeCountHint, element.response.toString()); + label = accessibleViewHint ? localize('singleCodeBlockHint', "{0}{1}1 code block: {2} {3}{4}", toolInvocationHint, fileTreeCountHint, tableCountHint, element.response.toString(), accessibleViewHint) : localize('singleCodeBlock', "{0} 1 code block: {1}", fileTreeCountHint, element.response.toString()); break; default: - label = accessibleViewHint ? localize('multiCodeBlockHint', "{0} {1} code blocks: {2}", fileTreeCountHint, codeBlockCount, element.response.toString(), accessibleViewHint) : localize('multiCodeBlock', "{0} {1} code blocks", fileTreeCountHint, codeBlockCount, element.response.toString()); + label = accessibleViewHint ? localize('multiCodeBlockHint', "{0}{1}{2} code blocks: {3}{4}", toolInvocationHint, fileTreeCountHint, tableCountHint, codeBlockCount, element.response.toString(), accessibleViewHint) : localize('multiCodeBlock', "{0} {1} code blocks", fileTreeCountHint, codeBlockCount, element.response.toString()); break; } return label; diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts index 30b1bd35..a61ab242 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts @@ -11,6 +11,10 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { IChatRequestVariableEntry } from '../common/chatModel.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ChatPromptAttachmentsCollection } from './chatAttachmentModel/chatPromptAttachmentsCollection.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { resizeImage } from './imageUtils.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { localize } from '../../../../nls.js'; export class ChatAttachmentModel extends Disposable { /** @@ -20,6 +24,8 @@ export class ChatAttachmentModel extends Disposable { constructor( @IInstantiationService private readonly initService: IInstantiationService, + @IFileService private readonly fileService: IFileService, + @IDialogService private readonly dialogService: IDialogService, ) { super(); @@ -67,17 +73,50 @@ export class ChatAttachmentModel extends Disposable { this._onDidChangeContext.fire(); } - addFile(uri: URI, range?: IRange) { + async addFile(uri: URI, range?: IRange) { + if (/\.(png|jpe?g|gif|bmp|webp)$/i.test(uri.path)) { + this.addContext(await this.asImageVariableEntry(uri)); + return; + } + 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, + }; + } + + async asImageVariableEntry(uri: URI): Promise { + const fileName = basename(uri); + const readFile = await this.fileService.readFile(uri); + if (readFile.size > 30 * 1024 * 1024) { // 30 MB + this.dialogService.error(localize('imageTooLarge', 'Image is too large'), localize('imageTooLargeMessage', 'The image {0} is too large to be attached.', fileName)); + throw new Error('Image is too large'); + } + const resizedImage = await resizeImage(readFile.value.buffer); + return { + id: uri.toString(), + name: fileName, + fullName: uri.path, + value: resizedImage, + isImage: true, + isFile: false, + references: [{ reference: uri, kind: 'reference' }] }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts index 047a6a80..64445d15 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts @@ -26,16 +26,16 @@ import { IConfigurationService } from '../../../../../platform/configuration/com * This object most likely was explicitly attached by the user. */ export const toChatVariable = ( - reference: Pick, + reference: Pick, isRoot: boolean, ): IChatRequestVariableEntry => { - const { uri, isPromptSnippet } = reference; + const { uri, isPromptFile: isPromptFile } = reference; // default `id` is the stringified `URI` let id = `${uri}`; // for prompt files, we add a prefix to the `id` - if (isPromptSnippet) { + if (isPromptFile) { // the default prefix that is used for all prompt files let prefix = 'vscode.prompt.instructions'; // if the reference is the root object, add the `.root` suffix @@ -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 00000000..1873df4b --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------------------------- + * 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 } from '../common/chatModel.js'; +import { ILanguageModelChatMetadataAndIdentifier, ILanguageModelsService } from '../common/languageModels.js'; +import { hookUpResourceAttachmentDragAndContextMenu, hookUpSymbolAttachmentDragAndContextMenu } from './chatContentParts/chatAttachmentsContentPart.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.$('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); + 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; + 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 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 2c641306..aaf5e335 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 { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; import { basename, dirname } from '../../../../../base/common/path.js'; @@ -40,7 +39,7 @@ 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, isImageVariableEntry, isLinkVariableEntry, isPasteVariableEntry } from '../../common/chatModel.js'; +import { IChatRequestVariableEntry, isImageVariableEntry, isPasteVariableEntry } from '../../common/chatModel.js'; import { ChatResponseReferencePartStatusKind, IChatContentReference } from '../../common/chatService.js'; import { convertUint8ArrayToString } from '../imageUtils.js'; @@ -73,6 +72,7 @@ export class ChatAttachmentsContentPart extends Disposable { } } + // TODO@joyceerhl adopt chat attachment widgets private initAttachedContext(container: HTMLElement) { dom.clearNode(container); this.attachedContextDisposables.clear(); @@ -181,10 +181,6 @@ export class ChatAttachmentsContentPart extends Disposable { this.attachedContextDisposables.add(this.instantiationService.invokeFunction(accessor => hookUpResourceAttachmentDragAndContextMenu(accessor, widget, resource))); } } - } 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() }); } else { const attachmentLabel = attachment.fullName ?? attachment.name; const withIcon = attachment.icon?.id ? `$(${attachment.icon.id}) ${attachmentLabel}` : attachmentLabel; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts new file mode 100644 index 00000000..b78f3037 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ButtonWithIcon } from '../../../../../base/browser/ui/button/button.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; +import { Emitter } from '../../../../../base/common/event.js'; +import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; +import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { localize } from '../../../../../nls.js'; +import { IChatRendererContent } from '../../common/chatViewModel.js'; +import { ChatTreeItem, IChatCodeBlockInfo } from '../chat.js'; +import { IChatContentPart, IChatContentPartRenderContext } from './chatContentParts.js'; +import { $ } from './chatReferencesContentPart.js'; +import { EditorPool } from './chatMarkdownContentPart.js'; +import { CodeBlockPart, ICodeBlockData, ICodeBlockRenderOptions } from '../codeBlockPart.js'; +import { ITextModel } from '../../../../../editor/common/model.js'; +import { IDisposableReference } from './chatCollections.js'; +import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { autorun, IObservable, observableValue } from '../../../../../base/common/observable.js'; + + +export abstract class ChatCollapsibleContentPart extends Disposable implements IChatContentPart { + + private _domNode?: HTMLElement; + + protected readonly _onDidChangeHeight = this._register(new Emitter()); + public readonly onDidChangeHeight = this._onDidChangeHeight.event; + + protected readonly hasFollowingContent: boolean; + protected _isExpanded = observableValue(this, false); + + constructor( + private readonly title: IMarkdownString | string, + protected readonly context: IChatContentPartRenderContext, + ) { + super(); + this.hasFollowingContent = this.context.contentIndex + 1 < this.context.content.length; + } + + get domNode(): HTMLElement { + this._domNode ??= this.init(); + return this._domNode; + } + + protected init(): HTMLElement { + const referencesLabel = this.title; + + + const buttonElement = $('.chat-used-context-label', undefined); + + const collapseButton = this._register(new ButtonWithIcon(buttonElement, { + buttonBackground: undefined, + buttonBorder: undefined, + buttonForeground: undefined, + buttonHoverBackground: undefined, + buttonSecondaryBackground: undefined, + buttonSecondaryForeground: undefined, + buttonSecondaryHoverBackground: undefined, + buttonSeparator: undefined + })); + this._domNode = $('.chat-used-context', undefined, buttonElement); + collapseButton.label = referencesLabel; + + this._register(collapseButton.onDidClick(() => { + const value = this._isExpanded.get(); + this._isExpanded.set(!value, undefined); + })); + + this._register(autorun(r => { + const value = this._isExpanded.read(r); + collapseButton.icon = value ? Codicon.chevronDown : Codicon.chevronRight; + this._domNode?.classList.toggle('chat-used-context-collapsed', !value); + this.updateAriaLabel(collapseButton.element, typeof referencesLabel === 'string' ? referencesLabel : referencesLabel.value, this.isExpanded()); + + if (this._domNode?.isConnected) { + queueMicrotask(() => { + this._onDidChangeHeight.fire(); + }); + } + })); + + const content = this.initContent(); + this._domNode.appendChild(content); + return this._domNode; + } + + protected abstract initContent(): HTMLElement; + + abstract hasSameContent(other: IChatRendererContent, followingContent: IChatRendererContent[], element: ChatTreeItem): boolean; + + private updateAriaLabel(element: HTMLElement, label: string, expanded?: boolean): void { + element.ariaLabel = expanded ? localize('usedReferencesExpanded', "{0}, expanded", label) : localize('usedReferencesCollapsed', "{0}, collapsed", label); + } + + addDisposable(disposable: IDisposable): void { + this._register(disposable); + } + + get expanded(): IObservable { + return this._isExpanded; + } + + protected isExpanded(): boolean { + return this._isExpanded.get(); + } + + protected setExpanded(value: boolean): void { + this._isExpanded.set(value, undefined); + } +} + + +export class ChatCollapsibleEditorContentPart extends ChatCollapsibleContentPart { + + private readonly _editorReference: IDisposableReference; + private readonly _contentDomNode: HTMLElement; + + private _currentWidth: number = 0; + + readonly codeblocks: IChatCodeBlockInfo[] = []; + + constructor( + title: IMarkdownString | string, + context: IChatContentPartRenderContext, + private readonly editorPool: EditorPool, + private readonly textModel: Promise, + private readonly languageId: string, + private readonly options: ICodeBlockRenderOptions = {}, + private readonly codeBlockInfo: IChatCodeBlockInfo, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + super(title, context); + this._contentDomNode = $('div.chat-collapsible-editor-content'); + this._editorReference = this.editorPool.get(); + this.codeblocks = [{ + ...codeBlockInfo, + focus: () => { + this._editorReference.object.focus(); + codeBlockInfo.focus(); + } + }]; + } + + override dispose(): void { + this._editorReference?.dispose(); + super.dispose(); + } + + protected initContent(): HTMLElement { + const data: ICodeBlockData = { + languageId: this.languageId, + textModel: this.textModel, + codeBlockIndex: this.codeBlockInfo.codeBlockIndex, + codeBlockPartIndex: 0, + element: this.context.element, + parentContextKeyService: this.contextKeyService, + renderOptions: this.options + }; + + this._editorReference.object.render(data, this._currentWidth || 300); + this._register(this._editorReference.object.onDidChangeContentHeight(() => this._onDidChangeHeight.fire())); + this._contentDomNode.appendChild(this._editorReference.object.element); + + this._register(autorun(r => { + const value = this._isExpanded.read(r); + this._contentDomNode.style.display = value ? 'block' : 'none'; + })); + + + return this._contentDomNode; + } + + hasSameContent(other: IChatRendererContent, followingContent: IChatRendererContent[], element: ChatTreeItem): boolean { + // For now, we consider content different unless it's exactly the same instance + return false; + } + + layout(width: number): void { + this._currentWidth = width; + this._editorReference.object.layout(width); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts index 68a8d913..c3361ccf 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 761e83ff..824fcd43 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts @@ -5,19 +5,26 @@ import * as dom from '../../../../../base/browser/dom.js'; import './media/chatConfirmationWidget.css'; -import { Button } from '../../../../../base/browser/ui/button/button.js'; +import { Button, ButtonWithDropdown, IButton, IButtonOptions } from '../../../../../base/browser/ui/button/button.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { IMarkdownString, MarkdownString } from '../../../../../base/common/htmlContent.js'; 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 { autorun, observableValue } from '../../../../../base/common/observable.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; +import { Action } from '../../../../../base/common/actions.js'; +import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IHostService } from '../../../../services/host/browser/host.js'; export interface IChatConfirmationButton { label: string; isSecondary?: boolean; tooltip?: string; data: any; + moreActions?: IChatConfirmationButton[]; } abstract class BaseChatConfirmationWidget extends Disposable { @@ -42,11 +49,16 @@ abstract class BaseChatConfirmationWidget extends Disposable { constructor( title: string, buttons: IChatConfirmationButton[], + expandableMessage: boolean, @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IHostService private readonly _hostService: IHostService, ) { super(); const elements = dom.h('.chat-confirmation-widget@root', [ + dom.h('.chat-confirmation-widget-expando@expando'), dom.h('.chat-confirmation-widget-title@title'), dom.h('.chat-confirmation-widget-message@message'), dom.h('.chat-confirmation-buttons-container@buttonsContainer'), @@ -54,13 +66,53 @@ abstract class BaseChatConfirmationWidget extends Disposable { this._domNode = elements.root; this.markdownRenderer = this.instantiationService.createInstance(MarkdownRenderer, {}); - const renderedTitle = this._register(this.markdownRenderer.render(new MarkdownString(title), { + if (expandableMessage) { + const expanded = observableValue(this, false); + const btn = this._register(new Button(elements.expando, {})); + + this._register(autorun(r => { + const value = expanded.read(r); + btn.icon = value ? Codicon.chevronDown : Codicon.chevronRight; + elements.message.classList.toggle('hidden', !value); + this._onDidChangeHeight.fire(); + })); + + this._register(btn.onDidClick(() => { + const value = expanded.get(); + expanded.set(!value, undefined); + })); + } + + const renderedTitle = this._register(this.markdownRenderer.render(new MarkdownString(title, { supportThemeIcons: true }), { asyncRenderCallback: () => this._onDidChangeHeight.fire(), })); elements.title.append(renderedTitle.element); this.messageElement = elements.message; buttons.forEach(buttonData => { - const button = this._register(new Button(elements.buttonsContainer, { ...defaultButtonStyles, secondary: buttonData.isSecondary, title: buttonData.tooltip })); + const buttonOptions: IButtonOptions = { ...defaultButtonStyles, secondary: buttonData.isSecondary, title: buttonData.tooltip }; + + let button: IButton; + if (buttonData.moreActions) { + button = new ButtonWithDropdown(elements.buttonsContainer, { + ...buttonOptions, + contextMenuProvider: contextMenuService, + addPrimaryActionToDropdown: false, + actions: buttonData.moreActions.map(action => this._register(new Action( + action.label, + action.label, + undefined, + true, + () => { + this._onDidClick.fire(action); + return Promise.resolve(); + }, + ))), + }); + } else { + button = new Button(elements.buttonsContainer, buttonOptions); + } + + this._register(button); button.label = buttonData.label; this._register(button.onDidClick(() => this._onDidClick.fire(buttonData))); }); @@ -68,6 +120,13 @@ abstract class BaseChatConfirmationWidget extends Disposable { protected renderMessage(element: HTMLElement): void { this.messageElement.append(element); + + if (this._configurationService.getValue('chat.focusWindowOnConfirmation')) { + const targetWindow = dom.getWindow(element); + if (!targetWindow.document.hasFocus()) { + this._hostService.focus(targetWindow, { force: true /* Application may not be active */ }); + } + } } } @@ -77,8 +136,11 @@ export class ChatConfirmationWidget extends BaseChatConfirmationWidget { private readonly message: string | IMarkdownString, buttons: IChatConfirmationButton[], @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IHostService hostService: IHostService, ) { - super(title, buttons, instantiationService); + super(title, buttons, false, instantiationService, contextMenuService, configurationService, hostService); const renderedMessage = this._register(this.markdownRenderer.render( typeof this.message === 'string' ? new MarkdownString(this.message) : this.message, @@ -92,10 +154,14 @@ export class ChatCustomConfirmationWidget extends BaseChatConfirmationWidget { constructor( title: string, messageElement: HTMLElement, + messageElementIsExpandable: boolean, buttons: IChatConfirmationButton[], @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IConfigurationService configurationService: IConfigurationService, + @IHostService hostService: IHostService, ) { - super(title, buttons, instantiationService); + super(title, buttons, messageElementIsExpandable, instantiationService, contextMenuService, configurationService, hostService); 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 d51c29af..b939a1ad 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts @@ -5,11 +5,12 @@ 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 { autorun } from '../../../../../base/common/observable.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { autorun, IObservable } from '../../../../../base/common/observable.js'; import { equalsIgnoreCase } from '../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; @@ -26,15 +27,16 @@ 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'; import { IMarkdownVulnerability } from '../../common/annotations.js'; -import { IChatEditingService, IEditSessionEntryDiff } from '../../common/chatEditingService.js'; +import { IEditSessionEntryDiff } from '../../common/chatEditingService.js'; import { IChatProgressRenderableResponseContent } from '../../common/chatModel.js'; -import { IChatMarkdownContent, IChatUndoStop } from '../../common/chatService.js'; +import { IChatMarkdownContent, IChatService, IChatUndoStop } from '../../common/chatService.js'; import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; -import { CodeBlockModelCollection } from '../../common/codeBlockModelCollection.js'; +import { CodeBlockEntry, CodeBlockModelCollection } from '../../common/codeBlockModelCollection.js'; import { IChatCodeBlockInfo } from '../chat.js'; import { IChatRendererDelegate } from '../chatListRenderer.js'; import { ChatMarkdownDecorationsRenderer } from '../chatMarkdownDecorationsRenderer.js'; @@ -48,7 +50,6 @@ const $ = dom.$; export interface IChatMarkdownContentPartOptions { readonly codeBlockRenderOptions?: ICodeBlockRenderOptions; - readonly renderCodeBlockPills?: boolean; } export class ChatMarkdownContentPart extends Disposable implements IChatContentPart { @@ -88,11 +89,18 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP // and within this part to find it within the codeblocks array let globalCodeBlockIndexStart = codeBlockStartIndex; let thisPartCodeBlockIndexStart = 0; + + // Don't set to 'false' for responses, respect defaults + const markedOpts = isRequestVM(element) ? { + gfm: true, + breaks: true, + } : undefined; + const result = this._register(renderer.render(markdown.content, { fillInIncompleteTokens, codeBlockRendererSync: (languageId, text, raw) => { const isCodeBlockComplete = !isResponseVM(context.element) || context.element.isComplete || !raw || codeblockHasClosingBackticks(raw); - if ((!text || (text.startsWith('') && !text.includes('\n'))) && !isCodeBlockComplete && rendererOptions.renderCodeBlockPills) { + if ((!text || (text.startsWith('; let range: Range | undefined; let vulns: readonly IMarkdownVulnerability[] | undefined; - let codemapperUri: URI | undefined; + let codeblockEntry: CodeBlockEntry | undefined; if (equalsIgnoreCase(languageId, localFileLanguageId)) { try { const parsedBody = parseLocalFileData(text); @@ -116,7 +124,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP const modelEntry = this.codeBlockModelCollection.getOrCreate(sessionId, element, globalIndex); const fastUpdateModelEntry = this.codeBlockModelCollection.updateSync(sessionId, element, globalIndex, { text, languageId, isComplete: isCodeBlockComplete }); vulns = modelEntry.vulns; - codemapperUri = fastUpdateModelEntry.codemapperUri; + codeblockEntry = fastUpdateModelEntry; textModel = modelEntry.model; } @@ -127,9 +135,9 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP if (hideToolbar !== undefined) { renderOptions.hideToolbar = hideToolbar; } - const codeBlockInfo: ICodeBlockData = { languageId, textModel, codeBlockIndex: globalIndex, codeBlockPartIndex: thisPartIndex, element, range, parentContextKeyService: contextKeyService, vulns, codemapperUri, renderOptions }; + const codeBlockInfo: ICodeBlockData = { languageId, textModel, codeBlockIndex: globalIndex, codeBlockPartIndex: thisPartIndex, element, range, parentContextKeyService: contextKeyService, vulns, codemapperUri: codeblockEntry?.codemapperUri, renderOptions }; - if (!rendererOptions.renderCodeBlockPills || element.isCompleteAddedRequest || !codemapperUri) { + if (element.isCompleteAddedRequest || !codeblockEntry?.codemapperUri || !codeblockEntry.isEdit) { const ref = this.renderCodeBlock(codeBlockInfo, text, isCodeBlockComplete, currentWidth); this.allRefs.push(ref); @@ -142,7 +150,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP readonly ownerMarkdownPartId = ownerMarkdownPartId; readonly codeBlockIndex = globalIndex; readonly elementId = element.id; - readonly isStreaming = !rendererOptions.renderCodeBlockPills; + readonly isStreaming = false; codemapperUri = undefined; // will be set async public get uri() { // here we must do a getter because the ref.object is rendered @@ -175,7 +183,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP readonly codeBlockIndex = globalIndex; readonly elementId = element.id; readonly isStreaming = !isCodeBlockComplete; - readonly codemapperUri = codemapperUri; + readonly codemapperUri = codeblockEntry?.codemapperUri; public get uri() { return undefined; } @@ -190,7 +198,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP } }, asyncRenderCallback: () => this._onDidChangeHeight.fire(), - })); + }, markedOpts)); const markdownDecorationsRenderer = instantiationService.createInstance(ChatMarkdownDecorationsRenderer); this._register(markdownDecorationsRenderer.walkTreeAndAnnotateReferenceLinks(markdown, result.element)); @@ -229,7 +237,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP hasSameContent(other: IChatProgressRenderableResponseContent): boolean { return other.kind === 'markdownContent' && !!(other.content.value === this.markdown.content.value - || this.rendererOptions.renderCodeBlockPills && this.codeblocks.at(-1)?.isStreaming && this.codeblocks.at(-1)?.codemapperUri !== undefined && other.content.value.lastIndexOf('```') === this.markdown.content.value.lastIndexOf('```')); + || this.codeblocks.at(-1)?.isStreaming && this.codeblocks.at(-1)?.codemapperUri !== undefined && other.content.value.lastIndexOf('```') === this.markdown.content.value.lastIndexOf('```')); } layout(width: number): void { @@ -294,6 +302,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; @@ -314,7 +325,8 @@ class CollapsedCodeBlock extends Disposable { @IContextMenuService private readonly contextMenuService: IContextMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IMenuService private readonly menuService: IMenuService, - @IChatEditingService private readonly chatEditingService: IChatEditingService, + @IHoverService private readonly hoverService: IHoverService, + @IChatService private readonly chatService: IChatService, ) { super(); this.element = $('.chat-codeblock-pill-widget'); @@ -350,10 +362,13 @@ class CollapsedCodeBlock extends Disposable { this._uri = uri; + const session = this.chatService.getSession(this.sessionId); const iconText = this.labelService.getUriBasenameLabel(uri); - const editSession = this.chatEditingService.getEditingSession(this.sessionId); - const modifiedEntry = editSession?.getEntry(uri); - const isComplete = !modifiedEntry?.isCurrentlyBeingModifiedBy.get(); + + let editSession = session?.editingSessionObs?.promiseResult.get()?.data; + let modifiedEntry = editSession?.getEntry(uri); + let modifiedByResponse = modifiedEntry?.isCurrentlyBeingModifiedBy.get(); + const isComplete = !modifiedByResponse || modifiedByResponse.requestId !== this.requestId; let iconClasses: string[] = []; if (isStreaming || !isComplete) { @@ -375,8 +390,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,20 +401,26 @@ 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); } }; - const diffBetweenStops = modifiedEntry && editSession - ? editSession.getEntryDiffBetweenStops(modifiedEntry.modifiedURI, this.requestId, this.inUndoStop) - : undefined; + let diffBetweenStops: IObservable | undefined; // Show a percentage progress that is driven by the rewrite this._progressStore.add(autorun(r => { + if (!editSession) { + editSession = session?.editingSessionObs?.promiseResult.read(r)?.data; + modifiedEntry = editSession?.getEntry(uri); + } + + modifiedByResponse = modifiedEntry?.isCurrentlyBeingModifiedBy.read(r); + const isComplete = !modifiedByResponse || modifiedByResponse.requestId !== this.requestId; const rewriteRatio = modifiedEntry?.rewriteRatio.read(r); - const isComplete = !modifiedEntry?.isCurrentlyBeingModifiedBy.read(r); if (!isStreaming && !isComplete) { const value = rewriteRatio; labelDetail.textContent = value === 0 || !value ? localize('chat.codeblock.generating', "Generating edits...") : localize('chat.codeblock.applyingPercentage', "Applying edits ({0}%)...", Math.round(value * 100)); @@ -411,9 +431,29 @@ class CollapsedCodeBlock extends Disposable { labelDetail.textContent = ''; } + if (!diffBetweenStops) { + diffBetweenStops = modifiedEntry && editSession + ? editSession.getEntryDiffBetweenStops(modifiedEntry.modifiedURI, this.requestId, this.inUndoStop) + : undefined; + } + if (!isStreaming && isComplete && diffBetweenStops) { renderDiff(diffBetweenStops.read(r)); } })); } + + private updateTooltip(tooltip: string): void { + this.tooltip = tooltip; + if (!this.hover.value) { + this.hover.value = this.hoverService.setupDelayedHover(this.element, () => ( + { + content: this.tooltip!, + appearance: { compact: true, showPointer: true }, + position: { hoverPosition: HoverPosition.BELOW }, + persistence: { hideOnKeyDown: true }, + } + )); + } + } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts index e3f08938..c0017f21 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts @@ -53,10 +53,7 @@ export class ChatProgressContentPart extends Disposable implements IChatContentP alert(progress.content.value); } const codicon = icon ? icon : this.showSpinner ? ThemeIcon.modify(Codicon.loading, 'spin') : Codicon.check; - const markdown = new MarkdownString(progress.content.value, { - supportThemeIcons: true - }); - const result = this._register(renderer.render(markdown)); + const result = this._register(renderer.render(progress.content)); result.element.classList.add('progress-step'); this.renderFileWidgets(result.element); @@ -111,7 +108,7 @@ export class ChatWorkingProgressContentPart extends ChatProgressContentPart impl kind: 'progressMessage', content: workingProgress.isPaused ? new MarkdownString().appendText(localize('pausedMessage', "Paused")) : - new MarkdownString().appendText(localize('workingMessage', "Working")) + new MarkdownString().appendText(localize('workingMessage', "Working...")) }; super(progressMessage, renderer, context, undefined, undefined, workingProgress.isPaused ? Codicon.debugPause : undefined, instantiationService, chatMarkdownAnchorService); } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts index 97985ca4..e41a5813 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts @@ -5,6 +5,7 @@ import * as dom from '../../../../../base/browser/dom.js'; import { Button } from '../../../../../base/browser/ui/button/button.js'; +import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../../base/common/actions.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter } from '../../../../../base/common/event.js'; import { MarkdownString } from '../../../../../base/common/htmlContent.js'; @@ -14,6 +15,7 @@ import { assertType } from '../../../../../base/common/types.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { localize } from '../../../../../nls.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; +import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js'; import { asCssVariable, textLinkForeground } from '../../../../../platform/theme/common/colorRegistry.js'; import { IChatResponseViewModel } from '../../common/chatViewModel.js'; @@ -42,7 +44,8 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar element: IChatResponseViewModel, renderer: MarkdownRenderer, @IChatWidgetService chatWidgetService: IChatWidgetService, - @ICommandService commandService: ICommandService + @ICommandService commandService: ICommandService, + @ITelemetryService telemetryService: ITelemetryService, ) { super(); @@ -99,7 +102,9 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar }; this._register(button1.onDidClick(async () => { - await commandService.executeCommand('workbench.action.chat.upgradePlan'); + const commandId = 'workbench.action.chat.upgradePlan'; + telemetryService.publicLog2('workbenchActionExecuted', { id: commandId, from: 'chat-response' }); + await commandService.executeCommand(commandId); 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 427e7b2f..3d5115ef 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../../../../../base/browser/dom.js'; -import { ButtonWithIcon } from '../../../../../base/browser/ui/button/button.js'; import { IListRenderer, IListVirtualDelegate } from '../../../../../base/browser/ui/list/list.js'; import { coalesce } from '../../../../../base/common/arrays.js'; import { Codicon } from '../../../../../base/common/codicons.js'; -import { Emitter, Event } from '../../../../../base/common/event.js'; +import { Event } from '../../../../../base/common/event.js'; import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; -import { Disposable, DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { matchesSomeScheme, Schemas } from '../../../../../base/common/network.js'; import { basename } from '../../../../../base/common/path.js'; import { basenameOrAuthority, isEqualAuthority } from '../../../../../base/common/resources.js'; @@ -38,86 +37,46 @@ 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'; import { ChatTreeItem, IChatWidgetService } from '../chat.js'; +import { ChatCollapsibleContentPart } from './chatCollapsibleContentPart.js'; import { IDisposableReference, ResourcePool } from './chatCollections.js'; -import { IChatContentPart, IChatContentPartRenderContext } from './chatContentParts.js'; +import { IChatContentPartRenderContext } from './chatContentParts.js'; -const $ = dom.$; +export const $ = dom.$; export interface IChatReferenceListItem extends IChatContentReference { title?: string; description?: string; state?: WorkingSetEntryState; excluded?: boolean; - isMarkedReadonly?: boolean; } export type IChatCollapsibleListItem = IChatReferenceListItem | IChatWarningMessage; -export class ChatCollapsibleListContentPart extends Disposable implements IChatContentPart { - public domNode!: HTMLElement; - - private readonly _onDidChangeHeight = this._register(new Emitter()); - public readonly onDidChangeHeight = this._onDidChangeHeight.event; - - private hasFollowingContent!: boolean; +export class ChatCollapsibleListContentPart extends ChatCollapsibleContentPart { constructor( private readonly data: ReadonlyArray, - private readonly labelOverride: IMarkdownString | string | undefined, - protected readonly context: IChatContentPartRenderContext, + labelOverride: IMarkdownString | string | undefined, + context: IChatContentPartRenderContext, private readonly contentReferencesListPool: CollapsibleListPool, @IOpenerService private readonly openerService: IOpenerService, @IMenuService private readonly menuService: IMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { - super(); - - this.init(); + super(labelOverride ?? (data.length > 1 ? + localize('usedReferencesPlural', "Used {0} references", data.length) : + localize('usedReferencesSingular', "Used {0} reference", 1)), context); } - protected init() { - this.hasFollowingContent = this.context.contentIndex + 1 < this.context.content.length; - const referencesLabel = this.labelOverride ?? (this.data.length > 1 ? - localize('usedReferencesPlural', "Used {0} references", this.data.length) : - localize('usedReferencesSingular', "Used {0} reference", 1)); - - const icon = () => { - return this.isExpanded() ? Codicon.chevronDown : Codicon.chevronRight; - }; - const buttonElement = $('.chat-used-context-label', undefined); - - const collapseButton = this._register(new ButtonWithIcon(buttonElement, { - buttonBackground: undefined, - buttonBorder: undefined, - buttonForeground: undefined, - buttonHoverBackground: undefined, - buttonSecondaryBackground: undefined, - buttonSecondaryForeground: undefined, - buttonSecondaryHoverBackground: undefined, - buttonSeparator: undefined - })); - this.domNode = $('.chat-used-context', undefined, buttonElement); - collapseButton.label = referencesLabel; - collapseButton.icon = icon(); - this.updateAriaLabel(collapseButton.element, typeof referencesLabel === 'string' ? referencesLabel : referencesLabel.value, this.isExpanded()); - this.domNode.classList.toggle('chat-used-context-collapsed', !this.isExpanded()); - this._register(collapseButton.onDidClick(() => { - this.setExpanded(!this.isExpanded()); - collapseButton.icon = icon(); - this.domNode.classList.toggle('chat-used-context-collapsed', !this.isExpanded()); - this._onDidChangeHeight.fire(); - this.updateAriaLabel(collapseButton.element, typeof referencesLabel === 'string' ? referencesLabel : referencesLabel.value, this.isExpanded()); - })); - + protected override initContent(): HTMLElement { const ref = this._register(this.contentReferencesListPool.get()); const list = ref.object; - this.domNode.appendChild(list.getHTMLElement().parentElement!); this._register(list.onDidOpen((e) => { if (e.element && 'reference' in e.element && typeof e.element.reference === 'object') { @@ -171,28 +130,13 @@ export class ChatCollapsibleListContentPart extends Disposable implements IChatC list.layout(height); list.getHTMLElement().style.height = `${height}px`; list.splice(0, list.length, this.data); + + return list.getHTMLElement().parentElement!; } hasSameContent(other: IChatRendererContent, followingContent: IChatRendererContent[], element: ChatTreeItem): boolean { return other.kind === 'references' && other.references.length === this.data.length && (!!followingContent.length === this.hasFollowingContent); } - - private updateAriaLabel(element: HTMLElement, label: string, expanded?: boolean): void { - element.ariaLabel = expanded ? localize('usedReferencesExpanded', "{0}, expanded", label) : localize('usedReferencesCollapsed', "{0}, collapsed", label); - } - - addDisposable(disposable: IDisposable): void { - this._register(disposable); - } - - private _isExpanded = false; - protected isExpanded(): boolean { - return this._isExpanded; - } - - protected setExpanded(value: boolean): void { - this._isExpanded = value; - } } export interface IChatUsedReferencesListOptions { @@ -212,11 +156,9 @@ export class ChatUsedReferencesListContentPart extends ChatCollapsibleListConten @IContextMenuService contextMenuService: IContextMenuService, ) { super(data, labelOverride, context, contentReferencesListPool, openerService, menuService, instantiationService, contextMenuService); - super.init(); - } - - protected override init(): void { - // prevent it from calling overridden methods until after the constructor runs and this.options is set + if (data.length === 0) { + dom.hide(this.domNode); + } } protected override isExpanded(): boolean { @@ -475,7 +417,6 @@ class CollapsibleListRenderer implements IListRenderer editor.object.focus(), + isStreaming: false, + ownerMarkdownPartId: this.codeblocksPartId, + uri: model.uri, + uriPromise: Promise.resolve(model.uri) + }); + this._register(editor.object.onDidChangeContentHeight(() => { + editor.object.layout(this.currentWidthDelegate()); + this._onDidChangeHeight.fire(); + })); + this._register(model.onDidChangeContent(e => { + try { + inputData.rawInput = JSON.parse(model.getValue()); + } catch { + // ignore + } + })); + + elements.editor.append(editor.object.element); + } + this.markdownPart = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, chatMarkdownContent, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions })); + elements.message.append(this.markdownPart.domNode); + this._register(this.markdownPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); confirmWidget = this._register(this.instantiationService.createInstance( ChatCustomConfirmationWidget, title, - this.markdownPart.domNode, + elements.root, + toolInvocation.toolSpecificData?.kind === 'input', buttons )); } + const hasToolConfirmation = ChatContextKeys.Editing.hasToolConfirmation.bindTo(this.contextKeyService); + hasToolConfirmation.set(true); + this._register(confirmWidget.onDidClick(button => { - toolInvocation.confirmed.complete(button.data); + switch (button.data as ConfirmationOutcome) { + case ConfirmationOutcome.AllowGlobally: + this.languageModelToolsService.setToolAutoConfirmation(toolInvocation.toolId, 'profile', true); + toolInvocation.confirmed.complete(true); + break; + case ConfirmationOutcome.AllowWorkspace: + this.languageModelToolsService.setToolAutoConfirmation(toolInvocation.toolId, 'workspace', true); + toolInvocation.confirmed.complete(true); + break; + case ConfirmationOutcome.AllowSession: + this.languageModelToolsService.setToolAutoConfirmation(toolInvocation.toolId, 'memory', true); + toolInvocation.confirmed.complete(true); + break; + case ConfirmationOutcome.Allow: + toolInvocation.confirmed.complete(true); + break; + case ConfirmationOutcome.Disallow: + toolInvocation.confirmed.complete(false); + break; + } })); this._register(confirmWidget.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + this._register(toDisposable(() => hasToolConfirmation.reset())); toolInvocation.confirmed.p.then(() => { + hasToolConfirmation.reset(); this._onNeedsRerender.fire(); }); return confirmWidget.domNode; @@ -291,6 +399,7 @@ class ChatToolInvocationSubPart extends Disposable { ChatCustomConfirmationWidget, title, element, + false, buttons )); @@ -355,9 +464,47 @@ class ChatToolInvocationSubPart extends Disposable { return progressPart.domNode; } + private createInputOutputMarkdownProgressPart(message: string | IMarkdownString, inputOutputData: IToolResultInputOutputDetails): HTMLElement { + + const model = this._register(this.modelService.createModel( + `${inputOutputData.input}\n\n${inputOutputData.output}`, + this.languageService.createById('json') + )); + + const collapsibleListPart = this._register(this.instantiationService.createInstance( + ChatCollapsibleEditorContentPart, + message, + this.context, + this.editorPool, + Promise.resolve(model), + model.getLanguageId(), + { + hideToolbar: true, + reserveWidth: 19, + maxHeightInLines: 13, + verticalPadding: 5, + editorOptions: { + wordWrap: 'on' + } + }, + { + codeBlockIndex: this.codeBlockStartIndex, + codemapperUri: undefined, + elementId: this.context.element.id, + focus: () => { }, + isStreaming: false, + ownerMarkdownPartId: this.codeblocksPartId, + uri: model.uri, + uriPromise: Promise.resolve(model.uri) + } + )); + this._register(collapsibleListPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + return collapsibleListPart.domNode; + } + private createResultList( message: string | IMarkdownString, - toolDetails: NonNullable, + toolDetails: Array, ): HTMLElement { const collapsibleListPart = this._register(this.instantiationService.createInstance( ChatCollapsibleListContentPart, 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 4d711ebf..94e2f791 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css @@ -7,6 +7,9 @@ border: 1px solid var(--vscode-chat-requestBorder); border-radius: 4px; padding: 8px 12px 12px; + display: flex; + flex-wrap: wrap; + align-items: center; } .chat-confirmation-widget:not(:last-child) { @@ -21,6 +24,15 @@ margin: 0 0 4px 0; } +.chat-confirmation-widget .chat-confirmation-buttons-container, +.chat-confirmation-widget .chat-confirmation-widget-message { + flex-basis: 100%; +} + +.chat-confirmation-widget .chat-confirmation-widget-message.hidden { + display: none; +} + .chat-confirmation-widget .chat-confirmation-widget-message .rendered-markdown p { margin-top: 0; } diff --git a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts index 0de0ecc7..ba80693c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts @@ -7,7 +7,9 @@ import { DataTransfers } from '../../../../base/browser/dnd.js'; import { $, DragAndDropObserver } from '../../../../base/browser/dom.js'; import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; import { coalesce } from '../../../../base/common/arrays.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; +import { UriList } from '../../../../base/common/dataTransfer.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { Mimes } from '../../../../base/common/mime.js'; import { basename } from '../../../../base/common/resources.js'; @@ -19,16 +21,20 @@ 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 { IFileService } from '../../../../platform/files/common/files.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; import { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; +import { ISharedWebContentExtractorService } from '../../../../platform/webContentExtractor/common/webContentExtractor.js'; import { isUntitledResourceEditorInput } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IExtensionService, isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; import { UntitledTextEditorInput } from '../../../services/untitled/common/untitledTextEditorInput.js'; import { IChatRequestVariableEntry, IDiagnosticVariableEntry, IDiagnosticVariableEntryFilterData, ISymbolVariableEntry } from '../common/chatModel.js'; +import { IChatWidgetService } from './chat.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; import { IChatInputStyles } from './chatInputPart.js'; +import { imageToHash } from './chatPasteProviders.js'; import { resizeImage } from './imageUtils.js'; enum ChatDragAndDropType { @@ -55,7 +61,10 @@ export class ChatDragAndDrop extends Themable { @IFileService private readonly fileService: IFileService, @IEditorService private readonly editorService: IEditorService, @IDialogService private readonly dialogService: IDialogService, - @ITextModelService private readonly textModelService: ITextModelService + @ITextModelService private readonly textModelService: ITextModelService, + @ISharedWebContentExtractorService private readonly webContentExtractorService: ISharedWebContentExtractorService, + @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, + @ILogService private readonly logService: ILogService, ) { super(themeService); @@ -166,8 +175,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)) { @@ -239,14 +248,12 @@ export class ChatDragAndDrop extends Themable { return this.resolveSymbolsAttachContext(data); } - // Removing HTML support for now - // if (containsDragType(e, 'text/html')) { - // const data = e.dataTransfer?.getData('text/html'); - // return data ? this.resolveHTMLAttachContext(data) : []; - // } + const editorDragData = extractEditorsDropData(e); + if (editorDragData.length === 0 && !containsDragType(e, DataTransfers.INTERNAL_URI_LIST) && containsDragType(e, Mimes.uriList) && ((containsDragType(e, Mimes.html) || containsDragType(e, Mimes.text)))) { + return this.resolveHTMLAttachContext(e); + } - const data = extractEditorsDropData(e); - return coalesce(await Promise.all(data.map(editorInput => { + return coalesce(await Promise.all(editorDragData.map(editorInput => { return this.resolveAttachContext(editorInput); }))); } @@ -320,55 +327,68 @@ export class ChatDragAndDrop extends Themable { }); } - // private async resolveHTMLAttachContext(data: string): Promise { - // const displayName = localize('dragAndDroppedImageName', 'Image from URL'); - // let finalDisplayName = displayName; + private async downloadImageAsUint8Array(url: string): Promise { + try { + const extractedImages = await this.webContentExtractorService.readImage(URI.parse(url), CancellationToken.None); + if (extractedImages) { + return extractedImages.buffer; + } + } catch (error) { + this.logService.warn('Fetch failed:', error); + } - // for (let appendValue = 2; this.attachmentModel.attachments.some(attachment => attachment.name === finalDisplayName); appendValue++) { - // finalDisplayName = `${displayName} ${appendValue}`; - // } + // TODO: use dnd provider to insert text @justschen + const selection = this.chatWidgetService.lastFocusedWidget?.inputEditor.getSelection(); + if (selection && this.chatWidgetService.lastFocusedWidget) { + this.chatWidgetService.lastFocusedWidget.inputEditor.executeEdits('chatInsertUrl', [{ range: selection, text: url }]); + } - // const { src, alt } = extractImageAttributes(data); - // finalDisplayName = alt ?? finalDisplayName; + this.logService.warn(`Image URLs must end in .jpg, .png, .gif, .webp, or .bmp. Failed to fetch image from this URL: ${url}`); + return undefined; + } - // 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 async resolveHTMLAttachContext(e: DragEvent): 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 dataFromFile = await this.extractImageFromFile(e); + if (dataFromFile) { + return [await this.createImageVariable(await resizeImage(dataFromFile), finalDisplayName)]; + } + + const dataFromUrl = await this.extractImageFromUrl(e); + const variableEntries: IChatRequestVariableEntry[] = []; + if (dataFromUrl) { + for (const url of dataFromUrl) { + if (/^data:image\/[a-z]+;base64,/.test(url)) { + variableEntries.push(await this.createImageVariable(await resizeImage(url), finalDisplayName, URI.parse(url))); + } else if (/^https?:\/\/.+/.test(url)) { + const imageData = await this.downloadImageAsUint8Array(url); + if (imageData) { + variableEntries.push(await this.createImageVariable(await resizeImage(imageData), finalDisplayName, URI.parse(url), url)); + } + } + } + } + + return variableEntries; + } + + private async createImageVariable(data: Uint8Array, name: string, uri?: URI, id?: string,): Promise { + return { + id: id || await imageToHash(data), + name: name, + value: data, + isImage: true, + isFile: false, + isDirectory: false, + references: uri ? [{ reference: uri, kind: 'reference' }] : [] + }; + } private resolveMarkerAttachContext(markers: MarkerTransferData[]): IDiagnosticVariableEntry[] { return markers.map((marker): IDiagnosticVariableEntry => { @@ -422,6 +442,45 @@ export class ChatDragAndDrop extends Themable { this.overlays.forEach(overlay => this.updateOverlayStyles(overlay.overlay)); this.overlayTextBackground = this.getColor(this.styles.listBackground) || ''; } + + + + private async extractImageFromFile(e: DragEvent): Promise { + const files = e.dataTransfer?.files; + if (files && files.length > 0) { + const file = files[0]; + if (file.type.startsWith('image/')) { + try { + const buffer = await file.arrayBuffer(); + return new Uint8Array(buffer); + } catch (error) { + this.logService.error('Error reading file:', error); + return undefined; + } + } + } + + return undefined; + } + + private async extractImageFromUrl(e: DragEvent): Promise { + const textUrl = e.dataTransfer?.getData('text/uri-list'); + if (textUrl) { + try { + const uris = UriList.parse(textUrl); + if (uris.length > 0) { + return uris; + } + } catch (error) { + this.logService.error('Error parsing URI list:', error); + return undefined; + } + } + + return undefined; + } + + } async function getResourceAttachContext(resource: URI, isDirectory: boolean, textModelService: ITextModelService): Promise { @@ -488,17 +547,3 @@ function symbolId(resource: URI, range?: IRange): string { } return resource.fsPath + rangePart; } - -// function extractImageAttributes(html: string): { src: string; alt?: string } { -// const imgTagRegex = /]+src=["']([^"']+)["'][^>]*>/; -// const altRegex = /alt=["']([^"']+)["']/; - -// const match = imgTagRegex.exec(html); -// if (match) { -// const src = match[1]; -// const altMatch = match[0].match(altRegex); -// return { src, alt: altMatch ? altMatch[1] : undefined }; -// } - -// return { src: '', alt: undefined }; -// } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index cc9c7529..fa505f7d 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,28 +45,48 @@ 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); - - if (!editingSession) { - return; - } - - return this.runEditingSessionAction(accessor, editingSession, chatWidget, ...args); + return this.runEditingSessionAction(accessor, context.editingSession, context.chatWidget, ...args); } 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.lastFocusedWidget ?? chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Panel).find(w => w.isUnifiedPanelWidget); + } else { + chatWidget = chatWidgetService.getWidgetsByLocations(ChatAgentLocation.EditingSession).at(0); + } + } + + if (!chatWidget?.viewModel) { + return; + } + + const chatSessionId = chatWidget.viewModel.model.sessionId; + const editingSession = chatEditingService.getEditingSession(chatSessionId); + + if (!editingSession) { + return; + } + + return { editingSession, chatWidget }; +} + abstract class WorkingSetAction extends EditingSessionAction { @@ -91,57 +108,6 @@ 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({ @@ -187,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(); } } }); @@ -290,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: [ @@ -299,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)) } ] }); @@ -325,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, }, @@ -381,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() } ] }); @@ -389,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 @@ -417,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)) } ], }); @@ -466,7 +430,7 @@ registerAction2(class AddFilesToWorkingSetAction extends EditingSessionAction { } for (const file of uris) { - editingSession.addFileToWorkingSet(file); + await chatWidget.attachmentModel.addFile(file); } } }); @@ -475,7 +439,7 @@ registerAction2(class RemoveAction extends Action2 { constructor() { super({ id: 'workbench.action.chat.undoEdits', - title: localize2('chat.undoEdits.label', "Undo Edits"), + title: localize2('chat.undoEdits.label', "Undo Requests"), f1: false, category: CHAT_CATEGORY, icon: Codicon.x, @@ -484,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.inChatSession, EditorContextKeys.textInputFocus.negate()), weight: KeybindingWeight.WorkbenchContrib, }, menu: [ @@ -492,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: ChatContextKeys.isRequest } ] }); @@ -512,14 +476,13 @@ registerAction2(class RemoveAction extends Action2 { const configurationService = accessor.get(IConfigurationService); const dialogService = accessor.get(IDialogService); - const chatEditingService = accessor.get(IChatEditingService); const chatService = accessor.get(IChatService); const chatModel = chatService.getSession(item.sessionId); - if (chatModel?.initialLocation !== ChatAgentLocation.EditingSession) { + if (!chatModel) { return; } - const session = chatEditingService.getEditingSession(chatModel.sessionId); + const session = chatModel.editingSession; if (!session) { return; } @@ -656,7 +619,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.Ask), 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 5cbf3e71..3b7237f1 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 { IChatEditingService, IModifiedFileEntry, IModifiedFileEntryChangeHunk, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { 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[] = []; @@ -69,7 +70,6 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito private readonly _entry: IModifiedFileEntry, private readonly _editor: ICodeEditor, documentDiffInfo: IObservable, - @IChatEditingService chatEditingService: IChatEditingService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IEditorService private readonly _editorService: IEditorService, @IAccessibilitySignalService private readonly _accessibilitySignalsService: IAccessibilitySignalService, @@ -78,6 +78,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; @@ -220,24 +223,26 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito this._store.add(toDisposable(restoreActualOptions)); - const shouldBeReadOnly = derived(this, r => { + const renderAsBeingModified = derived(this, r => { return enabledObs.read(r) && Boolean(_entry.isCurrentlyBeingModifiedBy.read(r)); }); this._store.add(autorun(r => { - const value = shouldBeReadOnly.read(r); + const value = renderAsBeingModified.read(r); if (value) { actualOptions ??= { readOnly: this._editor.getOption(EditorOption.readOnly), - renderValidationDecorations: this._editor.getOption(EditorOption.renderValidationDecorations), - stickyScroll: this._editor.getOption(EditorOption.stickyScroll) + stickyScroll: this._editor.getOption(EditorOption.stickyScroll), + codeLens: this._editor.getOption(EditorOption.codeLens), + guides: this._editor.getOption(EditorOption.guides) }; this._editor.updateOptions({ readOnly: true, - renderValidationDecorations: 'off', - stickyScroll: { enabled: false } + stickyScroll: { enabled: false }, + codeLens: false, + guides: { indentation: false, bracketPairs: false } }); } else { restoreActualOptions(); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts index 2bf35d04..29299765 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/chatEditingEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts index 13643c63..2f50e60e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts @@ -195,8 +195,8 @@ class ChatEditorOverlayWidget { if (entry) { const progress = entry?.rewriteRatio.read(r); const message = progress === 0 - ? localize('generating', "Generating Edits") - : localize('applyingPercentage', "{0}% Applying Edits", Math.round(progress * 100)); + ? localize('generating', "Generating edits") + : localize('applyingPercentage', "{0}% Applying edits", Math.round(progress * 100)); return { message }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts index 706b1f26..48a0038e 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'; @@ -28,15 +28,17 @@ import { IModelService } from '../../../../../editor/common/services/model.js'; import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; import { IModelContentChangedEvent } from '../../../../../editor/common/textModelEvents.js'; import { localize } from '../../../../../nls.js'; +import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.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'; +import { IResolvedTextFileEditorModel, ITextFileService, stringToSnapshot } from '../../../../services/textfile/common/textfiles.js'; import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js'; import { IModifiedFileEntry, ChatEditKind, WorkingSetEntryState, IModifiedFileEntryEditorIntegration } from '../../common/chatEditingService.js'; import { IChatResponseModel } from '../../common/chatModel.js'; @@ -100,6 +102,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, @@ -107,9 +110,11 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie @IFilesConfigurationService fileConfigService: IFilesConfigurationService, @IChatService chatService: IChatService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, + @ITextFileService private readonly _textFileService: ITextFileService, @IFileService fileService: IFileService, - @IInstantiationService instantiationService: IInstantiationService + @IUndoRedoService undoRedoService: IUndoRedoService, + @IInstantiationService instantiationService: IInstantiationService, + @IAccessibilitySignalService private readonly _accessibilitySignalService: IAccessibilitySignalService, ) { super( resourceRef.object.textEditorModel.uri, @@ -119,6 +124,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie fileConfigService, chatService, fileService, + undoRedoService, instantiationService ); @@ -158,6 +164,17 @@ 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() { @@ -187,10 +204,12 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie }; } - restoreFromSnapshot(snapshot: ISnapshotEntry) { + restoreFromSnapshot(snapshot: ISnapshotEntry, restoreToDisk = true) { this._stateObs.set(snapshot.state, undefined); this.originalModel.setValue(snapshot.original); - this._setDocValue(snapshot.current); + if (restoreToDisk) { + this._setDocValue(snapshot.current); + } this._edit = snapshot.originalToCurrentEdit; this._updateDiffInfoSeq(); } @@ -199,12 +218,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 { @@ -266,13 +282,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.modifiedModel, null)); + return new SingleModelEditStackElement(label, 'chat.edit', this.modifiedModel, null); } async acceptAgentEdits(resource: URI, textEdits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise { @@ -335,6 +348,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie if (this._diffInfo.get().identical) { this._stateObs.set(WorkingSetEntryState.Accepted, undefined); } + this._accessibilitySignalService.playSignal(AccessibilitySignal.editsKept, { allowManyInParallel: true }); return true; } @@ -352,6 +366,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie if (this._diffInfo.get().identical) { this._stateObs.set(WorkingSetEntryState.Rejected, undefined); } + this._accessibilitySignalService.playSignal(AccessibilitySignal.editsUndone, { allowManyInParallel: true }); return true; } @@ -417,6 +432,21 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie this._diffInfo.set(nullDocumentDiff, tx); this._edit = OffsetEdit.empty; await this._collapse(tx); + + const config = this._fileConfigService.getAutoSaveConfiguration(this.modifiedURI); + if (!config.autoSave || !this._textFileService.isDirty(this.modifiedURI)) { + // SAVE after accept for manual-savers, for auto-savers + // trigger explict save to get save participants going + try { + await this._textFileService.save(this.modifiedURI, { + reason: SaveReason.EXPLICIT, + force: true, + ignoreErrorHandler: true + }); + } catch { + // ignored + } + } } protected override async _doReject(tx: ITransaction | undefined): Promise { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index 813bc0e3..eb68edac 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'; @@ -85,9 +86,10 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im protected _telemetryInfo: IModifiedEntryTelemetryInfo, kind: ChatEditKind, @IConfigurationService configService: IConfigurationService, - @IFilesConfigurationService fileConfigService: IFilesConfigurationService, + @IFilesConfigurationService protected _fileConfigService: IFilesConfigurationService, @IChatService protected readonly _chatService: IChatService, @IFileService protected readonly _fileService: IFileService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, @IInstantiationService protected readonly _instantiationService: IInstantiationService, ) { super(); @@ -120,7 +122,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im const autoSaveOff = this._store.add(new MutableDisposable()); this._store.add(autorun(r => { if (this.isCurrentlyBeingModifiedBy.read(r)) { - autoSaveOff.value = fileConfigService.disableAutoSave(this.modifiedURI); + autoSaveOff.value = _fileConfigService.disableAutoSave(this.modifiedURI); } else { autoSaveOff.clear(); } @@ -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; 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); @@ -270,7 +285,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im abstract equalsSnapshot(snapshot: ISnapshotEntry | undefined): boolean; - abstract restoreFromSnapshot(snapshot: ISnapshotEntry): void; + abstract restoreFromSnapshot(snapshot: ISnapshotEntry, restoreToDisk?: boolean): void; // --- inital content diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts index ba4c15c6..d687d403 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts @@ -3,127 +3,61 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RunOnceScheduler } from '../../../../../base/common/async.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 { StringSHA1 } from '../../../../../base/common/hash.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 { generateUuid } from '../../../../../base/common/uuid.js'; -import { EditOperation, ISingleEditOperation } from '../../../../../editor/common/core/editOperation.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, UndoRedoElementType } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { IEditorPane, SaveReason } from '../../../../common/editor.js'; import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; import { SnapshotContext } from '../../../../services/workingCopy/common/fileWorkingCopy.js'; import { NotebookTextDiffEditor } from '../../../notebook/browser/diff/notebookDiffEditor.js'; import { INotebookTextDiffEditor } from '../../../notebook/browser/diff/notebookDiffEditorBrowser.js'; -import { CellDiffInfo, computeDiff } from '../../../notebook/browser/diff/notebookDiffViewModel.js'; -import { CellEditState, getNotebookEditorFromEditorPane } from '../../../notebook/browser/notebookBrowser.js'; -import { INotebookEditorService } from '../../../notebook/browser/services/notebookEditorService.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, NotebookSetting, NotebookTextModelChangedEvent, TransientOptions } from '../../../notebook/common/notebookCommon.js'; +import { CellEditType, ICellDto2, ICellEditOperation, ICellReplaceEdit, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookSetting, NotebookTextModelChangedEvent, TransientOptions } from '../../../notebook/common/notebookCommon.js'; +import { computeDiff } from '../../../notebook/common/notebookDiff.js'; import { INotebookEditorModelResolverService } from '../../../notebook/common/notebookEditorModelResolverService.js'; import { INotebookLoggingService } from '../../../notebook/common/notebookLoggingService.js'; import { INotebookService } from '../../../notebook/common/notebookService.js'; import { INotebookEditorWorkerService } from '../../../notebook/common/services/notebookWorkerService.js'; -import { ChatEditKind, IEditSessionEntryDiff, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; +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 { createSnapshot, deserializeSnapshot, getNotebookSnapshotFileURI, restoreSnapshot } from './chatEditingModifiedNotebookSnapshot.js'; -import { ChatEditingNotebookDiffEditorIntegration, ChatEditingNotebookEditorIntegration, countChanges, ICellDiffInfo, sortCellChanges } from './chatEditingNotebookEditorIntegration.js'; -import { ChatEditingNotebookFileSystemProvider } from './chatEditingNotebookFileSystemProvider.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 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, - }; - } -} - export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifiedFileEntry { static NewModelCounter: number = 0; private readonly modifiedModel: NotebookTextModel; @@ -134,8 +68,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. @@ -144,20 +82,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(); @@ -166,9 +103,6 @@ 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 notebookEditorWorkerService = accessor.get(INotebookEditorWorkerService); - const loggingService = accessor.get(INotebookLoggingService); const resourceRef: IReference = await resolver.resolve(uri); const notebook = resourceRef.object.notebook; const originalUri = getNotebookSnapshotFileURI(telemetryInfo.sessionId, telemetryInfo.requestId, generateUuid(), notebook.uri.scheme === Schemas.untitled ? `/${notebook.uri.path}` : notebook.uri.path, notebook.viewType); @@ -180,32 +114,25 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie // 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 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 cellDiffInfo: CellDiffInfo[] = []; if (initialContent) { restoreSnapshot(originalRef.object.notebook, initialContent); - try { - const notebookDiff = await notebookEditorWorkerService.computeDiff(originalRef.object.resource, resourceRef.object.resource); - const result = computeDiff(originalRef.object.notebook, resourceRef.object.notebook, notebookDiff); - if (result.cellDiffInfo.length) { - cellDiffInfo.push(...result.cellDiffInfo); - } - } catch (ex) { - loggingService.error('Notebook Chat', 'Error computing diff:\n' + ex); - } } else { - originalRef.object.notebook.cells.forEach((_, index) => { - cellDiffInfo.push({ type: 'unchanged', originalCellIndex: index, modifiedCellIndex: index }); + 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 internalId = generateCellHash(cell.uri); + edits.push({ editType: CellEditType.PartialInternalMetadata, index, internalMetadata: { internalId } }); }); + resourceRef.object.notebook.applyEdits(edits, true, undefined, () => undefined, undefined, false); + originalRef.object.notebook.applyEdits(edits, true, undefined, () => undefined, undefined, false); } - initialContent = initialContent || createSnapshot(originalRef.object.notebook, options.serializer.options, configurationServie); - const instance = instantiationService.createInstance(ChatEditingModifiedNotebookEntry, resourceRef, originalRef, modifiedCells, originalCells, _multiDiffEntryDelegate, options.serializer.options, telemetryInfo, chatKind, initialContent, cellDiffInfo); + const instance = instantiationService.createInstance(ChatEditingModifiedNotebookEntry, resourceRef, originalRef, _multiDiffEntryDelegate, options.serializer.options, telemetryInfo, chatKind, initialContent); instance._register(disposables); return instance; }); @@ -232,125 +159,276 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie 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, - cellDiffInfo: CellDiffInfo[], @IConfigurationService private readonly configurationService: IConfigurationService, @IFilesConfigurationService fileConfigService: IFilesConfigurationService, @IChatService chatService: IChatService, @IFileService fileService: IFileService, @IInstantiationService instantiationService: IInstantiationService, @ITextModelService private readonly textModelService: ITextModelService, - @IModelService private readonly modelService: IModelService + @IModelService private readonly modelService: IModelService, + @IUndoRedoService undoRedoService: IUndoRedoService, + @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService, + @INotebookLoggingService private readonly loggingService: INotebookLoggingService, + @INotebookEditorModelResolverService private readonly notebookResolver: INotebookEditorModelResolverService, ) { - 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); - const diffs = cellDiffInfo.map((diff, i) => { - switch (diff.type) { - case 'unchanged': { - const modifiedCell = this.modifiedModel.cells[diff.modifiedCellIndex]; - const originalCell = this.originalModel.cells[diff.originalCellIndex]; - const originalCellModel = this.originalCellModels.get(originalCell.uri)!; - this.modifiedToOriginalCellMap.set(modifiedCell.uri, originalCellModel); - this.getOrCreateModifiedTextFileEntryForCell(modifiedCell); - return this.createUnchangedCellDiffInfo(diff.originalCellIndex, diff.modifiedCellIndex); - } - case 'delete': - return this.createDeleteCellDiffInfo(diff.originalCellIndex); - case 'insert': { - const cell = this.modifiedModel.cells[diff.modifiedCellIndex]; - return this.createInsertedCellDiffInfo(diff.modifiedCellIndex, this.modifiedCellModels.get(cell.uri)!); - } - default: { - const modifiedCell = this.modifiedModel.cells[diff.modifiedCellIndex]; - const originalCell = this.originalModel.cells[diff.originalCellIndex]; - const orgiginalCellModel = this.originalCellModels.get(originalCell.uri)!; - this.modifiedToOriginalCellMap.set(modifiedCell.uri, orgiginalCellModel); - this.getOrCreateModifiedTextFileEntryForCell(modifiedCell); + } - const entry = this.cellEntryMap.get(modifiedCell); - const diff2: IDocumentDiff2 = { - ...(entry?.diffInfo.get() ?? nullDocumentDiff), - keep: noopKeep, - undo: noopUndo, - modifiedModel: this.modifiedCellModels.get(modifiedCell.uri)!, - originalModel: this.originalCellModels.get(originalCell.uri)!, - }; - return { - modifiedCellIndex: diff.modifiedCellIndex, - originalCellIndex: diff.originalCellIndex, - diff: diff2, - type: 'modified' - } satisfies ICellDiffInfo; - } + initializeModelsFromDiffImpl(cellsDiffInfo: CellDiffInfo[]) { + this.cellEntryMap.forEach(entry => entry.dispose()); + this.cellEntryMap.clear(); + const diffs = cellsDiffInfo.map((cellDiff, i) => { + switch (cellDiff.type) { + case 'delete': + return this.createDeleteCellDiffInfo(cellDiff.originalCellIndex); + case 'insert': + return this.createInsertedCellDiffInfo(cellDiff.modifiedCellIndex); + default: + return this.createModifiedCellDiffInfo(cellDiff.modifiedCellIndex, cellDiff.originalCellIndex); } }); - this._cellDiffInfo.set(diffs, undefined); + this._cellsDiffInfo.set(diffs, undefined); this._changesCount.set(countChanges(diffs), undefined); } - createEmptyDiffs() { - this._cellDiffInfo.set(this.modifiedModel.cells.map((_, i) => this.createUnchangedCellDiffInfo(i, i)), undefined); + private computeRequestId: number = 0; + async initializeModelsFromDiff() { + const id = ++this.computeRequestId; + if (this._areOriginalAndModifiedIdenticalImpl()) { + const cellsDiffInfo: CellDiffInfo[] = this.modifiedModel.cells.map((_, index) => { + return { type: 'unchanged', originalCellIndex: index, modifiedCellIndex: index } satisfies CellDiffInfo; + }); + this.initializeModelsFromDiffImpl(cellsDiffInfo); + return; + } + const cellsDiffInfo: CellDiffInfo[] = []; + try { + this._isProcessingResponse.set(true, undefined); + const notebookDiff = await this.notebookEditorWorkerService.computeDiff(this.originalURI, this.modifiedURI); + if (id !== this.computeRequestId) { + return; + } + const result = computeDiff(this.originalModel, this.modifiedModel, notebookDiff); + if (result.cellDiffInfo.length) { + cellsDiffInfo.push(...result.cellDiffInfo); + } + } catch (ex) { + this.loggingService.error('Notebook Chat', 'Error computing diff:\n' + ex); + } finally { + this._isProcessingResponse.set(false, undefined); + } + this.initializeModelsFromDiffImpl(cellsDiffInfo); } + updateCellDiffInfo(cellsDiffInfo: ICellDiffInfo[], transcation: ITransaction | undefined) { + this._cellsDiffInfo.set(sortCellChanges(cellsDiffInfo), transcation); + this._changesCount.set(countChanges(cellsDiffInfo), transcation); - 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); - break; - } + if (currentState === WorkingSetEntryState.Rejected) { + return; + } + if (currentState === WorkingSetEntryState.Modified && didResetToOriginalContent) { + this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + this.updateCellDiffInfo([], undefined); + this.initializeModelsFromDiff(); + 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, false); + break; + } + case NotebookCellsChangeType.ModelChange: { + let cellDiffs = sortCellChanges(this._cellsDiffInfo.get()); + // Ensure the new notebook cells have internalIds + this._applyEditsSync(() => { + event.changes.forEach(change => { + change[2].forEach((cell, i) => { + if (cell.internalMetadata.internalId) { + return; + } + const index = change[0] + i; + const internalId = generateCellHash(cell.uri); + const edits: ICellEditOperation[] = [{ editType: CellEditType.PartialInternalMetadata, index, internalMetadata: { internalId } }]; + this.modifiedModel.applyEdits(edits, true, undefined, () => undefined, undefined, false); + cell.internalMetadata ??= {}; + cell.internalMetadata.internalId = internalId; + }); + }); + }); + 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, false); + } + 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, false); + } + 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, false); + } + 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, false); + } + 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, false); + } + break; + } + case NotebookCellsChangeType.Move: { + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements(event, this._cellsDiffInfo.get().slice()); + if (result) { + this.originalModel.applyEdits(result[1], true, undefined, () => undefined, undefined, false); + 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); + this.initializeModelsFromDiff(); + 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); + + const config = this._fileConfigService.getAutoSaveConfiguration(this.modifiedURI); + if (this.modifiedModel.uri.scheme !== Schemas.untitled && (!config.autoSave || !this.notebookResolver.isDirty(this.modifiedURI))) { + // SAVE after accept for manual-savers, for auto-savers + // trigger explict save to get save participants going + await this._applyEdits(async () => { + try { + await this.modifiedResourceRef.object.save({ + reason: SaveReason.EXPLICIT, + force: true, + }); + } catch { + // ignored + } + }); + } } 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 }); @@ -359,19 +437,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); } @@ -380,10 +458,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 { @@ -391,87 +469,173 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie this.cellEntryMap.forEach(entry => !entry.disposed && entry.clearCurrentEditLineDecoration()); } + protected override _createUndoRedoElement(response: IChatResponseModel): IUndoRedoElement | undefined { + const request = response.session.getRequests().find(req => req.id === response.requestId); + const label = request?.message.text ? localize('chatNotebookEdit1', "Chat Edit: '{0}'", request.message.text) : localize('chatNotebookEdit2', "Chat Edit"); + const transientOptions = this.transientOptions; + const outputSizeLimit = this.configurationService.getValue(NotebookSetting.outputBackupSizeLimit) * 1024; + + // create a snapshot of the current state of the model, before the next set of edits + let initial = createSnapshot(this.modifiedModel, transientOptions, outputSizeLimit); + let last = ''; + + return { + type: UndoRedoElementType.Resource, + resource: this.modifiedURI, + label, + code: 'chat.edit', + confirmBeforeUndo: false, + undo: async () => { + last = createSnapshot(this.modifiedModel, transientOptions, outputSizeLimit); + restoreSnapshot(this.modifiedModel, initial); + }, + redo: async () => { + initial = createSnapshot(this.modifiedModel, transientOptions, outputSizeLimit); + restoreSnapshot(this.modifiedModel, last); + } + }; + } + + 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 => { + this._applyEditsSync(async () => { + 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.modifiedModel.applyEdits([edit], true, undefined, () => undefined, undefined, false); + this.disposeDeletedCellEntries(); + + if (edit.editType !== CellEditType.Replace) { return; } + // Ensure cells have internal Ids. + edit.cells.forEach((_, i) => { + const index = edit.index + i; + const cell = this.modifiedModel.cells[index]; + if (cell.internalMetadata.internalId) { + return; + } + const internalId = generateCellHash(cell.uri); + const edits: ICellEditOperation[] = [{ editType: CellEditType.PartialInternalMetadata, index, internalMetadata: { internalId } }]; + this.modifiedModel.applyEdits(edits, true, undefined, () => undefined, undefined, false); + }); + + let diff: ICellDiffInfo[] = []; if (edit.count === 0) { - const diff = sortCellChanges(this._cellDiffInfo.get()).slice(); // All existing indexes are shifted by number of cells added. + 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) => { - const cell = this.modifiedModel.cells[edit.index + i]; - const modifiedCellModel: ITextModel = cell.textModel ?? this._register((await this.textModelService.createModelReference(cell.uri))).object.textEditorModel; - return this.createInsertedCellDiffInfo(edit.index + i, modifiedCellModel); - })); - 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); } 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 => { + 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); } @@ -481,16 +645,53 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie } return d; }); - this._cellDiffInfo.set(diff, undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + } + this.updateCellDiffInfo(diff, undefined); + } + + private computeStateAfterAcceptingRejectingChanges(accepted: boolean) { + const currentSnapshot = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService); + if (new SnapshotComparer(currentSnapshot).isEqual(this.originalModel)) { + const state = accepted ? WorkingSetEntryState.Accepted : WorkingSetEntryState.Rejected; + this._stateObs.set(state, undefined); } } - createUnchangedCellDiffInfo(originalCellIndex: number, modifiedCellIndex: number): ICellDiffInfo { - const cell = this.modifiedModel.cells[modifiedCellIndex]; - return { modifiedCellIndex, originalCellIndex, diff: this.getDiffForUnchangedCell(cell), type: 'unchanged' }; + createModifiedCellDiffInfo(modifiedCellIndex: number, originalCellIndex: number): ICellDiffInfo { + const modifiedCell = this.modifiedModel.cells[modifiedCellIndex]; + const originalCell = this.originalModel.cells[originalCellIndex]; + this.modifiedToOriginalCell.set(modifiedCell.uri, originalCell.uri); + const modifiedCellModelPromise = this.resolveCellModel(modifiedCell.uri); + const originalCellModelPromise = this.resolveCellModel(originalCell.uri); + + Promise.all([modifiedCellModelPromise, originalCellModelPromise]).then(([modifiedCellModel, originalCellModel]) => { + this.getOrCreateModifiedTextFileEntryForCell(modifiedCell, modifiedCellModel, originalCellModel); + }); + + const diff = observableValue('diff', nullDocumentDiff); + const unchangedCell: ICellDiffInfo = { + type: 'unchanged', + modifiedCellIndex, + originalCellIndex, + keep: async (changes: DetailedLineRangeMapping) => { + const [modifiedCellModel, originalCellModel] = await Promise.all([modifiedCellModelPromise, originalCellModelPromise]); + const entry = this.getOrCreateModifiedTextFileEntryForCell(modifiedCell, modifiedCellModel, originalCellModel); + return entry ? entry.keep(changes) : false; + }, + undo: async (changes: DetailedLineRangeMapping) => { + const [modifiedCellModel, originalCellModel] = await Promise.all([modifiedCellModelPromise, originalCellModelPromise]); + const entry = this.getOrCreateModifiedTextFileEntryForCell(modifiedCell, modifiedCellModel, originalCellModel); + return entry ? entry.undo(changes) : false; + }, + modifiedModel: new ObservablePromise(modifiedCellModelPromise), + originalModel: new ObservablePromise(originalCellModelPromise), + diff + }; + + return unchangedCell; + } - createInsertedCellDiffInfo(modifiedCellIndex: number, modifiedCellModel: ITextModel): ICellDiffInfo { + 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); @@ -503,43 +704,38 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie // 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, modifiedCellModel); - 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: modifiedCellModel, - 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)); @@ -550,12 +746,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; }; @@ -565,135 +761,91 @@ 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); + let diffs: ICellDiffInfo[] = []; + this._applyEditsSync(() => { + const index = this.modifiedModel.cells.indexOf(cell); + 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: { + internalId: cell.internalMetadata.internalId + } }; - 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 + mime: originalCell.mime, + internalMetadata: { + internalId: originalCell.internalMetadata.internalId + } }; - 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) - }; - diff.push(unchangedCell); - this._cellDiffInfo.set(sortCellChanges(diff), undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + let cellDiffs: ICellDiffInfo[] = []; + this._applyEditsSync(() => { + cellDiffs = adjustCellDiffForRevertingADeletedCell( + deletedOriginalIndex, + this._cellsDiffInfo.get(), + cellToInsert, + this.modifiedModel.applyEdits.bind(this.modifiedModel), + this.createModifiedCellDiffInfo.bind(this) + ); + }); + this.updateCellDiffInfo(cellDiffs, undefined); } private keepPreviouslyDeletedCell(deletedOriginalIndex: number) { // Delete this cell from original as well. const edit: ICellReplaceEdit = { cells: [], count: 1, editType: CellEditType.Replace, index: deletedOriginalIndex, }; - this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); - const diffs = sortCellChanges(this._cellDiffInfo.get()).slice() + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, false); + const diffs = sortCellChanges(this._cellsDiffInfo.get()) .filter(d => !(d.type === 'delete' && d.originalCellIndex === deletedOriginalIndex)) .map(diff => { if (diff.type !== 'insert' && diff.originalCellIndex > deletedOriginalIndex) { @@ -704,8 +856,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) { @@ -718,33 +869,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 totalNumberOfLines === 0 ? 0 : 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: SnapshotLanguageId, @@ -759,66 +894,100 @@ 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 { + override restoreFromSnapshot(snapshot: ISnapshotEntry, restoreToDisk = true): void { + this.updateCellDiffInfo([], undefined); this._stateObs.set(snapshot.state, undefined); restoreSnapshot(this.originalModel, snapshot.original); - restoreSnapshot(this.modifiedModel, snapshot.current); + if (restoreToDisk) { + 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 readonly cellTextModelMap = new ResourceMap(); + + 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'); + } + const model = this.cellTextModelMap.get(cell.uri) || this._register(await this.textModelService.createModelReference(cell.uri)).object.textEditorModel; + this.cellTextModelMap.set(cell.uri, model); + return model; + } + + 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, this.modifiedResourceRef.object.resource, 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); @@ -827,324 +996,13 @@ 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; } } -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 notebookUri: URI, - 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, - @INotebookEditorService private readonly notebookEditorService: INotebookEditorService - ) { - 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 { - const notebookEditor = this.notebookEditorService.retrieveExistingWidgetFromURI(this.notebookUri)?.value; - if (notebookEditor) { - const vm = notebookEditor.getCellByHandle(this.cell.handle); - vm?.updateEditState(CellEditState.Editing, 'chatEdit'); - } - - // 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); - } - } +function generateCellHash(cellUri: URI) { + const hash = new StringSHA1(); + hash.update(cellUri.toString()); + return hash.digest().substring(0, 8); } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookSnapshot.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookSnapshot.ts deleted file mode 100644 index 68e96412..00000000 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookSnapshot.ts +++ /dev/null @@ -1,103 +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 { decodeBase64, encodeBase64, VSBuffer } from '../../../../../base/common/buffer.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 { NotebookTextModel } from '../../../notebook/common/model/notebookTextModel.js'; -import { ICellDto2, 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); - } - catch (ex) { - console.error('Error restoring Notebook snapshot', ex); - } -} - -export function serializeSnapshot(data: NotebookData, transientOptions: TransientOptions | undefined): string { - data.cells.forEach(cell => { - const outputs = 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, - outputs, - mime: cell.mime, - source: cell.source, - collapseState: cell.collapseState, - // 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. - internalMetadata: undefined - } satisfies ICellDto2; - }); - return JSON.stringify([ - JSON.stringify(transientOptions) - , JSON.stringify(data, (_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/chatEditingNotebookEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookEditorIntegration.ts deleted file mode 100644 index 7306e571..00000000 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookEditorIntegration.ts +++ /dev/null @@ -1,582 +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 { NotebookModifiedCellDecorator } from '../../../notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.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; modifiedCellDecorator: NotebookModifiedCellDecorator; 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 modifiedCellDecorator = store.add(this.instantiationService.createInstance(NotebookModifiedCellDecorator, 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, - modifiedCellDecorator, - 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.modifiedCellDecorator.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 a3da95a5..498740b8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts @@ -13,9 +13,10 @@ import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from import { LinkedList } from '../../../../../base/common/linkedList.js'; import { ResourceMap } from '../../../../../base/common/map.js'; import { derived, IObservable, observableValueOpts, runOnChange, ValueWithChangeEventFromObservable } from '../../../../../base/common/observable.js'; +import { isEqual } from '../../../../../base/common/resources.js'; import { compare } from '../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; -import { assertType, isString } from '../../../../../base/common/types.js'; +import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { TextEdit } from '../../../../../editor/common/languages.js'; import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; @@ -25,24 +26,24 @@ import { IFileService } from '../../../../../platform/files/common/files.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 { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; +import { IStorageService } from '../../../../../platform/storage/common/storage.js'; import { IDecorationData, IDecorationsProvider, IDecorationsService } from '../../../../services/decorations/common/decorations.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js'; import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from '../../../multiDiffEditor/browser/multiDiffSourceResolverService.js'; import { CellUri } from '../../../notebook/common/notebookCommon.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; +import { INotebookService } from '../../../notebook/common/notebookService.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 { ChatModel, IChatResponseModel, isCellTextEditOperation } from '../../common/chatModel.js'; import { IChatService } from '../../common/chatService.js'; +import { ChatAgentLocation } from '../../common/constants.js'; +import { ChatEditorInput } from '../chatEditorInput.js'; import { AbstractChatEditingModifiedFileEntry } from './chatEditingModifiedFileEntry.js'; import { ChatEditingSession } from './chatEditingSession.js'; import { ChatEditingSnapshotTextModelContentProvider, ChatEditingTextModelContentProvider } from './chatEditingTextModelContentProviders.js'; - -const STORAGE_KEY_EDITING_SESSION = 'chat.editingSession'; - export class ChatEditingService extends Disposable implements IChatEditingService { _serviceBrand: undefined; @@ -73,6 +74,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic @ILogService logService: ILogService, @IExtensionService extensionService: IExtensionService, @IProductService productService: IProductService, + @INotebookService private readonly notebookService: INotebookService ) { super(); this._register(decorationsService.registerDecorationsProvider(_instantiationService.createInstance(ChatDecorationsProvider, this.editingSessionsObs))); @@ -103,21 +105,15 @@ export class ChatEditingService extends Disposable implements IChatEditingServic let storageTask: Promise | undefined; this._register(storageService.onWillSaveState(() => { - const sessionIds: string[] = []; const tasks: Promise[] = []; for (const session of this.editingSessionsObs.get()) { if (!session.isGlobalEditingSession) { continue; } - sessionIds.push(session.chatSessionId); tasks.push((session as ChatEditingSession).storeState()); } - if (sessionIds.length) { - storageService.store(STORAGE_KEY_EDITING_SESSION, sessionIds.join(), StorageScope.WORKSPACE, StorageTarget.MACHINE); - } - storageTask = Promise.resolve(storageTask) .then(() => Promise.all(tasks)) .finally(() => storageTask = undefined); @@ -132,28 +128,6 @@ export class ChatEditingService extends Disposable implements IChatEditingServic label: localize('join.chatEditingSession', "Saving chat edits history") }); })); - - const rawSessionsToRestore = storageService.get(STORAGE_KEY_EDITING_SESSION, StorageScope.WORKSPACE); - if (isString(rawSessionsToRestore)) { - - const sessionIds = rawSessionsToRestore.split(','); - - const tasks: Promise[] = []; - for (const sessionId of sessionIds) { - const chatModel = _chatService.getOrRestoreSession(sessionId); - if (!chatModel) { - logService.error(`Edit session session to restore is a non-existing chat session: ${rawSessionsToRestore}`); - continue; - } - tasks.push(this.startOrContinueGlobalEditingSession(chatModel.sessionId)); - } - - this._restoringEditingSession = Promise.all(tasks).finally(() => { - this._restoringEditingSession = undefined; - }); - - storageService.remove(STORAGE_KEY_EDITING_SESSION, StorageScope.WORKSPACE); - } } override dispose(): void { @@ -161,14 +135,17 @@ export class ChatEditingService extends Disposable implements IChatEditingServic super.dispose(); } - async startOrContinueGlobalEditingSession(chatSessionId: string): Promise { - await this._restoringEditingSession; + async startOrContinueGlobalEditingSession(chatModel: ChatModel, waitForRestore = true): Promise { + if (waitForRestore) { + await this._restoringEditingSession; + } - const session = this.getEditingSession(chatSessionId); + const session = this.getEditingSession(chatModel.sessionId); if (session) { return session; } - return this.createEditingSession(chatSessionId, true); + const result = await this.createEditingSession(chatModel, true); + return result; } @@ -189,11 +166,11 @@ export class ChatEditingService extends Disposable implements IChatEditingServic .find(candidate => candidate.chatSessionId === chatSessionId); } - async createEditingSession(chatSessionId: string, global: boolean = false): Promise { + async createEditingSession(chatModel: ChatModel, global: boolean = false): Promise { - assertType(this.getEditingSession(chatSessionId) === undefined, 'CANNOT have more than one editing session per chat session'); + assertType(this.getEditingSession(chatModel.sessionId) === undefined, 'CANNOT have more than one editing session per chat session'); - const session = this._instantiationService.createInstance(ChatEditingSession, chatSessionId, global, this._lookupEntry.bind(this)); + const session = this._instantiationService.createInstance(ChatEditingSession, chatModel.sessionId, global, this._lookupEntry.bind(this)); await session.init(); const list = this._sessionsObs.get(); @@ -202,7 +179,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic const store = new DisposableStore(); this._store.add(store); - store.add(this.installAutoApplyObserver(session)); + store.add(this.installAutoApplyObserver(session, chatModel)); store.add(session.onDidDispose(e => { removeSession(); @@ -215,8 +192,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic return session; } - private installAutoApplyObserver(session: ChatEditingSession): IDisposable { - const chatModel = this._chatService.getOrRestoreSession(session.chatSessionId); + private installAutoApplyObserver(session: ChatEditingSession, chatModel: ChatModel): IDisposable { if (!chatModel) { throw new ErrorNoTelemetry(`Edit session was created for a non-existing chat session: ${session.chatSessionId}`); } @@ -251,12 +227,14 @@ export class ChatEditingService extends Disposable implements IChatEditingServic return; } - editedFilesExist.set(uri, this._fileService.exists(uri).then((e) => { + const fileExists = this.notebookService.getNotebookTextModel(uri) ? Promise.resolve(true) : this._fileService.exists(uri); + editedFilesExist.set(uri, fileExists.then((e) => { if (!e) { return; } const activeUri = this._editorService.activeEditorPane?.input.resource; - const inactive = activeUri && session.workingSet.has(activeUri); + const inactive = this._editorService.activeEditorPane?.input instanceof ChatEditorInput && this._editorService.activeEditorPane.input.sessionId === session.chatSessionId || + Boolean(activeUri && session.entries.get().find(entry => isEqual(activeUri, entry.modifiedURI))); this._editorService.openEditor({ resource: uri, options: { inactive, preserveFocus: true, pinned: true } }); })); }; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index 2d7f1a46..98941d4c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -10,11 +10,9 @@ 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, 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'; import { URI } from '../../../../../base/common/uri.js'; import { IBulkEditService } from '../../../../../editor/browser/services/bulkEditService.js'; @@ -34,11 +32,9 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { ILogService } from '../../../../../platform/log/common/log.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { SaveReason } from '../../../../common/editor.js'; import { DiffEditorInput } from '../../../../common/editor/diffEditorInput.js'; import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { ITextFileService } from '../../../../services/textfile/common/textfiles.js'; import { MultiDiffEditor } from '../../../multiDiffEditor/browser/multiDiffEditor.js'; import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiffEditorInput.js'; import { INotebookService } from '../../../notebook/common/notebookService.js'; @@ -49,8 +45,10 @@ import { AbstractChatEditingModifiedFileEntry, IModifiedEntryTelemetryInfo, ISna import { ChatEditingModifiedDocumentEntry } from './chatEditingModifiedDocumentEntry.js'; import { ChatEditingTextModelContentProvider } from './chatEditingTextModelContentProviders.js'; import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCommon.js'; -import { ChatEditingModifiedNotebookDiff, ChatEditingModifiedNotebookEntry } from './chatEditingModifiedNotebookEntry.js'; +import { ChatEditingModifiedNotebookEntry } from './chatEditingModifiedNotebookEntry.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { ChatEditingModifiedNotebookDiff } from './notebook/chatEditingModifiedNotebookDiff.js'; +import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; const STORAGE_CONTENTS_FOLDER = 'contents'; const STORAGE_STATE_FILE = 'state.json'; @@ -141,19 +139,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; @@ -208,9 +193,9 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio @IEditorService private readonly _editorService: IEditorService, @IChatService private readonly _chatService: IChatService, @INotebookService private readonly _notebookService: INotebookService, - @ITextFileService private readonly _textFileService: ITextFileService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @IAccessibilitySignalService private readonly _accessibilitySignalService: IAccessibilitySignalService, ) { super(); } @@ -223,14 +208,15 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } await asyncTransaction(async tx => { this._pendingSnapshot = restoredSessionState.pendingSnapshot; - await this._restoreSnapshot(restoredSessionState.recentSnapshot, tx); + await this._restoreSnapshot(restoredSessionState.recentSnapshot, tx, false); this._linearHistory.set(restoredSessionState.linearHistory, tx); this._linearHistoryIndex.set(restoredSessionState.linearHistoryIndex, tx); this._state.set(ChatEditingSessionState.Idle, tx); }); + } else { + this._state.set(ChatEditingSessionState.Idle, undefined); } - this._triggerSaveParticipantsOnAccept(); this._register(autorun(reader => { const entries = this.entries.read(reader); entries.forEach(entry => { @@ -266,39 +252,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio return storage.storeState(state); } - private _triggerSaveParticipantsOnAccept() { - const im = this._register(new DisposableMap()); - const attachToEntry = (entry: IModifiedFileEntry) => { - return autorunDelta(entry.state, ({ lastValue, newValue }) => { - if (newValue === WorkingSetEntryState.Accepted && lastValue === WorkingSetEntryState.Modified) { - // Don't save a file if there's still pending changes. If there's not (e.g. - // the agentic flow with autosave) then save again to trigger participants. - if (!this._textFileService.isDirty(entry.modifiedURI)) { - this._textFileService.save(entry.modifiedURI, { - reason: SaveReason.EXPLICIT, - force: true, - ignoreErrorHandler: true, - }).catch(() => { - // ignored - }); - } - } - }); - }; - - this._register(autorunIterableDelta( - reader => this._entriesObs.read(reader), - ({ addedValues, removedValues }) => { - for (const entry of addedValues) { - im.set(entry, attachToEntry(entry)); - } - for (const entry of removedValues) { - im.deleteAndDispose(entry); - } - } - )); - } - private _findSnapshot(requestId: string): IChatEditingSessionSnapshot | undefined { return this._linearHistory.get().find(s => s.requestId === requestId); } @@ -430,10 +383,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(); @@ -520,7 +471,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } } - private async _restoreSnapshot({ workingSet, entries }: IChatEditingSessionStop, tx: ITransaction | undefined): Promise { + private async _restoreSnapshot({ workingSet, entries }: IChatEditingSessionStop, tx: ITransaction | undefined, restoreResolvedToDisk = true): Promise { this._workingSet = new ResourceMap(workingSet); // Reset all the files which are modified in this session state @@ -537,7 +488,8 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio // Restore all entries from the snapshot for (const snapshotEntry of entries.values()) { const entry = await this._getOrCreateModifiedFileEntry(snapshotEntry.resource, snapshotEntry.telemetryInfo); - entry.restoreFromSnapshot(snapshotEntry); + const restoreToDisk = snapshotEntry.state === WorkingSetEntryState.Modified || restoreResolvedToDisk; + entry.restoreFromSnapshot(snapshotEntry, restoreToDisk); entriesArr.push(entry); } @@ -561,9 +513,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); - } } } @@ -574,22 +523,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`); @@ -612,7 +545,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } } }); - + this._accessibilitySignalService.playSignal(AccessibilitySignal.editsKept, { allowManyInParallel: true }); this._onDidChange.fire(ChatEditingSessionChangeType.Other); } @@ -631,7 +564,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } } }); - + this._accessibilitySignalService.playSignal(AccessibilitySignal.editsUndone, { allowManyInParallel: true }); this._onDidChange.fire(ChatEditingSessionChangeType.Other); } @@ -656,7 +589,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio private _stopPromise: Promise | undefined; async stop(clearState = false): Promise { - this._stopPromise ??= this._performStop(); + this._stopPromise ??= Promise.allSettled([this._performStop(), this.storeState()]).then(() => { }); await this._stopPromise; if (clearState) { await this._instantiationService.createInstance(ChatEditingSessionStorage, this.chatSessionId).clearState(); @@ -674,11 +607,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } }); })); - - if (this._state.get() !== ChatEditingSessionState.Disposed) { - // session got disposed while we were closing editors and clearing state - this.dispose(); - } } override dispose() { @@ -760,61 +688,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio }; } - private _trackUntitledWorkingSetEntry(resource: URI) { - if (resource.scheme !== Schemas.untitled) { - return; - } - const untitled = this._textFileService.untitled.get(resource); - if (!untitled) { // Shouldn't happen - return; - } - - // Track this file until - // 1. it is removed from the working set - // 2. it is closed - // 3. we are disposed - const store = new DisposableStore(); - store.add(this.onDidChange(e => { - if (e === ChatEditingSessionChangeType.WorkingSet && !this._workingSet.get(resource)) { - // The user has removed the file from the working set - store.dispose(); - } - })); - store.add(this._textFileService.untitled.onDidSave(e => { - const existing = this._workingSet.get(resource); - if (isEqual(e.source, resource) && existing) { - this._workingSet.delete(resource); - this._workingSet.set(e.target, existing); - store.dispose(); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - })); - store.add(this._editorService.onDidCloseEditor((e) => { - if (isEqual(e.editor.resource, resource)) { - this._workingSet.delete(resource); - store.dispose(); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - })); - this._store.add(store); - } - - addFileToWorkingSet(resource: URI, description?: string, proposedState?: WorkingSetEntryState.Suggested): void { - const state = this._workingSet.get(resource); - if (proposedState === WorkingSetEntryState.Suggested) { - if (state !== undefined || this._removedTransientEntries.has(resource)) { - return; - } - this._workingSet.set(resource, { description, state: WorkingSetEntryState.Suggested }); - this._trackUntitledWorkingSetEntry(resource); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } else if (state === undefined || state.state === WorkingSetEntryState.Suggested) { - this._workingSet.set(resource, { description, state: WorkingSetEntryState.Attached }); - this._trackUntitledWorkingSetEntry(resource); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - } - private _getHistoryEntryByLinearIndex(index: number) { const history = this._linearHistory.get(); const searchedIndex = binarySearch2(history.length, (e) => history[e].startIndex - index); @@ -1050,7 +923,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio try { // If a notebook isn't open, then use the old synchronization approach. if (this._notebookService.hasSupportedNotebooks(notebookUri) && (this._notebookService.getNotebookTextModel(notebookUri) || ChatEditingModifiedNotebookEntry.canHandleSnapshotContent(initialContent))) { - return ChatEditingModifiedNotebookEntry.create(notebookUri, multiDiffEntryDelegate, telemetryInfo, chatKind, initialContent, this._instantiationService); + 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); @@ -1063,7 +936,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio await this._bulkEditService.apply({ edits: [{ newResource: resource }] }); this._editorService.openEditor({ resource, options: { inactive: true, preserveFocus: true, pinned: true } }); if (this._notebookService.hasSupportedNotebooks(notebookUri)) { - return ChatEditingModifiedNotebookEntry.create(resource, multiDiffEntryDelegate, telemetryInfo, chatKind, initialContent, this._instantiationService); + return await ChatEditingModifiedNotebookEntry.create(resource, multiDiffEntryDelegate, telemetryInfo, ChatEditKind.Created, initialContent, this._instantiationService); } else { return this._createModifiedFileEntry(resource, telemetryInfo, true, initialContent); } @@ -1105,8 +978,14 @@ class ChatEditingSessionStorage { public async restoreState(): Promise { const storageLocation = this._getStorageLocation(); + const fileContents = new Map>(); const getFileContent = (hash: string) => { - return this._fileService.readFile(joinPath(storageLocation, STORAGE_CONTENTS_FOLDER, hash)).then(content => content.value.toString()); + let readPromise = fileContents.get(hash); + if (!readPromise) { + readPromise = this._fileService.readFile(joinPath(storageLocation, STORAGE_CONTENTS_FOLDER, hash)).then(content => content.value.toString()); + fileContents.set(hash, readPromise); + } + return readPromise; }; const deserializeResourceMap = (resourceMap: ResourceMapDTO, deserialize: (value: any) => T, result: ResourceMap): ResourceMap => { resourceMap.forEach(([resourceURI, value]) => { @@ -1158,7 +1037,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; } @@ -1199,7 +1078,7 @@ class ChatEditingSessionStorage { try { const stat = await this._fileService.resolve(contentsFolder); stat.children?.forEach(child => { - if (child.isDirectory) { + if (child.isFile) { existingContents.add(child.name); } }); @@ -1218,9 +1097,7 @@ class ChatEditingSessionStorage { const shaComputer = new StringSHA1(); shaComputer.update(content); const sha = shaComputer.digest().substring(0, 7); - if (!existingContents.has(sha)) { - fileContents.set(sha, content); - } + fileContents.set(sha, content); return sha; }; const serializeResourceMap = (resourceMap: ResourceMap, serialize: (value: T) => any): ResourceMapDTO => { @@ -1267,7 +1144,9 @@ class ChatEditingSessionStorage { this._logService.debug(`chatEditingSession: Storing editing session at ${storageFolder.toString()}: ${fileContents.size} files`); for (const [hash, content] of fileContents) { - await this._fileService.writeFile(joinPath(contentsFolder, hash), VSBuffer.fromString(content)); + if (!existingContents.has(hash)) { + await this._fileService.writeFile(joinPath(contentsFolder, hash), VSBuffer.fromString(content)); + } } await this._fileService.writeFile(joinPath(storageFolder, STORAGE_STATE_FILE), VSBuffer.fromString(JSON.stringify(data, undefined, 2))); @@ -1355,7 +1234,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 00000000..5521a153 --- /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 00000000..3d70ef83 --- /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, outputSizeConfig: IConfigurationService | number): string { + const outputSizeLimit = (typeof outputSizeConfig === 'number' ? outputSizeConfig : outputSizeConfig.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 internalId = cell.internalMetadata?.internalId; + if (internalId) { + edits.push({ editType: CellEditType.PartialInternalMetadata, index, internalMetadata: { internalId } }); + } + }); + 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 00000000..b516adce --- /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 00000000..9418dc7c --- /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 00000000..ced1613c --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts @@ -0,0 +1,674 @@ +/*--------------------------------------------------------------------------------------------- + * 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, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { autorun, 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 { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.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 { NotebookModifiedCellDecorator } from '../../../../notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.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 { CellKind } from '../../../../notebook/common/notebookCommon.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); + } + + public override dispose(): void { + this.integration.dispose(); + super.dispose(); + } +} + +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 deletedCellDecorator: NotebookDeletedCellDecorator | undefined; + private insertedCellDecorator: NotebookInsertedCellDecorator | undefined; + private modifiedCellDecorator: NotebookModifiedCellDecorator | undefined; + + private readonly cellEditorIntegrations = new Map }>(); + + private readonly mdCellEditorAttached = observableValue(this, -1); + + private markupCellListeners = new Map(); + + 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, + @IAccessibilitySignalService private readonly accessibilitySignalService: IAccessibilitySignalService, + ) { + super(); + + const onDidChangeVisibleRanges = debouncedObservable(observableFromEvent(notebookEditor.onDidChangeVisibleRanges, () => notebookEditor.visibleRanges), 50); + + this._register(toDisposable(() => { + this.markupCellListeners.forEach((v) => v.dispose()); + })); + + 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) { + notebookEditor.setOptions({ isReadOnly: true }); + } else if (originalReadonly === false) { + notebookEditor.setOptions({ isReadOnly: false }); + // Ensure all cells area editable. + // We make use of chatEditingCodeEditorIntegration to handle cell diffing and navigation. + // However that also makes the cell read-only. We need to ensure that the cell is editable. + // E.g. first we make notebook readonly (in here), then cells end up being readonly because notebook is readonly. + // Then chatEditingCodeEditorIntegration makes cells readonly and keeps track of the original readonly state. + // However the cell is already readonly because the notebook is readonly. + // So when we restore the notebook to editable (in here), the cell is made editable again. + // But when chatEditingCodeEditorIntegration attempts to restore, it will restore the original readonly state. + // & from the perpspective of chatEditingCodeEditorIntegration, the cell was readonly & should continue to be readonly. + // To get around this, we wait for a few ms before restoring the original readonly state for each cell. + const timeout = setTimeout(() => { + notebookEditor.setOptions({ isReadOnly: true }); + notebookEditor.setOptions({ isReadOnly: false }); + disposable.dispose(); + }, 100); + const disposable = toDisposable(() => clearTimeout(timeout)); + this._register(disposable); + } + })); + + // 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) + ) { + lastModifyingRequestId = _entry.lastModifyingRequestId; + this.reveal(true); + } + })); + + // Build cell integrations (responsible for navigating changes within a cell and decorating cell text changes) + this._register(autorun(r => { + if (this.notebookEditor.textModel !== this.notebookModel) { + return; + } + 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; + } + this.mdCellEditorAttached.read(r); + + 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 (!cell || !originalModel || !modifiedModel) { + return; + } + if (!editor) { + if (!this.markupCellListeners.has(cell.handle) && cell.cellKind === CellKind.Markup) { + const cellModel = this.notebookEditor.getViewModel()?.viewCells.find(c => c.handle === cell.handle); + if (cellModel) { + const listener = cellModel.onDidChangeEditorAttachState(() => { + if (cellModel.editorAttached) { + this.mdCellEditorAttached.set(cell.handle, undefined); + listener.dispose(); + this.markupCellListeners.delete(cell.handle); + } + }); + this.markupCellListeners.set(cell.handle, listener); + } + } + 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); + })); + + const cellsAreVisible = onDidChangeVisibleRanges.map(v => v.length > 0); + const debouncedChanges = debouncedObservable(cellChanges, 10); + this._register(autorun(r => { + if (this.notebookEditor.textModel !== this.notebookModel || !cellsAreVisible.read(r) || !this.notebookEditor.getViewModel()) { + return; + } + // We can have inserted cells that have been accepted, in those cases we do not want any decorators on them. + const changes = debouncedChanges.read(r).filter(c => c.type === 'insert' ? !c.diff.read(r).identical : true); + const modifiedChanges = changes.filter(c => c.type === 'modified'); + + this.createDecorators(); + this.insertedCellDecorator?.apply(changes); + this.modifiedCellDecorator?.apply(modifiedChanges); + this.deletedCellDecorator?.apply(changes, originalModel); + })); + } + + private createDecorators() { + const cellChanges = this.cellChanges.get(); + const accessibilitySignalService = this.accessibilitySignalService; + + this.insertedCellDecorator ??= this._register(this.instantiationService.createInstance(NotebookInsertedCellDecorator, this.notebookEditor)); + this.modifiedCellDecorator ??= this._register(this.instantiationService.createInstance(NotebookModifiedCellDecorator, this.notebookEditor)); + + if (this.deletedCellDecorator) { + this._store.delete(this.deletedCellDecorator); + this.deletedCellDecorator.dispose(); + } + this.deletedCellDecorator = this._register(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.find(c => c.type === 'delete' && c.originalCellIndex === deletedCellIndex); + if (entry) { + return entry.keep(entry.diff.get().changes[0]); + } + accessibilitySignalService.playSignal(AccessibilitySignal.editsKept, { allowManyInParallel: true }); + return Promise.resolve(true); + }, + reject() { + const entry = cellChanges.find(c => c.type === 'delete' && c.originalCellIndex === deletedCellIndex); + if (entry) { + return entry.undo(entry.diff.get().changes[0]); + } + accessibilitySignalService.playSignal(AccessibilitySignal.editsUndone, { allowManyInParallel: true }); + return Promise.resolve(true); + }, + } satisfies IModifiedFileEntryChangeHunk; + } + })); + } + + 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.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.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 | undefined): Promise { + const targetLines = lines ?? new LineRange(0, 0); + await this.notebookEditor.focusNotebookCell(cell, 'container', { focusEditorLine: targetLines.startLineNumber }); + await this.notebookEditor.revealRangeInCenterAsync(cell, new Range(targetLines.startLineNumber, 0, targetLines.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 + // first check if we are at the end of the current change + switch (currentChange.change.type) { + case 'modified': + { + const cellIntegration = this.getCell(currentChange.change.modifiedCellIndex); + if (cellIntegration) { + if (cellIntegration.next(false)) { + this._currentChange.set({ change: currentChange.change, index: cellIntegration.currentIndex.get() }, undefined); + return true; + } + } + + 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': + { + const cellIntegration = this.getCell(currentChange.change.modifiedCellIndex); + if (cellIntegration) { + if (cellIntegration.previous(false)) { + this._currentChange.set({ change: currentChange.change, index: cellIntegration.currentIndex.get() }, undefined); + return true; + } + } + + const isFirstChangeInCell = currentChange.index === 0; + const change = isFirstChangeInCell ? changes[changes.indexOf(currentChange.change) - 1] : currentChange.change; + + if (change) { + const index = isFirstChangeInCell ? lastChangeIndex(change) : currentChange.index - 1; + return this._revealChange(change, index); + } + } + break; + case 'insert': + case 'delete': + { + // go to previous change directly + const prevChange = changes[changes.indexOf(currentChange.change) - 1]; + if (prevChange) { + return this._revealFirstOrLast(prevChange, false); + } + } + break; + default: + break; + } + + if (wrap) { + const lastChange = changes[changes.length - 1]; + if (lastChange) { + return this._revealFirstOrLast(lastChange, false); + } + } + + return false; + } + + enableAccessibleDiffView(): void { + const cell = this.notebookEditor.getActiveCell()?.model; + if (cell) { + const integration = this.cellEditorIntegrations.get(cell)?.integration; + integration?.enableAccessibleDiffView(); + } + } + acceptNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.accept(); + this.next(true); + } + rejectNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.reject(); + this.next(true); + } + async toggleDiff(_change: IModifiedFileEntryChangeHunk | undefined): Promise { + const defaultAgentName = this._chatAgentService.getDefaultAgent(ChatAgentLocation.EditingSession)?.fullName; + const diffInput = { + original: { resource: this._entry.originalURI, options: { selection: undefined } }, + modified: { resource: this._entry.modifiedURI, options: { selection: undefined } }, + label: defaultAgentName + ? localize('diff.agent', '{0} (changes from {1})', basename(this._entry.modifiedURI), defaultAgentName) + : localize('diff.generic', '{0} (changes from chat)', basename(this._entry.modifiedURI)) + } satisfies IResourceDiffEditorInput; + await this._editorService.openEditor(diffInput); + + } +} + +export class ChatEditingNotebookDiffEditorIntegration extends Disposable implements IModifiedFileEntryEditorIntegration { + private readonly _currentIndex = observableValue(this, -1); + readonly currentIndex: IObservable = this._currentIndex; + + constructor( + private readonly notebookDiffEditor: INotebookTextDiffEditor, + private readonly cellChanges: IObservable + ) { + super(); + + this._store.add(autorun(r => { + const index = notebookDiffEditor.currentChangedIndex.read(r); + const numberOfCellChanges = cellChanges.read(r).filter(c => !c.diff.read(r).identical); + if (numberOfCellChanges.length && index >= 0 && index < numberOfCellChanges.length) { + // Notebook Diff editor only supports navigating through changes to cells. + // However in chat we take changes to lines in the cells into account. + // So if we're on the second cell and first cell has 3 changes, then we're on the 4th change. + const changesSoFar = countChanges(numberOfCellChanges.slice(0, index + 1)); + this._currentIndex.set(changesSoFar - 1, undefined); + } else { + this._currentIndex.set(-1, undefined); + } + })); + } + + reveal(firstOrLast: boolean): void { + const changes = sortCellChanges(this.cellChanges.get().filter(c => c.type !== 'unchanged')); + if (!changes.length) { + return undefined; + } + if (firstOrLast) { + this.notebookDiffEditor.firstChange(); + } else { + this.notebookDiffEditor.lastChange(); + } + } + + next(_wrap: boolean): boolean { + const changes = this.cellChanges.get().filter(c => !c.diff.get().identical).length; + if (this.notebookDiffEditor.currentChangedIndex.get() === changes - 1) { + return false; + } + this.notebookDiffEditor.nextChange(); + return true; + } + + previous(_wrap: boolean): boolean { + const changes = this.cellChanges.get().filter(c => !c.diff.get().identical).length; + if (this.notebookDiffEditor.currentChangedIndex.get() === changes - 1) { + return false; + } + this.notebookDiffEditor.nextChange(); + return true; + } + + enableAccessibleDiffView(): void { + // + } + acceptNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.accept(); + this.next(true); + } + rejectNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.reject(); + this.next(true); + } + async toggleDiff(_change: IModifiedFileEntryChangeHunk | undefined): Promise { + // + } +} + +function areDocumentDiff2Equal(diff1: IDocumentDiff2, diff2: IDocumentDiff2): boolean { + if (diff1.changes !== diff2.changes) { + return false; + } + if (diff1.identical !== diff2.identical) { + return false; + } + if (diff1.moves !== diff2.moves) { + return false; + } + if (diff1.originalModel !== diff2.originalModel) { + return false; + } + if (diff1.modifiedModel !== diff2.modifiedModel) { + return false; + } + if (diff1.keep !== diff2.keep) { + return false; + } + if (diff1.undo !== diff2.undo) { + return false; + } + if (diff1.quitEarly !== diff2.quitEarly) { + return false; + } + return true; +} + +function lastChangeIndex(change: ICellDiffInfo): number { + if (change.type === 'modified') { + return change.diff.get().changes.length - 1; + } + return 0; +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookFileSystemProvider.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts similarity index 83% rename from src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookFileSystemProvider.ts rename to src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts index 05716980..b79dd102 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookFileSystemProvider.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer } from '../../../../../base/common/buffer.js'; -import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { Event } from '../../../../../base/common/event.js'; -import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; -import { ResourceMap } from '../../../../../base/common/map.js'; -import { ReadableStreamEvents } from '../../../../../base/common/stream.js'; -import { URI } from '../../../../../base/common/uri.js'; -import { FileSystemProviderCapabilities, FileType, IFileChange, IFileDeleteOptions, IFileOpenOptions, IFileOverwriteOptions, IFileReadStreamOptions, IFileService, IFileSystemProvider, IFileWriteOptions, IStat, IWatchOptions } from '../../../../../platform/files/common/files.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { IWorkbenchContribution } from '../../../../common/contributions.js'; -import { INotebookService } from '../../../notebook/common/notebookService.js'; -import { IChatEditingService } from '../../common/chatEditingService.js'; +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { CancellationToken } from '../../../../../../base/common/cancellation.js'; +import { Event } from '../../../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../../../base/common/map.js'; +import { ReadableStreamEvents } from '../../../../../../base/common/stream.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { FileSystemProviderCapabilities, FileType, IFileChange, IFileDeleteOptions, IFileOpenOptions, IFileOverwriteOptions, IFileReadStreamOptions, IFileService, IFileSystemProvider, IFileWriteOptions, IStat, IWatchOptions } from '../../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { IWorkbenchContribution } from '../../../../../common/contributions.js'; +import { INotebookService } from '../../../../notebook/common/notebookService.js'; +import { IChatEditingService } from '../../../common/chatEditingService.js'; import { ChatEditingNotebookSnapshotScheme, deserializeSnapshot } from './chatEditingModifiedNotebookSnapshot.js'; -import { ChatEditingSession } from './chatEditingSession.js'; +import { ChatEditingSession } from '../chatEditingSession.js'; export class ChatEditingNotebookFileSystemProviderContrib extends Disposable implements IWorkbenchContribution { 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 00000000..e2b9aed2 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/helpers.ts @@ -0,0 +1,418 @@ +/*--------------------------------------------------------------------------------------------- + * 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; + }); + let diffEntryIndex = -1; + let indexToInsertInOriginalModel: number | undefined = undefined; + if (cells.length) { + for (let i = 0; i < cellDiffInfo.length; i++) { + const diff = cellDiffInfo[i]; + if (typeof diff.modifiedCellIndex === 'number' && diff.modifiedCellIndex === change[0]) { + diffEntryIndex = i; + + if (typeof diff.originalCellIndex === 'number') { + indexToInsertInOriginalModel = diff.originalCellIndex; + } + break; + } + if (typeof diff.originalCellIndex === 'number') { + indexToInsertInOriginalModel = diff.originalCellIndex + 1; + } + } + + const edit: ICellEditOperation = { + editType: CellEditType.Replace, + cells, + index: indexToInsertInOriginalModel ?? 0, + 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 && diffEntryIndex >= 0) { + 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 ?? 0); + const modifiedCellIndex = change[0] + i; + const unchangedCell = createModifiedCellDiffInfo(modifiedCellIndex, originalCellIndex); + cellDiffInfo.splice((diffEntryIndex === -1 ? cellDiffInfo.length : 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 00000000..376d0ed8 --- /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 7b89c3d6..843bd3bb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditor.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditor.ts @@ -16,14 +16,15 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; import { IEditorOpenContext } from '../../../common/editor.js'; import { Memento } from '../../../common/memento.js'; +import { EDITOR_DRAG_AND_DROP_BACKGROUND } from '../../../common/theme.js'; +import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; +import { IChatModel, IExportableChatData, ISerializableChatData } 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 { 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'; export interface IChatEditorOptions extends IEditorOptions { target?: { sessionId: string } | { data: IExportableChatData | ISerializableChatData }; @@ -47,6 +48,7 @@ export class ChatEditor extends EditorPane { @IInstantiationService private readonly instantiationService: IInstantiationService, @IStorageService private readonly storageService: IStorageService, @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IChatService private readonly chatService: IChatService, ) { super(ChatEditorInput.EditorID, group, telemetryService, themeService, storageService); } @@ -67,7 +69,20 @@ export class ChatEditor extends EditorPane { ChatAgentLocation.Panel, undefined, { + autoScroll: mode => mode !== ChatMode.Ask, + renderFollowups: true, supportsFileReferences: true, + supportsAdditionalParticipants: true, + rendererOptions: { + renderTextEditsAsSummary: (uri) => { + return this.chatService.isEditingLocation(ChatAgentLocation.Panel); + }, + referencesExpandedWhenEmptyResponse: !this.chatService.isEditingLocation(ChatAgentLocation.Panel), + progressMessageAtBottomOfResponse: mode => mode !== ChatMode.Ask, + }, + enableImplicitContext: true, + enableWorkingSet: this.chatService.isEditingLocation(ChatAgentLocation.Panel) ? 'explicit' : undefined, + supportsChangingModes: this.chatService.isEditingLocation(ChatAgentLocation.Panel), }, { listForeground: editorForeground, @@ -127,6 +142,7 @@ export class ChatEditor extends EditorPane { // Need to set props individually on the memento this._viewState.inputValue = widgetViewState.inputValue; + this._viewState.inputState = widgetViewState.inputState; this._memento.saveMemento(); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index 5bac1b4a..7f112258 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -13,16 +13,18 @@ import { URI } from '../../../../base/common/uri.js'; import * as nls from '../../../../nls.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; -import { EditorInputCapabilities, IEditorSerializer, IUntypedEditorInput } from '../../../common/editor.js'; -import { EditorInput } from '../../../common/editor/editorInput.js'; +import { EditorInputCapabilities, IEditorIdentifier, IEditorSerializer, IUntypedEditorInput } from '../../../common/editor.js'; +import { EditorInput, IEditorCloseHandler } 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'; +import { ConfirmResult, IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { shouldShowClearEditingSessionConfirmation, showClearEditingSessionConfirmation } from './actions/chatActions.js'; const ChatEditorIcon = registerIcon('chat-editor-label-icon', Codicon.commentDiscussion, nls.localize('chatEditorLabelIcon', 'Icon of the chat editor label.')); -export class ChatEditorInput extends EditorInput { +export class ChatEditorInput extends EditorInput implements IEditorCloseHandler { static readonly countsInUse = new Set(); static readonly TypeID: string = 'workbench.input.chatSession'; @@ -50,7 +52,8 @@ export class ChatEditorInput extends EditorInput { constructor( readonly resource: URI, readonly options: IChatEditorOptions, - @IChatService private readonly chatService: IChatService + @IChatService private readonly chatService: IChatService, + @IDialogService private readonly dialogService: IDialogService, ) { super(); @@ -67,6 +70,23 @@ export class ChatEditorInput extends EditorInput { this._register(toDisposable(() => ChatEditorInput.countsInUse.delete(this.inputCount))); } + override closeHandler = this; + + showConfirm(): boolean { + return this.model?.editingSession ? shouldShowClearEditingSessionConfirmation(this.model.editingSession) : false; + } + + async confirm(editors: ReadonlyArray): Promise { + if (!this.model?.editingSession) { + return ConfirmResult.SAVE; + } + + const titleOverride = nls.localize('chatEditorConfirmTitle', "Close Chat Editor"); + const messageOverride = nls.localize('chat.startEditing.confirmation.pending.message.default', "Closing the chat editor will end your current edit session."); + const result = await showClearEditingSessionConfirmation(this.model.editingSession, this.dialogService, { titleOverride, messageOverride }); + return result ? ConfirmResult.SAVE : ConfirmResult.CANCEL; + } + override get editorId(): string | undefined { return ChatEditorInput.EditorID; } @@ -93,7 +113,8 @@ export class ChatEditorInput extends EditorInput { override async resolve(): Promise { if (typeof this.sessionId === 'string') { - this.model = this.chatService.getOrRestoreSession(this.sessionId); + this.model = await this.chatService.getOrRestoreSession(this.sessionId) + ?? this.chatService.startSession(ChatAgentLocation.Panel, CancellationToken.None); } else if (!this.options.target) { this.model = this.chatService.startSession(ChatAgentLocation.Panel, CancellationToken.None); } else if ('data' in this.options.target) { diff --git a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts index d600d02a..f4d55df9 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/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 83391be2..43b06df7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -12,18 +12,16 @@ 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 { IAction, Separator, toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../base/common/actions.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { HistoryNavigator2 } from '../../../../base/common/history.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { ResourceSet } from '../../../../base/common/map.js'; -import { basename, dirname } from '../../../../base/common/path.js'; import { isMacintosh } from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; import { IEditorConstructionOptions } from '../../../../editor/browser/config/editorConfiguration.js'; @@ -32,7 +30,7 @@ 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 { Range } from '../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../editor/common/model.js'; import { IModelService } from '../../../../editor/common/services/model.js'; import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; @@ -54,10 +52,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'; @@ -65,40 +61,46 @@ 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 { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { ISharedWebContentExtractorService } from '../../../../platform/webContentExtractor/common/webContentExtractor.js'; +import { ResourceLabels } from '../../../browser/labels.js'; +import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js'; import { AccessibilityCommandId } from '../../accessibility/common/accessibilityCommands.js'; import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions, setupSimpleEditorSelectionStyling } from '../../codeEditor/browser/simpleEditorOptions.js'; -import { revealInSideBarCommand } from '../../files/browser/fileActions.contribution.js'; -import { ChatAgentLocation, IChatAgentService } from '../common/chatAgents.js'; +import { IChatAgentService } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../common/chatEditingService.js'; -import { IChatRequestVariableEntry, isImageVariableEntry, isLinkVariableEntry, isPasteVariableEntry } from '../common/chatModel.js'; -import { IChatFollowup } from '../common/chatService.js'; +import { IChatEditingSession } from '../common/chatEditingService.js'; +import { ChatEntitlement, IChatEntitlementService } from '../common/chatEntitlementService.js'; +import { IChatRequestVariableEntry, isImageVariableEntry, isPasteVariableEntry } from '../common/chatModel.js'; +import { IChatFollowup, IChatService } from '../common/chatService.js'; import { IChatVariablesService } from '../common/chatVariables.js'; import { IChatResponseViewModel } from '../common/chatViewModel.js'; import { ChatInputHistoryMaxEntries, IChatHistoryEntry, IChatInputState, IChatWidgetHistoryService } from '../common/chatWidgetHistoryService.js'; +import { ChatAgentLocation, ChatConfiguration, ChatMode, validateChatMode } 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, ChatEditingSessionSubmitAction, ChatSubmitAction, 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, PasteAttachmentWidget } from './chatAttachmentWidgets.js'; import { IDisposableReference } from './chatContentParts/chatCollections.js'; import { CollapsibleListPool, IChatCollapsibleListItem } from './chatContentParts/chatReferencesContentPart.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 { convertUint8ArrayToString, resizeImage } from './imageUtils.js'; +import { ChatRelatedFiles } from './contrib/chatInputRelatedFilesContrib.js'; +import { resizeImage } from './imageUtils.js'; const $ = dom.$; @@ -121,11 +123,12 @@ interface IChatInputPartOptions { editorOverflowWidgetsDomNode?: HTMLElement; renderWorkingSet?: boolean; enableImplicitContext?: boolean; + supportsChangingModes?: boolean; + widgetViewKindTag: string; } export interface IWorkingSetEntry { uri: URI; - isMarkedReadonly?: boolean; } export class ChatInputPart extends Disposable implements IHistoryNavigationWidget { @@ -155,32 +158,14 @@ 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) { contextArr.push(this.implicitContext.toBaseEntry()); } - // retrieve links from the input editor - const linkOccurrences = this.inputEditor.getContribution(LinkDetector.ID)?.getAllLinkOccurrences() ?? []; - const linksSeen = new Set(); - for (const linkOccurrence of linkOccurrences) { - const link = linkOccurrence.link; - const uri = URI.isUri(link.url) ? link.url : link.url ? URI.parse(link.url) : undefined; - if (!uri || linksSeen.has(uri.toString())) { - continue; - } - - linksSeen.add(uri.toString()); - contextArr.push({ - kind: 'link', - id: uri.toString(), - name: uri.fsPath, - value: uri, - isFile: false, - }); - } - // factor in nested file links of a prompt into the implicit context const variables = this.variableService.getDynamicVariables(sessionId); for (const variable of variables) { @@ -217,6 +202,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()); @@ -280,6 +270,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge * Context key is set when prompt instructions are attached. */ private promptInstructionsAttached: IContextKey; + private chatMode: IContextKey; private readonly _waitForPersistedLanguageModel = this._register(new MutableDisposable()); private _onDidChangeCurrentLanguageModel = this._register(new Emitter()); @@ -288,6 +279,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.Ask; + public get currentMode(): ChatMode { + if (this.location === ChatAgentLocation.Panel && !this.chatService.unifiedViewEnabled) { + return ChatMode.Ask; + } + + 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; @@ -342,27 +347,29 @@ 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, + @ISharedWebContentExtractorService private readonly sharedWebExtracterService: ISharedWebContentExtractorService, + @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService, ) { super(); this._attachmentModel = this._register(this.instantiationService.createInstance(ChatAttachmentModel)); + 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; @@ -371,6 +378,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.chatMode = ChatContextKeys.chatMode.bindTo(contextKeyService); this.history = this.loadHistory(); this._register(this.historyService.onDidClearHistory(() => this.history = new HistoryNavigator2([{ text: '' }], ChatInputHistoryMaxEntries, historyKeyFn))); @@ -410,8 +418,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); @@ -419,28 +427,53 @@ 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(); + })); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ChatConfiguration.Edits2Enabled)) { + 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; + } + + mode = validateChatMode(mode) ?? (this.location === ChatAgentLocation.Panel ? ChatMode.Ask : ChatMode.Edit); + this._currentMode = mode; + this.chatMode.set(mode); + this._onDidChangeCurrentChatMode.fire(); } 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.configurationService.getValue(ChatConfiguration.Edits2Enabled))) { if (this.configurationService.getValue('chat.agent.allModels')) { return true; } @@ -469,21 +502,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 { @@ -504,7 +541,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return localize('chatInput', "Chat Input"); } - initForNewChatModel(state: IChatViewState): void { + initForNewChatModel(state: IChatViewState, modelIsEmpty: boolean): void { this.history = this.loadHistory(); this.history.add({ text: state.inputValue ?? this.history.current().text, @@ -516,6 +553,65 @@ 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); + } + + if (modelIsEmpty) { + const storageKey = this.getDefaultModeExperimentStorageKey(); + const hasSetDefaultMode = this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false); + if (!hasSetDefaultMode) { + Promise.all([ + this.experimentService.getTreatment('chat.defaultMode'), + this.experimentService.getTreatment('chat.defaultLanguageModel'), + ]).then(([defaultModeTreatment, defaultLanguageModelTreatment]) => { + if (typeof defaultModeTreatment === 'string') { + this.storageService.store(storageKey, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + const defaultMode = validateChatMode(defaultModeTreatment); + if (defaultMode) { + this.logService.trace(`Applying default mode from experiment: ${defaultMode}`); + this.setChatMode(defaultMode); + this.checkModelSupported(); + } + } + + if (typeof defaultLanguageModelTreatment === 'string' && this._currentMode === ChatMode.Agent) { + this.storageService.store(storageKey, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + this.logService.trace(`Applying default language model from experiment: ${defaultLanguageModelTreatment}`); + this.setExpModelOrWait(defaultLanguageModelTreatment); + } + }); + } + } + } + + private setExpModelOrWait(modelId: string) { + const model = this.languageModelsService.lookupLanguageModel(modelId); + if (model) { + this.setCurrentLanguageModel({ metadata: model, identifier: modelId }); + this.checkModelSupported(); + this._waitForPersistedLanguageModel.clear(); + } else { + this._waitForPersistedLanguageModel.value = this.languageModelsService.onDidChangeLanguageModels(e => { + const model = e.added?.find(m => m.identifier === modelId); + if (model) { + this._waitForPersistedLanguageModel.clear(); + + if (model.metadata.isUserSelectable) { + this.setCurrentLanguageModel({ metadata: model.metadata, identifier: modelId }); + this.checkModelSupported(); + } + } + }); + } + } + + private getDefaultModeExperimentStorageKey(): string { + const tag = this.options.widgetViewKindTag; + return `chat.${tag}.hasSetDefaultModeByExperiment`; } logInputHistory(): void { @@ -571,13 +667,17 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge if (historyAttachments.length > 0) { historyAttachments = (await Promise.all(historyAttachments.map(async (attachment) => { if (attachment.isImage && attachment.references?.length && URI.isUri(attachment.references[0].reference)) { + const currReference = attachment.references[0].reference; try { - const buffer = await this.fileService.readFile(attachment.references[0].reference); + const imageBinary = currReference.toString(true).startsWith('http') ? await this.sharedWebExtracterService.readImage(currReference, CancellationToken.None) : (await this.fileService.readFile(currReference)).value; + if (!imageBinary) { + return undefined; + } const newAttachment = { ...attachment }; - newAttachment.value = (isImageVariableEntry(attachment) && attachment.isPasted) ? buffer.value.buffer : await resizeImage(buffer.value.buffer); // if pasted image, we do not need to resize. + newAttachment.value = (isImageVariableEntry(attachment) && attachment.isPasted) ? imageBinary.buffer : await resizeImage(imageBinary.buffer); // if pasted image, we do not need to resize. return newAttachment; } catch (err) { - this.logService.error('Failed to restore image from history', err); + this.logService.error('Failed to fetch and reference.', err); return undefined; } } @@ -660,6 +760,12 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } } + validateCurrentMode(): void { + if (!this.agentService.hasToolsAgent && this._currentMode === ChatMode.Agent) { + this.setChatMode(ChatMode.Edit); + } + } + // A funtion that filters out specifically the `value` property of the attachment. private getFilteredEntry(query: string, inputState: IChatInputState): IChatHistoryEntry { const attachmentsWithoutImageValues = inputState.chatContextAttachments?.map(attachment => { @@ -755,7 +861,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge 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); @@ -856,13 +968,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge hiddenItemStrategy: HiddenItemStrategy.Ignore, // keep it lean when hiding items and avoid a "..." overflow menu actionViewItemProvider: (action, options) => { if (this.location === ChatAgentLocation.Panel || this.location === ChatAgentLocation.Editor) { - if ((action.id === ChatSubmitAction.ID || action.id === CancelAction.ID) && action instanceof MenuItemAction) { + if ((action.id === ChatSubmitAction.ID || action.id === CancelAction.ID || action.id === ChatEditingSessionSubmitAction.ID) && action instanceof MenuItemAction) { const dropdownAction = this.instantiationService.createInstance(MenuItemAction, { id: 'chat.moreExecuteActions', title: localize('notebook.moreExecuteActionsLabel', "More..."), icon: Codicon.chevronDown }, undefined, undefined, undefined, undefined); return this.instantiationService.createInstance(ChatSubmitDropdownActionItem, action, dropdownAction, { ...options, menuAsChild: false }); } } - if (action.id === ChatModelPickerActionId && action instanceof MenuItemAction) { + if (action.id === ChatSwitchToNextModelActionId && action instanceof MenuItemAction) { if (!this._currentLanguageModel) { this.setCurrentLanguageModelToDefault(); } @@ -871,7 +983,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() @@ -879,7 +993,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; @@ -952,36 +1070,37 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.renderAttachedContext(); })); - if (this.options.renderWorkingSet) { - 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, - getKeyBinding: () => undefined, - actionViewItemProvider: (action, options) => { - if (action.id === 'workbench.action.chat.editing.attachContext') { - 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; } - })); - 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); + 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._onDidChangeHeight.fire(); + } + })); + + this._register(this.selectedToolsModel.toolsActionItemViewItemProvider.onDidRender(() => this._onDidChangeHeight.fire())); } private renderAttachedContext() { const container = this.attachedContextContainer; - const oldHeight = container.offsetHeight; + // Note- can't measure attachedContextContainer, because it has `display: contents`, so measure the parent to check for height changes + const oldHeight = this.attachmentsContainer.offsetHeight; const store = new DisposableStore(); this.attachedContextDisposables.value = store; @@ -989,7 +1108,9 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const hoverDelegate = store.add(createInstantHoverDelegate()); const attachments = [...this.attachmentModel.attachments.entries()]; - dom.setVisibility(Boolean(attachments.length) || (this.addFilesToolbar && !this.addFilesToolbar.isEmpty()) || Boolean(this.implicitContext?.value) || !this.instructionAttachmentsPart.empty, this.attachmentsContainer); + 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; } @@ -1003,279 +1124,52 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.instructionAttachmentsPart.render(container); 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 isURL = isImageVariableEntry(attachment) && attachment.isURL; - const hoverElement = this.customAttachment(widget, attachment.name, hoverDelegate, ariaLabel, store, attachment.isImage, supportsVision, isURL, attachment.value as Uint8Array); - - 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) { - const buffer = attachment.value as Uint8Array; - this.createImageElements(buffer, widget, hoverElement); - store.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: false })); - } - - 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))); - } - } 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(PasteAttachmentWidget, 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 === 'diagnostic') { - if (attachment.filterUri) { - resource = attachment.filterUri ? URI.revive(attachment.filterUri) : undefined; - range = attachment.filterRange; - } else { - widget.style.cursor = 'pointer'; - store.add(dom.addDisposableListener(widget, dom.EventType.CLICK, () => { - this.commandService.executeCommand('workbench.panel.markers.view.focus'); - })); - } - } + attachmentWidget = this.instantiationService.createInstance(DefaultChatAttachmentWidget, resource, range, attachment, this._currentLanguageModel, shouldFocusClearButton, container, this._contextResourceLabels, 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))); - } - - 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); - } - } - })); - } - - widget.tabIndex = 0; - widget.ariaLabel = ariaLabel; + store.add(attachmentWidget); + store.add(attachmentWidget.onDidDelete((e) => { + this.handleAttachmentDeletion(e, index, attachment); + })); } - if (oldHeight !== container.offsetHeight) { + if (oldHeight !== this.attachmentsContainer.offsetHeight) { this._onDidChangeHeight.fire(); } } - private customAttachment(widget: HTMLElement, friendlyName: string, hoverDelegate: IHoverDelegate, ariaLabel: string, store: DisposableStore, isImage?: boolean, supportsVision?: boolean, isURL?: boolean, value?: Uint8Array): 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); + private handleAttachmentDeletion(e: globalThis.Event, index: number, attachment: IChatRequestVariableEntry) { + this._attachmentModel.delete(attachment.id); - const hoverElement = dom.$('div.chat-attached-context-hover'); - hoverElement.setAttribute('aria-label', ariaLabel); - - if (isURL && supportsVision && value) { - hoverElement.textContent = localize('chat.imageAttachmentHover', "{0}", convertUint8ArrayToString(value)); - store.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: true })); - } - - if (!supportsVision) { - widget.classList.add('warning'); - hoverElement.textContent = localize('chat.fileAttachmentHover', "{0} does not support this {1} type.", this.currentLanguageModel ? this.languageModelsService.lookupLanguageModel(this.currentLanguageModel)?.name : this.currentLanguageModel, isImage ? 'image' : 'file'); - store.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: true })); - } - - return hoverElement; - } - - private openResource(resource: URI, isDirectory: true): void; - private openResource(resource: URI, isDirectory: false, range: IRange | undefined): void; - private openResource(resource: URI, isDirectory?: boolean, range?: IRange): void { - if (isDirectory) { - // Reveal Directory in explorer - this.commandService.executeCommand(revealInSideBarCommand.id, resource); - return; - } - - // Open file in editor - const openTextEditorOptions: ITextEditorOptions | undefined = range ? { selection: range } : undefined; - const options: OpenInternalOptions = { - fromUserGesture: true, - editorOptions: openTextEditorOptions, - }; - this.openerService.open(resource, options); - } - - private attachButtonAndDisposables(widget: HTMLElement, index: number, attachment: IChatRequestVariableEntry, hoverDelegate: IHoverDelegate) { - const store = this.attachedContextDisposables.value; - if (!store) { - return; - } - - const clearButton = new Button(widget, { - supportIcons: true, - hoverDelegate, - title: localize('chat.attachment.clearButton', "Remove from context") - }); - - // If this item is rendering in place of the last attached context item, focus the clear button so the user can continue deleting attached context items with the keyboard - if (index === Math.min(this._indexOfLastAttachedContextDeletedWithKeyboard, this.attachmentModel.size - 1)) { - clearButton.focus(); - } - - store.add(clearButton); - clearButton.icon = Codicon.close; - store.add(Event.once(clearButton.onDidClick)((e) => { - this._attachmentModel.delete(attachment.id); - - // Set focus to the next attached context item if deletion was triggered by a keystroke (vs a mouse click) - if (dom.isKeyboardEvent(e)) { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - this._indexOfLastAttachedContextDeletedWithKeyboard = index; - } + // 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: '' }); + if (this._attachmentModel.size === 0) { + this.focus(); + } - // 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); - } - }; + 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); @@ -1295,16 +1189,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); } } @@ -1322,19 +1214,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; @@ -1406,13 +1286,17 @@ 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, @@ -1420,12 +1304,12 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge })); uriLabel.label = this.labelService.getUriBasenameLabel(uri); uriLabel.element.classList.add('monaco-icon-label'); - uriLabel.element.title = localize('suggeste.title', "{0} - {1}", this.labelService.getUriLabel(uri, { relative: true }), metadata.description ?? ''); + uriLabel.element.title = localize('suggeste.title', "{0} - {1}", this.labelService.getUriLabel(uri, { relative: true }), description ?? ''); - this._chatEditsActionsDisposables.add(uriLabel.onDidClick(() => { + this._chatEditsActionsDisposables.add(uriLabel.onDidClick(async () => { group.remove(); // REMOVE asap - this._attachmentModel.addFile(uri); - chatEditingSession.remove(WorkingSetEntryRemovalReason.User, uri); + await this._attachmentModel.addFile(uri); + this.relatedFiles?.remove(uri); })); const addButton = this._chatEditsActionsDisposables.add(new Button(anchor, { @@ -1436,10 +1320,10 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge })); addButton.icon = Codicon.add; addButton.setTitle(localize('chatEditingSession.addSuggested', 'Add suggestion')); - this._chatEditsActionsDisposables.add(addButton.onDidClick(() => { + this._chatEditsActionsDisposables.add(addButton.onDidClick(async () => { group.remove(); // REMOVE asap - this._attachmentModel.addFile(uri); - chatEditingSession.remove(WorkingSetEntryRemovalReason.User, uri); + await this._attachmentModel.addFile(uri); + this.relatedFiles?.remove(uri); })); const sep = document.createElement('div'); @@ -1546,7 +1430,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } } -const historyKeyFn = (entry: IChatHistoryEntry) => JSON.stringify(entry); +const historyKeyFn = (entry: IChatHistoryEntry) => JSON.stringify({ ...entry, state: { ...entry.state, chatMode: undefined } }); function getLastPosition(model: ITextModel): IPosition { return { lineNumber: model.getLineCount(), column: model.getLineLength(model.getLineCount()) + 1 }; @@ -1561,7 +1445,6 @@ class ChatSubmitDropdownActionItem extends DropdownWithPrimaryActionViewItem { options: IDropdownWithPrimaryActionViewItemOptions, @IMenuService menuService: IMenuService, @IContextMenuService contextMenuService: IContextMenuService, - @IChatAgentService chatAgentService: IChatAgentService, @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, @@ -1586,17 +1469,6 @@ class ChatSubmitDropdownActionItem extends DropdownWithPrimaryActionViewItem { const menu = menuService.createMenu(MenuId.ChatExecuteSecondary, contextKeyService); const setActions = () => { const secondary = getFlatActionBarActions(menu.getActions({ shouldForwardArgs: true })); - const secondaryAgent = chatAgentService.getSecondaryAgent(); - if (secondaryAgent) { - secondary.forEach(a => { - if (a.id === ChatSubmitSecondaryAgentAction.ID) { - a.label = localize('chat.submitToSecondaryAgent', "Send to @{0}", secondaryAgent.name); - } - - return a; - }); - } - this.update(dropdownAction, secondary); }; setActions(); @@ -1618,7 +1490,10 @@ class ModelPickerActionViewItem extends DropdownMenuActionViewItemWithKeybinding @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, + @IChatEntitlementService chatEntitlementService: IChatEntitlementService, @ICommandService commandService: ICommandService, + @IMenuService menuService: IMenuService, + @ITelemetryService telemetryService: ITelemetryService, ) { const modelActionsProvider: IActionProvider = { getActions: () => { @@ -1640,16 +1515,33 @@ 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) { - actions.push(new Separator()); - actions.push(toAction({ id: 'moreModels', label: localize('chat.moreModels', "Add More Models..."), run: () => commandService.executeCommand('workbench.action.chat.upgradePlan') })); - } + // Add menu contributions from extensions + const menuActions = menuService.getMenuActions(MenuId.ChatModelPicker, contextKeyService); + const menuContributions = getFlatActionBarActions(menuActions); + if (menuContributions.length > 0 || chatEntitlementService.entitlement === ChatEntitlement.Limited) { + actions.push(new Separator()); + } + actions.push(...menuContributions); + if (chatEntitlementService.entitlement === ChatEntitlement.Limited) { + actions.push(toAction({ + id: 'moreModels', label: localize('chat.moreModels', "Add more Models"), run: () => { + const commandId = 'workbench.action.chat.upgradePlan'; + telemetryService.publicLog2('workbenchActionExecuted', { id: commandId, from: 'chat-models' }); + commandService.executeCommand(commandId); + } + })); + } 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!); @@ -1671,44 +1563,73 @@ class ModelPickerActionViewItem extends DropdownMenuActionViewItemWithKeybinding 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, + @IChatAgentService chatAgentService: IChatAgentService, ) { - 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.Edit), + ]; + if (chatAgentService.hasToolsAgent) { + agentStateActions.push(makeAction(ChatMode.Agent)); + } + + if (chatService.unifiedViewEnabled) { + agentStateActions.unshift(makeAction(ChatMode.Ask)); + } + + 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.Ask: + default: + return localize('chat.askMode', "Ask"); + } } 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; } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 95674d71..03756411 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -19,7 +19,7 @@ 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'; @@ -43,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, ChatErrorLevel, 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'; @@ -60,7 +62,7 @@ import { ChatCodeCitationContentPart } from './chatContentParts/chatCodeCitation import { ChatCommandButtonContentPart } from './chatContentParts/chatCommandContentPart.js'; import { ChatConfirmationContentPart } from './chatContentParts/chatConfirmationContentPart.js'; import { IChatContentPart, IChatContentPartRenderContext } from './chatContentParts/chatContentParts.js'; -import { ChatMarkdownContentPart, EditorPool, IChatMarkdownContentPartOptions } from './chatContentParts/chatMarkdownContentPart.js'; +import { ChatMarkdownContentPart, EditorPool } from './chatContentParts/chatMarkdownContentPart.js'; import { ChatProgressContentPart, ChatWorkingProgressContentPart } from './chatContentParts/chatProgressContentPart.js'; import { ChatQuotaExceededPart } from './chatContentParts/chatQuotaExceededPart.js'; import { ChatCollapsibleListContentPart, ChatUsedReferencesListContentPart, CollapsibleListPool } from './chatContentParts/chatReferencesContentPart.js'; @@ -105,6 +107,7 @@ const forceVerboseLayoutTracing = false export interface IChatRendererDelegate { container: HTMLElement; getListLength(): number; + currentChatMode(): ChatMode; readonly onDidScroll?: Event; } @@ -162,6 +165,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer templateData.value.classList.remove('inline-progress'))); value.push({ content: new MarkdownString('', { supportHtml: true }), kind: 'markdownContent' }); } else { templateData.value.classList.remove('inline-progress'); @@ -759,9 +764,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer part.kind === 'toolInvocation' && !part.isComplete))) { + if ( + !lastPart || + lastPart.kind === 'references' || + (lastPart.kind === 'toolInvocation' && (lastPart.isComplete || lastPart.presentation === 'hidden')) || + ((lastPart.kind === 'textEditGroup' || lastPart.kind === 'notebookEditGroup') && lastPart.done && !partsToRender.some(part => part.kind === 'toolInvocation' && !part.isComplete))) { return true; } @@ -898,12 +908,11 @@ 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 { @@ -947,7 +956,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { this.updateItemHeight(templateData); })); @@ -986,7 +995,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { codeBlocksByResponseId[codeBlockStartIndex + i] = info; - info.uriPromise.then(uri => { + part.addDisposable!(thenIfNotDisposed(info.uriPromise, uri => { if (!uri) { return; } @@ -998,7 +1007,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { markdownPart.layout(this._currentLayoutWidth); this.updateItemHeight(templateData); diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 0c3186b3..3b763727 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/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts index 0d98223a..4f5e28a3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts @@ -4,29 +4,43 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce, isNonEmptyArray } from '../../../../base/common/arrays.js'; +import { Codicon } from '../../../../base/common/codicons.js'; import { toErrorMessage } from '../../../../base/common/errorMessage.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, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; import * as strings from '../../../../base/common/strings.js'; -import { localize } from '../../../../nls.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { URI } from '../../../../base/common/uri.js'; +import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; +import { localize, localize2 } from '../../../../nls.js'; +import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { ExtensionIdentifier, IExtensionManifest } from '../../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { ILogService } from '../../../../platform/log/common/log.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; -import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IViewsRegistry, Extensions as ViewExtensions } from '../../../common/views.js'; -import { IExtensionFeatureTableRenderer, IRenderedData, ITableData, IRowData, IExtensionFeaturesRegistry, Extensions } from '../../../services/extensionManagement/common/extensionFeatures.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { ViewPane } from '../../../browser/parts/views/viewPane.js'; +import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; +import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +import { IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.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 { IViewsService } from '../../../services/views/common/viewsService.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 { ChatViewId } from './chat.js'; +import { IChatService } from '../common/chatService.js'; +import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js'; +import { ChatViewId, showChatView } from './chat.js'; +import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID, ChatViewPane } from './chatViewPane.js'; // --- Chat Container & View Registration @@ -72,8 +86,8 @@ import { ChatViewId } from './chat.js'; // Registry.as(ViewExtensions.ViewsRegistry).registerViews(chatViewDescriptor, chatViewContainer); // --- Edits Container & View Registration - // Void commented this out + // const editsViewContainer: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ // id: CHAT_EDITING_SIDEBAR_PANEL_ID, // title: localize2('chatEditing.viewContainer.label', "Copilot Edits"), @@ -84,35 +98,6 @@ import { ChatViewId } from './chat.js'; // order: 101, // }, ViewContainerLocation.AuxiliaryBar, { doNotRegisterOpenCommand: true }); -// const editsViewDescriptor: IViewDescriptor[] = [{ -// id: 'workbench.panel.chat.view.edits', -// containerIcon: editsViewContainer.icon, -// containerTitle: editsViewContainer.title.value, -// singleViewPaneContainerTitle: editsViewContainer.title.value, -// name: editsViewContainer.title, -// canToggleVisibility: false, -// canMoveView: true, -// openCommandActionDescriptor: { -// id: CHAT_EDITING_SIDEBAR_PANEL_ID, -// title: editsViewContainer.title, -// mnemonicTitle: localize({ key: 'miToggleEdits', comment: ['&& denotes a mnemonic'] }, "Copilot Ed&&its"), -// keybindings: { -// primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI, -// linux: { -// primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyI -// } -// }, -// order: 2 -// }, -// ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.EditingSession }]), -// when: ContextKeyExpr.or( -// ChatContextKeys.Setup.hidden.negate(), -// ChatContextKeys.Setup.installed, -// ChatContextKeys.editingParticipantRegistered -// ) -// }]; -// Registry.as(ViewExtensions.ViewsRegistry).registerViews(editsViewDescriptor, editsViewContainer); - const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'chatParticipants', jsonSchema: { @@ -448,3 +433,145 @@ Registry.as(Extensions.ExtensionFeaturesRegistry).re }, renderer: new SyncDescriptor(ChatParticipantDataRenderer), }); + + +// Void commented this out +// // TODO@roblourens remove after a few months + +// export class MovedChatEditsViewPane extends ViewPane { +// override shouldShowWelcome(): boolean { +// return true; +// } +// } + +// const editsViewId = 'workbench.panel.chat.view.edits'; +// const baseEditsViewDescriptor: IViewDescriptor = { +// id: editsViewId, +// containerIcon: editsViewContainer.icon, +// containerTitle: editsViewContainer.title.value, +// singleViewPaneContainerTitle: editsViewContainer.title.value, +// name: editsViewContainer.title, +// canToggleVisibility: false, +// canMoveView: true, +// openCommandActionDescriptor: { +// id: CHAT_EDITING_SIDEBAR_PANEL_ID, +// title: editsViewContainer.title, +// mnemonicTitle: localize({ key: 'miToggleEdits', comment: ['&& denotes a mnemonic'] }, "Copilot Ed&&its"), +// keybindings: { +// primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyI, +// linux: { +// primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.KeyI +// } +// }, +// order: 2 +// }, +// ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.EditingSession }]), +// when: ContextKeyExpr.and( +// ContextKeyExpr.has(`config.${ChatConfiguration.UnifiedChatView}`).negate(), +// ContextKeyExpr.or( +// ChatContextKeys.Setup.hidden.negate(), +// ChatContextKeys.Setup.installed, +// ChatContextKeys.editingParticipantRegistered +// ) +// ) +// }; + +// const ShowMovedChatEditsView = new RawContextKey('showMovedChatEditsView', true, { type: 'boolean', description: localize('hideMovedChatEditsView', "True when the moved chat edits view should be hidden.") }); + +// class EditsViewContribution extends Disposable implements IWorkbenchContribution { +// static readonly ID = 'workbench.contrib.chatEditsView'; + +// private static readonly HideMovedEditsViewKey = 'chatEditsView.hideMovedEditsView'; + +// private readonly showWelcomeViewCtx: IContextKey; + +// constructor( +// @IConfigurationService private readonly configurationService: IConfigurationService, +// @IStorageService private readonly storageService: IStorageService, +// @IContextKeyService private readonly contextKeyService: IContextKeyService, +// @IChatService private readonly chatService: IChatService, +// ) { +// super(); + +// this.showWelcomeViewCtx = ShowMovedChatEditsView.bindTo(this.contextKeyService); + +// const unifiedViewEnabled = this.configurationService.getValue(ChatConfiguration.UnifiedChatView); + +// const movedEditsViewDescriptor = { +// ...baseEditsViewDescriptor, +// ctorDescriptor: new SyncDescriptor(MovedChatEditsViewPane), +// when: ContextKeyExpr.and( +// ContextKeyExpr.has(`config.${ChatConfiguration.UnifiedChatView}`), +// ShowMovedChatEditsView, +// ContextKeyExpr.or( +// ChatContextKeys.Setup.hidden.negate(), +// ChatContextKeys.Setup.installed, +// ChatContextKeys.editingParticipantRegistered +// ) +// ) +// }; + +// const editsViewToRegister = unifiedViewEnabled ? +// movedEditsViewDescriptor : baseEditsViewDescriptor; + +// if (unifiedViewEnabled) { +// this.init(); +// this.updateContextKey(); +// this.registerWelcomeView(); +// this.registerCommands(); +// } +// Registry.as(ViewExtensions.ViewsRegistry).registerViews([editsViewToRegister], editsViewContainer); +// } + +// private registerWelcomeView(): void { +// const welcomeViewMainMessage = localize('editsMovedMainMessage', "Copilot Edits has been moved to the [main Chat view](command:workbench.action.chat.open). You can switch between modes by using the dropdown in the Chat input box."); +// const okButton = `[${localize('ok', "Got it")}](command:_movedEditsView.ok)`; +// const welcomeViewFooterMessage = localize('editsMovedFooterMessage', "[Learn more](command:_movedEditsView.learnMore) about the Chat view."); + +// const viewsRegistry = Registry.as(ViewExtensions.ViewsRegistry); +// this._register(viewsRegistry.registerViewWelcomeContent(editsViewId, { +// content: [welcomeViewMainMessage, okButton, welcomeViewFooterMessage].join('\n\n'), +// renderSecondaryButtons: true, +// when: ShowMovedChatEditsView +// })); +// } + +// private markViewToHide(): void { +// this.storageService.store(EditsViewContribution.HideMovedEditsViewKey, true, StorageScope.APPLICATION, StorageTarget.USER); +// this.updateContextKey(); +// } + +// private init() { +// const hasChats = this.chatService.hasSessions(); +// if (!hasChats) { +// // No chats from previous sessions, might be a new user, so hide the view. +// // Could also be a previous user who happened to first open a workspace with no chats. +// this.markViewToHide(); +// } +// } + +// private updateContextKey(): void { +// const hidden = this.storageService.getBoolean(EditsViewContribution.HideMovedEditsViewKey, StorageScope.APPLICATION, false); +// const hasChats = this.chatService.hasSessions(); +// this.showWelcomeViewCtx.set(!hidden && hasChats); +// } + +// private registerCommands(): void { +// this._register(CommandsRegistry.registerCommand({ +// id: '_movedEditsView.ok', +// handler: async (accessor: ServicesAccessor) => { +// showChatView(accessor.get(IViewsService)); +// this.markViewToHide(); +// } +// })); +// this._register(CommandsRegistry.registerCommand({ +// id: '_movedEditsView.learnMore', +// handler: async (accessor: ServicesAccessor) => { +// const openerService = accessor.get(IOpenerService); +// openerService.open(URI.parse('https://aka.ms/vscode-chat-modes')); +// } +// })); +// } +// } + +// registerWorkbenchContribution2(EditsViewContribution.ID, EditsViewContribution, WorkbenchPhase.BlockRestore); diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index a4af4355..137fc147 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 00000000..73541fb8 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatSelectedTools.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { IAction } from '../../../../base/common/actions.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { autorun, derived, IObservable, observableFromEvent } 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 { ObservableMemento, observableMemento } from '../../../../platform/observable/common/observableMemento.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { ILanguageModelToolsService, IToolData, ToolDataSource } from '../common/languageModelToolsService.js'; + +/** + * New tools and new tool sources that come in should generally be enabled until + * the user disables them. To store things, we store only the buckets and + * individual tools that were disabled, so the new data sources that come in + * are enabled, and new tools that come in for data sources not disabled are + * also enabled. + */ +type StoredData = { disabledBuckets?: /* ToolDataSource.toKey */ readonly string[]; disabledTools?: readonly string[] }; + +const storedTools = observableMemento({ + defaultValue: {}, + key: 'chat/selectedTools', +}); + +export class ChatSelectedTools extends Disposable { + + private readonly _selectedTools: ObservableMemento; + + readonly tools: IObservable; + + readonly toolsActionItemViewItemProvider: IActionViewItemProvider & { onDidRender: Event }; + + constructor( + @ILanguageModelToolsService toolsService: ILanguageModelToolsService, + @IInstantiationService instaService: IInstantiationService, + @IStorageService storageService: IStorageService, + ) { + super(); + + this._selectedTools = this._register(storedTools(StorageScope.WORKSPACE, StorageTarget.MACHINE, storageService)); + + const allTools = observableFromEvent( + toolsService.onDidChangeTools, + () => Array.from(toolsService.getTools()).filter(t => t.supportsToolPicker) + ); + + const disabledData = this._selectedTools.map(data => { + return (data.disabledBuckets?.length || data.disabledTools?.length) && { + buckets: new Set(data.disabledBuckets), + toolIds: new Set(data.disabledTools), + }; + }); + + this.tools = derived(r => { + const disabled = disabledData.read(r); + const tools = allTools.read(r); + if (!disabled) { + return tools; + } + + return tools.filter(t => + !(disabled.toolIds.has(t.id) || disabled.buckets.has(ToolDataSource.toKey(t.source))) + ); + }); + + const toolsCount = derived(r => { + const count = allTools.read(r).length; + const enabled = this.tools.read(r).length; + return { count, enabled }; + }); + + const onDidRender = this._store.add(new Emitter()); + + this.toolsActionItemViewItemProvider = Object.assign( + (action: IAction, options: IActionViewItemOptions) => { + 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); + + const message = count === 0 + ? '$(tools)' + : enabled !== count + ? localize('tool.1', "{0} {1} of {2}", '$(tools)', enabled, count) + : localize('tool.0', "{0} {1}", '$(tools)', count); + + reset(this.label, ...renderLabelWithIcons(message)); + if (this.element?.isConnected) { + onDidRender.fire(); + } + })); + } + + }, action, { ...options, keybindingNotRenderedWithLabel: true }); + }, + { onDidRender: onDidRender.event } + ); + } + + update(disableBuckets: readonly ToolDataSource[], disableTools: readonly IToolData[]): void { + this._selectedTools.set({ + disabledBuckets: disableBuckets.map(ToolDataSource.toKey), + disabledTools: disableTools.map(t => t.id) + }, undefined); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 122d7581..8a8a9072 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -3,74 +3,73 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './media/chatViewSetup.css'; +import './media/chatSetup.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, markAsSingleton, 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 { ChatViewId, EditsViewId, ensureSideBarChatViewSize, preferCopilotEditsView, showCopilotView } from './chat.js'; +import { ChatEntitlement, ChatEntitlementContext, ChatEntitlementRequests, ChatEntitlementService, IChatEntitlementService } from '../common/chatEntitlementService.js'; +import { IChatProgress, IChatService } from '../common/chatService.js'; +import { CHAT_CATEGORY, CHAT_OPEN_ACTION_ID, CHAT_SETUP_ACTION_ID } from './actions/chatActions.js'; +import { ChatViewId, EditsViewId, ensureSideBarChatViewSize, IChatWidgetService, 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, ChatConfiguration, ChatMode, validateChatMode } 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'; +import { IChatRequestModel } from '../common/chatModel.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -81,83 +80,505 @@ 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 - -export class ChatEntitlementsService extends Disposable implements IChatEntitlementsService { - - declare _serviceBrand: undefined; - - readonly context: Lazy | undefined; - readonly requests: Lazy | undefined; - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - @IProductService productService: IProductService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService - ) { - super(); - - if ( - !productService.defaultChatAgent || // needs product config - (isWeb && !environmentService.remoteAuthority) // only enabled locally or a remote backend - ) { - return; - } - - const context = this.context = new Lazy(() => this._register(instantiationService.createInstance(ChatSetupContext))); - this.requests = new Lazy(() => this._register(instantiationService.createInstance(ChatSetupRequests, context.value))); - } - - async resolve(token: CancellationToken): Promise { - return this.requests?.value.forceResolveEntitlement(undefined, token); - } -} - -//#endregion - //#region Contribution +const ToolsAgentWhen = ContextKeyExpr.and( + ContextKeyExpr.equals(`config.${ChatConfiguration.AgentEnabled}`, true), + ChatContextKeys.Editing.agentModeDisallowed.negate(), + ContextKeyExpr.not(`previewFeaturesDisabled`) // Set by extension +); + +class SetupChatAgentImplementation extends Disposable implements IChatAgentImplementation { + + static register(instantiationService: IInstantiationService, location: ChatAgentLocation, isToolsAgent: boolean, context: ChatEntitlementContext, controller: Lazy): { disposable: IDisposable; agent: SetupChatAgentImplementation } { + return instantiationService.invokeFunction(accessor => { + const chatAgentService = accessor.get(IChatAgentService); + + let id: string; + let description = localize('chatDescription', "Ask Copilot"); + let welcomeMessageContent: IChatWelcomeMessageContent | undefined; + const baseMessage = localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use."); + switch (location) { + case ChatAgentLocation.Panel: + id = 'setup.chat'; + welcomeMessageContent = { + title: description, + message: new MarkdownString(baseMessage), + icon: Codicon.copilotLarge + }; + break; + case ChatAgentLocation.EditingSession: + id = isToolsAgent ? 'setup.agent' : 'setup.edits'; + description = isToolsAgent ? localize('agentDescription', "Edit files in your workspace in agent mode") : localize('editsDescription', "Edit files in your workspace"); + welcomeMessageContent = isToolsAgent ? + { + title: localize('editsTitle', "Edit with Copilot"), + message: new MarkdownString(localize('agentMessage', "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.", `https://aka.ms/vscode-copilot-agent`) + `\n\n${baseMessage}`), + 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.") + `\n\n${baseMessage}`), + icon: Codicon.copilotLarge + }; + break; + case ChatAgentLocation.Terminal: + id = 'setup.terminal'; + break; + case ChatAgentLocation.Editor: + id = 'setup.editor'; + break; + case ChatAgentLocation.Notebook: + id = 'setup.notebook'; + break; + } + + const disposable = new DisposableStore(); + + disposable.add(chatAgentService.registerAgent(id, { + id, + name: `${defaultChat.providerName} Copilot`, + isDefault: true, + isCore: true, + isToolsAgent, + when: isToolsAgent ? ToolsAgentWhen?.serialize() : undefined, + slashCommands: [], + disambiguation: [], + locations: [location], + metadata: { + welcomeMessageContent, + helpTextPrefix: SetupChatAgentImplementation.SETUP_NEEDED_MESSAGE + }, + description, + extensionId: nullExtensionDescription.identifier, + extensionDisplayName: nullExtensionDescription.name, + extensionPublisherId: nullExtensionDescription.publisher + })); + + const agent = disposable.add(instantiationService.createInstance(SetupChatAgentImplementation, context, controller, location)); + disposable.add(chatAgentService.registerAgentImplementation(id, agent)); + + return { agent, disposable }; + }); + } + + private static readonly SETUP_NEEDED_MESSAGE = new MarkdownString(localize('settingUpCopilotNeeded', "You need to set up Copilot to use Chat.")); + + private readonly _onUnresolvableError = this._register(new Emitter()); + readonly onUnresolvableError = this._onUnresolvableError.event; + + constructor( + private readonly context: ChatEntitlementContext, + private readonly controller: Lazy, + private readonly location: ChatAgentLocation, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { + super(); + } + + async invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void): Promise { + return this.instantiationService.invokeFunction(async accessor => { + const chatService = accessor.get(IChatService); // use accessor for lazy loading + const languageModelsService = accessor.get(ILanguageModelsService); // of chat related services + const chatWidgetService = accessor.get(IChatWidgetService); + const chatAgentService = accessor.get(IChatAgentService); + + return this.doInvoke(request, progress, chatService, languageModelsService, chatWidgetService, chatAgentService); + }); + } + + private async doInvoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatWidgetService: IChatWidgetService, chatAgentService: IChatAgentService): Promise { + if (!this.context.state.installed || this.context.state.entitlement === ChatEntitlement.Available || this.context.state.entitlement === ChatEntitlement.Unknown) { + return this.doInvokeWithSetup(request, progress, chatService, languageModelsService, chatWidgetService, chatAgentService); + } + + return this.doInvokeWithoutSetup(request, progress, chatService, languageModelsService, chatWidgetService, chatAgentService); + } + + private async doInvokeWithoutSetup(request: IChatAgentRequest, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatWidgetService: IChatWidgetService, chatAgentService: IChatAgentService): Promise { + const requestModel = chatWidgetService.getWidgetBySessionId(request.sessionId)?.viewModel?.model.getRequests().at(-1); + if (!requestModel) { + this.logService.error('[chat setup] Request model not found, cannot redispatch request.'); + return {}; // this should not happen + } + + progress({ + kind: 'progressMessage', + content: new MarkdownString(localize('waitingCopilot', "Getting Copilot ready.")), + }); + + await this.forwardRequestToCopilot(requestModel, progress, chatService, languageModelsService, chatAgentService, chatWidgetService); + + return {}; + } + + private _handlingForwardedRequest: string | undefined; + private async forwardRequestToCopilot(requestModel: IChatRequestModel, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatAgentService: IChatAgentService, chatWidgetService: IChatWidgetService): Promise { + + if (this._handlingForwardedRequest === requestModel.message.text) { + throw new Error('Already handling this request'); + } + + this._handlingForwardedRequest = requestModel.message.text; + + // We need a signal to know when we can resend the request to + // Copilot. Waiting for the registration of the agent is not + // enough, we also need a language model to be available. + + const whenLanguageModelReady = this.whenLanguageModelReady(languageModelsService); + const whenAgentReady = this.whenAgentReady(chatAgentService); + + if (whenLanguageModelReady instanceof Promise || whenAgentReady instanceof Promise) { + const timeoutHandle = setTimeout(() => { + progress({ + kind: 'progressMessage', + content: new MarkdownString(localize('waitingCopilot2', "Copilot is almost ready.")), + }); + }, 10000); + + try { + const ready = await Promise.race([ + timeout(20000).then(() => 'timedout'), + Promise.allSettled([whenLanguageModelReady, whenAgentReady]) + ]); + + if (ready === 'timedout') { + progress({ + kind: 'warning', + content: new MarkdownString(localize('copilotTookLongWarning', "Copilot took too long to get ready. Please try again.")) + }); + + // This means Copilot is unhealthy and we cannot retry the + // request. Signal this to the outside via an event. + this._onUnresolvableError.fire(); + return; + } + } finally { + clearTimeout(timeoutHandle); + } + } + + const widget = chatWidgetService.getWidgetBySessionId(requestModel.session.sessionId); + chatService.resendRequest(requestModel, { + mode: widget?.input.currentMode, + userSelectedModelId: widget?.input.currentLanguageModel, + }); + } + + private whenLanguageModelReady(languageModelsService: ILanguageModelsService): Promise | void { + for (const id of languageModelsService.getLanguageModelIds()) { + const model = languageModelsService.lookupLanguageModel(id); + if (model && model.isDefault) { + return; // we have language models! + } + } + + return Event.toPromise(Event.filter(languageModelsService.onDidChangeLanguageModels, e => e.added?.some(added => added.metadata.isDefault) ?? false)); + } + + private whenAgentReady(chatAgentService: IChatAgentService): Promise | void { + const defaultAgent = chatAgentService.getDefaultAgent(this.location); + if (defaultAgent && !defaultAgent.isCore) { + return; // we have a default agent from an extension! + } + + return Event.toPromise(Event.filter(chatAgentService.onDidChangeAgents, () => { + const defaultAgent = chatAgentService.getDefaultAgent(this.location); + return Boolean(defaultAgent && !defaultAgent.isCore); + })); + } + + private async doInvokeWithSetup(request: IChatAgentRequest, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatWidgetService: IChatWidgetService, chatAgentService: IChatAgentService): Promise { + this.telemetryService.publicLog2('workbenchActionExecuted', { id: CHAT_SETUP_ACTION_ID, from: 'chat' }); + + const requestModel = chatWidgetService.getWidgetBySessionId(request.sessionId)?.viewModel?.model.getRequests().at(-1); + + const setupListener = Event.runAndSubscribe(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)), + }); + break; + case ChatSetupStep.Installing: + progress({ + kind: 'progressMessage', + content: new MarkdownString(localize('installingCopilot', "Getting Copilot ready.")), + }); + break; + } + })); + + let success = undefined; + try { + success = await ChatSetup.getInstance(this.instantiationService, this.context, this.controller).run(); + } catch (error) { + this.logService.error(`[chat setup] Error during setup: ${toErrorMessage(error)}`); + } finally { + setupListener.dispose(); + } + + // User has agreed to run the setup + if (typeof success === 'boolean') { + if (success) { + if (requestModel) { + await this.forwardRequestToCopilot(requestModel, progress, chatService, languageModelsService, chatAgentService, chatWidgetService); + } + } else { + progress({ + kind: 'warning', + content: new MarkdownString(localize('copilotSetupError', "Copilot setup failed.")) + }); + } + } + + // User has cancelled the setup + else { + progress({ + kind: 'markdownContent', + content: SetupChatAgentImplementation.SETUP_NEEDED_MESSAGE, + }); + } + + return {}; + } +} + +enum ChatSetupStrategy { + Canceled = 0, + DefaultSetup = 1, + SetupWithoutEnterpriseProvider = 2, + SetupWithEnterpriseProvider = 3 +} + +class ChatSetup { + + private static instance: ChatSetup | undefined = undefined; + static getInstance(instantiationService: IInstantiationService, context: ChatEntitlementContext, controller: Lazy): ChatSetup { + let instance = ChatSetup.instance; + if (!instance) { + instance = ChatSetup.instance = instantiationService.invokeFunction(accessor => { + return new ChatSetup(context, controller, instantiationService, accessor.get(ITelemetryService), accessor.get(IContextMenuService), accessor.get(IWorkbenchLayoutService), accessor.get(IKeybindingService), accessor.get(IChatEntitlementService), accessor.get(ILogService)); + }); + } + + return instance; + } + + private pendingRun: Promise | undefined = undefined; + + private constructor( + private readonly context: ChatEntitlementContext, + private readonly controller: Lazy, + @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, + @IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService, + @ILogService private readonly logService: ILogService, + ) { } + + async run(): Promise { + if (this.pendingRun) { + return this.pendingRun; + } + + this.pendingRun = this.doRun(); + + try { + return await this.pendingRun; + } finally { + this.pendingRun = undefined; + } + } + + private async doRun(): Promise { + let setupStrategy: ChatSetupStrategy; + if (this.chatEntitlementService.entitlement === ChatEntitlement.Pro || this.chatEntitlementService.entitlement === ChatEntitlement.Limited) { + setupStrategy = ChatSetupStrategy.DefaultSetup; // existing pro/free users setup without a dialog + } else { + setupStrategy = await this.showDialog(); + } + + let success = undefined; + try { + switch (setupStrategy) { + case ChatSetupStrategy.SetupWithEnterpriseProvider: + success = await this.controller.value.setupWithProvider({ setupFromDialog: true, useEnterpriseProvider: true }); + break; + case ChatSetupStrategy.SetupWithoutEnterpriseProvider: + success = await this.controller.value.setupWithProvider({ setupFromDialog: true, useEnterpriseProvider: false }); + break; + case ChatSetupStrategy.DefaultSetup: + success = await this.controller.value.setup({ setupFromDialog: true }); + break; + } + } catch (error) { + this.logService.error(`[chat setup] Error during setup: ${toErrorMessage(error)}`); + success = false; + } + + return success; + } + + private async showDialog(): Promise { + const disposables = new DisposableStore(); + + let result: ChatSetupStrategy | undefined = undefined; + + const buttons = [this.getPrimaryButton(), localize('maybeLater', "Maybe Later")]; + + const dialog = disposables.add(new Dialog( + this.layoutService.activeContainer, + this.getDialogTitle(), + buttons, + createWorkbenchDialogOptions({ + type: 'none', + icon: Codicon.copilotLarge, + cancelId: buttons.length - 1, + renderBody: body => body.appendChild(this.createDialog(disposables)), + primaryButtonDropdown: { + contextMenuProvider: this.contextMenuService, + addPrimaryActionToDropdown: false, + actions: [ + toAction({ id: 'setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => result = ChatSetupStrategy.SetupWithoutEnterpriseProvider }), + toAction({ id: 'setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => result = ChatSetupStrategy.SetupWithEnterpriseProvider }), + ] + } + }, this.keybindingService, this.layoutService) + )); + + const { button } = await dialog.show(); + disposables.dispose(); + + return button === 0 ? result ?? ChatSetupStrategy.DefaultSetup : ChatSetupStrategy.Canceled; + } + + private getPrimaryButton(): string { + if (this.context.state.entitlement === ChatEntitlement.Unknown) { + return localize('signInButton', "Sign in"); + } + + return localize('useCopilotButton', "Use Copilot"); + } + + private getDialogTitle(): string { + if (this.context.state.entitlement === ChatEntitlement.Unknown) { + return this.context.state.registered ? localize('signUp', "Sign in to use Copilot") : localize('signUpFree', "Sign in to use Copilot for free"); + } + + if (this.context.state.entitlement === ChatEntitlement.Pro) { + return localize('copilotProTitle', "Start using Copilot Pro"); + } + + return this.context.state.registered ? localize('copilotTitle', "Start using Copilot") : localize('copilotFreeTitle', "Start using Copilot for free"); + } + + private createDialog(disposables: DisposableStore): HTMLElement { + const element = $('.chat-setup-view'); + + const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); + + // Header + const header = localize({ key: 'headerDialog', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer. Write code faster with completions, fix bugs and build new features across multiple files, and learn about your codebase through chat.", defaultChat.documentationUrl); + element.appendChild($('p.setup-header', undefined, disposables.add(markdown.render(new MarkdownString(header, { isTrusted: 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.setup-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.setup-settings', undefined, disposables.add(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element)); + } + + return element; + } +} + export class ChatSetupContribution extends Disposable implements IWorkbenchContribution { - static readonly ID = 'workbench.chat.setup'; + static readonly ID = 'workbench.contrib.chatSetup'; constructor( @IProductService private readonly productService: IProductService, @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, + @ILogService private readonly logService: ILogService, ) { super(); - const context = chatEntitlementsService.context?.value; - const requests = chatEntitlementsService.requests?.value; + 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 = markAsSingleton(new MutableDisposable()); // prevents flicker on window reload + + const updateRegistration = () => { + const disabled = context.state.hidden || !this.configurationService.getValue('chat.setupFromDialog'); + if (!disabled && !registration.value) { + const { agent: panelAgent, disposable: panelDisposable } = SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Panel, false, context, controller); + registration.value = combinedDisposable( + panelDisposable, + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Terminal, false, context, controller).disposable, + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Notebook, false, context, controller).disposable, + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Editor, false, context, controller).disposable, + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, false, context, controller).disposable, + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, true, context, controller).disposable, + panelAgent.onUnresolvableError(() => { + // An unresolvable error from our agent registrations means that + // Copilot is unhealthy for some reason. We clear our panel + // registration to give Copilot a chance to show a custom message + // to the user from the views and stop pretending as if there was + // a functional agent. + this.logService.error('[chat setup] Unresolvable error from Copilot agent registration, clearing registration.'); + panelDisposable.dispose(); + }) + ); + } else if (disabled && registration.value) { + registration.clear(); + } + }; + + this._register(Event.runAndSubscribe(Event.any( + context.onDidChange, + Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('chat.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 +587,14 @@ 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.or( ChatContextKeys.Setup.installed.negate(), - ChatContextKeys.Setup.canSignUp + ChatContextKeys.Entitlement.canSignUp ); + + const CHAT_SETUP_ACTION_LABEL = localize2('triggerChatSetup', "Use AI Features with Copilot for free..."); + class ChatSetupTriggerAction extends Action2 { constructor() { @@ -185,25 +608,59 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr id: MenuId.ChatTitleBarMenu, group: 'a_last', order: 1, - when: chatSetupTriggerContext + when: ContextKeyExpr.and( + chatSetupTriggerContext, + ContextKeyExpr.or( + ChatContextKeys.Setup.fromDialog.negate(), // reduce noise when using the skeleton-view approach + ChatContextKeys.Setup.hidden // but enforce it if copilot is hidden + ) + ) } }); } - override async run(accessor: ServicesAccessor): Promise { + override async run(accessor: ServicesAccessor, mode: ChatMode): Promise { const viewsService = accessor.get(IViewsService); const viewDescriptorService = accessor.get(IViewDescriptorService); const configurationService = accessor.get(IConfigurationService); const layoutService = accessor.get(IWorkbenchLayoutService); const statusbarService = accessor.get(IStatusbarService); + const instantiationService = accessor.get(IInstantiationService); + const dialogService = accessor.get(IDialogService); + const commandService = accessor.get(ICommandService); + const lifecycleService = accessor.get(ILifecycleService); await context.update({ hidden: false }); - showCopilotView(viewsService, layoutService); - ensureSideBarChatViewSize(viewDescriptorService, layoutService, viewsService); + const chatWidgetPromise = showCopilotView(viewsService, layoutService); + if (mode) { + const chatWidget = await chatWidgetPromise; + chatWidget?.input.setChatMode(mode); + } + + const setupFromDialog = configurationService.getValue('chat.setupFromDialog'); + if (!setupFromDialog) { + ensureSideBarChatViewSize(viewDescriptorService, layoutService, viewsService); + } statusbarService.updateEntryVisibility('chat.statusBarEntry', true); configurationService.updateValue('chat.commandCenter.enabled', true); + + if (setupFromDialog) { + const setup = ChatSetup.getInstance(instantiationService, context, controller); + const result = await setup.run(); + if (result === false && !lifecycleService.willShutdown) { + 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 +675,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 +727,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,14 +742,11 @@ 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' }); - openerService.open(URI.parse(defaultChat.upgradePlanUrl)); const entitlement = context.state.entitlement; @@ -329,456 +783,17 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const params = new URLSearchParams(url.query); this.telemetryService.publicLog2('workbenchActionExecuted', { id: CHAT_SETUP_ACTION_ID, from: 'url', detail: params.get('referrer') ?? undefined }); - await this.commandService.executeCommand(CHAT_SETUP_ACTION_ID); + await this.commandService.executeCommand(CHAT_SETUP_ACTION_ID, validateChatMode(params.get('mode'))); return true; } })); } - - private registerSetting(context: ChatSetupContext): void { - const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - - let lastNode: IConfigurationNode | undefined; - const registerSetting = () => { - const treatmentId = context.state.entitlement === ChatEntitlement.Limited ? - 'chatAgentMaxRequestsFree' : - 'chatAgentMaxRequestsPro'; - this.experimentService.getTreatment(treatmentId).then(value => { - const defaultValue = value ?? (context.state.entitlement === ChatEntitlement.Limited ? 5 : 15); - const node: IConfigurationNode = { - id: 'chatSidebar', - title: localize('interactiveSessionConfigurationTitle', "Chat"), - type: 'object', - properties: { - 'chat.agent.maxRequests': { - type: 'number', - markdownDescription: localize('chat.agent.maxRequests', "The maximum number of requests to allow Copilot Edits to use per-turn in agent mode. When the limit is reached, Copilot will ask the user to confirm that it should keep working. \n\n> **Note**: For users on the Copilot Free plan, note that each agent mode request currently uses one chat request."), - default: defaultValue, - tags: ['experimental'] - }, - } - }; - configurationRegistry.updateConfigurations({ remove: lastNode ? [lastNode] : [], add: [node] }); - lastNode = node; - }); - }; - this._register(Event.runAndSubscribe(Event.debounce(context.onDidChange, () => { }, 1000), () => registerSetting())); - } } //#endregion -//#region Chat Setup Request Service - -type EntitlementClassification = { - tid: { classification: 'EndUserPseudonymizedInformation'; purpose: 'BusinessInsight'; comment: 'The anonymized analytics id returned by the service'; endpoint: 'GoogleAnalyticsId' }; - entitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Flag indicating the chat entitlement state' }; - quotaChat: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of chat completions available to the user' }; - quotaCompletions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of chat completions available to the user' }; - quotaResetDate: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The date the quota will reset' }; - owner: 'bpasero'; - comment: 'Reporting chat setup entitlements'; -}; - -type EntitlementEvent = { - entitlement: ChatEntitlement; - tid: string; - quotaChat: number | undefined; - quotaCompletions: number | undefined; - quotaResetDate: string | undefined; -}; - -interface IEntitlementsResponse { - readonly access_type_sku: string; - readonly assigned_date: string; - readonly can_signup_for_limited: boolean; - readonly chat_enabled: boolean; - readonly analytics_tracking_id: string; - readonly limited_user_quotas?: { - readonly chat: number; - readonly completions: number; - }; - readonly 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) { - if (!token.isCancellationRequested) { - 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 - -//#region Setup Rendering +//#region Setup Controller type InstallChatClassification = { owner: 'bpasero'; @@ -786,11 +801,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'; + installResult: 'installed' | 'alreadyInstalled' | 'cancelled' | 'failedInstall' | 'failedNotSignedIn' | 'failedSignUp' | 'failedNotTrusted' | 'failedNoSession'; installDuration: number; signUpErrorCode: number | undefined; + setupFromDialog: boolean; }; enum ChatSetupStep { @@ -807,14 +824,11 @@ class ChatSetupController extends Disposable { private _step = ChatSetupStep.Initial; get step(): ChatSetupStep { return this._step; } - 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, @@ -828,6 +842,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(); @@ -836,7 +851,6 @@ class ChatSetupController extends Disposable { private registerListeners(): void { this._register(this.context.onDidChange(() => this._onDidChange.fire())); - this._register(this.lifecycleService.onWillShutdown(() => this.willShutdown = true)); } private setStep(step: ChatSetupStep): void { @@ -848,7 +862,7 @@ class ChatSetupController extends Disposable { this._onDidChange.fire(); } - async setup(options?: { forceSignIn: boolean }): Promise { + async setup(options?: { forceSignIn?: boolean; setupFromDialog?: 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, { @@ -856,32 +870,33 @@ class ChatSetupController extends Disposable { }); try { - await this.progressService.withProgress({ + return await this.progressService.withProgress({ location: ProgressLocation.Window, - command: CHAT_SETUP_ACTION_ID, + command: CHAT_OPEN_ACTION_ID, title, - }, () => this.doSetup(options?.forceSignIn ?? false, watch)); + }, () => this.doSetup(options ?? {}, watch)); } finally { badge.dispose(); } } - private async doSetup(forceSignIn: boolean, watch: StopWatch): Promise { + private async doSetup(options: { forceSignIn?: boolean; setupFromDialog?: boolean }, watch: StopWatch): Promise { this.context.suspend(); // reduces flicker let focusChatInput = false; + let success = false; try { - const providerId = ChatSetupRequests.providerId(this.configurationService); + const providerId = ChatEntitlementRequests.providerId(this.configurationService); let session: AuthenticationSession | undefined; let entitlement: ChatEntitlement | undefined; // Entitlement Unknown or `forceSignIn`: we need to sign-in user - if (this.context.state.entitlement === ChatEntitlement.Unknown || forceSignIn) { + if (this.context.state.entitlement === ChatEntitlement.Unknown || options.forceSignIn) { this.setStep(ChatSetupStep.SigningIn); - const result = await this.signIn(providerId); + const result = await this.signIn(providerId, options); 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: Boolean(options.setupFromDialog) }); + return false; } session = result.session; @@ -892,15 +907,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: Boolean(options.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, options, watch); const currentActiveElement = getActiveElement(); focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body; @@ -909,49 +924,50 @@ class ChatSetupController extends Disposable { this.context.resume(); } - if (focusChatInput) { + if (focusChatInput && !options.setupFromDialog) { (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); } + + return success; } - private async signIn(providerId: string): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { + private async signIn(providerId: string, options?: { setupFromDialog?: boolean }): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { let session: AuthenticationSession | undefined; - let entitlements: IChatEntitlements | undefined; + let entitlements; try { - showCopilotView(this.viewsService, this.layoutService); + if (!options?.setupFromDialog) { + 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}`); } - if (!session && !this.willShutdown) { + if (!session && !this.lifecycleService.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") }); if (confirmed) { - return this.signIn(providerId); + return this.signIn(providerId, options); } } 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, options: { setupFromDialog?: boolean }, watch: StopWatch): Promise { const wasInstalled = this.context.state.installed; let signUpResult: boolean | { errorCode: number } | undefined = undefined; try { - showCopilotView(this.viewsService, this.layoutService); + if (!options?.setupFromDialog) { + showCopilotView(this.viewsService, this.layoutService); + } if ( entitlement !== ChatEntitlement.Limited && // User is not signed up to Copilot Free @@ -966,35 +982,39 @@ 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: Boolean(options.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: Boolean(options.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: Boolean(options.setupFromDialog) }); + return false; } - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'installed', installDuration: watch.elapsed(), signUpErrorCode: undefined }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: wasInstalled ? 'alreadyInstalled' : 'installed', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog: Boolean(options.setupFromDialog) }); if (wasInstalled && signUpResult === true) { refreshTokens(this.commandService); } - await Promise.race([ - timeout(5000), // helps prevent flicker with sign-in welcome view - Event.toPromise(this.chatAgentService.onDidChangeAgents) // https://github.com/microsoft/vscode-copilot/issues/9274 - ]); + if (!options?.setupFromDialog) { + await Promise.race([ + timeout(5000), // helps prevent flicker with sign-in welcome view + Event.toPromise(this.chatAgentService.onDidChangeAgents) // https://github.com/microsoft/vscode-copilot/issues/9274 + ]); + } + + return true; } private async doInstall(): Promise { @@ -1013,7 +1033,7 @@ class ChatSetupController extends Disposable { } if (error) { - if (!this.willShutdown) { + if (!this.lifecycleService.willShutdown) { const { confirmed } = await this.dialogService.confirm({ type: Severity.Error, message: localize('unknownSetupError', "An error occurred while setting up Copilot. Would you like to try again?"), @@ -1029,143 +1049,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(options: { useEnterpriseProvider: boolean; setupFromDialog?: 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' @@ -1173,18 +1070,32 @@ class ChatSetupWelcomeContent extends Disposable { } }); - if (useEnterpriseProvider) { + if (options.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 (options.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({ ...options, forceSignIn: true }); } private async handleEnterpriseInstance(): Promise { @@ -1200,6 +1111,7 @@ class ChatSetupWelcomeContent extends Disposable { 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"...'), + ignoreFocusLost: true, value: uri, validateInput: async value => { isSingleWord = false; @@ -1215,7 +1127,7 @@ class ChatSetupWelcomeContent extends Disposable { }; } if (!fullUriRegEx.test(value)) { return { - content: localize('invalidEnterpriseInstance', 'Please enter a valid {0} instance (i.e. "octocat" or "https://octocat.ghe.com")', defaultChat.enterpriseProviderName), + content: localize('invalidEnterpriseInstance', 'You must enter a valid {0} instance (i.e. "octocat" or "https://octocat.ghe.com")', defaultChat.enterpriseProviderName), severity: Severity.Error }; } @@ -1257,134 +1169,124 @@ class ChatSetupWelcomeContent extends Disposable { //#endregion -//#region Context +//#region Setup View Welcome -interface IChatSetupContextState { - entitlement: ChatEntitlement; - hidden?: boolean; - installed?: boolean; - registered?: boolean; -} +class ChatSetupWelcomeContent extends Disposable { -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; - - 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(); + this.create(); } - private async checkExtensionInstallation(): Promise { + private create(): void { + const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); - // Await extensions to be ready to be queried - await this.extensionsWorkbenchService.queryLocal(); + // 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)); - // 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 - } + 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")) + ) + ) + ) + ); + } - const defaultChatExtension = this.extensionsWorkbenchService.local.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); - this.update({ installed: !!defaultChatExtension?.local && this.extensionEnablementService.isEnabled(defaultChatExtension.local) }); + // 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)); + + // 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({ useEnterpriseProvider: false }) }), + toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: 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', undefined, 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', undefined, 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))); } - 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)}`); + private update(freeContainer: HTMLElement, settingsContainer: HTMLElement, button: ButtonWithDropdown): void { + const showSettings = this.telemetryService.telemetryLevel !== TelemetryLevel.NONE; + let showFree: boolean; + let buttonLabel: string; - 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 - } + 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; } - if (typeof context.hidden === 'boolean') { - this._state.hidden = context.hidden; + 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; } - if (typeof context.entitlement === 'number') { - this._state.entitlement = context.entitlement; + setVisibility(showFree, freeContainer); + setVisibility(showSettings, settingsContainer); - 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 - } - } - - this.storageService.store(ChatSetupContext.CHAT_SETUP_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 setup] 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; + button.label = buttonLabel; + button.enabled = this.controller.step === ChatSetupStep.Initial; } } @@ -1392,6 +1294,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 bf3b280f..d4d6718a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus.ts @@ -6,40 +6,38 @@ 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 { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment, StatusbarEntryKind, 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, EventHelper, EventLike, 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 { coalesce } from '../../../../base/common/arrays.js'; -import { CTX_INLINE_CHAT_POSSIBLE } from '../../inlineChat/common/inlineChat.js'; -import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { Color } from '../../../../base/common/color.js'; -import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Gesture, EventType as TouchEventType } from '../../../../base/browser/touch.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; +import product from '../../../../platform/product/common/product.js'; import { isObject } from '../../../../base/common/types.js'; import { ILanguageService } from '../../../../editor/common/languages/language.js'; - -//#region --- colors +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { Button } from '../../../../base/browser/ui/button/button.js'; +import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from '../../../../base/common/actions.js'; +import { parseLinkedText } from '../../../../base/common/linkedText.js'; +import { Link } from '../../../../platform/opener/browser/link.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IChatStatusItemService, ChatStatusEntry } from './chatStatusItemService.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js'; +import { getCodeEditor } from '../../../../editor/browser/editorBrowser.js'; const gaugeBackground = registerColor('gauge.background', { dark: inputValidationInfoBorder, @@ -92,27 +90,28 @@ registerColor('gauge.errorForeground', { //#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'; + static readonly ID = 'workbench.contrib.chatStatusBarEntry'; private entry: IStatusbarEntryAccessor | undefined = undefined; - private readonly entryDisposables = this._register(new MutableDisposable()); - private dateFormatter = new Lazy(() => safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' })); + private dashboard = new Lazy(() => this.instantiationService.createInstance(ChatStatusDashboard)); + + private readonly activeCodeEditorListener = this._register(new MutableDisposable()); constructor( + @IChatEntitlementService private readonly chatEntitlementService: ChatEntitlementService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @IStatusbarService private readonly statusbarService: IStatusbarService, - @IChatQuotasService private readonly chatQuotasService: IChatQuotasService, - @IChatEntitlementsService private readonly chatEntitlementsService: IChatEntitlementsService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ICommandService private readonly commandService: ICommandService, - @IHoverService private readonly hoverService: IHoverService, @IEditorService private readonly editorService: IEditorService, - @IProductService private readonly productService: IProductService, - @ILanguageService private readonly languageService: ILanguageService + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); @@ -121,9 +120,15 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu } private async create(): Promise { - if (this.configurationService.getValue('chat.experimental.statusIndicator.enabled') === true) { - this.entry ||= this.statusbarService.addEntry(this.getEntryProps(), ChatStatusBarEntry.ID, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT }); - this.statusbarService.updateEntryVisibility(`${this.productService.defaultChatAgent?.extensionId}.status`, false); // TODO@bpasero: remove this eventually + const hidden = this.chatEntitlementService.sentiment === ChatSentiment.Disabled; + + if (!hidden) { + this.entry ||= this.statusbarService.addEntry(this.getEntryProps(), 'chat.statusBarEntry', StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT }); + + // TODO@bpasero: remove this eventually + const completionsStatusId = `${defaultChat.extensionId}.status`; + this.statusbarService.updateEntryVisibility(completionsStatusId, false); + this.statusbarService.overrideEntry(completionsStatusId, { name: localize('codeCompletionsStatus', "Copilot Code Completions"), text: localize('codeCompletionsStatusText', "$(copilot) Completions") }); } else { this.entry?.dispose(); this.entry = undefined; @@ -131,167 +136,283 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu } private registerListeners(): void { + 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()))); + + this._register(this.editorService.onDidActiveEditorChange(() => this.onDidActiveEditorChange())); + this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('chat.experimental.statusIndicator.enabled')) { - this.create(); + if (e.affectsConfiguration(defaultChat.completionsEnablementSetting)) { + this.entry?.update(this.getEntryProps()); } })); + } - 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; - } + private onDidActiveEditorChange(): void { + this.entry?.update(this.getEntryProps()); - if (e.affectsSome(contextKeysSet)) { - this.entry.update(this.getEntryProps()); - } - })); + this.activeCodeEditorListener.clear(); - this._register(this.chatQuotasService.onDidChangeQuotaExceeded(() => this.entry?.update(this.getEntryProps()))); + // Listen to language changes in the active code editor + const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorControl); + if (activeCodeEditor) { + this.activeCodeEditorListener.value = activeCodeEditor.onDidChangeModelLanguage(() => { + this.entry?.update(this.getEntryProps()); + }); + } } private getEntryProps(): IStatusbarEntry { - this.entryDisposables.clear(); - let text = '$(copilot)'; let ariaLabel = localize('chatStatus', "Copilot Status"); - let command: string | Command = TOGGLE_CHAT_ACTION_ID; - let tooltip: TooltipContent; let kind: StatusbarEntryKind | undefined; - // 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"); + 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 }); - kind = 'prominent'; - } - - // 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; - } - - // 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..."); - } - - // Copilot Limited User - else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.limited.key) === true) { - tooltip = { - element: token => { - const { container, disposables } = this.createContainer(token); - - // Quota Indicator - { - const { chatTotal, chatRemaining, completionsTotal, completionsRemaining, quotaResetDate } = this.chatQuotasService.quotas; - - container.appendChild($('div.header', undefined, localize('usageTitle', "Copilot Free Usage"))); - - const chatQuotaIndicator = this.createQuotaIndicator(container, chatTotal, chatRemaining, localize('chatsLabel', "Chat messages")); - const completionsQuotaIndicator = this.createQuotaIndicator(container, completionsTotal, completionsRemaining, localize('completionsLabel', "Code completions")); - - container.appendChild($('div.description', undefined, localize('limitQuota', "Limits will reset on {0}.", this.dateFormatter.value.format(quotaResetDate)))); - - this.chatEntitlementsService.resolve(token).then(() => { - if (token.isCancellationRequested) { - return; - } - - const { chatTotal, chatRemaining, completionsTotal, completionsRemaining } = this.chatQuotasService.quotas; - - chatQuotaIndicator(chatTotal, chatRemaining); - completionsQuotaIndicator(completionsTotal, completionsRemaining); - }); - } - - // Settings - { - container.appendChild($('hr')); - container.appendChild($('div.header', undefined, localize('settingsTitle', "Settings"))); - this.createSettings(container, disposables); - } - - // Shortcuts - { - container.appendChild($('hr')); - container.appendChild($('div.header', undefined, localize('keybindingsTitle', "Keybindings"))); - this.createShortcuts(container, disposables); - } - - return container; + // 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"); } - }; - command = ShowTooltipCommand; - } - // Any other User - else { - tooltip = { - element: token => { - const { container, disposables } = this.createContainer(token); + text = `$(copilot-warning) ${quotaWarning}`; + ariaLabel = quotaWarning; + kind = 'prominent'; + } - // Settings - { - container.appendChild($('div.header', undefined, localize('settingsTitle', "Settings"))); - this.createSettings(container, disposables); - } - - // Shortcuts - { - container.appendChild($('hr')); - container.appendChild($('div.header', undefined, localize('keybindingsTitle', "Keybindings"))); - this.createShortcuts(container, disposables); - } - - return container; - } - }; - command = ShowTooltipCommand; + // Completions Disabled + else if (this.editorService.activeTextEditorLanguageId && !isCompletionsEnabled(this.configurationService, this.editorService.activeTextEditorLanguageId)) { + text = `$(copilot-not-connected)`; + ariaLabel = localize('completionsDisabledStatus', "Code Completions Disabled"); + } } return { name: localize('chatStatus', "Copilot Status"), text, ariaLabel, - command, + command: ShowTooltipCommand, showInAllWindows: true, kind, - tooltip + tooltip: { element: token => this.dashboard.value.show(token) } }; } - private createContainer(token: CancellationToken): { container: HTMLElement; disposables: DisposableStore } { + override dispose(): void { + super.dispose(); + + this.entry?.dispose(); + this.entry = undefined; + } +} + +function isNewUser(chatEntitlementService: IChatEntitlementService): boolean { + return chatEntitlementService.sentiment !== ChatSentiment.Installed || // copilot not installed + chatEntitlementService.entitlement === ChatEntitlement.Available; // not yet signed up to copilot +} + +function canUseCopilot(chatEntitlementService: IChatEntitlementService): boolean { + const newUser = isNewUser(chatEntitlementService); + const signedOut = chatEntitlementService.entitlement === ChatEntitlement.Unknown; + const allQuotaReached = chatEntitlementService.quotas.chatQuotaExceeded && chatEntitlementService.quotas.completionsQuotaExceeded; + + return !newUser && !signedOut && !allQuotaReached; +} + +function isCompletionsEnabled(configurationService: IConfigurationService, modeId: string = '*'): boolean { + const result = configurationService.getValue>(defaultChat.completionsEnablementSetting); + if (!isObject(result)) { + return false; + } + + if (typeof result[modeId] !== 'undefined') { + return Boolean(result[modeId]); // go with setting if explicitly defined + } + + return Boolean(result['*']); // fallback to global setting otherwise +} + +interface ISettingsAccessor { + readSetting: () => boolean; + writeSetting: (value: boolean) => Promise; +} + +class ChatStatusDashboard extends Disposable { + + private readonly element = $('div.chat-status-bar-entry-tooltip'); + + private dateFormatter = new Lazy(() => safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' })); + private readonly entryDisposables = this._register(new MutableDisposable()); + + constructor( + @IChatEntitlementService private readonly chatEntitlementService: ChatEntitlementService, + @IChatStatusItemService private readonly chatStatusItemService: IChatStatusItemService, + @ICommandService private readonly commandService: ICommandService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IEditorService private readonly editorService: IEditorService, + @IHoverService private readonly hoverService: IHoverService, + @ILanguageService private readonly languageService: ILanguageService, + @IOpenerService private readonly openerService: IOpenerService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, + ) { + super(); + } + + show(token: CancellationToken): HTMLElement { + clearNode(this.element); + const disposables = this.entryDisposables.value = new DisposableStore(); disposables.add(token.onCancellationRequested(() => disposables.dispose())); - return { - container: $('div.chat-status-bar-entry-tooltip'), - disposables + let needsSeparator = false; + const addSeparator = (label: string | undefined) => { + if (needsSeparator) { + this.element.appendChild($('hr')); + needsSeparator = false; + } + + if (label) { + this.element.appendChild($('div.header', undefined, label)); + } + + needsSeparator = true; }; + + // Quota Indicator + if (this.chatEntitlementService.entitlement === ChatEntitlement.Limited) { + const { chatTotal, chatRemaining, completionsTotal, completionsRemaining, quotaResetDate, chatQuotaExceeded, completionsQuotaExceeded } = this.chatEntitlementService.quotas; + + addSeparator(localize('usageTitle', "Copilot Free Plan 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('workbench.action.chat.upgradePlan'))); + } + + (async () => { + await this.chatEntitlementService.update(token); + if (token.isCancellationRequested) { + return; + } + + const { chatTotal, chatRemaining, completionsTotal, completionsRemaining } = this.chatEntitlementService.quotas; + + chatQuotaIndicator(chatTotal, chatRemaining); + completionsQuotaIndicator(completionsTotal, completionsRemaining); + })(); + } + + // Contributions + { + for (const item of this.chatStatusItemService.getEntries()) { + addSeparator(undefined); + const chatItemDisposables = disposables.add(new MutableDisposable()); + + let rendered = this.renderContributedChatStatusItem(item); + chatItemDisposables.value = rendered.disposables; + this.element.appendChild(rendered.element); + + disposables.add(this.chatStatusItemService.onDidChange(e => { + if (e.entry.id === item.id) { + const oldEl = rendered.element; + + rendered = this.renderContributedChatStatusItem(e.entry); + chatItemDisposables.value = rendered.disposables; + + oldEl.replaceWith(rendered.element); + } + })); + } + } + + // Settings + { + addSeparator(localize('settingsTitle', "Settings")); + + this.createSettings(this.element, disposables); + } + + // New to Copilot / Signed out + { + const newUser = isNewUser(this.chatEntitlementService); + const signedOut = this.chatEntitlementService.entitlement === ChatEntitlement.Unknown; + if (newUser || signedOut) { + addSeparator(undefined); + + this.element.appendChild($('div.description', undefined, newUser ? localize('activateDescription', "Set up Copilot to use AI features.") : localize('signInDescription', "Sign in to use Copilot AI features."))); + + const button = disposables.add(new Button(this.element, { ...defaultButtonStyles })); + button.label = newUser ? localize('activateCopilotButton', "Set up Copilot") : localize('signInToUseCopilotButton', "Sign in to use Copilot"); + disposables.add(button.onDidClick(() => this.runCommandAndClose(newUser ? 'workbench.action.chat.triggerSetup' : () => this.chatEntitlementService.requests?.value.signIn()))); + } + } + + return this.element; + } + + private renderContributedChatStatusItem(item: ChatStatusEntry): { element: HTMLElement; disposables: DisposableStore } { + const disposables = new DisposableStore(); + + const entryEl = $('div.contribution'); + + entryEl.appendChild($('div.header', undefined, item.label)); + + const bodyEl = entryEl.appendChild($('div.body')); + + const descriptionEl = bodyEl.appendChild($('span.description')); + this.renderTextPlus(descriptionEl, item.description, disposables); + + if (item.detail) { + const itemElement = bodyEl.appendChild($('div.detail-item')); + this.renderTextPlus(itemElement, item.detail, disposables); + } + + return { element: entryEl, disposables }; + } + + private renderTextPlus(target: HTMLElement, text: string, store: DisposableStore): void { + for (const node of parseLinkedText(text).nodes) { + if (typeof node === 'string') { + const parts = renderLabelWithIcons(node); + target.append(...parts); + } else { + store.add(new Link(target, node, undefined, this.hoverService, this.openerService)); + } + } + } + + private runCommandAndClose(commandOrFn: string | Function): void { + if (typeof commandOrFn === 'function') { + commandOrFn(); + } else { + this.telemetryService.publicLog2('workbenchActionExecuted', { id: commandOrFn, from: 'chat-status' }); + this.commandService.executeCommand(commandOrFn); + } + + this.hoverService.hideHover(true); } private createQuotaIndicator(container: HTMLElement, total: number | undefined, remaining: number | undefined, label: string): (total: number | undefined, remaining: number | undefined) => void { @@ -334,130 +455,114 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu return update; } - private createShortcuts(container: HTMLElement, disposables: DisposableStore): HTMLElement { - const shortcuts = container.appendChild($('div.shortcuts')); - - const entries = coalesce([ - { text: localize('shortcuts.chat', "Chat"), id: CHAT_OPEN_ACTION_ID }, - { text: localize('shortcuts.copilotEdits', "Copilot Edits"), id: 'workbench.action.chat.openEditSession' }, - this.contextKeyService.contextMatchesRules(ContextKeyExpr.and( - CTX_INLINE_CHAT_POSSIBLE, - EditorContextKeys.writable, - EditorContextKeys.editorSimpleInput.negate() - )) ? { text: localize('shortcuts.inlineChat', "Inline Chat"), id: 'inlineChat.start' } : undefined, - { text: localize('shortcuts.quickChat', "Quick Chat"), id: 'workbench.action.quickchat.toggle' }, - ]); - - const onTrigger = (commandId: string, e: EventLike) => { - EventHelper.stop(e, true); - - this.hoverService.hideHover(true); - this.commandService.executeCommand(commandId); - }; - - for (const entry of entries) { - const keys = this.keybindingService.lookupKeybinding(entry.id); - if (!keys) { - continue; - } - - const shortcut = append(shortcuts, $('div.shortcut', { tabIndex: 0, role: 'button', 'aria-label': entry.text })); - - append(shortcut, $('span.shortcut-label', undefined, entry.text)); - - const shortcutKey = disposables.add(new KeybindingLabel(shortcut, OS, { ...defaultKeybindingLabelStyles })); - shortcutKey.set(keys); - - disposables.add(Gesture.addTarget(shortcut)); - [EventType.CLICK, TouchEventType.Tap].forEach(eventType => { - disposables.add(addDisposableListener(shortcut, eventType, e => onTrigger(entry.id, e))); - }); - - disposables.add(addDisposableListener(shortcut, EventType.KEY_DOWN, e => { - const event = new StandardKeyboardEvent(e); - if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { - onTrigger(entry.id, e); - } - })); - } - - return shortcuts; - } - private createSettings(container: HTMLElement, disposables: DisposableStore): HTMLElement { - const language = this.editorService.activeTextEditorLanguageId; + const modeId = this.editorService.activeTextEditorLanguageId; const settings = container.appendChild($('div.settings')); // --- Code Completions { const globalSetting = append(settings, $('div.setting')); - this.createCodeCompletionsSetting(globalSetting, localize('settings.codeCompletions', "Code completions (all files)"), '*', disposables); + this.createCodeCompletionsSetting(globalSetting, localize('settings.codeCompletions', "Code Completions (all files)"), '*', disposables); - if (language) { + if (modeId) { const languageSetting = append(settings, $('div.setting')); - this.createCodeCompletionsSetting(languageSetting, localize('settings.codeCompletionsLanguage', "Code completions ({0})", this.languageService.getLanguageName(language) ?? language), language, disposables); + this.createCodeCompletionsSetting(languageSetting, localize('settings.codeCompletionsLanguage', "Code Completions ({0})", this.languageService.getLanguageName(modeId) ?? modeId), modeId, disposables); } } + // --- Next Edit Suggestions + { + const setting = append(settings, $('div.setting')); + this.createNextEditSuggestionsSetting(setting, localize('settings.nextEditSuggestions', "Next Edit Suggestions"), modeId, this.getCompletionsSettingAccessor(modeId), disposables); + } + return settings; } - private createCodeCompletionsSetting(container: HTMLElement, label: string, language: string | '*', disposables: DisposableStore): void { - const settingId = 'github.copilot.enable'; - - const readSetting = () => { - const result = this.configurationService.getValue>(settingId); - if (!isObject(result)) { - return false; - } - - if (typeof result[language] !== 'undefined') { - return Boolean(result[language]); // go with setting if explicitly defined - } - - return Boolean(result['*']); // fallback to global setting otherwise - }; - const writeSetting = (checkbox: Checkbox) => { - let result = this.configurationService.getValue>(settingId); - if (!isObject(result)) { - result = Object.create(null); - } - - return this.configurationService.updateValue(settingId, { ...result, [language]: checkbox.checked }); - }; - - const settingCheckbox = disposables.add(new Checkbox(label, readSetting(), defaultCheckboxStyles)); - container.appendChild(settingCheckbox.domNode); + 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 (settingCheckbox?.enabled) { + if (checkbox?.enabled) { EventHelper.stop(e, true); - settingCheckbox.checked = !settingCheckbox.checked; - writeSetting(settingCheckbox); - settingCheckbox.focus(); + checkbox.checked = !checkbox.checked; + accessor.writeSetting(checkbox.checked); + checkbox.focus(); } })); }); - disposables.add(settingCheckbox.onChange(() => { - writeSetting(settingCheckbox); + disposables.add(checkbox.onChange(() => { + accessor.writeSetting(checkbox.checked); })); disposables.add(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(settingId)) { - settingCheckbox.checked = readSetting(); + checkbox.checked = Boolean(accessor.readSetting()); + } + })); + + if (!canUseCopilot(this.chatEntitlementService)) { + container.classList.add('disabled'); + checkbox.disable(); + } + + return checkbox; + } + + private createCodeCompletionsSetting(container: HTMLElement, label: string, modeId: string | undefined, disposables: DisposableStore): void { + this.createSetting(container, defaultChat.completionsEnablementSetting, label, this.getCompletionsSettingAccessor(modeId), disposables); + } + + private getCompletionsSettingAccessor(modeId = '*'): ISettingsAccessor { + const settingId = defaultChat.completionsEnablementSetting; + + return { + readSetting: () => isCompletionsEnabled(this.configurationService, modeId), + writeSetting: (value: boolean) => { + let result = this.configurationService.getValue>(settingId); + if (!isObject(result)) { + result = Object.create(null); + } + + return this.configurationService.updateValue(settingId, { ...result, [modeId]: value }); + } + }; + } + + private createNextEditSuggestionsSetting(container: HTMLElement, label: string, modeId: string | undefined, completionsSettingAccessor: ISettingsAccessor, disposables: DisposableStore): void { + const nesSettingId = defaultChat.nextEditSuggestionsSetting; + const completionsSettingId = defaultChat.completionsEnablementSetting; + const resource = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + + const checkbox = this.createSetting(container, nesSettingId, label, { + readSetting: () => this.textResourceConfigurationService.getValue(resource, nesSettingId), + writeSetting: (value: boolean) => this.textResourceConfigurationService.updateValue(resource, nesSettingId, value) + }, disposables); + + // enablement of NES depends on completions setting + // so we have to update our checkbox state accordingly + + if (!completionsSettingAccessor.readSetting()) { + container.classList.add('disabled'); + checkbox.disable(); + } + + disposables.add(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(completionsSettingId)) { + if (completionsSettingAccessor.readSetting() && canUseCopilot(this.chatEntitlementService)) { + checkbox.enable(); + container.classList.remove('disabled'); + } else { + checkbox.disable(); + container.classList.add('disabled'); + } } })); } - - override dispose(): void { - super.dispose(); - - this.entry?.dispose(); - this.entry = undefined; - } } diff --git a/src/vs/workbench/contrib/chat/browser/chatStatusItemService.ts b/src/vs/workbench/contrib/chat/browser/chatStatusItemService.ts new file mode 100644 index 00000000..3f009e58 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatStatusItemService.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from '../../../../base/common/event.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; + +export const IChatStatusItemService = createDecorator('IChatStatusItemService'); + +export interface IChatStatusItemService { + readonly _serviceBrand: undefined; + + readonly onDidChange: Event; + + setOrUpdateEntry(entry: ChatStatusEntry): void; + + deleteEntry(id: string): void; + + getEntries(): Iterable; +} + + +export interface IChatStatusItemChangeEvent { + readonly entry: ChatStatusEntry; +} + +export type ChatStatusEntry = { + id: string; + label: string; + description: string; + detail: string | undefined; +}; + + +class ChatStatusItemService implements IChatStatusItemService { + readonly _serviceBrand: undefined; + + private readonly _entries = new Map(); + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + setOrUpdateEntry(entry: ChatStatusEntry): void { + const isUpdate = this._entries.has(entry.id); + this._entries.set(entry.id, entry); + if (isUpdate) { + this._onDidChange.fire({ entry }); + } + } + + deleteEntry(id: string): void { + this._entries.delete(id); + } + + getEntries(): Iterable { + return this._entries.values(); + } +} + +registerSingleton(IChatStatusItemService, ChatStatusItemService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/browser/chatVariables.ts b/src/vs/workbench/contrib/chat/browser/chatVariables.ts index 68bae12a..f822997f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/chatVariables.ts @@ -6,11 +6,12 @@ import { coalesce } from '../../../../base/common/arrays.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'; @@ -20,6 +21,7 @@ export class ChatVariablesService implements IChatVariablesService { constructor( @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, @IViewsService private readonly viewsService: IViewsService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { } @@ -73,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) { @@ -84,7 +87,12 @@ export class ChatVariablesService implements IChatVariablesService { if (key === 'file' && typeof value !== 'string') { const uri = URI.isUri(value) ? value : value.uri; const range = 'range' in value ? value.range : undefined; - widget.attachmentModel.addFile(uri, range); + await 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 6e956308..9d7c8445 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -25,16 +25,18 @@ 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'; interface IViewPaneState extends IChatViewState { sessionId?: string; + hasMigratedCurrentSession?: boolean; } export const CHAT_SIDEBAR_OLD_VIEW_PANEL_ID = 'workbench.panel.chatSidebar'; @@ -50,6 +52,8 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { private defaultParticipantRegistrationFailed = false; private didUnregisterProvider = false; + private _restoringSession: Promise | undefined; + constructor( private readonly chatOptions: { location: ChatAgentLocation.Panel | ChatAgentLocation.EditingSession }, options: IViewPaneOptions, @@ -73,25 +77,46 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { // View state for the ViewPane is currently global per-provider basically, but some other strictly per-model state will require a separate memento. this.memento = new Memento('interactive-session-view-' + CHAT_PROVIDER_ID + (this.chatOptions.location === ChatAgentLocation.EditingSession ? `-edits` : ''), this.storageService); this.viewState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE) as IViewPaneState; + + if (this.chatService.unifiedViewEnabled && this.chatOptions.location === ChatAgentLocation.Panel && !this.viewState.hasMigratedCurrentSession) { + const editsMemento = new Memento('interactive-session-view-' + CHAT_PROVIDER_ID + `-edits`, this.storageService); + const lastEditsState = editsMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE) as IViewPaneState; + if (lastEditsState.sessionId) { + this.logService.trace(`ChatViewPane: last edits session was ${lastEditsState.sessionId}`); + if (!this.chatService.isPersistedSessionEmpty(lastEditsState.sessionId)) { + this.logService.info(`ChatViewPane: migrating ${lastEditsState.sessionId} to unified view`); + this.viewState.sessionId = lastEditsState.sessionId; + this.viewState.inputValue = lastEditsState.inputValue; + this.viewState.inputState = { + ...lastEditsState.inputState, + chatMode: lastEditsState.inputState?.chatMode ?? ChatMode.Edit + }; + this.viewState.hasMigratedCurrentSession = true; + } + } + } + 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; - - // 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` - // so it should fire onDidChangeViewWelcomeState. - const wasVisible = this._widget.visible; - try { - this._widget.setVisible(false); - this.updateModel(model); - this.defaultParticipantRegistrationFailed = false; - this.didUnregisterProvider = false; - this._onDidChangeViewWelcomeState.fire(); - } finally { - this.widget.setVisible(wasVisible); - } + if (!this._widget?.viewModel && !this._restoringSession) { + const info = this.getTransferredOrPersistedSessionInfo(); + this._restoringSession = + (info.sessionId ? this.chatService.getOrRestoreSession(info.sessionId) : Promise.resolve(undefined)).then(async model => { + // 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` + // so it should fire onDidChangeViewWelcomeState. + const wasVisible = this._widget.visible; + try { + this._widget.setVisible(false); + await this.updateModel(model, info.inputValue || info.mode ? { inputState: { chatMode: info.mode }, inputValue: info.inputValue } : undefined); + this.defaultParticipantRegistrationFailed = false; + this.didUnregisterProvider = false; + this._onDidChangeViewWelcomeState.fire(); + } finally { + this.widget.setVisible(wasVisible); + } + }); + this._restoringSession.finally(() => this._restoringSession = undefined); } } else if (this._widget?.viewModel?.initState === ChatModelInitState.Initialized) { // Model is initialized, and the default agent disappeared, so show welcome view @@ -115,11 +140,11 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { } : undefined; } - private updateModel(model?: IChatModel | undefined, viewState?: IChatViewState): void { + private async updateModel(model?: IChatModel | undefined, viewState?: IChatViewState): Promise { this.modelDisposables.clear(); model = model ?? (this.chatService.transferredSessionData?.sessionId && this.chatService.transferredSessionData?.location === this.chatOptions.location - ? this.chatService.getOrRestoreSession(this.chatService.transferredSessionData.sessionId) + ? await this.chatService.getOrRestoreSession(this.chatService.transferredSessionData.sessionId) : this.chatService.startSession(this.chatOptions.location, CancellationToken.None)); if (!model) { throw new Error('Could not start chat session'); @@ -139,23 +164,26 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { override shouldShowWelcome(): boolean { const showSetup = this.contextKeyService.contextMatchesRules(ChatContextKeys.SetupViewCondition); const noPersistedSessions = !this.chatService.hasSessions(); - const shouldShow = this.didUnregisterProvider || !this._widget?.viewModel && noPersistedSessions || this.defaultParticipantRegistrationFailed || showSetup; - this.logService.trace(`ChatViewPane#shouldShowWelcome(${this.chatOptions.location}) = ${shouldShow}: didUnregister=${this.didUnregisterProvider} || noViewModel:${!this._widget?.viewModel} && noPersistedSessions=${noPersistedSessions} || defaultParticipantRegistrationFailed=${this.defaultParticipantRegistrationFailed} || showSetup=${showSetup}`); + const hasCoreAgent = this.chatAgentService.getAgents().some(agent => agent.isCore && agent.locations.includes(this.chatOptions.location)); + const shouldShow = !hasCoreAgent && (this.didUnregisterProvider || !this._widget?.viewModel && noPersistedSessions || this.defaultParticipantRegistrationFailed || showSetup); + this.logService.trace(`ChatViewPane#shouldShowWelcome(${this.chatOptions.location}) = ${shouldShow}: hasCoreAgent=${hasCoreAgent} didUnregister=${this.didUnregisterProvider} || noViewModel=${!this._widget?.viewModel} && noPersistedSessions=${noPersistedSessions} || defaultParticipantRegistrationFailed=${this.defaultParticipantRegistrationFailed} || showSetup=${showSetup}`); 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 { + protected override async renderBody(parent: HTMLElement): Promise { try { super.renderBody(parent); @@ -171,21 +199,21 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this.chatOptions.location, { viewId: this.id }, { - autoScroll: this.chatOptions.location === ChatAgentLocation.EditingSession, + autoScroll: mode => mode !== ChatMode.Ask, renderFollowups: this.chatOptions.location === ChatAgentLocation.Panel, supportsFileReferences: true, supportsAdditionalParticipants: this.chatOptions.location === ChatAgentLocation.Panel, rendererOptions: { - renderCodeBlockPills: this.chatOptions.location === ChatAgentLocation.EditingSession, 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: mode => mode !== ChatMode.Ask, }, 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 +229,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 +238,9 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this._onDidChangeViewWelcomeState.fire(); } })); - const model = sessionId ? this.chatService.getOrRestoreSession(sessionId) : undefined; + const model = info.sessionId ? await this.chatService.getOrRestoreSession(info.sessionId) : undefined; - this.updateModel(model); + await this.updateModel(model, info.inputValue || info.mode ? { inputState: { chatMode: info.mode }, inputValue: info.inputValue } : undefined); } catch (e) { this.logService.error(e); throw e; @@ -223,26 +251,26 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this._widget.acceptInput(query); } - private clear(): void { + private async clear(): Promise { if (this.widget.viewModel) { - this.chatService.clearSession(this.widget.viewModel.sessionId); + await this.chatService.clearSession(this.widget.viewModel.sessionId); } // Grab the widget's latest view state because it will be loaded back into the widget this.updateViewState(); - this.updateModel(undefined); + await this.updateModel(undefined); // Update the toolbar context with new sessionId this.updateActions(); } - loadSession(sessionId: string, viewState?: IChatViewState): void { + async loadSession(sessionId: string, viewState?: IChatViewState): Promise { if (this.widget.viewModel) { - this.chatService.clearSession(this.widget.viewModel.sessionId); + await this.chatService.clearSession(this.widget.viewModel.sessionId); } - const newModel = this.chatService.getOrRestoreSession(sessionId); - this.updateModel(newModel, viewState); + const newModel = await this.chatService.getOrRestoreSession(sessionId); + await this.updateModel(newModel, viewState); } focusInput(): void { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 65c48bcd..efe05719 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -8,6 +8,7 @@ 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'; @@ -37,9 +38,10 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet 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 { chatAgentLeader, ChatRequestAgentPart, chatSubcommandLeader, formatChatQuestion, IParsedChatRequest } from '../common/chatParserTypes.js'; import { ChatRequestParser } from '../common/chatRequestParser.js'; @@ -48,6 +50,7 @@ import { IChatSlashCommandService } from '../common/chatSlashCommands.js'; import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from '../common/chatViewModel.js'; import { IChatInputState } from '../common/chatWidgetHistoryService.js'; import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js'; +import { ChatAgentLocation, ChatConfiguration, ChatMode } from '../common/constants.js'; import { ChatTreeItem, IChatAcceptInputOptions, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatListItemRendererOptions, IChatWidget, IChatWidgetService, IChatWidgetViewContext, IChatWidgetViewOptions } from './chat.js'; import { ChatAccessibilityProvider } from './chatAccessibilityProvider.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; @@ -149,6 +152,7 @@ export class ChatWidget extends Disposable implements IChatWidget { private container!: HTMLElement; private welcomeMessageContainer!: HTMLElement; private persistedWelcomeMessage: IChatWelcomeMessageContent | undefined; + private readonly welcomePart: MutableDisposable = this._register(new MutableDisposable()); private bodyDimension: dom.Dimension | undefined; private visibleChangeCount = 0; @@ -157,6 +161,7 @@ export class ChatWidget extends Disposable implements IChatWidget { private canRequestBePaused: IContextKey; private agentInInput: IContextKey; + private _visible = false; public get visible() { return this._visible; @@ -200,7 +205,7 @@ export class ChatWidget extends Disposable implements IChatWidget { return { text: '', parts: [] }; } - this.parsedChatRequest = this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(this.viewModel!.sessionId, this.getInput(), this.location, { selectedAgent: this._lastSelectedAgent }); + this.parsedChatRequest = this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(this.viewModel!.sessionId, this.getInput(), this.location, { selectedAgent: this._lastSelectedAgent, mode: this.input.currentMode }); } return this.parsedChatRequest; @@ -217,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, @@ -253,6 +263,8 @@ export class ChatWidget extends Disposable implements IChatWidget { ChatContextKeys.inChatSession.bindTo(contextKeyService).set(true); ChatContextKeys.location.bindTo(contextKeyService).set(this._location.location); ChatContextKeys.inQuickChat.bindTo(contextKeyService).set(isQuickChat(this)); + ChatContextKeys.inUnifiedChat.bindTo(contextKeyService) + .set(this._location.location === ChatAgentLocation.Panel && !!this.viewOptions.supportsChangingModes && this.configurationService.getValue(ChatConfiguration.UnifiedChatView)); this.agentInInput = ChatContextKeys.inputHasAgent.bindTo(contextKeyService); this.requestInProgress = ChatContextKeys.requestInProgress.bindTo(contextKeyService); this.isRequestPaused = ChatContextKeys.isRequestPaused.bindTo(contextKeyService); @@ -343,24 +355,6 @@ export class ChatWidget extends Disposable implements IChatWidget { this.renderChatEditingSessionState(); })); - if (this._location.location === ChatAgentLocation.EditingSession) { - let currentEditSession: IChatEditingSession | undefined = undefined; - this._register(this.onDidChangeViewModel(async () => { - const sessionId = this._viewModel?.sessionId; - if (sessionId) { - if (sessionId !== currentEditSession?.chatSessionId) { - currentEditSession = await chatEditingService.startOrContinueGlobalEditingSession(sessionId); - } - } else { - if (currentEditSession) { - const session = currentEditSession; - currentEditSession = undefined; - await session.stop(); - } - } - })); - } - this._register(codeEditorService.registerCodeEditorOpenHandler(async (input: ITextResourceEditorInput, _source: ICodeEditor | null, _sideBySide?: boolean): Promise => { const resource = input.resource; if (resource.scheme !== Schemas.vscodeChatCodeBlock) { @@ -469,7 +463,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`)); @@ -478,6 +471,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, { @@ -528,6 +522,11 @@ export class ChatWidget extends Disposable implements IChatWidget { focusInput(): void { this.inputPart.focus(); + + // Sometimes focusing the input part is not possible, + // but we'd like to be the last focused chat widget, + // so we emit an optimistic onDidFocus event nonetheless. + this._onDidFocus.fire(); } hasInputFocus(): boolean { @@ -538,7 +537,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(); } @@ -614,7 +613,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); } @@ -627,21 +626,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) { @@ -654,13 +654,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?.getItems().length === 0) { + // TODO@roblourens hack- only Chat mode supports sample questions + this.renderFollowups(this.input.currentMode === ChatMode.Ask ? this.viewModel.model.sampleQuestions : undefined); + } + } + private async renderFollowups(items: IChatFollowup[] | undefined, response?: IChatResponseViewModel): Promise { this.inputPart.renderFollowups(items, response); @@ -695,7 +702,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 @@ -724,6 +732,7 @@ export class ChatWidget extends Disposable implements IChatWidget { location: this.location, userSelectedModelId: this.input.currentLanguageModel, hasInstructionAttachments: this.input.hasInstructionAttachments, + mode: this.input.currentMode, }; this.chatService.resendRequest(request, options).catch(e => this.logService.error('FAILED to rerun request', e)); } @@ -827,6 +836,16 @@ export class ChatWidget extends Disposable implements IChatWidget { this._onDidChangeContentHeight.fire(); } + private getWidgetViewKindTag(): string { + if (!this.viewContext) { + return 'editor'; + } else if ('viewId' in this.viewContext) { + return 'view'; + } else { + return 'quick'; + } + } + private createInput(container: HTMLElement, options?: { renderFollowups: boolean; renderStyle?: 'compact' | 'minimal' }): void { this.inputPart = this._register(this.instantiationService.createInstance(ChatInputPart, this.location, @@ -836,7 +855,9 @@ 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, + widgetViewKindTag: this.getWidgetViewKindTag() }, this.styles, () => this.collectInputState() @@ -859,7 +880,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; @@ -917,6 +938,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 { @@ -969,7 +995,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.viewModel = undefined; this.onDidChangeItems(); })); - this.inputPart.initForNewChatModel(viewState); + this.inputPart.initForNewChatModel(viewState, model.getRequests().length === 0); this.contribs.forEach(c => { if (c.setInputState && viewState.inputState?.[c.id]) { c.setInputState(viewState.inputState?.[c.id]); @@ -1057,10 +1083,6 @@ export class ChatWidget extends Disposable implements IChatWidget { return await this.chatService.resendRequest(lastRequest, options); } - async acceptInputWithPrefix(prefix: string): Promise { - this._acceptInput({ prefix }); - } - private collectInputState(): IChatInputState { const inputState: IChatInputState = {}; this.contribs.forEach(c => { @@ -1071,23 +1093,19 @@ export class ChatWidget extends Disposable implements IChatWidget { return inputState; } - private async _acceptInput(query: { query: string } | { prefix: string } | undefined, options?: IChatAcceptInputOptions): Promise { + private async _acceptInput(query: { query: string } | undefined, options?: IChatAcceptInputOptions): Promise { if (this.viewModel?.requestInProgress && this.viewModel.requestPausibility !== ChatPauseState.Paused) { return; } if (this.viewModel) { this._onDidAcceptInput.fire(); - if (!this.viewOptions.autoScroll) { - this.scrollLock = false; - } + this.scrollLock = !!checkModeOption(this.input.currentMode, this.viewOptions.autoScroll); const editorValue = this.getInput(); const requestId = this.chatAccessibilityService.acceptRequest(); - const input = !query ? editorValue : - 'query' in query ? query.query : - `${query.prefix} ${editorValue}`; - const isUserQuery = !query || 'prefix' in query; + const input = !query ? editorValue : query.query; + const isUserQuery = !query; const { promptInstructions } = this.inputPart.attachmentModel; const instructionsEnabled = promptInstructions.featureEnabled; @@ -1102,29 +1120,9 @@ 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(); + if (this.viewOptions.enableWorkingSet !== undefined && this.input.currentMode !== ChatMode.Ask) { 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)); - } - } - - 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(); @@ -1152,19 +1150,21 @@ 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); + this.input.validateCurrentMode(); 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) { @@ -1230,7 +1230,9 @@ 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 (this.viewOptions.renderStyle === 'compact' || this.viewOptions.renderStyle === 'minimal') { + this.listContainer.style.removeProperty('--chat-current-response-min-height'); + } else { this.listContainer.style.setProperty('--chat-current-response-min-height', listHeight * .75 + 'px'); } @@ -1250,7 +1252,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(); } @@ -1361,8 +1363,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); } } @@ -1393,6 +1396,10 @@ export class ChatWidgetService extends Disposable implements IChatWidgetService return this._lastFocusedWidget; } + getAllWidgets(): ReadonlyArray { + return this._widgets; + } + getWidgetsByLocations(location: ChatAgentLocation): ReadonlyArray { return this._widgets.filter(w => w.location === location); } diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index 05276d45..399a2f9e 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -69,6 +69,8 @@ import { ChatTreeItem } from './chat.js'; import { IChatRendererDelegate } from './chatListRenderer.js'; import { ChatEditorOptions } from './chatOptions.js'; import { emptyProgressRunner, IEditorProgressService } from '../../../../platform/progress/common/progress.js'; +import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js'; +import { SnippetController2 } from '../../../../editor/contrib/snippet/browser/snippetController2.js'; const $ = dom.$; @@ -140,6 +142,7 @@ export interface ICodeBlockRenderOptions { verticalPadding?: number; reserveWidth?: number; editorOptions?: IEditorOptions; + maxHeightInLines?: number; } const defaultCodeblockPadding = 10; @@ -312,6 +315,8 @@ export class CodeBlockPart extends Disposable { GlyphHoverController.ID, MessageController.ID, GotoDefinitionAtPositionEditorContribution.ID, + SuggestController.ID, + SnippetController2.ID, ColorDetector.ID, LinkDetector.ID, @@ -362,9 +367,15 @@ export class CodeBlockPart extends Disposable { layout(width: number): void { const contentHeight = this.getContentHeight(); + + let height = contentHeight; + if (this.currentCodeBlockData?.renderOptions?.maxHeightInLines) { + height = Math.min(contentHeight, this.editor.getOption(EditorOption.lineHeight) * this.currentCodeBlockData?.renderOptions?.maxHeightInLines); + } + const editorBorder = 2; width = width - editorBorder - (this.currentCodeBlockData?.renderOptions?.reserveWidth ?? 0); - this.editor.layout({ width, height: contentHeight }); + this.editor.layout({ width, height }); this.updatePaddingForLayout(); } @@ -394,12 +405,12 @@ export class CodeBlockPart extends Disposable { return; } - this.layout(width); this.editor.updateOptions({ ...this.getEditorOptionsFromConfig(), ariaLabel: localize('chat.codeBlockLabel', "Code block {0}", data.codeBlockIndex + 1), }); - this.toolbar.setAriaLabel(localize('chat.codeBlockToolbarLabel', "Toolbar for code block {0}", data.codeBlockIndex + 1)); + this.layout(width); + this.toolbar.setAriaLabel(localize('chat.codeBlockToolbarLabel', "Code block {0}", data.codeBlockIndex + 1)); if (data.renderOptions?.hideToolbar) { dom.hide(this.toolbar.getElement()); } else { diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts index 96c04a3f..48ba9a62 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts @@ -154,7 +154,7 @@ export class ChatDynamicVariableModel extends Disposable implements IChatWidgetC // if the `prompt snippets` feature is enabled, and file is a `prompt snippet`, // start resolving nested file references immediately and subscribe to updates - if (variable instanceof ChatFileReference && variable.isPromptSnippet) { + if (variable instanceof ChatFileReference && variable.isPromptFile) { // subscribe to variable changes variable.onUpdate(() => { this.updateDecorations(); @@ -742,23 +742,24 @@ export async function createMarkersQuickPick(accessor: ServicesAccessor, level: 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 new Promise(resolve => { - quickPick.onDidHide(() => resolve(undefined)); - quickPick.onDidAccept(ev => { + 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(() => quickPick.dispose()); + }).finally(() => store.dispose()); } export class SelectAndInsertProblemAction extends Action2 { diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts index 480c6f78..afcd62f2 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts @@ -18,10 +18,10 @@ import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { EditorsOrder } from '../../../../common/editor.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { getNotebookEditorFromEditorPane, INotebookEditor } from '../../../notebook/browser/notebookBrowser.js'; -import { ChatAgentLocation } from '../../common/chatAgents.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'; @@ -46,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(); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 60ce7464..804fd576 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -36,11 +36,12 @@ 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'; @@ -81,7 +82,7 @@ class SlashCommandCompletions extends Disposable { return; } - const slashCommands = this.chatSlashCommandService.getCommands(widget.location); + const slashCommands = this.chatSlashCommandService.getCommands(widget.location, widget.input.currentMode); if (!slashCommands) { return null; } @@ -121,7 +122,7 @@ class SlashCommandCompletions extends Disposable { return; } - const slashCommands = this.chatSlashCommandService.getCommands(widget.location); + const slashCommands = this.chatSlashCommandService.getCommands(widget.location, widget.input.currentMode); if (!slashCommands) { return null; } @@ -215,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.Ask) { return; } @@ -265,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; } @@ -305,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.Ask) { return; } @@ -324,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; } @@ -366,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.Ask) { return; } @@ -691,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.Ask && widget.viewModel && widget.viewModel.model.editingSession) { 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) { diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index e0997913..06957dd2 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -112,7 +112,7 @@ class InputEditorDecorations extends Disposable { } if (!inputValue) { - const defaultAgent = this.chatAgentService.getDefaultAgent(this.widget.location); + const defaultAgent = this.chatAgentService.getDefaultAgent(this.widget.location, this.widget.input.currentMode); const decoration: IDecorationOptions[] = [ { range: { @@ -301,7 +301,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 3860b5d5..44c4d6d8 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/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index ab12b8c9..4c3465c0 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -6,19 +6,36 @@ import { renderStringAsPlaintext } from '../../../../base/browser/markdownRenderer.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; import { Emitter } from '../../../../base/common/event.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Iterable } from '../../../../base/common/iterator.js'; +import { Lazy } from '../../../../base/common/lazy.js'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { LRUCache } 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 { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.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 { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import * as JSONContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; import { ILogService } from '../../../../platform/log/common/log.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { 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'; -import { CountTokensCallback, ILanguageModelToolsService, IToolData, IToolImpl, IToolInvocation, IToolResult } from '../common/languageModelToolsService.js'; +import { ChatConfiguration } from '../common/constants.js'; +import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolResult, stringifyPromptTsxPart } from '../common/languageModelToolsService.js'; + +const jsonSchemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); interface IToolEntry { data: IToolData; @@ -36,26 +53,44 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo private _tools = new Map(); private _toolContextKeys = new Set(); - + private readonly _ctxToolsCount: IContextKey; private _callsByRequestId = new Map(); + private _workspaceToolConfirmStore: Lazy; + private _profileToolConfirmStore: Lazy; + private _memoryToolConfirmStore = new Set(); + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, @IExtensionService private readonly _extensionService: IExtensionService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatService private readonly _chatService: IChatService, @IDialogService private readonly _dialogService: IDialogService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILogService private readonly _logService: ILogService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { super(); + this._workspaceToolConfirmStore = new Lazy(() => this._register(this._instantiationService.createInstance(ToolConfirmStore, StorageScope.WORKSPACE))); + this._profileToolConfirmStore = new Lazy(() => this._register(this._instantiationService.createInstance(ToolConfirmStore, StorageScope.PROFILE))); + this._register(this._contextKeyService.onDidChangeContext(e => { if (e.affectsSome(this._toolContextKeys)) { // Not worth it to compute a delta here unless we have many tools changing often this._onDidChangeToolsScheduler.schedule(); } })); + + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ChatConfiguration.ExtensionToolsEnabled)) { + this._onDidChangeToolsScheduler.schedule(); + } + })); + + this._ctxToolsCount = ChatContextKeys.Tools.toolsCount.bindTo(_contextKeyService); } registerToolData(toolData: IToolData): IDisposable { @@ -64,12 +99,23 @@ 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)); + let store: DisposableStore | undefined; + if (toolData.inputSchema) { + store = new DisposableStore(); + const schemaUrl = URI.from({ scheme: Schemas.vscode, authority: 'schemas', path: `/lm/tool/${toolData.id}` }).toString(); + jsonSchemaRegistry.registerSchema(schemaUrl, toolData.inputSchema, store); + store.add(jsonSchemaRegistry.registerSchemaAssociation(schemaUrl, `/lm/tool/${toolData.id}/tool_input.json`)); + } + return toDisposable(() => { + store?.dispose(); this._tools.delete(toolData.id); + this._ctxToolsCount.set(this._tools.size); this._refreshAllToolContextKeys(); this._onDidChangeToolsScheduler.schedule(); }); @@ -100,7 +146,16 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo getTools(): Iterable> { const toolDatas = Iterable.map(this._tools.values(), i => i.data); - return Iterable.filter(toolDatas, toolData => !toolData.when || this._contextKeyService.contextMatchesRules(toolData.when)); + const extensionToolsEnabled = this._configurationService.getValue(ChatConfiguration.ExtensionToolsEnabled); + return Iterable.filter( + toolDatas, + toolData => { + const satisfiesWhenClause = !toolData.when || this._contextKeyService.contextMatchesRules(toolData.when); + const satisfiesExternalToolCheck = toolData.source.type === 'extension' && !extensionToolsEnabled ? + !toolData.source.isExternalTool : + true; + return satisfiesWhenClause && satisfiesExternalToolCheck; + }); } getTool(id: string): IToolData | undefined { @@ -125,6 +180,22 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo return undefined; } + setToolAutoConfirmation(toolId: string, scope: 'workspace' | 'profile' | 'memory', autoConfirm = true): void { + if (scope === 'workspace') { + this._workspaceToolConfirmStore.value.setAutoConfirm(toolId, autoConfirm); + } else if (scope === 'profile') { + this._profileToolConfirmStore.value.setAutoConfirm(toolId, autoConfirm); + } else { + this._memoryToolConfirmStore.add(toolId); + } + } + + resetToolAutoConfirmation(): void { + this._workspaceToolConfirmStore.value.reset(); + this._profileToolConfirmStore.value.reset(); + this._memoryToolConfirmStore.clear(); + } + async invokeTool(dto: IToolInvocation, countTokens: CountTokensCallback, token: CancellationToken): Promise { this._logService.trace(`[LanguageModelToolsService#invokeTool] Invoking tool ${dto.toolId} with parameters ${JSON.stringify(dto.parameters)}`); @@ -160,6 +231,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo const request = model.getRequests().at(-1)!; requestId = request.id; + dto.modelId = request.modelId; // Replace the token with a new token that we can cancel when cancelToolCallsForRequest is called if (!this._callsByRequestId.has(requestId)) { @@ -180,25 +252,29 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo })); token = source.token; - const prepared = tool.impl.prepareToolInvocation ? - await tool.impl.prepareToolInvocation(dto.parameters, token) - : undefined; + const prepared = await this.prepareToolInvocation(tool, dto, token); + toolInvocation = new ChatToolInvocation(prepared, tool.data, dto.callId); + if (this.shouldAutoConfirm(tool.data.id, tool.data.runsInWorkspace)) { + toolInvocation.confirmed.complete(true); + } - toolInvocation = new ChatToolInvocation(prepared, tool.data); model.acceptResponseProgress(request, toolInvocation); if (prepared?.confirmationMessages) { + this._accessibilityService.alert(localize('toolConfirmationMessage', "Action required: {0}", prepared.confirmationMessages.title)); const userConfirmed = await toolInvocation.confirmed.p; if (!userConfirmed) { throw new CancellationError(); } dto.toolSpecificData = toolInvocation?.toolSpecificData; + + if (dto.toolSpecificData?.kind === 'input') { + dto.parameters = dto.toolSpecificData.rawInput; + dto.toolSpecificData = undefined; + } } } else { - const prepared = tool.impl.prepareToolInvocation ? - await tool.impl.prepareToolInvocation(dto.parameters, token) - : undefined; - + const prepared = await this.prepareToolInvocation(tool, dto, token); if (prepared?.confirmationMessages) { const result = await this._dialogService.confirm({ message: prepared.confirmationMessages.title, detail: renderStringAsPlaintext(prepared.confirmationMessages.message) }); if (!result.confirmed) { @@ -212,13 +288,16 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } toolResult = await tool.impl.invoke(dto, countTokens, token); + this.ensureToolDetails(dto, toolResult, tool.data); + this._telemetryService.publicLog2( 'languageModelToolInvoked', { result: 'success', chatSessionId: dto.context?.sessionId, toolId: tool.data.id, - toolExtensionId: tool.data.extensionId?.value, + toolExtensionId: tool.data.source.type === 'extension' ? tool.data.source.extensionId.value : undefined, + toolSourceKind: tool.data.source.type, }); return toolResult; } catch (err) { @@ -229,8 +308,10 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo result, chatSessionId: dto.context?.sessionId, toolId: tool.data.id, - toolExtensionId: tool.data.extensionId?.value, + toolExtensionId: tool.data.source.type === 'extension' ? tool.data.source.extensionId.value : undefined, + toolSourceKind: tool.data.source.type, }); + this._logService.error(`[LanguageModelToolsService#invokeTool] Error from tool ${dto.toolId}: ${toErrorMessage(err)}. With parameters ${JSON.stringify(dto.parameters)}`); throw err; } finally { toolInvocation?.complete(toolResult); @@ -241,6 +322,86 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } } + private async prepareToolInvocation(tool: IToolEntry, dto: IToolInvocation, token: CancellationToken): Promise { + let prepared = tool.impl!.prepareToolInvocation ? + await tool.impl!.prepareToolInvocation(dto.parameters, token) + : undefined; + + if (!prepared?.confirmationMessages && tool.data.requiresConfirmation && tool.data.source.type === 'extension') { + if (!prepared) { + prepared = {}; + } + + const toolWarning = localize( + 'tool.warning', + "{0} This tool is from the extension `{1}`. Please carefully review any requested actions.", + '$(info)', + tool.data.source.extensionId.value, + ); + prepared.confirmationMessages = { + title: localize('msg.title', "Run {0}", `"${tool.data.displayName}"`), + message: new MarkdownString((tool.data.userDescription ?? tool.data.modelDescription) + '\n\n' + toolWarning, { supportThemeIcons: true }), + allowAutoConfirm: true, + }; + } + + if (prepared?.confirmationMessages) { + if (prepared.toolSpecificData?.kind !== 'terminal' && typeof prepared.confirmationMessages.allowAutoConfirm !== 'boolean') { + prepared.confirmationMessages.allowAutoConfirm = true; + } + + if (!prepared.toolSpecificData && tool.data.alwaysDisplayInputOutput) { + prepared.toolSpecificData = { + kind: 'input', + rawInput: dto.parameters, + }; + } + } + + return prepared; + } + + private ensureToolDetails(dto: IToolInvocation, toolResult: IToolResult, toolData: IToolData): void { + if (!toolResult.toolResultDetails && toolData.alwaysDisplayInputOutput) { + toolResult.toolResultDetails = { + input: JSON.stringify(dto.parameters, undefined, 2), + output: this.toolResultToString(toolResult), + }; + } + } + + private toolResultToString(toolResult: IToolResult): string { + const strs = []; + for (const part of toolResult.content) { + if (part.kind === 'text') { + strs.push(part.value); + } else if (part.kind === 'promptTsx') { + strs.push(stringifyPromptTsxPart(part)); + } + } + return strs.join(''); + } + + private shouldAutoConfirm(toolId: string, runsInWorkspace: boolean | undefined): boolean { + if (this._workspaceToolConfirmStore.value.getAutoConfirm(toolId) || this._profileToolConfirmStore.value.getAutoConfirm(toolId) || this._memoryToolConfirmStore.has(toolId)) { + return true; + } + + const config = this._configurationService.inspect>('chat.tools.autoApprove'); + + // If we know the tool runs at a global level, only consider the global config. + // If we know the tool runs at a workspace level, use those specific settings when appropriate. + let value = config.value ?? config.defaultValue; + if (typeof runsInWorkspace === 'boolean') { + value = config.userLocalValue ?? config.applicationValue; + if (runsInWorkspace) { + value = config.workspaceValue ?? config.workspaceFolderValue ?? config.userRemoteValue ?? value; + } + } + + return value === true || (typeof value === 'object' && value.hasOwnProperty(toolId) && value[toolId] === true); + } + private cleanupCallDisposables(requestId: string, store: DisposableStore): void { const disposables = this._callsByRequestId.get(requestId); if (disposables) { @@ -267,6 +428,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo super.dispose(); this._callsByRequestId.forEach(calls => dispose(calls)); + this._ctxToolsCount.reset(); } } @@ -275,6 +437,7 @@ type LanguageModelToolInvokedEvent = { chatSessionId: string | undefined; toolId: string; toolExtensionId: string | undefined; + toolSourceKind: string; }; type LanguageModelToolInvokedClassification = { @@ -282,6 +445,58 @@ type LanguageModelToolInvokedClassification = { chatSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat session that the tool was used within, if applicable.' }; toolId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the tool used.' }; toolExtensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension that contributed the tool.' }; + toolSourceKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The source (mcp/extension/internal) of the tool.' }; owner: 'roblourens'; comment: 'Provides insight into the usage of language model tools.'; }; + +class ToolConfirmStore extends Disposable { + private static readonly STORED_KEY = 'chat/autoconfirm'; + + private _autoConfirmTools: LRUCache = new LRUCache(100); + private _didChange = false; + + constructor( + private readonly _scope: StorageScope, + @IStorageService private readonly storageService: IStorageService, + ) { + super(); + + const stored = storageService.getObject(ToolConfirmStore.STORED_KEY, this._scope); + if (stored) { + for (const key of stored) { + this._autoConfirmTools.set(key, true); + } + } + + this._register(storageService.onWillSaveState(() => { + if (this._didChange) { + this.storageService.store(ToolConfirmStore.STORED_KEY, [...this._autoConfirmTools.keys()], this._scope, StorageTarget.MACHINE); + this._didChange = false; + } + })); + } + + public reset() { + this._autoConfirmTools.clear(); + this._didChange = true; + } + + public getAutoConfirm(toolId: string): boolean { + if (this._autoConfirmTools.get(toolId)) { + this._didChange = true; + return true; + } + + return false; + } + + public setAutoConfirm(toolId: string, autoConfirm: boolean): void { + if (autoConfirm) { + this._autoConfirmTools.set(toolId, true); + } else { + this._autoConfirmTools.delete(toolId); + } + this._didChange = true; + } +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index da52cbb6..ea0a6ea8 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -373,6 +373,33 @@ .progress-container .rendered-markdown [data-code] { margin: 0; } + + .tool-input-output-part { + display: flex; + flex-wrap: wrap; + align-items: center; + } + + .tool-input-output-part .rendered-markdown p { + margin: inherit; + } + + .tool-input-output-part .expando { + display: flex; + align-items: center; + cursor: pointer; + } + + .tool-input-output-part .input-output { + display: none; + padding: 6px 0; + flex-basis: 100%; + width: 100%; + } + + .tool-input-output-part.expanded .input-output { + display: inherit; + } } .interactive-item-container .value > .rendered-markdown li > p { @@ -435,11 +462,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; } @@ -693,6 +715,7 @@ have to be updated for changes to the rules above, or to support more deeply nes gap: 4px; margin-top: 6px; flex-wrap: wrap; + cursor: default; } .chat-related-files { @@ -700,6 +723,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 { @@ -1116,10 +1140,14 @@ 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 { height: 20px; - gap: 0px; - color: var(--vscode-descriptionForeground); + color: var(--vscode-foreground); } +.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; @@ -1135,11 +1163,50 @@ 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); + color: var(--vscode-foreground); 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); + } + } + } +} + .action-item.chat-attached-context-attachment.chat-add-files .action-label.codicon::before { font: normal normal normal 16px/1 codicon; } @@ -1190,12 +1257,19 @@ have to be updated for changes to the rules above, or to support more deeply nes font-size: 100% !important; } +.interactive-session .chat-input-container .chat-attached-context { + display: contents; +} + .interactive-session .chat-attached-context { display: flex; flex-wrap: wrap; - cursor: default; gap: 4px; - width: 100%; +} + +.interactive-session .chat-attachment-toolbar .actions-container { + gap: 4px; + flex-wrap: wrap; } .interactive-session .interactive-input-part.compact .chat-attached-context { @@ -1592,12 +1666,16 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-item-container .chat-command-button .monaco-button, -.chat-confirmation-widget .chat-confirmation-buttons-container .monaco-button { +.chat-confirmation-widget .chat-confirmation-buttons-container .monaco-button:not(.monaco-dropdown-button) { text-align: left; width: initial; padding: 4px 8px; } +.chat-confirmation-widget .chat-confirmation-buttons-container .monaco-button.monaco-dropdown-button { + padding: 0 3px; +} + .interactive-item-container .chat-confirmation-widget .interactive-result-code-block { margin-bottom: 8px; } diff --git a/src/vs/workbench/contrib/chat/browser/media/chatSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatSetup.css new file mode 100644 index 00000000..b042970d --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/media/chatSetup.css @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.chat-welcome-view .chat-setup-view { + + text-align: center; + + .chat-features-container { + display: flex; + justify-content: center; + text-align: initial; + border-radius: 2px; + border: 1px solid var(--vscode-chat-requestBorder); + background-color: var(--vscode-chat-requestBackground); + } +} + +.dialog-message-body .chat-setup-view { + + p { + margin-top: 0; + margin-bottom: 0; + } + + p.setup-legal { + font-size: 12px; + color: var(--vscode-descriptionForeground); + margin-top: 2em; + } + + p.setup-settings { + font-size: 12px; + color: var(--vscode-descriptionForeground); + margin-top: 1em; + } +} + +.dialog-message-body .chat-setup-view, +.chat-welcome-view .chat-setup-view { + + p { + width: 100%; + } + + .chat-feature-container { + display: flex; + align-items: center; + gap: 6px; + padding: 5px 10px 5px 10px; + } + + .chat-feature-container .codicon[class*='codicon-'] { + font-size: 16px; + } + + .codicon[class*='codicon-'] { + font-size: 13px; + line-height: 1.4em; + vertical-align: bottom; + } + + .button-container { + padding-top: 20px; + } + + /** Dropdown Button */ + .monaco-button-dropdown { + width: 100%; + } + + .monaco-button-dropdown .monaco-text-button { + width: 100%; + } +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chatStatus.css b/src/vs/workbench/contrib/chat/browser/media/chatStatus.css index aecbe61a..a8fe6942 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatStatus.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatStatus.css @@ -25,75 +25,18 @@ color: var(--vscode-descriptionForeground); } -/* Settings */ - -.chat-status-bar-entry-tooltip .settings { - display: flex; - flex-direction: column; - gap: 5px; +.chat-status-bar-entry-tooltip .monaco-button { + margin-top: 5px; + margin-bottom: 5px; } -.chat-status-bar-entry-tooltip .settings .setting { +/* Setup for New User */ + +.chat-status-bar-entry-tooltip .setup .chat-feature-container { 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; -} - -/* Shortcuts */ - -.chat-status-bar-entry-tooltip .shortcuts { - display: flex; - flex-direction: column; - gap: 2px; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut { - display: flex; - gap: 15px; - - /** Make room for the hover feedback */ - margin-left: -3px; - margin-right: -3px; - padding-left: 3px; - padding-right: 3px; - margin-top: -1px; - margin-bottom: -1px; - padding-top: 1px; - padding-bottom: 1px; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut:hover { - outline: 1px dashed var(--vscode-toolbar-hoverOutline); - outline-offset: -1px; - background-color: var(--vscode-toolbar-hoverBackground); - border-radius: 3px; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut .shortcut-label { - flex: 1; - cursor: pointer; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut .monaco-keybinding { - cursor: pointer; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut .monaco-keybinding > .monaco-keybinding-key { - padding: 2px 4px; - font-size: 10px; + gap: 5px; + padding: 4px; } /* Quota Indicator */ @@ -137,3 +80,47 @@ .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); +} + +/* Contributions */ + +.chat-status-bar-entry-tooltip .contribution .body { + display: flex; + flex-direction: row; + gap: 6px; + + .detail-item { + margin-left: auto; + color: var(--vscode-descriptionForeground); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css b/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css index 4d99cd50..ebee2479 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css @@ -57,14 +57,6 @@ div.chat-welcome-view { margin-top: 5px; gap: 9px; justify-content: center; - - & > .chat-welcome-view-indicator { - font-size: 11px; - color: var(--vscode-badge-foreground); - background: var(--vscode-badge-background); - padding: 1px 8px; - border-radius: 14px; - } } & > .chat-welcome-view-message { 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 e3f61c98..2cde08cb 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 @@ -7,18 +7,24 @@ import { localize } from '../../../../../../../nls.js'; import { createPromptFile } from './utils/createPromptFile.js'; import { CHAT_CATEGORY } from '../../../actions/chatActions.js'; import { askForPromptName } from './dialogs/askForPromptName.js'; +import { ChatContextKeys } from '../../../../common/chatContextKeys.js'; +import { ILogService } from '../../../../../../../platform/log/common/log.js'; 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 { ContextKeyExpr } from '../../../../../../../platform/contextkey/common/contextkey.js'; +import { MenuId, MenuRegistry } from '../../../../../../../platform/actions/common/actions.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 { CONFIGURE_SYNC_COMMAND_ID } from '../../../../../../services/userDataSync/common/userDataSync.js'; +import { IUserDataSyncEnablementService, SyncResource } from '../../../../../../../platform/userDataSync/common/userDataSync.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { INotificationService, NeverShowAgainScope, Severity } from '../../../../../../../platform/notification/common/notification.js'; /** * Base command ID prefix. @@ -52,13 +58,16 @@ const command = async ( accessor: ServicesAccessor, type: IPromptPath['type'], ): Promise => { + const logService = accessor.get(ILogService); const fileService = accessor.get(IFileService); const labelService = accessor.get(ILabelService); const openerService = accessor.get(IOpenerService); - const commandService = accessor.get(ICommandService); const promptsService = accessor.get(IPromptsService); + const commandService = accessor.get(ICommandService); const quickInputService = accessor.get(IQuickInputService); + const notificationService = accessor.get(INotificationService); const workspaceService = accessor.get(IWorkspaceContextService); + const userDataSyncEnablementService = accessor.get(IUserDataSyncEnablementService); const fileName = await askForPromptName(type, quickInputService); if (!fileName) { @@ -80,17 +89,63 @@ const command = async ( const content = localize( 'workbench.command.prompts.create.initial-content', - "Add prompt contents..", + "Add prompt contents...", ); const promptUri = await createPromptFile({ fileName, folder: selectedFolder, content, fileService, - commandService, + openerService, }); await openerService.open(promptUri); + + if (type !== 'user') { + return; + } + + // due to PII concerns, synchronization of the 'user' reusable prompts + // is disabled by default, but we want to make that fact clear to the user + // hence after a 'user' prompt is create, we check if the synchronization + // was explicitly configured before, and if it wasn't, we show a suggestion + // to enable the synchronization logic in the Settings Sync configuration + + const isConfigured = userDataSyncEnablementService + .isResourceEnablementConfigured(SyncResource.Prompts); + const isSettingsSyncEnabled = userDataSyncEnablementService.isEnabled(); + + // if prompts synchronization has already been configured before or + // if settings sync service is currently disabled, nothing to do + if ((isConfigured === true) || (isSettingsSyncEnabled === false)) { + return; + } + + // show suggestion to enable synchronization of the user prompts to the user + notificationService.prompt( + Severity.Info, + localize( + 'workbench.command.prompts.create.user.enable-sync-notification', + "User prompts are not currently synchronized. Do you want to enable synchronization of the user prompts?", + ), + [ + { + label: localize('enable.capitalized', "Enable"), + run: () => { + commandService.executeCommand(CONFIGURE_SYNC_COMMAND_ID) + .catch((error) => { + logService.error(`Failed to run '${CONFIGURE_SYNC_COMMAND_ID}' command: ${error}.`); + }); + }, + } + ], + { + neverShowAgain: { + id: 'workbench.command.prompts.create.user.enable-sync-notification', + scope: NeverShowAgainScope.PROFILE, + }, + }, + ); }; /** @@ -109,7 +164,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: LOCAL_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, handler: commandFactory('local'), - when: PromptsConfig.enabledCtx, + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled), }); /** @@ -119,29 +174,29 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: USER_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, handler: commandFactory('user'), - when: PromptsConfig.enabledCtx, + 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 }, - PromptsConfig.enabledCtx, -); + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled) +}); /** * Register the "Create User Prompt" command in the command palette. */ -appendToCommandPalette( - { +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { id: USER_COMMAND_ID, title: USER_COMMAND_TITLE, category: CHAT_CATEGORY, }, - PromptsConfig.enabledCtx, -); + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled) +}); 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 d449726d..4027531b 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 { IOpenerService } from '../../../../../../../../platform/opener/common/opener.js'; import { isPromptFile, PROMPT_FILE_EXTENSION } from '../../../../../../../../platform/prompts/common/constants.js'; -import { ICommandService } from '../../../../../../../../platform/commands/common/commands.js'; /** * Options for the {@link createPromptFile} utility. @@ -33,7 +33,7 @@ interface ICreatePromptFileOptions { readonly content: string; fileService: IFileService; - commandService: ICommandService; + openerService: IOpenerService; } /** @@ -47,7 +47,7 @@ interface ICreatePromptFileOptions { export const createPromptFile = async ( options: ICreatePromptFileOptions, ): Promise => { - const { fileName, folder, content, fileService, commandService } = options; + const { fileName, folder, content, fileService, openerService } = options; const promptUri = URI.joinPath(folder, fileName); @@ -67,7 +67,7 @@ export const createPromptFile = async ( ); // prompt file already exists so open it - await commandService.executeCommand('vscode.open', promptUri); + await openerService.open(promptUri); return promptUri; } 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 efad5945..caacc52b 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/usePromptCommand.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/usePromptCommand.ts @@ -7,13 +7,14 @@ import { localize } from '../../../../../../nls.js'; import { URI } from '../../../../../../base/common/uri.js'; import { CHAT_CATEGORY } from '../../actions/chatActions.js'; import { IChatWidget, IChatWidgetService } from '../../chat.js'; +import { ChatContextKeys } from '../../../common/chatContextKeys.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 { ContextKeyExpr } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { MenuId, MenuRegistry } from '../../../../../../platform/actions/common/actions.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'; @@ -53,12 +54,10 @@ 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); @@ -133,17 +132,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib, primary: COMMAND_KEY_BINDING, handler: command, - when: PromptsConfig.enabledCtx, + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled), }); /** * Register the "Use Prompt" command in the `command palette`. */ -appendToCommandPalette( - { +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { id: COMMAND_ID, title: localize('commands.prompts.use.title', "Use Prompt"), - category: CHAT_CATEGORY, + category: CHAT_CATEGORY }, - PromptsConfig.enabledCtx, -); + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled) +}); diff --git a/src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts b/src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts index eb8eadcf..286f01f3 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,13 +148,9 @@ 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"))); } // Message diff --git a/src/vs/workbench/contrib/chat/common/annotations.ts b/src/vs/workbench/contrib/chat/common/annotations.ts index c06eda1f..fa770db9 100644 --- a/src/vs/workbench/contrib/chat/common/annotations.ts +++ b/src/vs/workbench/contrib/chat/common/annotations.ts @@ -57,7 +57,8 @@ export function annotateSpecialMarkdownContent(response: Iterable${item.uri.toString()}`; + const isEditText = item.isEdit ? ` isEdit` : ''; + const markdownText = `${item.uri.toString()}`; const merged = appendMarkdownString(previousItem.content, new MarkdownString(markdownText)); result[previousItemIndex] = { ...previousItem, content: merged }; } @@ -99,12 +100,15 @@ export function annotateVulnerabilitiesInText(response: ReadonlyArray(.*?)<\/vscode_codeblock_uri>/ms.exec(text); - if (match && match[1]) { - const result = URI.parse(match[1]); - const textWithoutResult = text.substring(0, match.index) + text.substring(match.index + match[0].length); - return { uri: result, textWithoutResult }; +export function extractCodeblockUrisFromText(text: string): { uri: URI; isEdit?: boolean; textWithoutResult: string } | undefined { + const match = /(.*?)<\/vscode_codeblock_uri>/ms.exec(text); + if (match) { + const [all, isEdit, uriString] = match; + if (uriString) { + const result = URI.parse(uriString); + const textWithoutResult = text.substring(0, match.index) + text.substring(match.index + all.length); + return { uri: result, textWithoutResult, isEdit: !!isEdit }; + } } return undefined; } diff --git a/src/vscode-dts/vscode.proposed.chatReadonlyPromptReference.d.ts b/src/vs/workbench/contrib/chat/common/chat.ts similarity index 54% rename from src/vscode-dts/vscode.proposed.chatReadonlyPromptReference.d.ts rename to src/vs/workbench/contrib/chat/common/chat.ts index 0d91b3ed..7e2b00c0 100644 --- a/src/vscode-dts/vscode.proposed.chatReadonlyPromptReference.d.ts +++ b/src/vs/workbench/contrib/chat/common/chat.ts @@ -3,15 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ChatMode } from './constants.js'; -declare module 'vscode' { - - export interface ChatPromptReference { - /** - * When true, the user has indicated at the reference is informational only. - * The model should avoid changing or suggesting changes to the reference. - */ - readonly isReadonly?: boolean; +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 633010ae..6ef8775d 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,32 +37,12 @@ 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; fullName?: string; description?: string; + /** This is string, not ContextKeyExpression, because dealing with serializing/deserializing is hard and need a better pattern for this */ when?: string; extensionId: ExtensionIdentifier; extensionPublisherId: string; @@ -74,6 +55,8 @@ export interface IChatAgentData { isToolsAgent?: boolean; /** This agent is not contributed in package.json, but is registered dynamically */ isDynamic?: boolean; + /** This agent is contributed from core and not from an extension */ + isCore?: boolean; metadata: IChatAgentMetadata; slashCommands: IChatAgentCommand[]; locations: ChatAgentLocation[]; @@ -136,7 +119,6 @@ export interface IChatAgentMetadata { helpTextPrefix?: string | IMarkdownString; helpTextVariablesPrefix?: string | IMarkdownString; helpTextPostfix?: string | IMarkdownString; - isSecondary?: boolean; // Invoked by ctrl/cmd+enter icon?: URI; iconDark?: URI; themeIcon?: ThemeIcon; @@ -145,6 +127,7 @@ export interface IChatAgentMetadata { followupPlaceholder?: string; isSticky?: boolean; requester?: IChatRequesterInformation; + welcomeMessageContent?: IChatWelcomeMessageContent; } @@ -163,6 +146,7 @@ export interface IChatAgentRequest { acceptedConfirmationData?: any[]; rejectedConfirmationData?: any[]; userSelectedModelId?: string; + userSelectedTools?: string[]; } export interface IChatQuestion { @@ -206,9 +190,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,18 +213,15 @@ 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) */ getContributedDefaultAgent(location: ChatAgentLocation): IChatAgentData | undefined; - getSecondaryAgent(): IChatAgentData | undefined; updateAgent(id: string, updateMetadata: IChatAgentMetadata): void; } -const ChatToolsAgentModeStorageKey = 'chat.toolsAgentMode'; - export class ChatAgentService extends Disposable implements IChatAgentService { public static readonly AGENT_LEADER = '@'; @@ -254,21 +233,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 +254,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,33 +381,34 @@ export class ChatAgentService extends Disposable implements IChatAgentService { this._onDidChangeAgents.fire(new MergedChatAgent(agent.data, agent.impl)); } - getDefaultAgent(location: ChatAgentLocation): IChatAgent | undefined { - return findLast(this.getActivatedAgents(), a => { - if (location === ChatAgentLocation.EditingSession && this.toolsAgentModeEnabled !== !!a.isToolsAgent) { + getDefaultAgent(location: ChatAgentLocation, mode?: ChatMode): IChatAgent | undefined { + if (mode === ChatMode.Edit || mode === ChatMode.Agent) { + location = ChatAgentLocation.EditingSession; + } + + return this._preferExtensionAgent(this.getActivatedAgents().filter(a => { + if ((mode === ChatMode.Agent) !== !!a.isToolsAgent) { return false; } return !!a.isDefault && a.locations.includes(location); - }); + })); } - 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 { - return this.getAgents().find(a => !!a.isDefault && a.locations.includes(location)); + return this._preferExtensionAgent(this.getAgents().filter(a => !!a.isDefault && a.locations.includes(location))); } - getSecondaryAgent(): IChatAgentData | undefined { - // TODO also static - return Iterable.find(this._agents.values(), a => !!a.data.metadata.isSecondary)?.data; + private _preferExtensionAgent(agents: T[]): T | undefined { + // We potentially have multiple agents on the same location, + // contributed from core and from extensions. + // This method will prefer the last extensions provided agent + // falling back to the last core agent if no extension agent is found. + return findLast(agents, agent => !agent.isCore) ?? agents.at(-1); } getAgent(id: string, includeDisabled = false): IChatAgentData | undefined { @@ -480,7 +450,16 @@ export class ChatAgentService extends Disposable implements IChatAgentService { } getAgentsByName(name: string): IChatAgentData[] { - return this.getAgents().filter(a => a.name === name); + return this._preferExtensionAgents(this.getAgents().filter(a => a.name === name)); + } + + private _preferExtensionAgents(agents: T[]): T[] { + // We potentially have multiple agents on the same location, + // contributed from core and from extensions. + // This method will prefer the extensions provided agents + // falling back to the original agents array extension agent is found. + const extensionAgents = agents.filter(a => !a.isCore); + return extensionAgents.length > 0 ? extensionAgents : agents; } agentHasDupeName(id: string): boolean { @@ -609,6 +588,7 @@ export class MergedChatAgent implements IChatAgent { get extensionDisplayName(): string { return this.data.extensionDisplayName; } get isDefault(): boolean | undefined { return this.data.isDefault; } get isToolsAgent(): boolean | undefined { return this.data.isToolsAgent; } + get isCore(): boolean | undefined { return this.data.isCore; } get metadata(): IChatAgentMetadata { return this.data.metadata; } get slashCommands(): IChatAgentCommand[] { return this.data.slashCommands; } get locations(): ChatAgentLocation[] { return this.data.locations; } @@ -733,6 +713,10 @@ export class ChatAgentNameService implements IChatAgentNameService { * Returns true if the agent is allowed to use this name */ getAgentNameRestriction(chatAgentData: IChatAgentData): boolean { + if (chatAgentData.isCore) { + return true; // core agents are always allowed to use any name + } + // TODO would like to use observables here but nothing uses it downstream and I'm not sure how to combine these two const nameAllowed = this.checkAgentNameRestriction(chatAgentData.name, chatAgentData).get(); const fullNameAllowed = !chatAgentData.fullName || this.checkAgentNameRestriction(chatAgentData.fullName.replace(/\s/g, ''), chatAgentData).get(); diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index b88c1ac7..bf8c4ab0 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.Ask, { 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,41 +51,63 @@ 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.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 SetupViewCondition = ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.hidden.negate(), - ChatContextKeys.Setup.installed.negate() - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.canSignUp, - ChatContextKeys.Setup.installed - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.signedOut, - ChatContextKeys.Setup.installed - ) - )!; + 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.and( + Setup.fromDialog.negate(), + ContextKeyExpr.or( + ContextKeyExpr.and( + ChatContextKeys.Setup.hidden.negate(), + ChatContextKeys.Setup.installed.negate() + ), + ContextKeyExpr.and( + ChatContextKeys.Entitlement.canSignUp, + ChatContextKeys.Setup.installed + ), + ContextKeyExpr.and( + ChatContextKeys.Entitlement.signedOut, + ChatContextKeys.Setup.installed + ) + ))!; export const chatQuotaExceeded = new RawContextKey('chatQuotaExceeded', false, true); export const completionsQuotaExceeded = new RawContextKey('completionsQuotaExceeded', false, true); 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 48431090..629bbd1c 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'; @@ -15,7 +14,7 @@ import { RawContextKey } from '../../../../platform/contextkey/common/contextkey import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IEditorPane } from '../../../common/editor.js'; import { ICellEditOperation } from '../../notebook/common/notebookCommon.js'; -import { IChatResponseModel } from './chatModel.js'; +import { ChatModel, IChatResponseModel } from './chatModel.js'; export const IChatEditingService = createDecorator('chatEditingService'); @@ -23,7 +22,7 @@ export interface IChatEditingService { _serviceBrand: undefined; - startOrContinueGlobalEditingSession(chatSessionId: string): Promise; + startOrContinueGlobalEditingSession(chatModel: ChatModel): Promise; getEditingSession(chatSessionId: string): IChatEditingSession | undefined; @@ -35,7 +34,7 @@ export interface IChatEditingService { /** * Creates a new short lived editing session */ - createEditingSession(chatSessionId: string): Promise; + createEditingSession(chatModel: ChatModel): Promise; //#region related files @@ -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 00000000..66ea4b5a --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts @@ -0,0 +1,886 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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'; +import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.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, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + ) { + 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 ( + response.res.statusCode === 401 || + response.res.statusCode === 403 || + response.res.statusCode === 404 + ) ? { entitlement: ChatEntitlement.Unknown /* treat as signed out */ } : { 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); + + if (!this.lifecycleService.willShutdown) { + const { confirmed } = await this.dialogService.confirm({ + type: Severity.Error, + message: localize('unknownSignUpError', "An error occurred while signing up for the Copilot Free plan. Would you like to try again?"), + detail, + primaryButton: localize('retry', "Retry") + }); + + return confirmed; + } + + return false; + } + + private onUnprocessableSignUpError(logMessage: string, logDetails: string): void { + this.logService.error(logMessage); + + if (!this.lifecycleService.willShutdown) { + this.dialogService.prompt({ + type: Severity.Error, + message: localize('unprocessableSignUpError', "An error occurred while signing up for the Copilot Free plan."), + 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, + @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)}`); + + 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 c0f6f1ce..00000000 --- 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 abd866ee..c393e9a8 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -12,7 +12,7 @@ import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { revive } from '../../../../base/common/marshalling.js'; import { Schemas } from '../../../../base/common/network.js'; import { equals } from '../../../../base/common/objects.js'; -import { IObservable, ITransaction, observableValue } from '../../../../base/common/observable.js'; +import { IObservable, ITransaction, ObservablePromise, observableValue } from '../../../../base/common/observable.js'; import { basename, isEqual } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI, UriComponents, UriDto, isUriComponents } from '../../../../base/common/uri.js'; @@ -24,17 +24,18 @@ import { localize } from '../../../../nls.js'; import { ILogService } from '../../../../platform/log/common/log.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 { IChatEditingService, IChatEditingSession } from './chatEditingService.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; @@ -84,11 +85,6 @@ export interface ICommandResultVariableEntry extends Omit { - readonly kind: 'link'; - readonly value: URI; -} - export interface IImageVariableEntry extends Omit { readonly kind: 'image'; readonly isPasted?: boolean; @@ -162,7 +158,7 @@ export interface IDiagnosticVariableEntry extends Omit | undefined; + readonly editingSession?: IChatEditingSession | undefined; toggleLastRequestPaused(paused?: boolean): void; /** * Sets requests as 'disabled', removing them from the UI. If a request ID @@ -1208,11 +1202,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; @@ -1313,11 +1302,21 @@ export class ChatModel extends Disposable implements IChatModel { return this._initialLocation; } + private _editingSession: ObservablePromise | undefined; + get editingSessionObs(): ObservablePromise | undefined { + return this._editingSession; + } + + get editingSession(): IChatEditingSession | undefined { + return this._editingSession?.promiseResult.get()?.data; + } + constructor( private readonly initialData: ISerializableChatData | IExportableChatData | undefined, private readonly _initialLocation: ChatAgentLocation, @ILogService private readonly logService: ILogService, @IChatAgentService private readonly chatAgentService: IChatAgentService, + @IChatEditingService private readonly chatEditingService: IChatEditingService, ) { super(); @@ -1337,6 +1336,14 @@ export class ChatModel extends Disposable implements IChatModel { this._initialResponderAvatarIconUri = isUriComponents(initialData?.responderAvatarIconUri) ? URI.revive(initialData.responderAvatarIconUri) : initialData?.responderAvatarIconUri; } + startEditingSession(isGlobalEditingSession?: boolean): void { + const editingSessionPromise = isGlobalEditingSession ? + this.chatEditingService.startOrContinueGlobalEditingSession(this) : + this.chatEditingService.createEditingSession(this); + this._editingSession = new ObservablePromise(editingSessionPromise); + this._editingSession.promise.then(editingSession => this._store.isDisposed ? editingSession.dispose() : this._register(editingSession)); + } + private _deserialize(obj: IExportableChatData): ChatRequestModel[] { const requests = obj.requests; if (!Array.isArray(requests)) { @@ -1353,7 +1360,7 @@ export class ChatModel extends Disposable implements IChatModel { // Old messages don't have variableData, or have it in the wrong (non-array) shape const variableData: IChatRequestVariableData = this.reviveVariableData(raw.variableData); - const request = new ChatRequestModel(this, parsedRequest, variableData, raw.timestamp ?? -1, undefined, undefined, undefined, undefined, undefined, raw.requestId); + const request = new ChatRequestModel(this, parsedRequest, variableData, raw.timestamp ?? -1, undefined, undefined, undefined, undefined, undefined, undefined, raw.requestId); request.shouldBeRemovedOnSend = raw.isHidden ? { requestId: raw.requestId } : raw.shouldBeRemovedOnSend; if (raw.response || raw.result || (raw as any).responseErrorDetails) { const agent = (raw.agent && 'metadata' in raw.agent) ? // Check for the new format, ignore entries in the old format @@ -1434,14 +1441,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(); @@ -1486,8 +1492,8 @@ export class ChatModel extends Disposable implements IChatModel { }); } - addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], workingSet?: URI[], isCompleteAddedRequest?: boolean): ChatRequestModel { - const request = new ChatRequestModel(this, message, variableData, Date.now(), attempt, confirmation, locationData, attachments, isCompleteAddedRequest); + addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], isCompleteAddedRequest?: boolean, modelId?: string): ChatRequestModel { + const request = new ChatRequestModel(this, message, variableData, Date.now(), attempt, confirmation, locationData, attachments, isCompleteAddedRequest, modelId); request.response = new ChatResponseModel([], this, chatAgent, slashCommand, request.id, undefined, undefined, undefined, undefined, undefined, undefined, isCompleteAddedRequest); this._requests.push(request); diff --git a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts index 164b8778..2542fa4c 100644 --- a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts @@ -7,10 +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 { 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 diff --git a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts index 401211b3..92549dc2 100644 --- a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { RawChatParticipantLocation } from './constants.js'; + export interface IRawChatCommandContribution { name: string; description: string; @@ -13,8 +15,6 @@ export interface IRawChatCommandContribution { disambiguation?: { category: string; categoryName?: string /** Deprecated */; description: string; examples: string[] }[]; } -export type RawChatParticipantLocation = 'panel' | 'terminal' | 'notebook' | 'editing-session'; - export interface IRawChatParticipantContribution { id: string; name: string; diff --git a/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts b/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts index 8a601045..9c3570e9 100644 --- a/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts +++ b/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts @@ -6,7 +6,7 @@ import { DeferredPromise } from '../../../../../base/common/async.js'; import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; import { localize } from '../../../../../nls.js'; -import { IChatTerminalToolInvocationData, IChatToolInvocation, IChatToolInvocationSerialized } from '../chatService.js'; +import { IChatTerminalToolInvocationData, IChatToolInputInvocationData, IChatToolInvocation, IChatToolInvocationSerialized } from '../chatService.js'; import { IPreparedToolInvocation, IToolConfirmationMessages, IToolData, IToolResult } from '../languageModelToolsService.js'; export class ChatToolInvocation implements IChatToolInvocation { @@ -41,10 +41,11 @@ export class ChatToolInvocation implements IChatToolInvocation { public pastTenseMessage: string | IMarkdownString | undefined; private _confirmationMessages: IToolConfirmationMessages | undefined; public readonly presentation: IPreparedToolInvocation['presentation']; + public readonly toolId: string; - public readonly toolSpecificData?: IChatTerminalToolInvocationData; + public readonly toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData; - constructor(preparedInvocation: IPreparedToolInvocation | undefined, toolData: IToolData) { + constructor(preparedInvocation: IPreparedToolInvocation | undefined, toolData: IToolData, public readonly toolCallId: string) { const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${toolData.displayName}"`); const invocationMessage = preparedInvocation?.invocationMessage ?? defaultMessage; this.invocationMessage = invocationMessage; @@ -52,6 +53,7 @@ export class ChatToolInvocation implements IChatToolInvocation { this._confirmationMessages = preparedInvocation?.confirmationMessages; this.presentation = preparedInvocation?.presentation; this.toolSpecificData = preparedInvocation?.toolSpecificData; + this.toolId = toolData.id; if (!this._confirmationMessages) { // No confirmation needed @@ -92,6 +94,7 @@ export class ChatToolInvocation implements IChatToolInvocation { isComplete: this._isComplete, resultDetails: this._resultDetails, toolSpecificData: this.toolSpecificData, + toolCallId: this.toolCallId, }; } } 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 9d4a350a..00000000 --- a/src/vs/workbench/contrib/chat/common/chatQuotasService.ts +++ /dev/null @@ -1,135 +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, - chatRemaining: typeof quotas.chatRemaining === 'number' ? Math.max(0, quotas.chatRemaining) : 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 230b17fc..ecf72050 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.Ask) { 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; @@ -192,14 +194,14 @@ export class ChatRequestParser { return new ChatRequestAgentSubcommandPart(slashRange, slashEditorRange, subCommand); } } else { - const slashCommands = this.slashCommandService.getCommands(location); + const slashCommands = this.slashCommandService.getCommands(location, context?.mode ?? ChatMode.Ask); const slashCommand = slashCommands.find(c => c.command === command); if (slashCommand) { // Valid standalone slash command 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 diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 8340deb8..4ba63bfd 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 { @@ -168,6 +169,7 @@ export interface IChatAgentVulnerabilityDetails { export interface IChatResponseCodeblockUriPart { kind: 'codeblockUri'; uri: URI; + isEdit?: boolean; } export interface IChatAgentMarkdownContentWithVulnerability { @@ -216,9 +218,14 @@ export interface IChatTerminalToolInvocationData { language: string; } +export interface IChatToolInputInvocationData { + kind: 'input'; + rawInput: any; +} + export interface IChatToolInvocation { presentation: IPreparedToolInvocation['presentation']; - toolSpecificData?: IChatTerminalToolInvocationData; + toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData; /** Presence of this property says that confirmation is required */ confirmationMessages?: IToolConfirmationMessages; confirmed: DeferredPromise; @@ -227,6 +234,8 @@ export interface IChatToolInvocation { invocationMessage: string | IMarkdownString; pastTenseMessage: string | IMarkdownString | undefined; resultDetails: IToolResult['toolResultDetails']; + readonly toolId: string; + readonly toolCallId: string; isCompletePromise: Promise; isComplete: boolean; @@ -239,12 +248,13 @@ export interface IChatToolInvocation { */ export interface IChatToolInvocationSerialized { presentation: IPreparedToolInvocation['presentation']; - toolSpecificData?: IChatTerminalToolInvocationData; + toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData; invocationMessage: string | IMarkdownString; pastTenseMessage: string | IMarkdownString | undefined; resultDetails: IToolResult['toolResultDetails']; isConfirmed: boolean | undefined; isComplete: boolean; + toolCallId: string; kind: 'toolInvocationSerialized'; } @@ -411,7 +421,7 @@ export interface IChatTransferredSessionData { sessionId: string; inputValue: string; location: ChatAgentLocation; - toolsAgentModeEnabled: boolean; + mode: ChatMode; } export interface IChatSendRequestResponseState { @@ -444,7 +454,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; @@ -479,9 +491,10 @@ export interface IChatService { isEnabled(location: ChatAgentLocation): boolean; hasSessions(): boolean; - startSession(location: ChatAgentLocation, token: CancellationToken): ChatModel; + startSession(location: ChatAgentLocation, token: CancellationToken, isGlobalEditingSession?: boolean): ChatModel; getSession(sessionId: string): IChatModel | undefined; - getOrRestoreSession(sessionId: string): IChatModel | undefined; + getOrRestoreSession(sessionId: string): Promise; + isPersistedSessionEmpty(sessionId: string): boolean; loadSessionFromContent(data: IExportableChatData | ISerializableChatData): IChatModel | undefined; /** @@ -493,18 +506,23 @@ export interface IChatService { adoptRequest(sessionId: string, request: IChatRequestModel): Promise; removeRequest(sessionid: string, requestId: string): Promise; cancelCurrentRequestForSession(sessionId: string): void; - clearSession(sessionId: string): void; + clearSession(sessionId: string): Promise; addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, attempt: number | undefined, response: IChatCompleteResponse): void; - getHistory(): IChatDetail[]; + getHistory(): Promise; setChatSessionTitle(sessionId: string, title: string): void; - clearAllHistoryEntries(): void; - removeHistoryEntry(sessionId: string): void; + clearAllHistoryEntries(): Promise; + removeHistoryEntry(sessionId: string): Promise; + getChatStorageFolder(): URI; + logChatIndex(): void; onDidPerformUserAction: Event; notifyUserAction(event: IChatUserActionEvent): void; 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 f519d27d..f6b2c371 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'; @@ -13,7 +14,7 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { Disposable, DisposableMap, IDisposable } from '../../../../base/common/lifecycle.js'; import { revive } from '../../../../base/common/marshalling.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; -import { URI, UriComponents } from '../../../../base/common/uri.js'; +import { URI } from '../../../../base/common/uri.js'; import { isLocation } from '../../../../editor/common/languages.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -25,28 +26,23 @@ 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'; import { IChatCompleteResponse, IChatDetail, IChatFollowup, IChatProgress, IChatSendRequestData, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatTransferredSessionData, IChatUserActionEvent } from './chatService.js'; import { ChatServiceTelemetry } from './chatServiceTelemetry.js'; +import { ChatSessionStore, IChatTransfer2 } from './chatSessionStore.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'; const serializedChatKey = 'interactive.sessions'; const globalChatKey = 'chat.workspaceTransfer'; -interface IChatTransfer { - toWorkspace: UriComponents; - timestampInMilliseconds: number; - chat: ISerializableChatData; - inputValue: string; - location: ChatAgentLocation; - toolsAgentModeEnabled: boolean; -} + const SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS = 1000 * 60; type ChatProviderInvokedEvent = { @@ -133,6 +129,22 @@ export class ChatService extends Disposable implements IChatService { private readonly _sessionFollowupCancelTokens = this._register(new DisposableMap()); private readonly _chatServiceTelemetry: ChatServiceTelemetry; + private readonly _chatSessionStore: ChatSessionStore; + + @memoize + public get unifiedViewEnabled(): boolean { + return !!this.configurationService.getValue(ChatConfiguration.UnifiedChatView); + } + + @memoize + private get useFileStorage(): boolean { + return this.configurationService.getValue(ChatConfiguration.UseFileStorage); + } + + private get isEmptyWindow(): boolean { + const workspace = this.workspaceContextService.getWorkspace(); + return !workspace.configuration && workspace.folders.length === 0; + } constructor( @IStorageService private readonly storageService: IStorageService, @@ -150,8 +162,8 @@ export class ChatService extends Disposable implements IChatService { super(); this._chatServiceTelemetry = this.instantiationService.createInstance(ChatServiceTelemetry); - const isEmptyWindow = !workspaceContextService.getWorkspace().folders.length; - const sessionData = storageService.get(serializedChatKey, isEmptyWindow ? StorageScope.APPLICATION : StorageScope.WORKSPACE, ''); + + const sessionData = storageService.get(serializedChatKey, this.isEmptyWindow ? StorageScope.APPLICATION : StorageScope.WORKSPACE, ''); if (sessionData) { this._persistedSessions = this.deserializeChats(sessionData); const countsForLog = Object.keys(this._persistedSessions).length; @@ -171,10 +183,15 @@ export class ChatService extends Disposable implements IChatService { sessionId: transferredChat.sessionId, inputValue: transferredData.inputValue, location: transferredData.location, - toolsAgentModeEnabled: transferredData.toolsAgentModeEnabled, + mode: transferredData.mode, }; } + this._chatSessionStore = this._register(this.instantiationService.createInstance(ChatSessionStore)); + if (this.useFileStorage) { + this._chatSessionStore.migrateDataIfNeeded(() => this._persistedSessions); + } + this._register(storageService.onWillSaveState(() => this.saveState())); } @@ -184,31 +201,53 @@ export class ChatService extends Disposable implements IChatService { private saveState(): void { const liveChats = Array.from(this._sessionModels.values()) - .filter(session => session.initialLocation === ChatAgentLocation.Panel || session.initialLocation === ChatAgentLocation.EditingSession) - .filter(session => session.getRequests().length > 0); + .filter(session => session.initialLocation === ChatAgentLocation.Panel || session.initialLocation === ChatAgentLocation.EditingSession); - const isEmptyWindow = !this.workspaceContextService.getWorkspace().folders.length; - if (isEmptyWindow) { - this.syncEmptyWindowChats(liveChats); + if (this.useFileStorage) { + this._chatSessionStore.storeSessions(liveChats); } else { - let allSessions: (ChatModel | ISerializableChatData)[] = liveChats; - allSessions = allSessions.concat( - Object.values(this._persistedSessions) - .filter(session => !this._sessionModels.has(session.sessionId)) - .filter(session => session.requests.length)); - allSessions.sort((a, b) => (b.creationDate ?? 0) - (a.creationDate ?? 0)); - allSessions = allSessions.slice(0, maxPersistedSessions); - if (allSessions.length) { - this.trace('onWillSaveState', `Persisting ${allSessions.length} sessions`); + if (this.isEmptyWindow) { + this.syncEmptyWindowChats(liveChats); + } else { + let allSessions: (ChatModel | ISerializableChatData)[] = liveChats; + allSessions = allSessions.concat( + Object.values(this._persistedSessions) + .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`); + } + + const serialized = JSON.stringify(allSessions); + + if (allSessions.length) { + this.trace('onWillSaveState', `Persisting ${serialized.length} chars`); + } + + this.storageService.store(serializedChatKey, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } - const serialized = JSON.stringify(allSessions); - - if (allSessions.length) { - this.trace('onWillSaveState', `Persisting ${serialized.length} chars`); - } - - this.storageService.store(serializedChatKey, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } this._deletedChatIds.clear(); @@ -269,13 +308,18 @@ export class ChatService extends Disposable implements IChatService { this._onDidPerformUserAction.fire(action); } - setChatSessionTitle(sessionId: string, title: string): void { + async setChatSessionTitle(sessionId: string, title: string): Promise { const model = this._sessionModels.get(sessionId); if (model) { model.setCustomTitle(title); return; } + if (this.useFileStorage) { + await this._chatSessionStore.setSessionTitle(sessionId, title); + return; + } + const session = this._persistedSessions[sessionId]; if (session) { session.customTitle = title; @@ -326,8 +370,8 @@ export class ChatService extends Disposable implements IChatService { } } - private getTransferredSessionData(): IChatTransfer | undefined { - const data: IChatTransfer[] = this.storageService.getObject(globalChatKey, StorageScope.PROFILE, []); + private getTransferredSessionData(): IChatTransfer2 | undefined { + const data: IChatTransfer2[] = this.storageService.getObject(globalChatKey, StorageScope.PROFILE, []); const workspaceUri = this.workspaceContextService.getWorkspace().folders[0]?.uri; if (!workspaceUri) { return; @@ -345,11 +389,33 @@ export class ChatService extends Disposable implements IChatService { /** * Returns an array of chat details for all persisted chat sessions that have at least one request. - * The array is sorted by creation date in descending order. * Chat sessions that have already been loaded into the chat view are excluded from the result. * Imported chat sessions are also excluded from the result. */ - getHistory(): IChatDetail[] { + async getHistory(): Promise { + if (this.useFileStorage) { + const liveSessionItems = Array.from(this._sessionModels.values()) + .filter(session => !session.isImported && (session.initialLocation !== ChatAgentLocation.EditingSession || this.unifiedViewEnabled)) + .map(session => { + const title = session.title || localize('newChat', "New Chat"); + return { + sessionId: session.sessionId, + title, + lastMessageDate: session.lastMessageDate, + isActive: true, + } satisfies IChatDetail; + }); + + const index = await this._chatSessionStore.getIndex(); + const entries = Object.values(index) + .filter(entry => !this._sessionModels.has(entry.sessionId) && !entry.isImported && !entry.isEmpty) + .map((entry): IChatDetail => ({ + ...entry, + isActive: this._sessionModels.has(entry.sessionId), + })); + return [...liveSessionItems, ...entries]; + } + const persistedSessions = Object.values(this._persistedSessions) .filter(session => session.requests.length > 0) .filter(session => !this._sessionModels.has(session.sessionId)); @@ -379,7 +445,12 @@ export class ChatService extends Disposable implements IChatService { return [...liveSessionItems, ...persistedSessionItems]; } - removeHistoryEntry(sessionId: string): void { + async removeHistoryEntry(sessionId: string): Promise { + if (this.useFileStorage) { + await this._chatSessionStore.deleteSession(sessionId); + return; + } + if (this._persistedSessions[sessionId]) { this._deletedChatIds.add(sessionId); delete this._persistedSessions[sessionId]; @@ -387,19 +458,28 @@ export class ChatService extends Disposable implements IChatService { } } - clearAllHistoryEntries(): void { + async clearAllHistoryEntries(): Promise { + if (this.useFileStorage) { + await this._chatSessionStore.clearAllSessions(); + return; + } + Object.values(this._persistedSessions).forEach(session => this._deletedChatIds.add(session.sessionId)); this._persistedSessions = {}; this.saveState(); } - startSession(location: ChatAgentLocation, token: CancellationToken): ChatModel { + startSession(location: ChatAgentLocation, token: CancellationToken, isGlobalEditingSession: boolean = true): ChatModel { this.trace('startSession'); - return this._startSession(undefined, location, token); + return this._startSession(undefined, location, isGlobalEditingSession, token); } - private _startSession(someSessionHistory: IExportableChatData | ISerializableChatData | undefined, location: ChatAgentLocation, token: CancellationToken): ChatModel { + private _startSession(someSessionHistory: IExportableChatData | ISerializableChatData | undefined, location: ChatAgentLocation, isGlobalEditingSession: boolean, token: CancellationToken): ChatModel { const model = this.instantiationService.createInstance(ChatModel, someSessionHistory, location); + if (location === ChatAgentLocation.EditingSession || (this.unifiedViewEnabled && location === ChatAgentLocation.Panel)) { + model.startEditingSession(isGlobalEditingSession); + } + this._sessionModels.set(model.sessionId, model); this.initializeSession(model, token); return model; @@ -416,16 +496,22 @@ export class ChatService extends Disposable implements IChatService { throw new ErrorNoTelemetry('No default agent contributed'); } - await this.extensionService.activateByEvent(`onChatParticipant:${defaultAgentData.id}`); + if (this.configurationService.getValue('chat.setupFromDialog')) { + // Activate the default extension provided agent but do not wait + // for it to be ready so that the session can be used immediately + // without having to wait for the agent to be ready. + this.extensionService.activateByEvent(`onChatParticipant:${defaultAgentData.id}`); + } else { + // No setup participant to fall back on- wait for extension activation + await this.extensionService.activateByEvent(`onChatParticipant:${defaultAgentData.id}`); - const defaultAgent = this.chatAgentService.getActivatedAgents().find(agent => agent.id === defaultAgentData.id); - if (!defaultAgent) { - throw new ErrorNoTelemetry('No default agent registered'); + const defaultAgent = this.chatAgentService.getActivatedAgents().find(agent => agent.id === defaultAgentData.id); + if (!defaultAgent) { + 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(); } catch (err) { this.trace('startSession', `initializeSession failed: ${err}`); model.setInitializationError(err); @@ -438,31 +524,50 @@ export class ChatService extends Disposable implements IChatService { return this._sessionModels.get(sessionId); } - getOrRestoreSession(sessionId: string): ChatModel | undefined { + async getOrRestoreSession(sessionId: string): Promise { this.trace('getOrRestoreSession', `sessionId: ${sessionId}`); const model = this._sessionModels.get(sessionId); if (model) { return model; } - const sessionData = revive(this._persistedSessions[sessionId]); + let sessionData: ISerializableChatData | undefined; + if (this.useFileStorage) { + sessionData = revive(await this._chatSessionStore.readSession(sessionId)); + } else { + sessionData = revive(this._persistedSessions[sessionId]); + } + if (!sessionData) { return undefined; } - const session = this._startSession(sessionData, sessionData.initialLocation ?? ChatAgentLocation.Panel, CancellationToken.None); + const session = this._startSession(sessionData, sessionData.initialLocation ?? ChatAgentLocation.Panel, true, CancellationToken.None); const isTransferred = this.transferredSessionData?.sessionId === sessionId; if (isTransferred) { - this.chatAgentService.toggleToolsAgentMode(this.transferredSessionData.toolsAgentModeEnabled); + // TODO + // this.chatAgentService.toggleToolsAgentMode(this.transferredSessionData.toolsAgentModeEnabled); this._transferredSessionData = undefined; } return session; } + /** + * This is really just for migrating data from the edit session location to the panel. + */ + isPersistedSessionEmpty(sessionId: string): boolean { + const session = this._persistedSessions[sessionId]; + if (session) { + return session.requests.length === 0; + } + + return this._chatSessionStore.isSessionEmpty(sessionId); + } + loadSessionFromContent(data: IExportableChatData | ISerializableChatData): IChatModel | undefined { - return this._startSession(data, data.initialLocation ?? ChatAgentLocation.Panel, CancellationToken.None); + return this._startSession(data, data.initialLocation ?? ChatAgentLocation.Panel, true, CancellationToken.None); } async resendRequest(request: IChatRequestModel, options?: IChatSendRequestOptions): Promise { @@ -482,7 +587,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 +640,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 +661,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}`; } @@ -648,7 +753,7 @@ export class ChatService extends Disposable implements IChatService { if (agentPart || (defaultAgent && !commandPart)) { const prepareChatAgentRequest = async (agent: IChatAgentData, command?: IChatAgentCommand, enableCommandDetection?: boolean, chatRequest?: ChatRequestModel, isParticipantDetected?: boolean): Promise => { const initVariableData: IChatRequestVariableData = { variables: [] }; - request = chatRequest ?? model.addRequest(parsedRequest, initVariableData, attempt, agent, command, options?.confirmation, options?.locationData, options?.attachedContext); + request = chatRequest ?? model.addRequest(parsedRequest, initVariableData, attempt, agent, command, options?.confirmation, options?.locationData, options?.attachedContext, undefined, options?.userSelectedModelId); let variableData: IChatRequestVariableData; let message: string; @@ -678,11 +783,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 +978,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; @@ -952,7 +1058,7 @@ export class ChatService extends Disposable implements IChatService { const parsedRequest = typeof message === 'string' ? this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(sessionId, message) : message; - const request = model.addRequest(parsedRequest, variableData || { variables: [] }, attempt ?? 0, undefined, undefined, undefined, undefined, undefined, undefined, true); + const request = model.addRequest(parsedRequest, variableData || { variables: [] }, attempt ?? 0, undefined, undefined, undefined, undefined, undefined, true); if (typeof response.message === 'string') { // TODO is this possible? model.acceptResponseProgress(request, { content: new MarkdownString(response.message), kind: 'markdownContent' }); @@ -974,7 +1080,7 @@ export class ChatService extends Disposable implements IChatService { this._pendingRequests.deleteAndDispose(sessionId); } - clearSession(sessionId: string): void { + async clearSession(sessionId: string): Promise { this.trace('clearSession', `sessionId: ${sessionId}`); const model = this._sessionModels.get(sessionId); if (!model) { @@ -982,11 +1088,23 @@ export class ChatService extends Disposable implements IChatService { } if (model.initialLocation === ChatAgentLocation.Panel) { - // Turn all the real objects into actual JSON, otherwise, calling 'revive' may fail when it tries to - // assign values to properties that are getters- microsoft/vscode-copilot-release#1233 - const sessionData: ISerializableChatData = JSON.parse(JSON.stringify(model)); - sessionData.isNew = true; - this._persistedSessions[sessionId] = sessionData; + if (this.useFileStorage) { + if (model.getRequests().length === 0) { + await this._chatSessionStore.deleteSession(sessionId); + } else { + await this._chatSessionStore.storeSessions([model]); + } + } else { + if (model.getRequests().length === 0) { + delete this._persistedSessions[sessionId]; + } else { + // Turn all the real objects into actual JSON, otherwise, calling 'revive' may fail when it tries to + // assign values to properties that are getters- microsoft/vscode-copilot-release#1233 + const sessionData: ISerializableChatData = JSON.parse(JSON.stringify(model)); + sessionData.isNew = true; + this._persistedSessions[sessionId] = sessionData; + } + } } this._sessionModels.deleteAndDispose(sessionId); @@ -996,7 +1114,11 @@ export class ChatService extends Disposable implements IChatService { } public hasSessions(): boolean { - return Object.values(this._persistedSessions).length > 0; + if (this.useFileStorage) { + return this._chatSessionStore.hasSessions(); + } else { + return Object.values(this._persistedSessions).length > 0; + } } transferChatSession(transferredSessionData: IChatTransferredSessionData, toWorkspace: URI): void { @@ -1005,19 +1127,31 @@ export class ChatService extends Disposable implements IChatService { throw new Error(`Failed to transfer session. Unknown session ID: ${transferredSessionData.sessionId}`); } - const existingRaw: IChatTransfer[] = this.storageService.getObject(globalChatKey, StorageScope.PROFILE, []); + const existingRaw: IChatTransfer2[] = this.storageService.getObject(globalChatKey, StorageScope.PROFILE, []); existingRaw.push({ chat: model.toJSON(), timestampInMilliseconds: Date.now(), 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; + } + + getChatStorageFolder(): URI { + return this._chatSessionStore.getChatStorageFolder(); + } + + logChatIndex(): void { + this._chatSessionStore.logIndex(); + } } function getCodeBlocks(text: string): string[] { diff --git a/src/vs/workbench/contrib/chat/common/chatSessionStore.ts b/src/vs/workbench/contrib/chat/common/chatSessionStore.ts new file mode 100644 index 00000000..e7c01872 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatSessionStore.ts @@ -0,0 +1,474 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { VSBuffer } from '../../../../base/common/buffer.js'; +import { toErrorMessage } from '../../../../base/common/errorMessage.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { revive } from '../../../../base/common/marshalling.js'; +import { joinPath } from '../../../../base/common/resources.js'; +import { URI } from '../../../../base/common/uri.js'; +import { localize } from '../../../../nls.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { FileOperationResult, IFileService, toFileOperationResult } from '../../../../platform/files/common/files.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js'; +import { ChatModel, ISerializableChatData, ISerializableChatDataIn, ISerializableChatsData, normalizeSerializableChatData } from './chatModel.js'; +import { ChatAgentLocation, ChatMode } from './constants.js'; + +const maxPersistedSessions = 25; + +const ChatIndexStorageKey = 'chat.ChatSessionStore.index'; +// const ChatTransferIndexStorageKey = 'ChatSessionStore.transferIndex'; + +export class ChatSessionStore extends Disposable { + private readonly storageRoot: URI; + private readonly previousEmptyWindowStorageRoot: URI | undefined; + // private readonly transferredSessionStorageRoot: URI; + + private readonly storeQueue = new Sequencer(); + + private storeTask: Promise | undefined; + private shuttingDown = false; + + constructor( + @IFileService private readonly fileService: IFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ILogService private readonly logService: ILogService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IStorageService private readonly storageService: IStorageService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + ) { + super(); + + const workspace = this.workspaceContextService.getWorkspace(); + const isEmptyWindow = !workspace.configuration && workspace.folders.length === 0; + const workspaceId = this.workspaceContextService.getWorkspace().id; + this.storageRoot = isEmptyWindow ? + joinPath(this.userDataProfilesService.defaultProfile.globalStorageHome, 'emptyWindowChatSessions') : + joinPath(this.environmentService.workspaceStorageHome, workspaceId, 'chatSessions'); + + this.previousEmptyWindowStorageRoot = isEmptyWindow ? + joinPath(this.environmentService.workspaceStorageHome, 'no-workspace', 'chatSessions') : + undefined; + + // TODO tmpdir + // this.transferredSessionStorageRoot = joinPath(this.environmentService.workspaceStorageHome, 'transferredChatSessions'); + + this._register(this.lifecycleService.onWillShutdown(e => { + this.shuttingDown = true; + if (!this.storeTask) { + return; + } + + e.join(this.storeTask, { + id: 'join.chatSessionStore', + label: localize('join.chatSessionStore', "Saving chat history") + }); + })); + } + + async storeSessions(sessions: ChatModel[]): Promise { + if (this.shuttingDown) { + // Don't start this task if we missed the chance to block shutdown + return; + } + + try { + this.storeTask = this.storeQueue.queue(async () => { + try { + await Promise.all(sessions.map(session => this.writeSession(session))); + await this.trimEntries(); + await this.flushIndex(); + } catch (e) { + this.reportError('storeSessions', 'Error storing chat sessions', e); + } + }); + await this.storeTask; + } finally { + this.storeTask = undefined; + } + } + + // async storeTransferSession(transferData: IChatTransfer, session: ISerializableChatData): Promise { + // try { + // const content = JSON.stringify(session, undefined, 2); + // await this.fileService.writeFile(this.transferredSessionStorageRoot, VSBuffer.fromString(content)); + // } catch (e) { + // this.reportError('sessionWrite', 'Error writing chat session', e); + // return; + // } + + // const index = this.getTransferredSessionIndex(); + // index[transferData.toWorkspace.toString()] = transferData; + // try { + // this.storageService.store(ChatTransferIndexStorageKey, index, StorageScope.PROFILE, StorageTarget.MACHINE); + // } catch (e) { + // this.reportError('storeTransferSession', 'Error storing chat transfer session', e); + // } + // } + + // private getTransferredSessionIndex(): IChatTransferIndex { + // try { + // const data: IChatTransferIndex = this.storageService.getObject(ChatTransferIndexStorageKey, StorageScope.PROFILE, {}); + // return data; + // } catch (e) { + // this.reportError('getTransferredSessionIndex', 'Error reading chat transfer index', e); + // return {}; + // } + // } + + private async writeSession(session: ChatModel | ISerializableChatData): Promise { + try { + const index = this.internalGetIndex(); + const storageLocation = this.getStorageLocation(session.sessionId); + const content = JSON.stringify(session, undefined, 2); + await this.fileService.writeFile(storageLocation, VSBuffer.fromString(content)); + + // Write succeeded, update index + index.entries[session.sessionId] = getSessionMetadata(session); + } catch (e) { + this.reportError('sessionWrite', 'Error writing chat session', e); + } + } + + private async flushIndex(): Promise { + const index = this.internalGetIndex(); + try { + this.storageService.store(ChatIndexStorageKey, index, this.getIndexStorageScope(), StorageTarget.MACHINE); + } catch (e) { + // Only if JSON.stringify fails, AFAIK + this.reportError('indexWrite', 'Error writing index', e); + } + } + + private getIndexStorageScope(): StorageScope { + const workspace = this.workspaceContextService.getWorkspace(); + const isEmptyWindow = !workspace.configuration && workspace.folders.length === 0; + return isEmptyWindow ? StorageScope.APPLICATION : StorageScope.WORKSPACE; + } + + private async trimEntries(): Promise { + const index = this.internalGetIndex(); + const entries = Object.entries(index.entries) + .sort((a, b) => b[1].lastMessageDate - a[1].lastMessageDate) + .map(([id]) => id); + + if (entries.length > maxPersistedSessions) { + const entriesToDelete = entries.slice(maxPersistedSessions); + for (const entry of entriesToDelete) { + delete index.entries[entry]; + } + + this.logService.trace(`ChatSessionStore: Trimmed ${entriesToDelete.length} old chat sessions from index`); + } + } + + private async internalDeleteSession(sessionId: string): Promise { + const index = this.internalGetIndex(); + if (!index.entries[sessionId]) { + return; + } + + const storageLocation = this.getStorageLocation(sessionId); + try { + await this.fileService.del(storageLocation); + } catch (e) { + if (toFileOperationResult(e) !== FileOperationResult.FILE_NOT_FOUND) { + this.reportError('sessionDelete', 'Error deleting chat session', e); + } + } finally { + delete index.entries[sessionId]; + } + } + + hasSessions(): boolean { + return Object.keys(this.internalGetIndex().entries).length > 0; + } + + isSessionEmpty(sessionId: string): boolean { + const index = this.internalGetIndex(); + return index.entries[sessionId]?.isEmpty ?? true; + } + + async deleteSession(sessionId: string): Promise { + await this.storeQueue.queue(async () => { + await this.internalDeleteSession(sessionId); + await this.flushIndex(); + }); + } + + async clearAllSessions(): Promise { + await this.storeQueue.queue(async () => { + const index = this.internalGetIndex(); + const entries = Object.keys(index.entries); + this.logService.info(`ChatSessionStore: Clearing ${entries.length} chat sessions`); + await Promise.all(entries.map(entry => this.internalDeleteSession(entry))); + await this.flushIndex(); + }); + } + + public async setSessionTitle(sessionId: string, title: string): Promise { + await this.storeQueue.queue(async () => { + const index = this.internalGetIndex(); + if (index.entries[sessionId]) { + index.entries[sessionId].title = title; + } + }); + } + + private reportError(reasonForTelemetry: string, message: string, error?: Error): void { + this.logService.error(`ChatSessionStore: ` + message, toErrorMessage(error)); + + const fileOperationReason = error && toFileOperationResult(error); + type ChatSessionStoreErrorData = { + reason: string; + fileOperationReason: number; + // error: Error; + }; + type ChatSessionStoreErrorClassification = { + owner: 'roblourens'; + comment: 'Detect issues related to managing chat sessions'; + reason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Info about the error that occurred' }; + fileOperationReason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'An error code from the file service' }; + // error: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Info about the error that occurred' }; + }; + this.telemetryService.publicLog2('chatSessionStoreError', { + reason: reasonForTelemetry, + fileOperationReason: fileOperationReason ?? -1 + }); + } + + private indexCache: IChatSessionIndexData | undefined; + private internalGetIndex(): IChatSessionIndexData { + if (this.indexCache) { + return this.indexCache; + } + + const data = this.storageService.get(ChatIndexStorageKey, this.getIndexStorageScope(), undefined); + if (!data) { + this.indexCache = { version: 1, entries: {} }; + return this.indexCache; + } + + try { + const index = JSON.parse(data) as unknown; + if (isChatSessionIndex(index)) { + // Success + this.indexCache = index; + } else { + this.reportError('invalidIndexFormat', `Invalid index format: ${data}`); + this.indexCache = { version: 1, entries: {} }; + } + + return this.indexCache; + } catch (e) { + // Only if JSON.parse fails + this.reportError('invalidIndexJSON', `Index corrupt: ${data}`, e); + this.indexCache = { version: 1, entries: {} }; + return this.indexCache; + } + } + + async getIndex(): Promise { + return this.storeQueue.queue(async () => { + return this.internalGetIndex().entries; + }); + } + + logIndex(): void { + const data = this.storageService.get(ChatIndexStorageKey, this.getIndexStorageScope(), undefined); + this.logService.info('ChatSessionStore index: ', data); + } + + async migrateDataIfNeeded(getInitialData: () => ISerializableChatsData | undefined): Promise { + await this.storeQueue.queue(async () => { + const data = this.storageService.get(ChatIndexStorageKey, this.getIndexStorageScope(), undefined); + const needsMigrationFromStorageService = !data; + if (needsMigrationFromStorageService) { + const initialData = getInitialData(); + if (initialData) { + await this.migrate(initialData); + } + } + }); + } + + private async migrate(initialData: ISerializableChatsData): Promise { + const numSessions = Object.keys(initialData).length; + this.logService.info(`ChatSessionStore: Migrating ${numSessions} chat sessions from storage service to file system`); + + await Promise.all(Object.values(initialData).map(async session => { + await this.writeSession(session); + })); + + await this.flushIndex(); + } + + public async readSession(sessionId: string): Promise { + return await this.storeQueue.queue(async () => { + let rawData: string | undefined; + const storageLocation = this.getStorageLocation(sessionId); + try { + rawData = (await this.fileService.readFile(storageLocation)).value.toString(); + } catch (e) { + this.reportError('sessionReadFile', `Error reading chat session file ${sessionId}`, e); + + if (toFileOperationResult(e) === FileOperationResult.FILE_NOT_FOUND && this.previousEmptyWindowStorageRoot) { + rawData = await this.readSessionFromPreviousLocation(sessionId); + } + + if (!rawData) { + return undefined; + } + } + + try { + // TODO Copied from ChatService.ts, cleanup + const session: ISerializableChatDataIn = revive(JSON.parse(rawData)); // Revive serialized URIs in session data + // Revive serialized markdown strings in response data + for (const request of session.requests) { + if (Array.isArray(request.response)) { + request.response = request.response.map((response) => { + if (typeof response === 'string') { + return new MarkdownString(response); + } + return response; + }); + } else if (typeof request.response === 'string') { + request.response = [new MarkdownString(request.response)]; + } + } + + return normalizeSerializableChatData(session); + } catch (err) { + this.reportError('malformedSession', `Malformed session data in ${storageLocation.fsPath}: [${rawData.substring(0, 20)}${rawData.length > 20 ? '...' : ''}]`, err); + return undefined; + } + }); + } + + private async readSessionFromPreviousLocation(sessionId: string): Promise { + let rawData: string | undefined; + + if (this.previousEmptyWindowStorageRoot) { + const storageLocation2 = joinPath(this.previousEmptyWindowStorageRoot, `${sessionId}.json`); + try { + rawData = (await this.fileService.readFile(storageLocation2)).value.toString(); + this.logService.info(`ChatSessionStore: Read chat session ${sessionId} from previous location`); + } catch (e) { + this.reportError('sessionReadFile', `Error reading chat session file ${sessionId} from previous location`, e); + return undefined; + } + } + + return rawData; + } + + private getStorageLocation(chatSessionId: string): URI { + return joinPath(this.storageRoot, `${chatSessionId}.json`); + } + + public getChatStorageFolder(): URI { + return this.storageRoot; + } +} + +interface IChatSessionEntryMetadata { + sessionId: string; + title: string; + lastMessageDate: number; + isImported?: boolean; + initialLocation?: ChatAgentLocation; + + /** + * This only exists because the migrated data from the storage service had empty sessions persisted, and it's impossible to know which ones are + * currently in use. Now, `clearSession` deletes empty sessions, so old ones shouldn't take up space in the store anymore, but we still need to + * filter the old ones out of history. + */ + isEmpty?: boolean; +} + +function isChatSessionEntryMetadata(obj: unknown): obj is IChatSessionEntryMetadata { + return ( + !!obj && + typeof obj === 'object' && + typeof (obj as IChatSessionEntryMetadata).sessionId === 'string' && + typeof (obj as IChatSessionEntryMetadata).title === 'string' && + typeof (obj as IChatSessionEntryMetadata).lastMessageDate === 'number' + ); +} + +export type IChatSessionIndex = Record; + +interface IChatSessionIndexData { + version: 1; + entries: IChatSessionIndex; +} + +// TODO if we update the index version: +// Don't throw away index when moving backwards in VS Code version. Try to recover it. But this scenario is hard. +function isChatSessionIndex(data: unknown): data is IChatSessionIndexData { + if (typeof data !== 'object' || data === null) { + return false; + } + + const index = data as IChatSessionIndexData; + if (index.version !== 1) { + return false; + } + + if (typeof index.entries !== 'object' || index.entries === null) { + return false; + } + + for (const key in index.entries) { + if (!isChatSessionEntryMetadata(index.entries[key])) { + return false; + } + } + + return true; +} + +function getSessionMetadata(session: ChatModel | ISerializableChatData): IChatSessionEntryMetadata { + const title = session instanceof ChatModel ? + (session.title || localize('newChat', "New Chat")) : + session.customTitle ?? ChatModel.getDefaultTitle(session.requests); + return { + sessionId: session.sessionId, + title, + lastMessageDate: session.lastMessageDate, + isImported: session.isImported, + initialLocation: session.initialLocation, + isEmpty: session instanceof ChatModel ? session.getRequests().length === 0 : session.requests.length === 0 + }; +} + +export interface IChatTransfer { + toWorkspace: URI; + timestampInMilliseconds: number; + inputValue: string; + location: ChatAgentLocation; + mode: ChatMode; +} + +export interface IChatTransfer2 extends IChatTransfer { + chat: ISerializableChatData; +} + +// type IChatTransferDto = Dto; + +/** + * Map of destination workspace URI to chat transfer data + */ +// type IChatTransferIndex = Record; diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 20ec2aff..1da03b0d 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, ChatMode } from './constants.js'; //#region slash service, commands etc @@ -25,6 +25,7 @@ export interface IChatSlashData { */ executeImmediately?: boolean; locations: ChatAgentLocation[]; + modes?: ChatMode[]; } export interface IChatSlashFragment { @@ -42,7 +43,7 @@ export interface IChatSlashCommandService { readonly onDidChangeCommands: Event; registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable; executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; - getCommands(location: ChatAgentLocation): Array; + getCommands(location: ChatAgentLocation, mode: ChatMode): Array; hasCommand(id: string): boolean; } @@ -81,8 +82,10 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom }); } - getCommands(location: ChatAgentLocation): Array { - return Array.from(this._commands.values(), v => v.data).filter(c => c.locations.includes(location)); + getCommands(location: ChatAgentLocation, mode: ChatMode): Array { + return Array + .from(this._commands.values(), v => v.data) + .filter(c => c.locations.includes(location) && (!c.modes || c.modes.includes(mode))); } hasCommand(id: string): boolean { 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 00000000..22a2eb31 --- /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 2edc51c4..809d5637 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, 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; diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 063bd4b0..a152eb57 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -583,7 +583,7 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi } this._register(_model.onDidChange(() => { - // This should be true, if the model is changing + // This is set when the response is loading, but the model can change later for other reasons if (this._contentUpdateTimings) { const now = Date.now(); const wordCount = countWords(_model.entireResponse.getMarkdown()); @@ -608,9 +608,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi lastWordCount: wordCount }; } - - } else { - this.logService.warn('ChatResponseViewModel#onDidChange: got model update but contentUpdateTimings is not initialized'); } // new data -> new id, new content to render diff --git a/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts b/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts index d2ab31d5..4522c0b7 100644 --- a/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts +++ b/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts @@ -8,10 +8,10 @@ import { URI } from '../../../../base/common/uri.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { Memento } from '../../../common/memento.js'; -import { ChatAgentLocation } from './chatAgents.js'; import { WorkingSetEntryState } from './chatEditingService.js'; import { IChatRequestVariableEntry } from './chatModel.js'; import { CHAT_PROVIDER_ID } from './chatParticipantContribTypes.js'; +import { ChatAgentLocation, ChatMode } from './constants.js'; export interface IChatHistoryEntry { text: string; @@ -23,6 +23,7 @@ export interface IChatInputState { [key: string]: any; chatContextAttachments?: ReadonlyArray; chatWorkingSet?: ReadonlyArray<{ uri: URI; state: WorkingSetEntryState }>; + chatMode?: ChatMode; } export const IChatWidgetHistoryService = createDecorator('IChatWidgetHistoryService'); diff --git a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts index 59c605b6..7ce74db6 100644 --- a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts +++ b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts @@ -21,10 +21,11 @@ interface CodeBlockContent { readonly isComplete: boolean; } -interface CodeBlockEntry { +export interface CodeBlockEntry { readonly model: Promise; readonly vulns: readonly IMarkdownVulnerability[]; readonly codemapperUri?: URI; + readonly isEdit?: boolean; } export class CodeBlockModelCollection extends Disposable { @@ -33,6 +34,7 @@ export class CodeBlockModelCollection extends Disposable { model: Promise>; vulns: readonly IMarkdownVulnerability[]; codemapperUri?: URI; + isEdit?: boolean; }>(); /** @@ -63,7 +65,8 @@ export class CodeBlockModelCollection extends Disposable { return { model: entry.model.then(ref => ref.object.textEditorModel), vulns: entry.vulns, - codemapperUri: entry.codemapperUri + codemapperUri: entry.codemapperUri, + isEdit: entry.isEdit, }; } @@ -117,7 +120,7 @@ export class CodeBlockModelCollection extends Disposable { const codeblockUri = extractCodeblockUrisFromText(newText); if (codeblockUri) { - this.setCodemapperUri(sessionId, chat, codeBlockIndex, codeblockUri.uri); + this.setCodemapperUri(sessionId, chat, codeBlockIndex, codeblockUri.uri, codeblockUri.isEdit); } if (content.isComplete) { @@ -144,7 +147,7 @@ export class CodeBlockModelCollection extends Disposable { const codeblockUri = extractCodeblockUrisFromText(newText); if (codeblockUri) { - this.setCodemapperUri(sessionId, chat, codeBlockIndex, codeblockUri.uri); + this.setCodemapperUri(sessionId, chat, codeBlockIndex, codeblockUri.uri, codeblockUri.isEdit); newText = codeblockUri.textWithoutResult; } @@ -182,10 +185,11 @@ export class CodeBlockModelCollection extends Disposable { return entry; } - private setCodemapperUri(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number, codemapperUri: URI) { + private setCodemapperUri(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, codeBlockIndex: number, codemapperUri: URI, isEdit?: boolean) { const entry = this._models.get(this.getKey(sessionId, chat, codeBlockIndex)); if (entry) { entry.codemapperUri = codemapperUri; + entry.isEdit = isEdit; } } 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 00000000..052f3d01 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/constants.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * 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.unifiedChatView', + UseFileStorage = 'chat.useFileStorage', + AgentEnabled = 'chat.agent.enabled', + Edits2Enabled = 'chat.edits2.enabled', + ExtensionToolsEnabled = 'chat.extensionTools.enabled', +} + +export enum ChatMode { + Ask = 'ask', + Edit = 'edit', + Agent = 'agent' +} + +export function validateChatMode(mode: unknown): ChatMode | undefined { + switch (mode) { + case ChatMode.Ask: + case ChatMode.Edit: + case ChatMode.Agent: + return mode as ChatMode; + default: + return undefined; + } +} + +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 8a692ee4..9453dc07 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -14,11 +14,13 @@ import { ContextKeyExpression } from '../../../../platform/contextkey/common/con import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { Location } from '../../../../editor/common/languages.js'; -import { IChatTerminalToolInvocationData } from './chatService.js'; +import { IChatTerminalToolInvocationData, IChatToolInputInvocationData } from './chatService.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { PromptElementJSON, stringifyPromptElementJSON } from './tools/promptTsxTypes.js'; export interface IToolData { id: string; - extensionId?: ExtensionIdentifier; + source: ToolDataSource; toolReferenceName?: string; icon?: { dark: URI; light?: URI } | ThemeIcon; when?: ContextKeyExpression; @@ -28,6 +30,37 @@ export interface IToolData { modelDescription: string; inputSchema?: IJSONSchema; canBeReferencedInPrompt?: boolean; + /** + * True if the tool runs in the (possibly remote) workspace, false if it runs + * on the host, undefined if known. + */ + runsInWorkspace?: boolean; + requiresConfirmation?: boolean; + alwaysDisplayInputOutput?: boolean; + supportsToolPicker?: boolean; +} + +export type ToolDataSource = + | { + type: 'extension'; + extensionId: ExtensionIdentifier; + /** + * True for tools contributed through extension API from third-party extensions, so they can be disabled by policy. + * False for built-in tools, MCP tools are handled differently. + */ + isExternalTool: boolean; + } + | { type: 'mcp'; collectionId: string; definitionId: string } + | { type: 'internal' }; + +export namespace ToolDataSource { + export function toKey(source: ToolDataSource): string { + switch (source.type) { + case 'extension': return `extension:${source.extensionId.value}`; + case 'mcp': return `mcp:${source.collectionId}:${source.definitionId}`; + case 'internal': return 'internal'; + } + } } export interface IToolInvocation { @@ -37,7 +70,9 @@ export interface IToolInvocation { tokenBudget?: number; context: IToolInvocationContext | undefined; chatRequestId?: string; - toolSpecificData?: IChatTerminalToolInvocationData; + chatInteractionId?: string; + toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData; + modelId?: string; } export interface IToolInvocationContext { @@ -48,10 +83,19 @@ export function isToolInvocationContext(obj: any): obj is IToolInvocationContext return typeof obj === 'object' && typeof obj.sessionId === 'string'; } +export interface IToolResultInputOutputDetails { + readonly input: string; + readonly output: string; +} + +export function isToolResultInputOutputDetails(obj: any): obj is IToolResultInputOutputDetails { + return typeof obj === 'object' && typeof obj?.input === 'string' && typeof obj?.output === 'string'; +} + export interface IToolResult { content: (IToolResultPromptTsxPart | IToolResultTextPart)[]; toolResultMessage?: string | IMarkdownString; - toolResultDetails?: Array; + toolResultDetails?: Array | IToolResultInputOutputDetails; } export interface IToolResultPromptTsxPart { @@ -59,6 +103,10 @@ export interface IToolResultPromptTsxPart { value: unknown; } +export function stringifyPromptTsxPart(part: IToolResultPromptTsxPart): string { + return stringifyPromptElementJSON(part.value as PromptElementJSON); +} + export interface IToolResultTextPart { kind: 'text'; value: string; @@ -67,6 +115,7 @@ export interface IToolResultTextPart { export interface IToolConfirmationMessages { title: string; message: string | IMarkdownString; + allowAutoConfirm?: boolean; } export interface IPreparedToolInvocation { @@ -74,7 +123,7 @@ export interface IPreparedToolInvocation { pastTenseMessage?: string | IMarkdownString; confirmationMessages?: IToolConfirmationMessages; presentation?: 'hidden' | undefined; - toolSpecificData?: IChatTerminalToolInvocationData; + toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData; } export interface IToolImpl { @@ -95,5 +144,14 @@ export interface ILanguageModelToolsService { getTool(id: string): IToolData | undefined; getToolByName(name: string): IToolData | undefined; invokeTool(invocation: IToolInvocation, countTokens: CountTokensCallback, token: CancellationToken): Promise; + setToolAutoConfirmation(toolId: string, scope: 'workspace' | 'profile' | 'memory', autoConfirm?: boolean): void; + resetToolAutoConfirmation(): void; cancelToolCallsForRequest(requestId: string): void; } + +export function createToolInputUri(toolOrId: IToolData | string): URI { + if (typeof toolOrId !== 'string') { + toolOrId = toolOrId.id; + } + return URI.from({ scheme: Schemas.inMemory, path: `/lm/tool/${toolOrId}/tool_input.json` }); +} diff --git a/src/vs/workbench/contrib/chat/common/languageModels.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts index 3db81d29..ba03b133 100644 --- a/src/vs/workbench/contrib/chat/common/languageModels.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { VSBuffer } from '../../../../base/common/buffer.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Iterable } from '../../../../base/common/iterator.js'; @@ -29,6 +30,43 @@ export interface IChatMessageTextPart { value: string; } +export interface IChatMessageImagePart { + type: 'image_url'; + value: IChatImageURLPart; +} + +export interface IChatImageURLPart { + /** + * The image's MIME type (e.g., "image/png", "image/jpeg"). + */ + mimeType: ChatImageMimeType; + + /** + * The raw binary data of the image, encoded as a Uint8Array. Note: do not use base64 encoding. Maximum image size is 5MB. + */ + data: VSBuffer; +} + +/** + * Enum for supported image MIME types. + */ +export enum ChatImageMimeType { + PNG = 'image/png', + JPEG = 'image/jpeg', + GIF = 'image/gif', + WEBP = 'image/webp', + BMP = 'image/bmp', +} + +/** + * Specifies the detail level of the image. + */ +export enum ImageDetailLevel { + Low = 'low', + High = 'high' +} + + export interface IChatMessageToolResultPart { type: 'tool_result'; toolCallId: string; @@ -36,7 +74,7 @@ export interface IChatMessageToolResultPart { isError?: boolean; } -export type IChatMessagePart = IChatMessageTextPart | IChatMessageToolResultPart | IChatResponseToolUsePart; +export type IChatMessagePart = IChatMessageTextPart | IChatMessageToolResultPart | IChatResponseToolUsePart | IChatMessageImagePart; export interface IChatMessage { readonly name?: string | undefined; diff --git a/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts b/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts index 84e7d625..e19588df 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. */ @@ -40,22 +42,6 @@ export abstract class ParseError extends Error { } } -/** - * A generic error for failing to resolve prompt contents stream. - */ -export class FailedToResolveContentsStream extends ParseError { - public override errorType = 'FailedToResolveContentsStream'; - - constructor( - public readonly uri: URI, - public readonly originalError: unknown, - message: string = `Failed to resolve prompt contents stream for '${uri.toString()}': ${originalError}.`, - ) { - super(message); - } -} - - /** * Base resolve error class used when file reference resolution fails. */ @@ -71,6 +57,22 @@ export abstract class ResolveError extends ParseError { } } +/** + * A generic error for failing to resolve prompt contents stream. + */ +export class FailedToResolveContentsStream extends ResolveError { + public override errorType = 'FailedToResolveContentsStream'; + + constructor( + uri: URI, + public readonly originalError: unknown, + message: string = `Failed to resolve prompt contents stream for '${uri.toString()}': ${originalError}.`, + ) { + 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 06d8e162..b1bd3de2 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 5efc84d9..fd04befb 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); + super(range, VARIABLE_NAME, path); } /** - * Get full text of the file reference token. + * Create a {@link FileReference} from a {@link PromptVariableWithData} instance. + * @throws if variable name is not equal to {@link VARIABLE_NAME}. */ - public get text(): string { - return `${TOKEN_START}${this.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`. - */ - public static fromWord(word: Word): FileReference { - const { text } = word; - + public static from(variable: PromptVariableWithData) { assert( - text.startsWith(TOKEN_START), - `The reference must start with "${TOKEN_START}", got ${text}.`, + variable.name === VARIABLE_NAME, + `Variable name must be '${VARIABLE_NAME}', got '${variable.name}'.`, ); - const maybeReference = text.split(TOKEN_START); - - assert( - maybeReference.length === 2, - `The expected reference format is "${TOKEN_START}:filesystem-path", got ${text}.`, + return new FileReference( + variable.range, + variable.data, ); - - const [first, second] = maybeReference; - - assert( - first === '', - `The reference must start with "${TOKEN_START}", got ${first}.`, - ); - - 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)) { + if ((other instanceof FileReference) === false) { return false; } - if (!(other instanceof FileReference)) { - 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/chat/common/promptSyntax/codecs/tokens/promptToken.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptToken.ts new file mode 100644 index 00000000..38a9d69d --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptToken.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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 '../../../../../../../editor/common/codecs/baseToken.js'; + +/** + * 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 00000000..449926fa --- /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/constants.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts index 3768c6c5..7d5d89d8 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts @@ -3,16 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PROMPT_FILE_EXTENSION } from '../../../../../platform/prompts/common/constants.js'; +import { LanguageFilter } from '../../../../../editor/common/languageSelector.js'; +import { COPILOT_CUSTOM_INSTRUCTIONS_FILENAME, PROMPT_FILE_EXTENSION } from '../../../../../platform/prompts/common/constants.js'; /** * Documentation link for the reusable prompts feature. */ export const DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-prompt-snippets'; +/** + * Supported reusable prompt file patterns. + */ +const REUSABLE_PROMPT_FILE_PATTERNS = Object.freeze([ + /** + * Any file that has the prompt file extension. + * See {@link PROMPT_FILE_EXTENSION}. + */ + `**/*${PROMPT_FILE_EXTENSION}`, + + /** + * Copilot custom instructions file inside a `.github` folder. + */ + `**/.github/${COPILOT_CUSTOM_INSTRUCTIONS_FILENAME}`, +]); + /** * Prompt files language selector. */ -export const LANGUAGE_SELECTOR = Object.freeze({ - pattern: `**/*${PROMPT_FILE_EXTENSION}`, +export const LANGUAGE_SELECTOR: LanguageFilter = Object.freeze({ + pattern: `{${REUSABLE_PROMPT_FILE_PATTERNS.join(',')}}`, }); 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 512c29e9..87bf7504 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,8 @@ 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 { isPromptFile } from '../../../../../../platform/prompts/common/constants.js'; +import { OpenFailed, NotPromptFile, ResolveError, FolderReference } from '../../promptFileReferenceErrors.js'; import { FileChangesEvent, FileChangeType, IFileService } from '../../../../../../platform/files/common/files.js'; /** @@ -81,7 +82,7 @@ export class FilePromptContentProvider extends PromptContentsProviderBase, > extends ObservableDisposable implements IPromptContentsProvider { + public abstract readonly uri: URI; + public abstract createNew(promptContentsSource: { uri: URI }): IPromptContentsProvider; + public abstract override toString(): string; + + /** + * Function to get contents stream for the provider. This function should + * throw a `ResolveError` or its derivative if the contents cannot be parsed. + * + * @param changesEvent The event that triggered the change. The special + * `'full'` value means that everything has changed hence entire prompt + * contents need to be re-parsed from scratch. + */ + protected abstract getContentsStream( + changesEvent: TChangeEvent | 'full', + cancellationToken?: CancellationToken, + ): Promise; + /** * Internal event emitter for the prompt contents change event. Classes that extend * this abstract class are responsible to use this emitter to fire the contents change @@ -47,40 +63,16 @@ export abstract class PromptContentsProviderBase< this._register(this.onChangeEmitter.event(this.onContentsChanged, this)); } - /** - * Function to get contents stream for the provider. This function should - * throw a `ParseError` or its derivative if the contents cannot be parsed. - * - * @param changesEvent The event that triggered the change. The special - * `'full'` value means that everything has changed hence entire prompt - * contents need to be re-parsed from scratch. - */ - protected abstract getContentsStream( - changesEvent: TChangeEvent | 'full', - cancellationToken?: CancellationToken, - ): Promise; - - /** - * URI reference associated with the prompt contents. - */ - public abstract readonly uri: URI; - - /** - * Return a string representation of this object - * for debugging/tracing purposes. - */ - public abstract override toString(): string; - /** * Event emitter for the prompt contents change event. * See {@linkcode onContentChanged} for more details. */ - private readonly onContentChangedEmitter = this._register(new Emitter()); + 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 +104,7 @@ export abstract class PromptContentsProviderBase< this.onContentChangedEmitter.fire(stream); }) .catch((error) => { - if (error instanceof ParseError) { + if (error instanceof ResolveError) { this.onContentChangedEmitter.fire(error); return; @@ -140,11 +132,4 @@ export abstract class PromptContentsProviderBase< return this; } - - /** - * Check if the current URI points to a prompt snippet. - */ - public isPromptSnippet(): boolean { - return isPromptFile(this.uri); - } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index b980da87..71907911 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -3,16 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IPromptContentsProvider } from './types.js'; +import { URI } from '../../../../../../base/common/uri.js'; import { VSBuffer } from '../../../../../../base/common/buffer.js'; import { ITextModel } from '../../../../../../editor/common/model.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; +import { FilePromptContentProvider } from './filePromptContentsProvider.js'; import { PromptContentsProviderBase } from './promptContentsProviderBase.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { newWriteableStream, ReadableStream } from '../../../../../../base/common/stream.js'; import { IModelContentChangedEvent } from '../../../../../../editor/common/textModelEvents.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { TextModel } from '../../../../../../editor/common/model/textModel.js'; /** - * Prompt contents provider for a {@linkcode ITextModel} instance. + * Prompt contents provider for a {@link ITextModel} instance. */ export class TextModelContentsProvider extends PromptContentsProviderBase { /** @@ -22,6 +27,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase(null); const linesCount = this.model.getLineCount(); - // provide the changed lines to the stream incrementaly and asynchronously + // provide the changed lines to the stream incrementally and asynchronously // to avoid blocking the main thread and save system resources used let i = 1; const interval = setInterval(() => { @@ -74,7 +80,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase void, + callback: (streamOrError: VSBufferReadableStream | ResolveError) => void, ): IDisposable; + + /** + * Subscribe to `onDispose` event of the contents provider. + */ + onDispose(callback: () => void): this; + + /** + * Create a new instance of prompt contents provider. + */ + createNew( + promptContentsSource: { uri: URI }, + ): IPromptContentsProvider; } 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 00000000..7d46c72d --- /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 7f026bdf..26f63084 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 937c7e6b..bd5dc68c 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -3,37 +3,27 @@ * 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'; import { assertDefined } from '../../../../../../base/common/types.js'; import { IPromptContentsProvider } from '../contentProviders/types.js'; +import { IPromptReference, IResolveError, ITopError } from './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 { 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. @@ -44,11 +34,11 @@ export type TErrorCondition = OpenFailed | RecursiveReference | FolderReference * Base prompt parser class that provides a common interface for all * prompt parsers that are responsible for parsing chat prompt syntax. */ -export abstract class BasePromptParser extends ObservableDisposable { +export class BasePromptParser extends ObservableDisposable { /** * List of file references in the current branch of the file reference tree. */ - private readonly _references: PromptFileReference[] = []; + private readonly _references: IPromptReference[] = []; /** * The event is fired when lines or their content change. @@ -65,13 +55,17 @@ export abstract class BasePromptParser extend return this; } - private _errorCondition?: ParseError; + /** + * If failed to parse prompt contents, this property has + * an error object that describes the failure reason. + */ + 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; } @@ -141,7 +135,7 @@ export abstract class BasePromptParser extend } constructor( - private readonly promptContentsProvider: T, + private readonly promptContentsProvider: TContentsProvider, seenReferences: string[] = [], @IInstantiationService protected readonly instantiationService: IInstantiationService, @ILogService protected readonly logService: ILogService, @@ -149,7 +143,6 @@ export abstract class BasePromptParser extend super(); this._onUpdate.fire = this._onUpdate.fire.bind(this._onUpdate); - this._register(promptContentsProvider); // to prevent infinite file recursion, we keep track of all references in // the current branch of the file reference tree and check if the current @@ -157,7 +150,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(); @@ -178,6 +174,9 @@ export abstract class BasePromptParser extend this.firstParseResult.complete(); }), ); + + // dispose self when contents provider is disposed + this.promptContentsProvider.onDispose(this.dispose.bind(this)); } /** @@ -196,7 +195,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 +208,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 +224,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 @@ -256,16 +259,24 @@ export abstract class BasePromptParser extend token: FileReference | MarkdownLink, seenReferences: string[], ): this { - const fileReference = this.instantiationService - .createInstance(PromptFileReference, token, this.dirname, seenReferences); - this._references.push(fileReference); + const referenceUri = extUri.resolvePath(this.dirname, token.path); + const contentProvider = this.promptContentsProvider.createNew({ uri: referenceUri }); - fileReference.onUpdate(this._onUpdate.fire); - fileReference.start(); + const reference = this.instantiationService + .createInstance(PromptReference, contentProvider, token, seenReferences); + // the content provider is exclusively owned by the reference + // hence dispose it when the reference is disposed + reference.onDispose(contentProvider.dispose.bind(contentProvider)); + + this._references.push(reference); + + reference.onUpdate(this._onUpdate.fire); this._onUpdate.fire(); + reference.start(); + return this; } @@ -345,7 +356,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 +364,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 +381,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 +410,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[] = []; + public get allErrors(): readonly IResolveError[] { + const result: IResolveError[] = []; - // 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; + for (const reference of this.references) { + const { errorCondition } = reference; - return errorCondition && !(errorCondition instanceof NotPromptFile); - }) - // map to error condition objects - .map((childReference): ParseError => { - const { errorCondition } = childReference; + if (errorCondition && (!(errorCondition instanceof NotPromptFile))) { + result.push({ + originalError: errorCondition, + parentUri: this.uri, + }); + } - // `must` always be `true` because of the `filter` call above - assertDefined( - errorCondition, - `Error condition must be present for '${childReference.uri.path}'.`, - ); - - return errorCondition; - }); - - result.push(...childErrorConditions); + // recursively collect all possible errors of its children + result.push(...reference.allErrors); + } return result; } @@ -439,72 +455,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) { + const childErrors: ResolveError[] = [...this.errors]; + const nestedErrors: IResolveError[] = []; + for (const reference of this.references) { + nestedErrors.push(...reference.allErrors); + } + + if (childErrors.length === 0 && nestedErrors.length === 0) { return undefined; } + const firstDirectChildError = childErrors[0]; + const firstNestedChildError = nestedErrors[0]; + const hasDirectChildError = (firstDirectChildError !== undefined); - // 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); + const firstChildError = (hasDirectChildError) + ? { + originalError: firstDirectChildError, + parentUri: this.uri, + } + : firstNestedChildError; - // 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 totalErrorsCount = childErrors.length + nestedErrors.length; - const moreSuffix = restErrors.length > 0 - ? `\n-\n +${restErrors.length} more error${restErrors.length > 1 ? 's' : ''}` - : ''; + const subject = (hasDirectChildError) + ? 'child' + : 'indirect-child'; - 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 (error instanceof FailedToResolveContentsStream) { - return `${errorMessages.streamOpenFailed} '${error.uri.path}'.`; - } - - // 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 recursivePathString = recursivePath - .map((path) => { - return basename(URI.file(path)); - }) - .join(' -> '); - - return `${errorMessages.recursion}:\n${recursivePathString}`; - } - - return error.message; + return new TopError({ + errorSubject: subject, + originalError: firstChildError.originalError, + parentUri: firstChildError.parentUri, + errorsCount: totalErrorsCount, + }); } /** @@ -517,7 +509,7 @@ export abstract class BasePromptParser extend /** * Check if the current reference points to a prompt snippet file. */ - public get isPromptSnippet(): boolean { + public get isPromptFile(): boolean { return isPromptFile(this.uri); } @@ -545,28 +537,33 @@ 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`) - * or a markdown link(`[#file:file.md](/path/to/file.md)`). + * Prompt reference object represents any reference inside prompt 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 { - public readonly type = 'file'; - +export class PromptReference extends ObservableDisposable implements IPromptReference { public readonly range = this.token.range; public readonly path: string = this.token.path; public readonly text: string = this.token.text; + /** + * Instance of underlying prompt parser object. + */ + private readonly parser: BasePromptParser; + constructor( + private readonly promptContentsProvider: IPromptContentsProvider, public readonly token: FileReference | MarkdownLink, - dirname: URI, seenReferences: string[] = [], @IInstantiationService initService: IInstantiationService, - @ILogService logService: ILogService, ) { - const fileUri = extUri.resolvePath(dirname, token.path); - const provider = initService.createInstance(FilePromptContentProvider, fileUri); + super(); - super(provider, seenReferences, initService, logService); + this.parser = this._register(initService.createInstance( + BasePromptParser, + this.promptContentsProvider, + seenReferences, + )); } /** @@ -575,7 +572,7 @@ export class PromptFileReference extends BasePromptParser void): this { + this.parser.onUpdate(callback); + + return this; + } + + public get resolveFailed(): boolean | undefined { + return this.parser.resolveFailed; + } + + public get errorCondition(): ResolveError | undefined { + return this.parser.errorCondition; + } + + public get topError(): ITopError | undefined { + return this.parser.topError; + } + + public get uri(): URI { + return this.parser.uri; + } + + public get isPromptFile(): boolean { + return this.parser.isPromptFile; + } + + public get errors(): readonly ResolveError[] { + return this.parser.errors; + } + + public get allErrors(): readonly IResolveError[] { + return this.parser.allErrors; + } + + public get references(): readonly IPromptReference[] { + return this.parser.references; + } + + public get allReferences(): readonly IPromptReference[] { + return this.parser.allReferences; + } + + public get allValidReferences(): readonly IPromptReference[] { + return this.parser.allValidReferences; + } + + public async settled(): Promise { + await this.parser.settled(); + + return this; + } + + public async allSettled(): Promise { + await this.parser.allSettled(); + + return this; + } + + /** + * Returns a string representation of this object. + */ + public override toString() { + return `prompt-reference/${this.type}:${this.subtype}/${this.token}`; } } /** - * A tiny utility object that helps us to track existance + * A tiny utility object that helps us to track existence * of at least one parse result from the content provider. */ class FirstParseResult extends DeferredPromise { diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts index f7fc6a57..33a2edd9 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts @@ -22,6 +22,8 @@ export class FilePromptParser extends BasePromptParser this.dispose()); + const contentsProvider = initService.createInstance(TextModelContentsProvider, model); super(contentsProvider, seenReferences, initService, logService); + + this._register(contentsProvider); } /** 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 00000000..97584c8a --- /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 325e5c3e..096ada44 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. @@ -45,7 +70,7 @@ export interface IPromptReference extends IDisposable { /** * The full range of the prompt reference in the source text, - * including the {@linkcode linkRange} and any additional + * including the {@link linkRange} and any additional * parts the reference may contain (e.g., the `#file:` prefix). */ readonly range: Range; @@ -68,14 +93,14 @@ export interface IPromptReference extends IDisposable { /** * Whether the current reference points to a prompt snippet file. */ - readonly isPromptSnippet: boolean; + readonly isPromptFile: boolean; /** * Flag that indicates if resolving this reference failed. * The `undefined` means that no attempt to resolve the reference * was made so far or such an attempt is still in progress. * - * See also {@linkcode errorCondition}. + * See also {@link errorCondition}. */ readonly resolveFailed: boolean | undefined; @@ -83,21 +108,26 @@ export interface IPromptReference extends IDisposable { * If failed to resolve the reference this property contains * an error object that describes the failure reason. * - * See also {@linkcode resolveFailed}. + * See also {@link 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. @@ -131,16 +161,27 @@ export interface IPromptReference extends IDisposable { * and contents for all possible nested child references are * completely parsed and entire tree of references is built. * - * The same as {@linkcode settled} but for all prompts in + * The same as {@link settled} but for all prompts in * the reference tree. */ allSettled(): Promise; } /** - * The special case of the {@linkcode IPromptReference} that pertains + * The special case of the {@link 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 dc1ac08c..97297a4d 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -85,9 +85,9 @@ export class PromptsService extends Disposable implements IPromptsService { const userLocations = [this.userDataService.currentProfile.promptsHome]; const prompts = await Promise.all([ - this.fileLocator.listFilesIn(userLocations, []) + this.fileLocator.listFilesIn(userLocations) .then(withType('user')), - this.fileLocator.listFiles([]) + this.fileLocator.listFiles() .then(withType('local')), ]); @@ -97,8 +97,8 @@ export class PromptsService extends Disposable implements IPromptsService { public getSourceFolders( type: IPromptPath['type'], ): readonly IPromptPath[] { - // sanity check to make sure we don't miss a new prompt type - // added in the future + // sanity check to make sure we don't miss a new + // prompt type that could be added in the future assert( type === 'local' || type === 'user', `Unknown prompt type '${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 2d787402..975d45f5 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts @@ -4,174 +4,363 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; +import { match } from '../../../../../../base/common/glob.js'; +import { assert } from '../../../../../../base/common/assert.js'; +import { isAbsolute } from '../../../../../../base/common/path.js'; import { ResourceSet } from '../../../../../../base/common/map.js'; -import { dirname, extUri } from '../../../../../../base/common/resources.js'; import { IFileService } from '../../../../../../platform/files/common/files.js'; import { PromptsConfig } from '../../../../../../platform/prompts/common/config.js'; -import { isPromptFile } from '../../../../../../platform/prompts/common/constants.js'; +import { basename, dirname, extUri } from '../../../../../../base/common/resources.js'; import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { isPromptFile, PROMPT_FILE_EXTENSION } from '../../../../../../platform/prompts/common/constants.js'; /** - * Class to locate prompt files. + * Utility class to locate prompt files. */ export class PromptFilesLocator { constructor( @IFileService private readonly fileService: IFileService, - @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configService: IConfigurationService, + @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, ) { } /** * List all prompt files from the filesystem. * - * @param exclude List of `URIs` to exclude from the result. * @returns List of prompt files found in the workspace. */ - public async listFiles( - exclude: readonly URI[], - ): Promise { - return await this.listFilesIn( - this.getConfigBasedSourceFolders(), - exclude, - ); + public async listFiles(): Promise { + const configuredLocations = PromptsConfig.promptSourceFolders(this.configService); + const absoluteLocations = toAbsoluteLocations(configuredLocations, this.workspaceService); + + return await this.listFilesIn(absoluteLocations); } /** * Lists all prompt files in the provided folders. * - * @param folders List of `URIs` to search for prompt files in. - * @param exclude List of `URIs` to exclude from the result. + * @throws if any of the provided folder paths is not an `absolute path`. + * + * @param absoluteLocations List of prompt file source folders to search for prompt files in. Must be absolute paths. * @returns List of prompt files found in the provided folders. */ public async listFilesIn( folders: readonly URI[], - exclude: readonly URI[], ): Promise { - // create a set from the list of URIs for convenience - const excludeSet: Set = new Set(); - for (const excludeUri of exclude) { - excludeSet.add(excludeUri.path); - } - - // filter out the excluded paths from the folders list - const cleanFolders = folders - .filter((folder) => { - return !excludeSet.has(folder.path); - }); - - return await this.findInstructionFiles(cleanFolders, excludeSet); + return await this.findInstructionFiles(folders); } /** - * Get all possible prompt file source folders based on the current - * workspace folder structure. + * Get all possible unambiguous prompt file source folders based on + * the current workspace folder structure. * - * @returns List of possible prompt file folders. + * This method is currently primarily used by the `> Create Prompt` + * command that providers users with the list of destination folders + * for a newly created prompt file. Because such a list cannot contain + * paths that include `glob pattern` in them, we need to process config + * values and try to create a list of clear and unambiguous locations. + * + * @returns List of possible unambiguous prompt file folders. */ public getConfigBasedSourceFolders(): readonly URI[] { - const paths = new ResourceSet(); - const sourceFolders = PromptsConfig.promptSourceFolders(this.configService); + const configuredLocations = PromptsConfig.promptSourceFolders(this.configService); + const absoluteLocations = toAbsoluteLocations(configuredLocations, this.workspaceService); + + // locations in the settings can contain glob patterns so we need + // to process them to get "clean" paths; the goal here is to have + // a list of unambiguous folder paths where prompt files are stored + const result = new ResourceSet(); + for (const absoluteLocation of absoluteLocations) { + let { path } = absoluteLocation; + const baseName = basename(absoluteLocation); + + // if a path ends with a well-known "any file" pattern, remove + // it so we can get the dirname path of that setting value + const filePatterns = ['*.md', `*${PROMPT_FILE_EXTENSION}`]; + for (const filePattern of filePatterns) { + if (baseName === filePattern) { + path = URI.joinPath(absoluteLocation, '..').path; - // otherwise for each folder provided in the configuration, create - // a URI per each folder in the current workspace - for (const sourceFolderName of sourceFolders) { - // if source folder is an absolute path, add the path as is - // without trying to resolve it against the workspace folders - const sourceFolderUri = URI.file(sourceFolderName); - if (sourceFolderUri.path === sourceFolderName) { - if (paths.has(sourceFolderUri)) { continue; } + } - paths.add(sourceFolderUri); + // likewise, if the pattern ends with single `*` (any file name) + // remove it to get the dirname path of the setting value + if (baseName === '*') { + path = URI.joinPath(absoluteLocation, '..').path; + } + + // if after replacing the "file name" glob pattern, the path + // still contains a glob pattern, then ignore the path + if (isValidGlob(path) === true) { continue; } - const { folders } = this.workspaceService.getWorkspace(); - for (const folder of folders) { - // create the source path as a path relative to the workspace - // folder, or as an absolute path if the absolute value is provided - const relativeFolderUri = extUri.resolvePath(folder.uri, sourceFolderName); - if (!paths.has(relativeFolderUri)) { - paths.add(relativeFolderUri); - } + result.add(URI.file(path)); + } - // if not inside a workspace, we are done - if (folders.length <= 1) { - continue; - } + return [...result]; + } - // if inside a multi-root workspace, consider the specified prompts source folder - // inside the workspace root, to allow users to use some (e.g., `.github/prompts`) - // folder as a top-level folder in the workspace - const workspaceRootUri = dirname(folder.uri); - const workspaceFolderUri = extUri.resolvePath(workspaceRootUri, sourceFolderName); - // if we already have this folder in the list, skip it - if (paths.has(workspaceFolderUri)) { - continue; - } + /** + * Finds all existent prompt files in the provided source folders. + * + * @throws if any of the provided folder paths is not an `absolute path`. + * + * @param absoluteLocations List of prompt file source folders to search for prompt files in. Must be absolute paths. + * @returns List of prompt files found in the provided source folders. + */ + private async findInstructionFiles( + absoluteLocations: readonly URI[], + ): Promise { + // find all prompt files in the provided locations, then match + // the found file paths against (possible) glob patterns + const paths = new ResourceSet(); + for (const absoluteLocation of absoluteLocations) { + assert( + isAbsolute(absoluteLocation.path), + `Provided location must be an absolute path, got '${absoluteLocation.path}'.`, + ); - // otherwise, if the prompt source folder is inside a top-level workspace folder, - // add it to the list of paths too; this helps to handle the case when a relative - // path must be resolved from `root` of the workspace - if (workspaceFolderUri.fsPath.startsWith(folder.uri.fsPath)) { - paths.add(workspaceFolderUri); + // normalize the glob pattern to always end with "any prompt file" pattern + // unless the last part of the path is already a glob pattern itself; this is + // to handle the case when a user specifies a file glob pattern at the end, e.g., + // "my-folder/*.md" or "my-folder/*" already include the prompt files + const location = (isValidGlob(basename(absoluteLocation)) || absoluteLocation.path.endsWith(PROMPT_FILE_EXTENSION)) + ? absoluteLocation + : extUri.joinPath(absoluteLocation, `*${PROMPT_FILE_EXTENSION}`); + + // find all prompt files in entire file tree, starting from + // a first parent folder that does not contain a glob pattern + const promptFiles = await findAllPromptFiles( + firstNonGlobParent(location), + this.fileService, + ); + + // filter out found prompt files to only include those that match + // the original glob pattern specified in the settings (if any) + for (const file of promptFiles) { + if (match(location.path, file.path)) { + paths.add(file); } } } return [...paths]; } +} - /** - * Finds all existent prompt files in the provided source folders. - * - * @param folders List of prompt file source folders to search for prompt files in. - * @param exclude Map of `path -> boolean` to exclude from the result. - * @returns List of prompt files found in the provided source folders. - */ - private async findInstructionFiles( - folders: readonly URI[], - exclude: ReadonlySet, - ): Promise { - const results = await this.fileService.resolveAll( - folders.map((resource) => { - return { resource }; - }), - ); +/** + * Checks if the provided `pattern` could be a valid glob pattern. + */ +export const isValidGlob = (pattern: string): boolean => { + let squareBrackets = false; + let squareBracketsCount = 0; - const files = []; - for (const result of results) { - const { stat, success } = result; + let curlyBrackets = false; + let curlyBracketsCount = 0; - if (!success) { - continue; - } - - if (!stat || !stat.children) { - continue; - } - - for (const child of stat.children) { - const { resource, isDirectory } = child; - - if (isDirectory) { - continue; - } - - if (!isPromptFile(resource)) { - continue; - } - - if (exclude.has(resource.path)) { - continue; - } - - files.push(resource); - } + let previousCharacter: string | undefined; + for (const char of pattern) { + // skip all escaped characters + if (previousCharacter === '\\') { + previousCharacter = char; + continue; } - return files; + if (char === '*') { + return true; + } + + if (char === '?') { + return true; + } + + if (char === '[') { + squareBrackets = true; + squareBracketsCount++; + + previousCharacter = char; + continue; + } + + if (char === ']') { + squareBrackets = true; + squareBracketsCount--; + previousCharacter = char; + continue; + } + + if (char === '{') { + curlyBrackets = true; + curlyBracketsCount++; + continue; + } + + if (char === '}') { + curlyBrackets = true; + curlyBracketsCount--; + previousCharacter = char; + continue; + } + + previousCharacter = char; } -} + + // if square brackets exist and are in pairs, this is a `valid glob` + if (squareBrackets && (squareBracketsCount === 0)) { + return true; + } + + // if curly brackets exist and are in pairs, this is a `valid glob` + if (curlyBrackets && (curlyBracketsCount === 0)) { + return true; + } + + return false; +}; + +/** + * Finds the first parent of the provided location that does not contain a `glob pattern`. + * + * @throws if the provided location is not an `absolute path`. + * + * ## Examples + * + * ```typescript + * assert.strictEqual( + * firstNonGlobParent(URI.file('/home/user/{folder1,folder2}/file.md')).path, + * URI.file('/home/user').path, + * 'Must find correct non-glob parent dirname.', + * ); + * ``` + */ +export const firstNonGlobParent = ( + location: URI, +): URI => { + // sanity check of the provided location + assert( + isAbsolute(location.path), + `Provided location must be an absolute path, got '${location.path}'.`, + ); + + // note! if though the folder name can be `invalid glob` here, it is still OK to + // use it as we don't really known if that is a glob pattern, or the folder + // name contains characters that can also be used in a glob pattern + if (isValidGlob(location.path) === false) { + return location; + } + + // if location is the root of the filesystem, we are done + const parent = dirname(location); + if (extUri.isEqual(parent, location)) { + return location; + } + + // otherwise, try again starting with the parent folder + return firstNonGlobParent(parent); +}; + +/** + * Finds all `prompt files` in the provided location and all of its subfolders. + */ +const findAllPromptFiles = async ( + location: URI, + fileService: IFileService, +): Promise => { + const result: URI[] = []; + + try { + const info = await fileService.resolve(location); + + if (info.isFile && isPromptFile(info.resource)) { + result.push(info.resource); + + return result; + } + + if (info.isDirectory && info.children) { + for (const child of info.children) { + if (child.isFile && isPromptFile(child.resource)) { + result.push(child.resource); + + continue; + } + + if (child.isDirectory) { + const promptFiles = await findAllPromptFiles(child.resource, fileService); + result.push(...promptFiles); + + continue; + } + } + + return result; + } + } catch (error) { + // noop + } + + return result; +}; + +/** + * Converts locations defined in `settings` to absolute filesystem path URIs. + * This conversion is needed because locations in settings can be relative, + * hence we need to resolve them based on the current workspace folders. + */ +const toAbsoluteLocations = ( + configuredLocations: readonly string[], + workspaceService: IWorkspaceContextService, +): readonly URI[] => { + const result = new ResourceSet(); + const { folders } = workspaceService.getWorkspace(); + + for (const configuredLocation of configuredLocations) { + if (isAbsolute(configuredLocation)) { + result.add(URI.file(configuredLocation)); + + continue; + } + + for (const workspaceFolder of folders) { + const absolutePath = extUri.resolvePath(workspaceFolder.uri, configuredLocation); + + // a sanity check on the expected outcome of the `resolvePath()` call + assert( + isAbsolute(absolutePath.path), + `Provided location must be an absolute path, got '${absolutePath.path}'.`, + ); + + if (result.has(absolutePath) === false) { + result.add(absolutePath); + } + + // if not inside a multi-root workspace, we are done + if (folders.length <= 1) { + continue; + } + + // if inside a multi-root workspace, consider the specified prompts source folder + // inside the workspace root, to allow users to use some (e.g., `.github/prompts`) + // folder as a top-level folder in the workspace + const workspaceRootUri = dirname(workspaceFolder.uri); + const workspaceFolderUri = extUri.resolvePath(workspaceRootUri, configuredLocation); + // if we already have this folder in the list, skip it + if (result.has(workspaceFolderUri) === true) { + continue; + } + + // otherwise, if the prompt source folder is inside a top-level workspace folder, + // add it to the list of paths too; this helps to handle the case when a relative + // path must be resolved from `root` of the workspace + if (workspaceFolderUri.fsPath.startsWith(workspaceFolder.uri.fsPath)) { + result.add(workspaceFolderUri); + } + } + } + + return [...result]; +}; diff --git a/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts b/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts index 27b3a2b8..c5fa1964 100644 --- a/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts +++ b/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts @@ -7,14 +7,17 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { IDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun } from '../../../../../base/common/observable.js'; +import { isEqual } from '../../../../../base/common/resources.js'; import { URI, UriComponents } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { localize } from '../../../../../nls.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; import { SaveReason } from '../../../../common/editor.js'; +import { GroupsOrder, IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.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'; import { IChatService } from '../../common/chatService.js'; import { ILanguageModelIgnoredFilesService } from '../../common/ignoredFiles.js'; @@ -47,6 +50,7 @@ export const EditToolData: IToolData = { id: InternalEditToolId, displayName: localize('chat.tools.editFile', "Edit File"), modelDescription: `Edit a file in the workspace. Use this tool once per file that needs to be modified, even if there are multiple changes for a file. Generate the "explanation" property first. ${codeInstructions}`, + source: { type: 'internal' }, inputSchema: { type: 'object', properties: { @@ -71,11 +75,12 @@ export class EditTool implements IToolImpl { constructor( @IChatService private readonly chatService: IChatService, - @IChatEditingService private readonly chatEditingService: IChatEditingService, @ICodeMapperService private readonly codeMapperService: ICodeMapperService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @ILanguageModelIgnoredFilesService private readonly ignoredFilesService: ILanguageModelIgnoredFilesService, @ITextFileService private readonly textFileService: ITextFileService, + @INotebookService private readonly notebookService: INotebookService, + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, ) { } async invoke(invocation: IToolInvocation, countTokens: CountTokensCallback, token: CancellationToken): Promise { @@ -84,9 +89,20 @@ export class EditTool implements IToolImpl { } const parameters = invocation.parameters as EditToolParams; - const uri = URI.revive(parameters.file); // TODO@roblourens do revive in MainThreadLanguageModelTools - if (!this.workspaceContextService.isInsideWorkspace(uri)) { - throw new Error(`File ${uri.fsPath} can't be edited because it's not inside the current workspace`); + const fileUri = URI.revive(parameters.file); // TODO@roblourens do revive in MainThreadLanguageModelTools + const uri = CellUri.parse(fileUri)?.notebook || fileUri; + + if (!this.workspaceContextService.isInsideWorkspace(uri) && !this.notebookService.getNotebookTextModel(uri)) { + const groupsByLastActive = this.editorGroupsService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE); + const uriIsOpenInSomeEditor = groupsByLastActive.some((group) => { + return group.editors.some((editor) => { + return isEqual(editor.resource, uri); + }); + }); + + if (!uriIsOpenInSomeEditor) { + throw new Error(`File ${uri.fsPath} can't be edited because it's not inside the current workspace`); + } } if (await this.ignoredFilesService.fileIsIgnored(uri, token)) { @@ -112,19 +128,29 @@ export class EditTool implements IToolImpl { }); model.acceptResponseProgress(request, { kind: 'codeblockUri', - uri + uri, + isEdit: true }); model.acceptResponseProgress(request, { kind: 'markdownContent', content: new MarkdownString(parameters.code + '\n````\n') }); - model.acceptResponseProgress(request, { - kind: 'textEdit', - edits: [], - uri - }); + // Signal start. + if (this.notebookService.hasSupportedNotebooks(uri) && (this.notebookService.getNotebookTextModel(uri))) { + model.acceptResponseProgress(request, { + kind: 'notebookEdit', + edits: [], + uri + }); + } else { + model.acceptResponseProgress(request, { + kind: 'textEdit', + edits: [], + uri + }); + } - const editSession = this.chatEditingService.getEditingSession(model.sessionId); + const editSession = model.editingSession; if (!editSession) { throw new Error('This tool must be called from within an editing session'); } @@ -142,7 +168,12 @@ export class EditTool implements IToolImpl { }, }, token); - model.acceptResponseProgress(request, { kind: 'textEdit', uri, edits: [], done: true }); + // Signal end. + if (this.notebookService.hasSupportedNotebooks(uri) && (this.notebookService.getNotebookTextModel(uri))) { + model.acceptResponseProgress(request, { kind: 'notebookEdit', uri, 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/insertNotebookCellsTool.ts b/src/vs/workbench/contrib/chat/common/tools/insertNotebookCellsTool.ts new file mode 100644 index 00000000..f623e892 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/tools/insertNotebookCellsTool.ts @@ -0,0 +1,219 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { IDisposable } from '../../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../../base/common/observable.js'; +import { URI, UriComponents } from '../../../../../base/common/uri.js'; +import { generateUuid } from '../../../../../base/common/uuid.js'; +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 '../chatCodeMapperService.js'; +import { IChatEditingService } from '../chatEditingService.js'; +import { ChatModel } from '../chatModel.js'; +import { IChatService } from '../chatService.js'; +import { ILanguageModelIgnoredFilesService } from '../ignoredFiles.js'; +import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolResult } from '../languageModelToolsService.js'; +import { IToolInputProcessor } from './tools.js'; + +const codeInstructions = ` +The user is very smart and can understand how to insert cells to their new Notebook files +`; + +export const ExtensionEditToolId = 'vscode_insert_notebook_cells'; +export const InternalEditToolId = 'vscode_insert_notebook_cells_internal'; +export const EditToolData: IToolData = { + id: InternalEditToolId, + displayName: localize('chat.tools.editFile', "Edit File"), + modelDescription: `Insert cells into a new notebook n the workspace. Use this tool once per file that needs to be modified, even if there are multiple changes for a file. Generate the "explanation" property first. ${codeInstructions}`, + source: { type: 'internal' }, + inputSchema: { + type: 'object', + properties: { + explanation: { + type: 'string', + description: 'A short explanation of the edit being made. Can be the same as the explanation you showed to the user.', + }, + filePath: { + type: 'string', + description: 'An absolute path to the file to edit, or the URI of a untitled, not yet named, file, such as `untitled:Untitled-1.', + }, + cells: { + type: 'array', + description: 'The cells to insert to apply to the file. ' + codeInstructions + } + }, + required: ['explanation', 'filePath', 'code'] + } +}; + +export class EditTool implements IToolImpl { + + constructor( + @IChatService private readonly chatService: IChatService, + @IChatEditingService private readonly chatEditingService: IChatEditingService, + @ICodeMapperService private readonly codeMapperService: ICodeMapperService, + @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 { + if (!invocation.context) { + throw new Error('toolInvocationToken is required for this tool'); + } + + const parameters = invocation.parameters as EditToolParams; + const uri = URI.revive(parameters.file); // TODO@roblourens do revive in MainThreadLanguageModelTools + if (!this.workspaceContextService.isInsideWorkspace(uri)) { + throw new Error(`File ${uri.fsPath} can't be edited because it's not inside the current workspace`); + } + + if (await this.ignoredFilesService.fileIsIgnored(uri, token)) { + throw new Error(`File ${uri.fsPath} can't be edited because it is configured to be ignored by Copilot`); + } + + const model = this.chatService.getSession(invocation.context?.sessionId) as ChatModel; + const request = model.getRequests().at(-1)!; + + // Undo stops mark groups of response data in the output. Operations, such + // as text edits, that happen between undo stops are all done or undone together. + if (request.response?.response.getMarkdown().length) { + // slightly hacky way to avoid an extra 'no-op' undo stop at the start of responses that are just edits + model.acceptResponseProgress(request, { + kind: 'undoStop', + id: generateUuid(), + }); + } + + model.acceptResponseProgress(request, { + kind: 'markdownContent', + content: new MarkdownString('\n````\n') + }); + model.acceptResponseProgress(request, { + kind: 'codeblockUri', + uri + }); + model.acceptResponseProgress(request, { + kind: 'markdownContent', + content: new MarkdownString(parameters.code + '\n````\n') + }); + 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) { + throw new Error('This tool must be called from within an editing session'); + } + + const result = await this.codeMapperService.mapCode({ + codeBlocks: [{ code: parameters.code, resource: uri, markdownBeforeBlock: parameters.explanation }], + location: 'tool', + chatRequestId: invocation.chatRequestId + }, { + textEdit: (target, edits) => { + model.acceptResponseProgress(request, { kind: 'textEdit', uri: target, edits }); + }, + notebookEdit(target, edits) { + model.acceptResponseProgress(request, { kind: 'notebookEdit', uri: target, edits }); + }, + }, token); + + // 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); + } + + let dispose: IDisposable; + await new Promise((resolve) => { + // The file will not be modified until the first edits start streaming in, + // so wait until we see that it _was_ modified before waiting for it to be done. + let wasFileBeingModified = false; + + dispose = autorun((r) => { + + const entries = editSession.entries.read(r); + const currentFile = entries?.find((e) => e.modifiedURI.toString() === uri.toString()); + if (currentFile) { + if (currentFile.isCurrentlyBeingModifiedBy.read(r)) { + wasFileBeingModified = true; + } else if (wasFileBeingModified) { + resolve(true); + } + } + }); + }).finally(() => { + dispose.dispose(); + }); + + await this.textFileService.save(uri, { + reason: SaveReason.AUTO, + skipSaveParticipants: true, + }); + + return { + content: [{ kind: 'text', value: 'The file was edited successfully' }] + }; + } + + async prepareToolInvocation(parameters: any, token: CancellationToken): Promise { + return { + presentation: 'hidden' + }; + } +} + +export interface EditToolParams { + file: UriComponents; + explanation: string; + code: string; +} + +export interface EditToolRawParams { + filePath: string; + explanation: string; + code: string; +} + +export class EditToolInputProcessor implements IToolInputProcessor { + processInput(input: EditToolRawParams): EditToolParams { + if (!input.filePath) { + // Tool name collision, or input wasn't properly validated upstream + return input as any; + } + const filePath = input.filePath; + // Runs in EH, will be mapped + return { + file: filePath.startsWith('untitled:') ? URI.parse(filePath) : URI.file(filePath), + explanation: input.explanation, + code: input.code, + }; + } +} diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index e75fb616..467b4250 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -13,6 +13,7 @@ import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contex import { ExtensionIdentifier, IExtensionManifest } from '../../../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; import { Registry } from '../../../../../platform/registry/common/platform.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { Extensions, IExtensionFeaturesRegistry, IExtensionFeatureTableRenderer, IRenderedData, IRowData, ITableData } from '../../../../services/extensionManagement/common/extensionFeatures.js'; @@ -135,6 +136,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'; @@ -143,6 +146,7 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri constructor( @ILanguageModelToolsService languageModelToolsService: ILanguageModelToolsService, @ILogService logService: ILogService, + @IProductService productService: IProductService ) { languageModelToolsExtensionPoint.setHandler((extensions, delta) => { for (const extension of delta.added) { @@ -167,7 +171,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; } @@ -186,13 +197,22 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri }; } + // If OSS and the product.json is not set up, fall back to checking api proposal + const isBuiltinTool = productService.defaultChatAgent?.chatExtensionId ? + ExtensionIdentifier.equals(extension.description.identifier, productService.defaultChatAgent.chatExtensionId) : + isProposedApiEnabled(extension.description, 'chatParticipantPrivate'); const tool: IToolData = { ...rawTool, - extensionId: extension.description.identifier, + source: { type: 'extension', extensionId: extension.description.identifier, isExternalTool: !isBuiltinTool }, inputSchema: rawTool.inputSchema, id: rawTool.name, icon, when: rawTool.when ? ContextKeyExpr.deserialize(rawTool.when) : undefined, + requiresConfirmation: !isBuiltinTool, + alwaysDisplayInputOutput: !isBuiltinTool, + supportsToolPicker: isBuiltinTool ? + false : + rawTool.canBeReferencedInPrompt }; const disposable = languageModelToolsService.registerToolData(tool); this._registrationDisposables.set(toToolKey(extension.description.identifier, rawTool.name), disposable); diff --git a/src/vs/workbench/contrib/chat/common/tools/promptTsxTypes.ts b/src/vs/workbench/contrib/chat/common/tools/promptTsxTypes.ts new file mode 100644 index 00000000..20de7118 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/tools/promptTsxTypes.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * This is a subset of the types export from jsonTypes.d.ts in @vscode/prompt-tsx. + * It's just the types needed to stringify prompt-tsx tool results. + * It should be kept in sync with the types in that file. + */ + +export declare const enum PromptNodeType { + Piece = 1, + Text = 2 +} +export interface TextJSON { + type: PromptNodeType.Text; + text: string; + lineBreakBefore: boolean | undefined; +} +/** + * Constructor kind of the node represented by {@link PieceJSON}. This is + * less descriptive than the actual constructor, as we only care to preserve + * the element data that the renderer cares about. + */ +export declare const enum PieceCtorKind { + BaseChatMessage = 1, + Other = 2, + ImageChatMessage = 3 +} +export interface BasePieceJSON { + type: PromptNodeType.Piece; + ctor: PieceCtorKind.BaseChatMessage | PieceCtorKind.Other; + children: PromptNodeJSON[]; +} +export interface ImageChatMessagePieceJSON { + type: PromptNodeType.Piece; + ctor: PieceCtorKind.ImageChatMessage; + children: PromptNodeJSON[]; + props: { + src: string; + detail?: 'low' | 'high'; + }; +} +export type PieceJSON = BasePieceJSON | ImageChatMessagePieceJSON; +export type PromptNodeJSON = PieceJSON | TextJSON; +export interface PromptElementJSON { + node: PieceJSON; +} + +export function stringifyPromptElementJSON(element: PromptElementJSON): string { + const strs: string[] = []; + stringifyPromptNodeJSON(element.node, strs); + return strs.join(''); +} + +function stringifyPromptNodeJSON(node: PromptNodeJSON, strs: string[]): void { + if (node.type === PromptNodeType.Text) { + if (node.lineBreakBefore) { + strs.push('\n'); + } + + if (typeof node.text === 'string') { + strs.push(node.text); + } + } else if (node.ctor === PieceCtorKind.ImageChatMessage) { + // This case currently can't be hit by prompt-tsx + strs.push(''); + } else if (node.ctor === PieceCtorKind.BaseChatMessage || node.ctor === PieceCtorKind.Other) { + for (const child of node.children) { + stringifyPromptNodeJSON(child, strs); + } + } +} diff --git a/src/vs/workbench/contrib/chat/common/tools/tools.ts b/src/vs/workbench/contrib/chat/common/tools/tools.ts index cfdcf85f..eac67b13 100644 --- a/src/vs/workbench/contrib/chat/common/tools/tools.ts +++ b/src/vs/workbench/contrib/chat/common/tools/tools.ts @@ -28,3 +28,5 @@ export class BuiltinToolsContribution extends Disposable implements IWorkbenchCo export interface IToolInputProcessor { processInput(input: any): any; } + +export const InternalFetchWebPageToolId = 'vscode_fetchWebPage_internal'; diff --git a/src/vs/workbench/contrib/chat/common/voiceChatService.ts b/src/vs/workbench/contrib/chat/common/voiceChatService.ts index 71ca69ae..bcf91b3a 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/chatDeveloperActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatDeveloperActions.ts new file mode 100644 index 00000000..55db9683 --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatDeveloperActions.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 { Codicon } from '../../../../../base/common/codicons.js'; +import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; +import { localize2 } from '../../../../../nls.js'; +import { Categories } from '../../../../../platform/action/common/actionCommonCategories.js'; +import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { INativeHostService } from '../../../../../platform/native/common/native.js'; +import { IChatService } from '../../common/chatService.js'; + +export function registerChatDeveloperActions() { + registerAction2(OpenChatStorageFolderAction); +} + +class OpenChatStorageFolderAction extends Action2 { + static readonly ID = 'workbench.action.chat.openStorageFolder'; + + constructor() { + super({ + id: OpenChatStorageFolderAction.ID, + title: localize2('workbench.action.chat.openStorageFolder.label', "Open Chat Storage Folder"), + icon: Codicon.attach, + category: Categories.Developer, + f1: true + }); + } + + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + const chatService = accessor.get(IChatService); + const nativeHostService = accessor.get(INativeHostService); + const storagePath = chatService.getChatStorageFolder(); + nativeHostService.showItemInFolder(storagePath.fsPath); + } +} 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 9a33d55a..c47f9b9a 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,12 @@ 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'; +import { SearchContext } from '../../../search/common/constants.js'; +import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; +import Severity from '../../../../../base/common/severity.js'; +import { isCancellationError } from '../../../../../base/common/errors.js'; +import { toErrorMessage } from '../../../../../base/common/errorMessage.js'; //#region Speech to Text @@ -457,7 +463,8 @@ export class HoldToVoiceChatInChatViewAction extends Action2 { ChatContextKeys.requestInProgress.negate(), // disable when a chat request is in progress FocusInChatInput?.negate(), // when already in chat input, disable this action and prefer to start voice chat directly EditorContextKeys.focus.negate(), // do not steal the inline-chat keybinding - NOTEBOOK_EDITOR_FOCUSED.negate() // do not steal the notebook keybinding + NOTEBOOK_EDITOR_FOCUSED.negate(), // do not steal the notebook keybinding + SearchContext.SearchViewFocusedKey.negate() // do not steal the search keybinding ), primary: KeyMod.CtrlCmd | KeyCode.KeyI } @@ -1259,14 +1266,31 @@ abstract class BaseInstallSpeechProviderAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const contextKeyService = accessor.get(IContextKeyService); const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); + const dialogService = accessor.get(IDialogService); try { InstallingSpeechProvider.bindTo(contextKeyService).set(true); + await this.installExtension(extensionsWorkbenchService, dialogService); + } finally { + InstallingSpeechProvider.bindTo(contextKeyService).reset(); + } + } + + private async installExtension(extensionsWorkbenchService: IExtensionsWorkbenchService, dialogService: IDialogService): Promise { + try { await extensionsWorkbenchService.install(BaseInstallSpeechProviderAction.SPEECH_EXTENSION_ID, { justification: this.getJustification(), enable: true }, ProgressLocation.Notification); - } finally { - InstallingSpeechProvider.bindTo(contextKeyService).reset(); + } catch (error) { + const { confirmed } = await dialogService.confirm({ + type: Severity.Error, + message: localize('unknownSetupError', "An error occurred while setting up voice chat. Would you like to try again?"), + detail: error && !isCancellationError(error) ? toErrorMessage(error) : undefined, + primaryButton: localize('retry', "Retry") + }); + if (confirmed) { + return this.installExtension(extensionsWorkbenchService, dialogService); + } } } 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 0e888b7f..ef5956a2 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,28 @@ 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'; +import { registerChatDeveloperActions } from './actions/chatDeveloperActions.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); @@ -22,4 +43,7 @@ registerAction2(ReadChatResponseAloud); registerAction2(StopReadChatItemAloud); registerAction2(StopReadAloud); +registerChatDeveloperActions(); + 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 00000000..c8087d15 --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/tools/fetchPageTool.ts @@ -0,0 +1,185 @@ +/*--------------------------------------------------------------------------------------------- + * 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, IToolResultTextPart } from '../../common/languageModelToolsService.js'; +import { MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { InternalFetchWebPageToolId } from '../../common/tools/tools.js'; + +export const FetchWebPageToolData: IToolData = { + id: InternalFetchWebPageToolId, + displayName: 'Fetch Web Page', + canBeReferencedInPrompt: false, + 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.'), + source: { type: 'internal' }, + 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 parsedUriResults = this._parseUris((invocation.parameters as { urls?: string[] }).urls); + const validUris = Array.from(parsedUriResults.values()).filter((uri): uri is URI => !!uri); + if (!validUris.length) { + return { + content: [{ kind: 'text', value: localize('fetchWebPage.noValidUrls', 'No valid URLs provided.') }] + }; + } + + // We approved these via confirmation, so mark them as "approved" in this session + // if they are not approved via the trusted domain service. + for (const uri of validUris) { + if (!this._trustedDomainService.isValid(uri)) { + this._alreadyApprovedDomains.add(uri.toString(true)); + } + } + + const contents = await this._readerModeService.extract(validUris); + // Make an array that contains either the content or undefined for invalid URLs + const contentsWithUndefined: (string | undefined)[] = []; + let indexInContents = 0; + parsedUriResults.forEach((uri) => { + if (uri) { + contentsWithUndefined.push(contents[indexInContents]); + indexInContents++; + } else { + contentsWithUndefined.push(undefined); + } + }); + + return { content: this._getPromptPartsForResults(contentsWithUndefined) }; + } + + async prepareToolInvocation(parameters: any, token: CancellationToken): Promise { + const map = this._parseUris(parameters.urls); + const invalid = new Array(); + const valid = new Array(); + map.forEach((uri, url) => { + if (!uri) { + invalid.push(url); + } else { + valid.push(uri); + } + }); + const urlsNeedingConfirmation = valid.filter(url => !this._trustedDomainService.isValid(url) && !this._alreadyApprovedDomains.has(url.toString(true))); + + const pastTenseMessage = invalid.length + ? invalid.length > 1 + // If there are multiple invalid URLs, show them all + ? 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') + )) + // If there is only one invalid URL, show it + : new MarkdownString( + localize( + 'fetchWebPage.pastTenseMessage.singular', + 'Fetched web page, but the following was an invalid URL:\n\n{0}\n\n', invalid[0] + )) + // No invalid URLs + : new MarkdownString(); + + const invocationMessage = new MarkdownString(); + if (valid.length > 1) { + pastTenseMessage.appendMarkdown(localize('fetchWebPage.pastTenseMessageResult.plural', 'Fetched {0} web pages', valid.length)); + invocationMessage.appendMarkdown(localize('fetchWebPage.invocationMessage.plural', 'Fetching {0} web pages', valid.length)); + } else { + const url = valid[0].toString(); + // If the URL is too long, show it as a link... otherwise, show it as plain text + if (url.length > 400) { + pastTenseMessage.appendMarkdown(localize({ + key: 'fetchWebPage.pastTenseMessageResult.singularAsLink', + comment: [ + // Make sure the link syntax is correct + '{Locked="]({0})"}', + ] + }, 'Fetched [web page]({0})', url)); + invocationMessage.appendMarkdown(localize({ + key: 'fetchWebPage.invocationMessage.singularAsLink', + comment: [ + // Make sure the link syntax is correct + '{Locked="]({0})"}', + ] + }, 'Fetching [web page]({0})', url)); + } else { + pastTenseMessage.appendMarkdown(localize('fetchWebPage.pastTenseMessageResult.singular', 'Fetched {0}', url)); + invocationMessage.appendMarkdown(localize('fetchWebPage.invocationMessage.singular', 'Fetching {0}', url)); + } + } + + const result: IPreparedToolInvocation = { invocationMessage, 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, allowAutoConfirm: false }; + } + + return result; + } + + private _parseUris(urls?: string[]): Map { + const results = new Map(); + urls?.forEach(uri => { + try { + const uriObj = URI.parse(uri); + results.set(uri, uriObj); + } catch (e) { + results.set(uri, undefined); + } + }); + return results; + } + + private _getPromptPartsForResults(results: (string | undefined)[]): IToolResultTextPart[] { + return results.map(value => ({ + kind: 'text', + value: value || localize('fetchWebPage.invalidUrl', 'Invalid URL') + })); + } +} 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 00000000..42270fb0 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts @@ -0,0 +1,2075 @@ +/*--------------------------------------------------------------------------------------------- + * 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, 3, 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 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('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, 6, 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 the first cell, then insert a new cell at the top', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('1'), + }, + ]; + + const cell1 = createICell(CellKind.Code, 'print("Hello World")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([0, 0, [cell1]], + cellsDiffInfo, 2, 2, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 1, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell1.getValue(), + }], 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: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 2, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + 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'), + }, + ]); + }); + + test('Insert 1 cell at the bottom via chat, then user creats a new cell just below that', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + ]; + const cell1 = createICell(CellKind.Code, 'print("Hello World")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([2, 0, [cell1]], + cellsDiffInfo, 3, 1, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 1, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell1.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: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:1'), originalCellIndex: 1, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('InsertedModified:2'), + }, + ]); + }); + test('Insert 1 cell at the bottom via chat, then user creats anew cells above the previous new cell', 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: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('New1'), + }, + ]; + const cell1 = createICell(CellKind.Code, 'print("Hello World")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([2, 0, [cell1]], + cellsDiffInfo, 3, 2, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 2, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell1.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: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:2'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('InsertedModified:2'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('New1'), + }, + ]); + }); + test('Insert 1 cell at the bottom via chat, then user inserts a new cells below the previous new cell', 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: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('New1'), + }, + ]; + const cell1 = createICell(CellKind.Code, 'print("Hello World")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([3, 0, [cell1]], + cellsDiffInfo, 3, 2, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 2, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell1.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: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('InsertedModified:3'), + }, + ]); + }); + }); + + 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 220f55e8..f7612029 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts @@ -17,7 +17,7 @@ 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'; @@ -31,6 +31,8 @@ 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'; +import { NotebookTextModel } from '../../../notebook/common/model/notebookTextModel.js'; function getAgentData(id: string) { return { @@ -68,7 +70,10 @@ suite('ChatEditingService', function () { } }); collection.set(INotebookService, new class extends mock() { - override hasSupportedNotebooks(resource: URI): boolean { + override getNotebookTextModel(_uri: URI): NotebookTextModel | undefined { + return undefined; + } + override hasSupportedNotebooks(_resource: URI): boolean { return false; } }); @@ -110,14 +115,14 @@ suite('ChatEditingService', function () { assert.ok(editingService); const model = chatService.startSession(ChatAgentLocation.EditingSession, CancellationToken.None); - const session = await editingService.createEditingSession(model.sessionId, true); + const session = await editingService.createEditingSession(model, true); assert.strictEqual(session.chatSessionId, model.sessionId); assert.strictEqual(session.isGlobalEditingSession, true); await assertThrowsAsync(async () => { // DUPE not allowed - await editingService.createEditingSession(model.sessionId); + await editingService.createEditingSession(model); }); session.dispose(); @@ -131,7 +136,10 @@ suite('ChatEditingService', function () { 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 session = await model.editingSessionObs?.promise; + if (!session) { + assert.fail('session not created'); + } const chatRequest = model?.addRequest({ text: '', parts: [] }, { variables: [] }, 0); assertType(chatRequest.response); @@ -154,7 +162,6 @@ suite('ChatEditingService', function () { await entry.reject(undefined); - session.dispose(); model.dispose(); }); diff --git a/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts b/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts index fb866bbf..855c89ae 100644 --- a/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts @@ -39,7 +39,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; const disposable = service.registerToolData(toolData); @@ -52,7 +53,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; store.add(service.registerToolData(toolData)); @@ -71,20 +73,23 @@ suite('LanguageModelToolsService', () => { id: 'testTool1', modelDescription: 'Test Tool 1', when: ContextKeyEqualsExpr.create('testKey', false), - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; const toolData2: IToolData = { id: 'testTool2', modelDescription: 'Test Tool 2', when: ContextKeyEqualsExpr.create('testKey', true), - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; const toolData3: IToolData = { id: 'testTool3', modelDescription: 'Test Tool 3', - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; store.add(service.registerToolData(toolData1)); @@ -101,7 +106,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; store.add(service.registerToolData(toolData)); @@ -135,7 +141,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - displayName: 'Test Tool' + displayName: 'Test Tool', + source: { type: 'internal' }, }; store.add(service.registerToolData(toolData)); diff --git a/src/vs/workbench/contrib/chat/test/browser/mockChatWidget.ts b/src/vs/workbench/contrib/chat/test/browser/mockChatWidget.ts index cf64e837..97e619df 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; @@ -29,4 +29,8 @@ export class MockChatWidgetService implements IChatWidgetService { getWidgetsByLocations(location: ChatAgentLocation): ReadonlyArray { return []; } + + getAllWidgets(): ReadonlyArray { + throw new Error('Method not implemented.'); + } } 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 00000000..c746469f --- /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 580fa44c..cec80311 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 b8e38fcb..768f7892 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,14 @@ 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'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; suite('ChatModel', () => { const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); @@ -34,6 +37,7 @@ suite('ChatModel', () => { instantiationService.stub(IExtensionService, new TestExtensionService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IChatAgentService, testDisposables.add(instantiationService.createInstance(ChatAgentService))); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); }); test('Waits for initialization', async () => { @@ -159,7 +163,7 @@ suite('ChatModel', () => { model1.initialize(undefined); const text = 'hello'; - const request1 = model1.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { variables: [] }, 0, undefined, undefined, undefined, undefined, undefined, undefined, true); + const request1 = model1.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { variables: [] }, 0, undefined, undefined, undefined, undefined, undefined, true); assert.strictEqual(request1.isCompleteAddedRequest, true); assert.strictEqual(request1.response!.isCompleteAddedRequest, true); 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 7a271463..bd047108 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: '' }])]); @@ -187,8 +198,8 @@ suite('ChatRequestParser', () => { agentsService.getAgentsByName.returns([getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])]); instantiationService.stub(IChatAgentService, agentsService as any); - toolsService.getToolByName.onCall(0).returns({ id: 'get_selection', canBeReferencedInPrompt: true, displayName: '', modelDescription: '' } satisfies IToolData); - toolsService.getToolByName.onCall(1).returns({ id: 'get_debugConsole', canBeReferencedInPrompt: true, displayName: '', modelDescription: '' } satisfies IToolData); + toolsService.getToolByName.onCall(0).returns({ id: 'get_selection', canBeReferencedInPrompt: true, displayName: '', modelDescription: '', source: { type: 'internal' } } satisfies IToolData); + toolsService.getToolByName.onCall(1).returns({ id: 'get_debugConsole', canBeReferencedInPrompt: true, displayName: '', modelDescription: '', source: { type: 'internal' } } satisfies IToolData); parser = instantiationService.createInstance(ChatRequestParser); const result = parser.parseChatRequest('1', '@agent /subCommand \nPlease do with #selection\nand #debugConsole'); @@ -200,8 +211,8 @@ suite('ChatRequestParser', () => { agentsService.getAgentsByName.returns([getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])]); instantiationService.stub(IChatAgentService, agentsService as any); - toolsService.getToolByName.onCall(0).returns({ id: 'get_selection', canBeReferencedInPrompt: true, displayName: '', modelDescription: '' } satisfies IToolData); - toolsService.getToolByName.onCall(1).returns({ id: 'get_debugConsole', canBeReferencedInPrompt: true, displayName: '', modelDescription: '' } satisfies IToolData); + toolsService.getToolByName.onCall(0).returns({ id: 'get_selection', canBeReferencedInPrompt: true, displayName: '', modelDescription: '', source: { type: 'internal' } } satisfies IToolData); + toolsService.getToolByName.onCall(1).returns({ id: 'get_debugConsole', canBeReferencedInPrompt: true, displayName: '', modelDescription: '', source: { type: 'internal' } } satisfies IToolData); parser = instantiationService.createInstance(ChatRequestParser); const result = parser.parseChatRequest('1', '@agent Please \ndo /subCommand with #selection\nand #debugConsole'); 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 8334b23f..be6d91e9 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; +import { Event } from '../../../../../base/common/event.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { URI } from '../../../../../base/common/uri.js'; import { assertSnapshot } from '../../../../../base/test/common/snapshot.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; @@ -12,6 +14,7 @@ import { Range } from '../../../../../editor/common/core/range.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; @@ -20,20 +23,21 @@ 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 { IChatModel, ISerializableChatData } from '../../common/chatModel.js'; -import { IChatFollowup, IChatService } from '../../common/chatService.js'; -import { ChatService } from '../../common/chatServiceImpl.js'; -import { ChatSlashCommandService, IChatSlashCommandService } from '../../common/chatSlashCommands.js'; -import { IChatVariablesService } from '../../common/chatVariables.js'; -import { MockChatService } from './mockChatService.js'; -import { MockChatVariablesService } from './mockChatVariables.js'; import { IWorkbenchAssignmentService } from '../../../../services/assignment/common/assignmentService.js'; import { NullWorkbenchAssignmentService } from '../../../../services/assignment/test/common/nullAssignmentService.js'; import { IExtensionService, nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; 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 { 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'; +import { ChatSlashCommandService, IChatSlashCommandService } from '../../common/chatSlashCommands.js'; +import { IChatVariablesService } from '../../common/chatVariables.js'; +import { ChatAgentLocation } from '../../common/constants.js'; +import { MockChatService } from './mockChatService.js'; +import { MockChatVariablesService } from './mockChatVariables.js'; +import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js'; const chatAgentWithUsedContextId = 'ChatProviderWithUsedContext'; const chatAgentWithUsedContext: IChatAgent = { @@ -127,6 +131,8 @@ suite('ChatService', () => { instantiationService.stub(IChatSlashCommandService, testDisposables.add(instantiationService.createInstance(ChatSlashCommandService))); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IChatService, new MockChatService()); + instantiationService.stub(IEnvironmentService, { workspaceStorageHome: URI.file('/test/path/to/workspaceStorage') }); + instantiationService.stub(ILifecycleService, { onWillShutdown: Event.None }); chatAgentService = testDisposables.add(instantiationService.createInstance(ChatAgentService)); instantiationService.stub(IChatAgentService, chatAgentService); @@ -155,9 +161,9 @@ suite('ChatService', () => { storageService.flush(); const testService2 = testDisposables.add(instantiationService.createInstance(ChatService)); - const retrieved1 = testDisposables.add(testService2.getOrRestoreSession(session1.sessionId)!); + const retrieved1 = testDisposables.add((await testService2.getOrRestoreSession(session1.sessionId))!); await retrieved1.waitForInitialization(); - const retrieved2 = testDisposables.add(testService2.getOrRestoreSession(session2.sessionId)!); + const retrieved2 = testDisposables.add((await testService2.getOrRestoreSession(session2.sessionId))!); await retrieved2.waitForInitialization(); assert.deepStrictEqual(retrieved1.getRequests()[0]?.message.text, 'request 1'); assert.deepStrictEqual(retrieved2.getRequests()[0]?.message.text, 'request 2'); diff --git a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts index 4797a66c..57bc9108 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; @@ -37,7 +37,7 @@ export class MockChatService implements IChatService { // eslint-disable-next-line local/code-no-dangerous-type-assertions return this.sessions.get(sessionId) ?? {} as IChatModel; } - getOrRestoreSession(sessionId: string): IChatModel | undefined { + async getOrRestoreSession(sessionId: string): Promise { throw new Error('Method not implemented.'); } loadSessionFromContent(data: ISerializableChatData): IChatModel | undefined { @@ -61,19 +61,19 @@ export class MockChatService implements IChatService { cancelCurrentRequestForSession(sessionId: string): void { throw new Error('Method not implemented.'); } - clearSession(sessionId: string): void { + clearSession(sessionId: string): Promise { throw new Error('Method not implemented.'); } addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, attempt: number | undefined, response: IChatCompleteResponse): void { throw new Error('Method not implemented.'); } - getHistory(): IChatDetail[] { + async getHistory(): Promise { throw new Error('Method not implemented.'); } - clearAllHistoryEntries(): void { + async clearAllHistoryEntries() { throw new Error('Method not implemented.'); } - removeHistoryEntry(sessionId: string): void { + async removeHistoryEntry(sessionId: string) { throw new Error('Method not implemented.'); } @@ -90,4 +90,21 @@ 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.'); + } + + getChatStorageFolder(): URI { + throw new Error('Method not implemented.'); + } + + logChatIndex(): void { + throw new Error('Method not implemented.'); + } + + isPersistedSessionEmpty(sessionId: string): 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 9a59dc5e..267af76a 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/mockLanguageModelToolsService.ts b/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts index d854a0be..87a60b7f 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts @@ -22,6 +22,14 @@ export class MockLanguageModelToolsService implements ILanguageModelToolsService return Disposable.None; } + resetToolAutoConfirmation(): void { + + } + + setToolAutoConfirmation(toolId: string, scope: 'workspace' | 'profile', autoConfirm?: boolean): void { + + } + registerToolImplementation(name: string, tool: IToolImpl): IDisposable { return Disposable.None; } 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 dceacea2..b063215e 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 1f73d7fe..6f009d57 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 4a48b3a8..3478fd7d 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts @@ -7,14 +7,16 @@ import assert from 'assert'; import { randomInt } from '../../../../../../../../base/common/numbers.js'; import { Range } from '../../../../../../../../editor/common/core/range.js'; import { assertDefined } from '../../../../../../../../base/common/types.js'; +import { BaseToken } from '../../../../../../../../editor/common/codecs/baseToken.js'; +import { PromptToken } from '../../../../../common/promptSyntax/codecs/tokens/promptToken.js'; import { FileReference } from '../../../../../common/promptSyntax/codecs/tokens/fileReference.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../../base/test/common/utils.js'; -import { BaseToken } from '../../../../../../../../editor/common/codecs/baseToken.js'; +import { PromptVariable, PromptVariableWithData } from '../../../../../common/promptSyntax/codecs/tokens/promptVariable.js'; suite('FileReference', () => { ensureNoDisposablesAreLeakedInTestSuite(); - test('`linkRange`', () => { + test('• linkRange', () => { const lineNumber = randomInt(100, 1); const columnStartNumber = randomInt(100, 1); const path = `/temp/test/file-${randomInt(Number.MAX_SAFE_INTEGER)}.txt`; @@ -46,7 +48,7 @@ suite('FileReference', () => { ); }); - test('`path`', () => { + test('• path', () => { const lineNumber = randomInt(100, 1); const columnStartNumber = randomInt(100, 1); const link = `/temp/test/file-${randomInt(Number.MAX_SAFE_INTEGER)}.txt`; @@ -67,7 +69,7 @@ suite('FileReference', () => { ); }); - test('extends `BaseToken`', () => { + test('• extends `PromptVariableWithData` and others', () => { const lineNumber = randomInt(100, 1); const columnStartNumber = randomInt(100, 1); const link = `/temp/test/file-${randomInt(Number.MAX_SAFE_INTEGER)}.txt`; @@ -81,6 +83,21 @@ suite('FileReference', () => { ); const fileReference = new FileReference(range, link); + assert( + fileReference instanceof PromptVariableWithData, + 'Must extend `PromptVariableWithData`.', + ); + + assert( + fileReference instanceof PromptVariable, + 'Must extend `PromptVariable`.', + ); + + assert( + fileReference instanceof PromptToken, + 'Must extend `PromptToken`.', + ); + assert( fileReference instanceof BaseToken, 'Must extend `BaseToken`.', diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts index c13322ea..3f476745 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts @@ -21,6 +21,7 @@ import { TextModelPromptParser } from '../../../../common/promptSyntax/parsers/t import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; import { InMemoryFileSystemProvider } from '../../../../../../../platform/files/common/inMemoryFilesystemProvider.js'; import { TestInstantiationService } from '../../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { assertDefined } from '../../../../../../../base/common/types.js'; /** * Test helper to run unit tests for the {@link TextModelPromptParser} @@ -78,7 +79,14 @@ class TextModelPromptParserTest extends Disposable { const { references } = this.parser; for (let i = 0; i < expectedReferences.length; i++) { - expectedReferences[i].validateEqual(references[i]); + const reference = references[i]; + + assertDefined( + reference, + `Expected reference #${i} be ${expectedReferences[i]}, got 'undefined'.`, + ); + + expectedReferences[i].validateEqual(reference); } assert.strictEqual( diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts index f9c6214a..1677ae19 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts @@ -12,7 +12,7 @@ import { Range } from '../../../../../../editor/common/core/range.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { IMockFolder, MockFilesystem } from './testUtils/mockFilesystem.js'; import { IFileService } from '../../../../../../platform/files/common/files.js'; -import { IPromptFileReference } from '../../../common/promptSyntax/parsers/types.js'; +import { IPromptReference } from '../../../common/promptSyntax/parsers/types.js'; import { FileService } from '../../../../../../platform/files/common/fileService.js'; import { NullPolicyService } from '../../../../../../platform/policy/common/policy.js'; import { ILogService, NullLogService } from '../../../../../../platform/log/common/log.js'; @@ -98,7 +98,7 @@ class TestPromptFileReference extends Disposable { await rootReference.allSettled(); // resolve the root file reference including all nested references - const resolvedReferences: readonly (IPromptFileReference | undefined)[] = rootReference.allReferences; + const resolvedReferences: readonly (IPromptReference | undefined)[] = rootReference.allReferences; for (let i = 0; i < this.expectedReferences.length; i++) { const expectedReference = this.expectedReferences[i]; 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 45bd8552..2c91a7f7 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( @@ -155,7 +155,14 @@ export class ExpectedReference { const { references } = other; for (let i = 0; i < children.length; i++) { - children[i].validateEqual(references[i]); + const reference = references[i]; + + assertDefined( + reference, + `${errorPrefix} Expected reference #${i} be ${children[i]}, got 'undefined'.`, + ); + + children[i].validateEqual(reference); } if (references.length > children.length) { @@ -182,4 +189,11 @@ export class ExpectedReference { throw new Error(`${errorPrefix} Expected another reference '${expectedReference.options.text}', got 'undefined'.`); } } + + /** + * Returns a string representation of the reference. + */ + public toString(): string { + return `expected-reference/${this.options.text}`; + } } 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 8228a56d..4342e42b 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 @@ -13,9 +13,9 @@ import { IFileService } from '../../../../../../../platform/files/common/files.j import { PromptsConfig } from '../../../../../../../platform/prompts/common/config.js'; import { FileService } from '../../../../../../../platform/files/common/fileService.js'; import { ILogService, NullLogService } from '../../../../../../../platform/log/common/log.js'; -import { PromptFilesLocator } from '../../../../common/promptSyntax/utils/promptFilesLocator.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../base/test/common/utils.js'; import { mockObject, mockService } from '../../../../../../../platform/prompts/test/common/utils/mock.js'; +import { isValidGlob, PromptFilesLocator } from '../../../../common/promptSyntax/utils/promptFilesLocator.js'; import { InMemoryFileSystemProvider } from '../../../../../../../platform/files/common/inMemoryFilesystemProvider.js'; import { TestInstantiationService } from '../../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IConfigurationOverrides, IConfigurationService } from '../../../../../../../platform/configuration/common/configuration.js'; @@ -33,7 +33,7 @@ const mockConfigService = (value: T): IConfigurationService => { ); assert( - [PromptsConfig.CONFIG_KEY, PromptsConfig.LOCATIONS_CONFIG_KEY].includes(key), + [PromptsConfig.KEY, PromptsConfig.LOCATIONS_KEY].includes(key), `Unsupported configuration key '${key}'.`, ); @@ -109,7 +109,8 @@ suite('PromptFilesLocator', () => { const locator = await createPromptsLocator(undefined, EMPTY_WORKSPACE, []); assert.deepStrictEqual( - await locator.listFiles([]), + (await locator.listFiles()) + .map((file) => file.fsPath), [], 'No prompts must be found.', ); @@ -122,7 +123,7 @@ suite('PromptFilesLocator', () => { }, EMPTY_WORKSPACE, []); assert.deepStrictEqual( - await locator.listFiles([]), + await locator.listFiles(), [], 'No prompts must be found.', ); @@ -135,7 +136,7 @@ suite('PromptFilesLocator', () => { ], EMPTY_WORKSPACE, []); assert.deepStrictEqual( - await locator.listFiles([]), + await locator.listFiles(), [], 'No prompts must be found.', ); @@ -145,7 +146,8 @@ suite('PromptFilesLocator', () => { const locator = await createPromptsLocator(null, EMPTY_WORKSPACE, []); assert.deepStrictEqual( - await locator.listFiles([]), + (await locator.listFiles()) + .map((file) => file.fsPath), [], 'No prompts must be found.', ); @@ -155,7 +157,8 @@ suite('PromptFilesLocator', () => { const locator = await createPromptsLocator('/etc/hosts/prompts', EMPTY_WORKSPACE, []); assert.deepStrictEqual( - await locator.listFiles([]), + (await locator.listFiles()) + .map((file) => file.fsPath), [], 'No prompts must be found.', ); @@ -163,7 +166,7 @@ suite('PromptFilesLocator', () => { }); suite('• non-empty filesystem', () => { - test('• object config value', async () => { + test('• core logic', async () => { const locator = await createPromptsLocator( { '/Users/legomushroom/repos/prompts': true, @@ -207,553 +210,2201 @@ suite('PromptFilesLocator', () => { ]); assert.deepStrictEqual( - await locator.listFiles([]), + (await locator.listFiles()) + .map((file) => file.fsPath), [ - createURI('/Users/legomushroom/repos/prompts/test.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md'), - createURI('/tmp/prompts/translate.to-rust.prompt.md'), + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').path, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').path, + createURI('/tmp/prompts/translate.to-rust.prompt.md').path, ], 'Must find correct prompts.', ); }); + + suite('• absolute', () => { + suite('• wild card', () => { + const settings = [ + '/Users/legomushroom/repos/vscode/**', + '/Users/legomushroom/repos/vscode/**/*.prompt.md', + '/Users/legomushroom/repos/vscode/**/*.md', + '/Users/legomushroom/repos/vscode/**/*', + '/Users/legomushroom/repos/vscode/deps/**', + '/Users/legomushroom/repos/vscode/deps/**/*.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/*', + '/Users/legomushroom/repos/vscode/deps/**/*.md', + '/Users/legomushroom/repos/vscode/**/text/**', + '/Users/legomushroom/repos/vscode/**/text/**/*', + '/Users/legomushroom/repos/vscode/**/text/**/*.md', + '/Users/legomushroom/repos/vscode/**/text/**/*.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**', + '/Users/legomushroom/repos/vscode/deps/text/**/*', + '/Users/legomushroom/repos/vscode/deps/text/**/*.md', + '/Users/legomushroom/repos/vscode/deps/text/**/*.prompt.md', + ]; + + for (const setting of settings) { + test(`• '${setting}'`, async () => { + const locator = await createPromptsLocator( + { [setting]: true }, + EMPTY_WORKSPACE, + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'deps/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/deps/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific2.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } + }); + + suite(`• specific`, () => { + const testSettings = [ + [ + '/Users/legomushroom/repos/vscode/**/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/**/*specific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/*specific*.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/specific*', + '/Users/legomushroom/repos/vscode/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/**/unspecific2.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/**/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/nested/specific.prompt.md', + '/Users/legomushroom/repos/vscode/**/nested/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/nested/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/**/*spec*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/*spec*', + ], + [ + '/Users/legomushroom/repos/vscode/**/*spec*.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/deps/**/*spec*.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/text/**/*spec*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/nested/*spec*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/nested/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific*', + '/Users/legomushroom/repos/vscode/deps/**/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific*.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific2.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific1*.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific2*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific*', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific*.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific2.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific1*.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific2*.md', + ], + ]; + + for (const settings of testSettings) { + test(`• '${JSON.stringify(settings)}'`, async () => { + const vscodeSettings: Record = {}; + for (const setting of settings) { + vscodeSettings[setting] = true; + } + + const locator = await createPromptsLocator( + vscodeSettings, + EMPTY_WORKSPACE, + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'deps/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rawbot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific2.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } + }); + }); }); }); suite('• single-root workspace', () => { - suite('• non-empty filesystem', () => { - suite('• object config value', () => { - test('• core logic', async () => { - const locator = await createPromptsLocator( - { - '/Users/legomushroom/repos/prompts': true, - '/tmp/prompts/': true, - '/absolute/path/prompts': false, - '.copilot/prompts': true, - }, - [ - '/Users/legomushroom/repos/vscode', - ], - [ - { - name: '/Users/legomushroom/repos/prompts', - children: [ - { - name: 'test.prompt.md', - contents: 'Hello, World!', - }, - { - name: 'refactor-tests.prompt.md', - contents: 'some file content goes here', - }, - ], - }, - { - name: '/tmp/prompts', - children: [ - { - name: 'translate.to-rust.prompt.md', - contents: 'some more random file contents', - }, - ], - }, - { - name: '/absolute/path/prompts', - children: [ - { - name: 'some-prompt-file.prompt.md', - contents: 'hey hey hey', - }, - ], - }, - { - name: '/Users/legomushroom/repos/vscode', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'default.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'my.prompt.md', - contents: 'oh hi, bot!', - }, - ], - }, - ], - }, - ]); + suite('• glob pattern', () => { + suite('• relative', () => { + suite('• wild card', () => { + const testSettings = [ + '**', + '**/*.prompt.md', + '**/*.md', + '**/*', + 'deps/**', + 'deps/**/*.prompt.md', + 'deps/**/*', + 'deps/**/*.md', + '**/text/**', + '**/text/**/*', + '**/text/**/*.md', + '**/text/**/*.prompt.md', + 'deps/text/**', + 'deps/text/**/*', + 'deps/text/**/*.md', + 'deps/text/**/*.prompt.md', + ]; - assert.deepStrictEqual( - await locator.listFiles([]), - [ - createURI('/Users/legomushroom/repos/vscode/.github/prompts/my.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/test.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md'), - createURI('/tmp/prompts/translate.to-rust.prompt.md'), - createURI('/Users/legomushroom/repos/vscode/.copilot/prompts/default.prompt.md'), - ], - 'Must find correct prompts.', - ); + for (const setting of testSettings) { + test(`• '${setting}'`, async () => { + const locator = await createPromptsLocator( + { [setting]: true }, + ['/Users/legomushroom/repos/vscode'], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'deps/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/deps/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific2.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } }); - test('• with disabled `.github/prompts` location', async () => { - const locator = await createPromptsLocator( - { - '/Users/legomushroom/repos/prompts': true, - '/tmp/prompts/': true, - '/absolute/path/prompts': false, - '.copilot/prompts': true, - '.github/prompts': false, - }, + suite(`• specific`, () => { + const testSettings = [ [ - '/Users/legomushroom/repos/vscode', + '**/*specific*', ], [ - { - name: '/Users/legomushroom/repos/prompts', - children: [ - { - name: 'test.prompt.md', - contents: 'Hello, World!', - }, - { - name: 'refactor-tests.prompt.md', - contents: 'some file content goes here', - }, - ], - }, - { - name: '/tmp/prompts', - children: [ - { - name: 'translate.to-rust.prompt.md', - contents: 'some more random file contents', - }, - ], - }, - { - name: '/absolute/path/prompts', - children: [ - { - name: 'some-prompt-file.prompt.md', - contents: 'hey hey hey', - }, - ], - }, - { - name: '/Users/legomushroom/repos/vscode', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'default.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'my.prompt.md', - contents: 'oh hi, bot!', - }, - { - name: 'your.prompt.md', - contents: 'oh hi, bot!', - }, - ], - }, - ], - }, - ]); + '**/*specific*.prompt.md', + ], + [ + '**/*specific*.md', + ], + [ + '**/specific*', + '**/unspecific1.prompt.md', + '**/unspecific2.prompt.md', + ], + [ + '**/specific.prompt.md', + '**/unspecific*.prompt.md', + ], + [ + '**/nested/specific.prompt.md', + '**/nested/unspecific*.prompt.md', + ], + [ + '**/nested/*specific*', + ], + [ + '**/*spec*.prompt.md', + ], + [ + '**/*spec*', + ], + [ + '**/*spec*.md', + ], + [ + '**/deps/**/*spec*.md', + ], + [ + '**/text/**/*spec*.md', + ], + [ + 'deps/text/nested/*spec*', + ], + [ + 'deps/text/nested/*specific*', + ], + [ + 'deps/**/*specific*', + ], + [ + 'deps/**/specific*', + 'deps/**/unspecific*.prompt.md', + ], + [ + 'deps/**/specific*.md', + 'deps/**/unspecific*.md', + ], + [ + 'deps/**/specific.prompt.md', + 'deps/**/unspecific1.prompt.md', + 'deps/**/unspecific2.prompt.md', + ], + [ + 'deps/**/specific.prompt.md', + 'deps/**/unspecific1*.md', + 'deps/**/unspecific2*.md', + ], + [ + 'deps/text/**/*specific*', + ], + [ + 'deps/text/**/specific*', + 'deps/text/**/unspecific*.prompt.md', + ], + [ + 'deps/text/**/specific*.md', + 'deps/text/**/unspecific*.md', + ], + [ + 'deps/text/**/specific.prompt.md', + 'deps/text/**/unspecific1.prompt.md', + 'deps/text/**/unspecific2.prompt.md', + ], + [ + 'deps/text/**/specific.prompt.md', + 'deps/text/**/unspecific1*.md', + 'deps/text/**/unspecific2*.md', + ], + ]; - assert.deepStrictEqual( - await locator.listFiles([]), + for (const settings of testSettings) { + test(`• '${JSON.stringify(settings)}'`, async () => { + const vscodeSettings: Record = {}; + for (const setting of settings) { + vscodeSettings[setting] = true; + } + + const locator = await createPromptsLocator( + vscodeSettings, + ['/Users/legomushroom/repos/vscode'], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'deps/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rawbot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific2.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } + }); + }); + + suite('• absolute', () => { + suite('• wild card', () => { + const settings = [ + '/Users/legomushroom/repos/vscode/**', + '/Users/legomushroom/repos/vscode/**/*.prompt.md', + '/Users/legomushroom/repos/vscode/**/*.md', + '/Users/legomushroom/repos/vscode/**/*', + '/Users/legomushroom/repos/vscode/deps/**', + '/Users/legomushroom/repos/vscode/deps/**/*.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/*', + '/Users/legomushroom/repos/vscode/deps/**/*.md', + '/Users/legomushroom/repos/vscode/**/text/**', + '/Users/legomushroom/repos/vscode/**/text/**/*', + '/Users/legomushroom/repos/vscode/**/text/**/*.md', + '/Users/legomushroom/repos/vscode/**/text/**/*.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**', + '/Users/legomushroom/repos/vscode/deps/text/**/*', + '/Users/legomushroom/repos/vscode/deps/text/**/*.md', + '/Users/legomushroom/repos/vscode/deps/text/**/*.prompt.md', + ]; + + for (const setting of settings) { + test(`• '${setting}'`, async () => { + const locator = await createPromptsLocator( + { [setting]: true }, + ['/Users/legomushroom/repos/vscode'], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'deps/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/deps/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific2.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } + }); + + suite(`• specific`, () => { + const testSettings = [ [ - createURI('/Users/legomushroom/repos/prompts/test.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md'), - createURI('/tmp/prompts/translate.to-rust.prompt.md'), - createURI('/Users/legomushroom/repos/vscode/.copilot/prompts/default.prompt.md'), + '/Users/legomushroom/repos/vscode/**/*specific*', ], - 'Must find correct prompts.', - ); + [ + '/Users/legomushroom/repos/vscode/**/*specific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/*specific*.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/specific*', + '/Users/legomushroom/repos/vscode/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/**/unspecific2.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/**/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/nested/specific.prompt.md', + '/Users/legomushroom/repos/vscode/**/nested/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/nested/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/**/*spec*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/*spec*', + ], + [ + '/Users/legomushroom/repos/vscode/**/*spec*.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/deps/**/*spec*.md', + ], + [ + '/Users/legomushroom/repos/vscode/**/text/**/*spec*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/nested/*spec*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/nested/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific*', + '/Users/legomushroom/repos/vscode/deps/**/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific*.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific2.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific1*.md', + '/Users/legomushroom/repos/vscode/deps/**/unspecific2*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/*specific*', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific*', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific*.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific*.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific*.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific2.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/deps/text/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific1*.md', + '/Users/legomushroom/repos/vscode/deps/text/**/unspecific2*.md', + ], + ]; + + for (const settings of testSettings) { + test(`• '${JSON.stringify(settings)}'`, async () => { + const vscodeSettings: Record = {}; + for (const setting of settings) { + vscodeSettings[setting] = true; + } + + const locator = await createPromptsLocator( + vscodeSettings, + ['/Users/legomushroom/repos/vscode'], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'deps/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rawbot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/deps/text/nested/unspecific2.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } }); }); }); }); + test('• core logic', async () => { + const locator = await createPromptsLocator( + { + '/Users/legomushroom/repos/prompts': true, + '/tmp/prompts/': true, + '/absolute/path/prompts': false, + '.copilot/prompts': true, + }, + [ + '/Users/legomushroom/repos/vscode', + ], + [ + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'test.prompt.md', + contents: 'Hello, World!', + }, + { + name: 'refactor-tests.prompt.md', + contents: 'some file content goes here', + }, + ], + }, + { + name: '/tmp/prompts', + children: [ + { + name: 'translate.to-rust.prompt.md', + contents: 'some more random file contents', + }, + ], + }, + { + name: '/absolute/path/prompts', + children: [ + { + name: 'some-prompt-file.prompt.md', + contents: 'hey hey hey', + }, + ], + }, + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + ], + }, + ], + }, + ]); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/.github/prompts/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').fsPath, + createURI('/tmp/prompts/translate.to-rust.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/.copilot/prompts/default.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + + test('• with disabled `.github/prompts` location', async () => { + const locator = await createPromptsLocator( + { + '/Users/legomushroom/repos/prompts': true, + '/tmp/prompts/': true, + '/absolute/path/prompts': false, + '.copilot/prompts': true, + '.github/prompts': false, + }, + [ + '/Users/legomushroom/repos/vscode', + ], + [ + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'test.prompt.md', + contents: 'Hello, World!', + }, + { + name: 'refactor-tests.prompt.md', + contents: 'some file content goes here', + }, + ], + }, + { + name: '/tmp/prompts', + children: [ + { + name: 'translate.to-rust.prompt.md', + contents: 'some more random file contents', + }, + ], + }, + { + name: '/absolute/path/prompts', + children: [ + { + name: 'some-prompt-file.prompt.md', + contents: 'hey hey hey', + }, + ], + }, + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'your.prompt.md', + contents: 'oh hi, bot!', + }, + ], + }, + ], + }, + ]); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').path, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').path, + createURI('/tmp/prompts/translate.to-rust.prompt.md').path, + createURI('/Users/legomushroom/repos/vscode/.copilot/prompts/default.prompt.md').path, + ], + 'Must find correct prompts.', + ); + }); + suite('• multi-root workspace', () => { - suite('• non-empty filesystem', () => { - suite('• object config value', () => { - test('• without top-level `.github` folder', async () => { - const locator = await createPromptsLocator( + suite('• core logic', () => { + test('• without top-level `.github` folder', async () => { + const locator = await createPromptsLocator( + { + '/Users/legomushroom/repos/prompts': true, + '/tmp/prompts/': true, + '/absolute/path/prompts': false, + '.copilot/prompts': false, + }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/node', + ], + [ { - '/Users/legomushroom/repos/prompts': true, - '/tmp/prompts/': true, - '/absolute/path/prompts': false, - '.copilot/prompts': false, + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'test.prompt.md', + contents: 'Hello, World!', + }, + { + name: 'refactor-tests.prompt.md', + contents: 'some file content goes here', + }, + ], }, - [ - '/Users/legomushroom/repos/vscode', - '/Users/legomushroom/repos/node', - ], - [ - { - name: '/Users/legomushroom/repos/prompts', - children: [ - { - name: 'test.prompt.md', - contents: 'Hello, World!', - }, - { - name: 'refactor-tests.prompt.md', - contents: 'some file content goes here', - }, - ], - }, - { - name: '/tmp/prompts', - children: [ - { - name: 'translate.to-rust.prompt.md', - contents: 'some more random file contents', - }, - ], - }, - { - name: '/absolute/path/prompts', - children: [ - { - name: 'some-prompt-file.prompt.md', - contents: 'hey hey hey', - }, - ], - }, - { - name: '/Users/legomushroom/repos/vscode', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'prompt1.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'default.prompt.md', - contents: 'oh hi, bot!', - }, - ], - }, - ], - }, - { - name: '/Users/legomushroom/repos/node', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'prompt5.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'refactor-static-classes.prompt.md', - contents: 'file contents', - }, - ], - }, - ], - }, - // note! this folder is not part of the workspace, so prompt files are `ignored` - { - name: '/Users/legomushroom/repos/.github/prompts', - children: [ - { - name: 'prompt-name.prompt.md', - contents: 'oh hi, robot!', - }, - { - name: 'name-of-the-prompt.prompt.md', - contents: 'oh hi, raw bot!', - }, - ], - }, - ]); + { + name: '/tmp/prompts', + children: [ + { + name: 'translate.to-rust.prompt.md', + contents: 'some more random file contents', + }, + ], + }, + { + name: '/absolute/path/prompts', + children: [ + { + name: 'some-prompt-file.prompt.md', + contents: 'hey hey hey', + }, + ], + }, + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt1.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/node', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt5.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'refactor-static-classes.prompt.md', + contents: 'file contents', + }, + ], + }, + ], + }, + // note! this folder is not part of the workspace, so prompt files are `ignored` + { + name: '/Users/legomushroom/repos/.github/prompts', + children: [ + { + name: 'prompt-name.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'name-of-the-prompt.prompt.md', + contents: 'oh hi, raw bot!', + }, + ], + }, + ]); - assert.deepStrictEqual( - await locator.listFiles([]), - [ - createURI('/Users/legomushroom/repos/vscode/.github/prompts/default.prompt.md'), - createURI('/Users/legomushroom/repos/node/.github/prompts/refactor-static-classes.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/test.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md'), - createURI('/tmp/prompts/translate.to-rust.prompt.md'), - ], - 'Must find correct prompts.', - ); + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/.github/prompts/default.prompt.md').path, + createURI('/Users/legomushroom/repos/node/.github/prompts/refactor-static-classes.prompt.md').path, + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').path, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').path, + createURI('/tmp/prompts/translate.to-rust.prompt.md').path, + ], + 'Must find correct prompts.', + ); + }); + + test('• with top-level `.github` folder', async () => { + const locator = await createPromptsLocator( + { + '/Users/legomushroom/repos/prompts': true, + '/tmp/prompts/': true, + '/absolute/path/prompts': false, + '.copilot/prompts': false, + }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/node', + '/var/shared/prompts/.github', + ], + [ + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'test.prompt.md', + contents: 'Hello, World!', + }, + { + name: 'refactor-tests.prompt.md', + contents: 'some file content goes here', + }, + ], + }, + { + name: '/tmp/prompts', + children: [ + { + name: 'translate.to-rust.prompt.md', + contents: 'some more random file contents', + }, + ], + }, + { + name: '/absolute/path/prompts', + children: [ + { + name: 'some-prompt-file.prompt.md', + contents: 'hey hey hey', + }, + ], + }, + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt1.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/node', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt5.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'refactor-static-classes.prompt.md', + contents: 'file contents', + }, + ], + }, + ], + }, + // note! this folder is part of the workspace, so prompt files are `included` + { + name: '/var/shared/prompts/.github/prompts', + children: [ + { + name: 'prompt-name.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'name-of-the-prompt.prompt.md', + contents: 'oh hi, raw bot!', + }, + ], + }, + ]); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/.github/prompts/default.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/node/.github/prompts/refactor-static-classes.prompt.md').fsPath, + createURI('/var/shared/prompts/.github/prompts/prompt-name.prompt.md').fsPath, + createURI('/var/shared/prompts/.github/prompts/name-of-the-prompt.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').fsPath, + createURI('/tmp/prompts/translate.to-rust.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + + test('• with disabled `.github/prompts` location', async () => { + const locator = await createPromptsLocator( + { + '/Users/legomushroom/repos/prompts': true, + '/tmp/prompts/': true, + '/absolute/path/prompts': false, + '.copilot/prompts': false, + '.github/prompts': false, + }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/node', + '/var/shared/prompts/.github', + ], + [ + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'test.prompt.md', + contents: 'Hello, World!', + }, + { + name: 'refactor-tests.prompt.md', + contents: 'some file content goes here', + }, + ], + }, + { + name: '/tmp/prompts', + children: [ + { + name: 'translate.to-rust.prompt.md', + contents: 'some more random file contents', + }, + ], + }, + { + name: '/absolute/path/prompts', + children: [ + { + name: 'some-prompt-file.prompt.md', + contents: 'hey hey hey', + }, + ], + }, + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt1.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/node', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt5.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'refactor-static-classes.prompt.md', + contents: 'file contents', + }, + ], + }, + ], + }, + // note! this folder is part of the workspace, so prompt files are `included` + { + name: '/var/shared/prompts/.github/prompts', + children: [ + { + name: 'prompt-name.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'name-of-the-prompt.prompt.md', + contents: 'oh hi, raw bot!', + }, + ], + }, + ]); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').path, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').path, + createURI('/tmp/prompts/translate.to-rust.prompt.md').path, + ], + 'Must find correct prompts.', + ); + }); + + test('• mixed', async () => { + const locator = await createPromptsLocator( + { + '/Users/legomushroom/repos/**/*test*': true, + '.copilot/prompts': false, + '.github/prompts': true, + '/absolute/path/prompts/some-prompt-file.prompt.md': true, + }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/node', + '/var/shared/prompts/.github', + ], + [ + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'test.prompt.md', + contents: 'Hello, World!', + }, + { + name: 'refactor-tests.prompt.md', + contents: 'some file content goes here', + }, + { + name: 'elf.prompt.md', + contents: 'haalo!', + }, + ], + }, + { + name: '/tmp/prompts', + children: [ + { + name: 'translate.to-rust.prompt.md', + contents: 'some more random file contents', + }, + ], + }, + { + name: '/absolute/path/prompts', + children: [ + { + name: 'some-prompt-file.prompt.md', + contents: 'hey hey hey', + }, + ], + }, + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt1.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'default.prompt.md', + contents: 'oh hi, bot!', + }, + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/node', + children: [ + { + name: '.copilot/prompts', + children: [ + { + name: 'prompt5.prompt.md', + contents: 'oh hi, robot!', + }, + ], + }, + { + name: '.github/prompts', + children: [ + { + name: 'refactor-static-classes.prompt.md', + contents: 'file contents', + }, + ], + }, + ], + }, + // note! this folder is part of the workspace, so prompt files are `included` + { + name: '/var/shared/prompts/.github/prompts', + children: [ + { + name: 'prompt-name.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'name-of-the-prompt.prompt.md', + contents: 'oh hi, raw bot!', + }, + ], + }, + ]); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + // all of these are due to the `.github/prompts` setting + createURI('/Users/legomushroom/repos/vscode/.github/prompts/default.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/node/.github/prompts/refactor-static-classes.prompt.md').fsPath, + createURI('/var/shared/prompts/.github/prompts/prompt-name.prompt.md').fsPath, + createURI('/var/shared/prompts/.github/prompts/name-of-the-prompt.prompt.md').fsPath, + // all of these are due to the `/Users/legomushroom/repos/**/*test*` setting + createURI('/Users/legomushroom/repos/prompts/test.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md').fsPath, + // this one is due to the specific `/absolute/path/prompts/some-prompt-file.prompt.md` setting + createURI('/absolute/path/prompts/some-prompt-file.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + }); + + suite('• glob pattern', () => { + suite('• relative', () => { + suite('• wild card', () => { + const testSettings = [ + '**', + '**/*.prompt.md', + '**/*.md', + '**/*', + 'gen*/**', + 'gen*/**/*.prompt.md', + 'gen*/**/*', + 'gen*/**/*.md', + '**/gen*/**', + '**/gen*/**/*', + '**/gen*/**/*.md', + '**/gen*/**/*.prompt.md', + '{generic,general,gen}/**', + '{generic,general,gen}/**/*.prompt.md', + '{generic,general,gen}/**/*', + '{generic,general,gen}/**/*.md', + '**/{generic,general,gen}/**', + '**/{generic,general,gen}/**/*', + '**/{generic,general,gen}/**/*.md', + '**/{generic,general,gen}/**/*.prompt.md', + ]; + + for (const setting of testSettings) { + test(`• '${setting}'`, async () => { + const locator = await createPromptsLocator( + { [setting]: true }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/prompts', + ], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'gen/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'general', + children: [ + { + name: 'common.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'uncommon-10.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'license.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/gen/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific2.prompt.md').fsPath, + // - + createURI('/Users/legomushroom/repos/prompts/general/common.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/general/uncommon-10.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } }); - test('• with top-level `.github` folder', async () => { - const locator = await createPromptsLocator( - { - '/Users/legomushroom/repos/prompts': true, - '/tmp/prompts/': true, - '/absolute/path/prompts': false, - '.copilot/prompts': false, - }, + suite(`• specific`, () => { + const testSettings = [ [ - '/Users/legomushroom/repos/vscode', - '/Users/legomushroom/repos/node', - '/var/shared/prompts/.github', + '**/my.prompt.md', + '**/*specific*', + '**/*common*', ], [ - { - name: '/Users/legomushroom/repos/prompts', - children: [ - { - name: 'test.prompt.md', - contents: 'Hello, World!', - }, - { - name: 'refactor-tests.prompt.md', - contents: 'some file content goes here', - }, - ], - }, - { - name: '/tmp/prompts', - children: [ - { - name: 'translate.to-rust.prompt.md', - contents: 'some more random file contents', - }, - ], - }, - { - name: '/absolute/path/prompts', - children: [ - { - name: 'some-prompt-file.prompt.md', - contents: 'hey hey hey', - }, - ], - }, - { - name: '/Users/legomushroom/repos/vscode', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'prompt1.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'default.prompt.md', - contents: 'oh hi, bot!', - }, - ], - }, - ], - }, - { - name: '/Users/legomushroom/repos/node', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'prompt5.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'refactor-static-classes.prompt.md', - contents: 'file contents', - }, - ], - }, - ], - }, - // note! this folder is part of the workspace, so prompt files are `included` - { - name: '/var/shared/prompts/.github/prompts', - children: [ - { - name: 'prompt-name.prompt.md', - contents: 'oh hi, robot!', - }, - { - name: 'name-of-the-prompt.prompt.md', - contents: 'oh hi, raw bot!', - }, - ], - }, - ]); + '**/my.prompt.md', + '**/*specific*.prompt.md', + '**/*common*.prompt.md', + ], + [ + '**/my*.md', + '**/*specific*.md', + '**/*common*.md', + ], + [ + '**/my*.md', + '**/specific*', + '**/unspecific*', + '**/common*', + '**/uncommon*', + ], + [ + '**/my.prompt.md', + '**/specific.prompt.md', + '**/unspecific1.prompt.md', + '**/unspecific2.prompt.md', + '**/common.prompt.md', + '**/uncommon-10.prompt.md', + ], + [ + 'gen*/**/my.prompt.md', + 'gen*/**/*specific*', + 'gen*/**/*common*', + ], + [ + 'gen*/**/my.prompt.md', + 'gen*/**/*specific*.prompt.md', + 'gen*/**/*common*.prompt.md', + ], + [ + 'gen*/**/my*.md', + 'gen*/**/*specific*.md', + 'gen*/**/*common*.md', + ], + [ + 'gen*/**/my*.md', + 'gen*/**/specific*', + 'gen*/**/unspecific*', + 'gen*/**/common*', + 'gen*/**/uncommon*', + ], + [ + 'gen*/**/my.prompt.md', + 'gen*/**/specific.prompt.md', + 'gen*/**/unspecific1.prompt.md', + 'gen*/**/unspecific2.prompt.md', + 'gen*/**/common.prompt.md', + 'gen*/**/uncommon-10.prompt.md', + ], + [ + 'gen/text/my.prompt.md', + 'gen/text/nested/specific.prompt.md', + 'gen/text/nested/unspecific1.prompt.md', + 'gen/text/nested/unspecific2.prompt.md', + 'general/common.prompt.md', + 'general/uncommon-10.prompt.md', + ], + [ + 'gen/text/my.prompt.md', + 'gen/text/nested/*specific*', + 'general/*common*', + ], + [ + 'gen/text/my.prompt.md', + 'gen/text/**/specific.prompt.md', + 'gen/text/**/unspecific1.prompt.md', + 'gen/text/**/unspecific2.prompt.md', + 'general/*', + ], + [ + '{gen,general}/**/my.prompt.md', + '{gen,general}/**/*specific*', + '{gen,general}/**/*common*', + ], + [ + '{gen,general}/**/my.prompt.md', + '{gen,general}/**/*specific*.prompt.md', + '{gen,general}/**/*common*.prompt.md', + ], + [ + '{gen,general}/**/my*.md', + '{gen,general}/**/*specific*.md', + '{gen,general}/**/*common*.md', + ], + [ + '{gen,general}/**/my*.md', + '{gen,general}/**/specific*', + '{gen,general}/**/unspecific*', + '{gen,general}/**/common*', + '{gen,general}/**/uncommon*', + ], + [ + '{gen,general}/**/my.prompt.md', + '{gen,general}/**/specific.prompt.md', + '{gen,general}/**/unspecific1.prompt.md', + '{gen,general}/**/unspecific2.prompt.md', + '{gen,general}/**/common.prompt.md', + '{gen,general}/**/uncommon-10.prompt.md', + ], + ]; - assert.deepStrictEqual( - await locator.listFiles([]), - [ - createURI('/Users/legomushroom/repos/vscode/.github/prompts/default.prompt.md'), - createURI('/Users/legomushroom/repos/node/.github/prompts/refactor-static-classes.prompt.md'), - createURI('/var/shared/prompts/.github/prompts/prompt-name.prompt.md'), - createURI('/var/shared/prompts/.github/prompts/name-of-the-prompt.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/test.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md'), - createURI('/tmp/prompts/translate.to-rust.prompt.md'), - ], - 'Must find correct prompts.', - ); + for (const settings of testSettings) { + test(`• '${JSON.stringify(settings)}'`, async () => { + const vscodeSettings: Record = {}; + for (const setting of settings) { + vscodeSettings[setting] = true; + } + + const locator = await createPromptsLocator( + vscodeSettings, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/prompts', + ], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'gen/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'general', + children: [ + { + name: 'common.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'uncommon-10.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'license.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/gen/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific2.prompt.md').fsPath, + // - + createURI('/Users/legomushroom/repos/prompts/general/common.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/general/uncommon-10.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } + }); + }); + + suite('• absolute', () => { + suite('• wild card', () => { + const testSettings = [ + '/Users/legomushroom/repos/**', + '/Users/legomushroom/repos/**/*.prompt.md', + '/Users/legomushroom/repos/**/*.md', + '/Users/legomushroom/repos/**/*', + '/Users/legomushroom/repos/**/gen*/**', + '/Users/legomushroom/repos/**/gen*/**/*.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/*', + '/Users/legomushroom/repos/**/gen*/**/*.md', + '/Users/legomushroom/repos/**/gen*/**', + '/Users/legomushroom/repos/**/gen*/**/*', + '/Users/legomushroom/repos/**/gen*/**/*.md', + '/Users/legomushroom/repos/**/gen*/**/*.prompt.md', + '/Users/legomushroom/repos/{vscode,prompts}/**', + '/Users/legomushroom/repos/{vscode,prompts}/**/*.prompt.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/*.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/*', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**/*.prompt.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**/*', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**/*.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**/*', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**/*.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/gen*/**/*.prompt.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**/*.prompt.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**/*', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**/*.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**/*', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**/*.md', + '/Users/legomushroom/repos/{vscode,prompts}/**/{general,gen}/**/*.prompt.md', + ]; + + for (const setting of testSettings) { + test(`• '${setting}'`, async () => { + const locator = await createPromptsLocator( + { [setting]: true }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/prompts', + ], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'gen/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'general', + children: [ + { + name: 'common.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'uncommon-10.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'license.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/gen/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific2.prompt.md').fsPath, + // - + createURI('/Users/legomushroom/repos/prompts/general/common.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/general/uncommon-10.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } }); - test('• with disabled `.github/prompts` location', async () => { - const locator = await createPromptsLocator( - { - '/Users/legomushroom/repos/prompts': true, - '/tmp/prompts/': true, - '/absolute/path/prompts': false, - '.copilot/prompts': false, - '.github/prompts': false, - }, + suite(`• specific`, () => { + const testSettings = [ [ - '/Users/legomushroom/repos/vscode', - '/Users/legomushroom/repos/node', - '/var/shared/prompts/.github', + '/Users/legomushroom/repos/**/my.prompt.md', + '/Users/legomushroom/repos/**/*specific*', + '/Users/legomushroom/repos/**/*common*', ], [ - { - name: '/Users/legomushroom/repos/prompts', - children: [ - { - name: 'test.prompt.md', - contents: 'Hello, World!', - }, - { - name: 'refactor-tests.prompt.md', - contents: 'some file content goes here', - }, - ], - }, - { - name: '/tmp/prompts', - children: [ - { - name: 'translate.to-rust.prompt.md', - contents: 'some more random file contents', - }, - ], - }, - { - name: '/absolute/path/prompts', - children: [ - { - name: 'some-prompt-file.prompt.md', - contents: 'hey hey hey', - }, - ], - }, - { - name: '/Users/legomushroom/repos/vscode', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'prompt1.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'default.prompt.md', - contents: 'oh hi, bot!', - }, - ], - }, - ], - }, - { - name: '/Users/legomushroom/repos/node', - children: [ - { - name: '.copilot/prompts', - children: [ - { - name: 'prompt5.prompt.md', - contents: 'oh hi, robot!', - }, - ], - }, - { - name: '.github/prompts', - children: [ - { - name: 'refactor-static-classes.prompt.md', - contents: 'file contents', - }, - ], - }, - ], - }, - // note! this folder is part of the workspace, so prompt files are `included` - { - name: '/var/shared/prompts/.github/prompts', - children: [ - { - name: 'prompt-name.prompt.md', - contents: 'oh hi, robot!', - }, - { - name: 'name-of-the-prompt.prompt.md', - contents: 'oh hi, raw bot!', - }, - ], - }, - ]); + '/Users/legomushroom/repos/**/my.prompt.md', + '/Users/legomushroom/repos/**/*specific*.prompt.md', + '/Users/legomushroom/repos/**/*common*.prompt.md', + ], + [ + '/Users/legomushroom/repos/**/my*.md', + '/Users/legomushroom/repos/**/*specific*.md', + '/Users/legomushroom/repos/**/*common*.md', + ], + [ + '/Users/legomushroom/repos/**/my*.md', + '/Users/legomushroom/repos/**/specific*', + '/Users/legomushroom/repos/**/unspecific*', + '/Users/legomushroom/repos/**/common*', + '/Users/legomushroom/repos/**/uncommon*', + ], + [ + '/Users/legomushroom/repos/**/my.prompt.md', + '/Users/legomushroom/repos/**/specific.prompt.md', + '/Users/legomushroom/repos/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/**/unspecific2.prompt.md', + '/Users/legomushroom/repos/**/common.prompt.md', + '/Users/legomushroom/repos/**/uncommon-10.prompt.md', + ], + [ + '/Users/legomushroom/repos/**/gen*/**/my.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/*specific*', + '/Users/legomushroom/repos/**/gen*/**/*common*', + ], + [ + '/Users/legomushroom/repos/**/gen*/**/my.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/*specific*.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/*common*.prompt.md', + ], + [ + '/Users/legomushroom/repos/**/gen*/**/my*.md', + '/Users/legomushroom/repos/**/gen*/**/*specific*.md', + '/Users/legomushroom/repos/**/gen*/**/*common*.md', + ], + [ + '/Users/legomushroom/repos/**/gen*/**/my*.md', + '/Users/legomushroom/repos/**/gen*/**/specific*', + '/Users/legomushroom/repos/**/gen*/**/unspecific*', + '/Users/legomushroom/repos/**/gen*/**/common*', + '/Users/legomushroom/repos/**/gen*/**/uncommon*', + ], + [ + '/Users/legomushroom/repos/**/gen*/**/my.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/specific.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/unspecific2.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/common.prompt.md', + '/Users/legomushroom/repos/**/gen*/**/uncommon-10.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/gen/text/my.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/nested/specific.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/nested/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/nested/unspecific2.prompt.md', + '/Users/legomushroom/repos/prompts/general/common.prompt.md', + '/Users/legomushroom/repos/prompts/general/uncommon-10.prompt.md', + ], + [ + '/Users/legomushroom/repos/vscode/gen/text/my.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/nested/*specific*', + '/Users/legomushroom/repos/prompts/general/*common*', + ], + [ + '/Users/legomushroom/repos/vscode/gen/text/my.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/**/specific.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/vscode/gen/text/**/unspecific2.prompt.md', + '/Users/legomushroom/repos/prompts/general/*', + ], + [ + '/Users/legomushroom/repos/**/{gen,general}/**/my.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/*specific*', + '/Users/legomushroom/repos/**/{gen,general}/**/*common*', + ], + [ + '/Users/legomushroom/repos/**/{gen,general}/**/my.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/*specific*.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/*common*.prompt.md', + ], + [ + '/Users/legomushroom/repos/**/{gen,general}/**/my*.md', + '/Users/legomushroom/repos/**/{gen,general}/**/*specific*.md', + '/Users/legomushroom/repos/**/{gen,general}/**/*common*.md', + ], + [ + '/Users/legomushroom/repos/**/{gen,general}/**/my*.md', + '/Users/legomushroom/repos/**/{gen,general}/**/specific*', + '/Users/legomushroom/repos/**/{gen,general}/**/unspecific*', + '/Users/legomushroom/repos/**/{gen,general}/**/common*', + '/Users/legomushroom/repos/**/{gen,general}/**/uncommon*', + ], + [ + '/Users/legomushroom/repos/**/{gen,general}/**/my.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/specific.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/unspecific2.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/common.prompt.md', + '/Users/legomushroom/repos/**/{gen,general}/**/uncommon-10.prompt.md', + ], + [ + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/my.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/*specific*', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/*common*', + ], + [ + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/my.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/*specific*.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/*common*.prompt.md', + ], + [ + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/my*.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/*specific*.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/*common*.md', + ], + [ + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/my*.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/specific*', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/unspecific*', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/common*', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/uncommon*', + ], + [ + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/my.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/specific.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/unspecific1.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/unspecific2.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/common.prompt.md', + '/Users/legomushroom/repos/{prompts,vscode,copilot}/{gen,general}/**/uncommon-10.prompt.md', + ], + ]; - assert.deepStrictEqual( - await locator.listFiles([]), - [ - createURI('/Users/legomushroom/repos/prompts/test.prompt.md'), - createURI('/Users/legomushroom/repos/prompts/refactor-tests.prompt.md'), - createURI('/tmp/prompts/translate.to-rust.prompt.md'), - ], - 'Must find correct prompts.', - ); + for (const settings of testSettings) { + test(`• '${JSON.stringify(settings)}'`, async () => { + const vscodeSettings: Record = {}; + for (const setting of settings) { + vscodeSettings[setting] = true; + } + + const locator = await createPromptsLocator( + vscodeSettings, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/prompts', + ], + [ + { + name: '/Users/legomushroom/repos/vscode', + children: [ + { + name: 'gen/text', + children: [ + { + name: 'my.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'nested', + children: [ + { + name: 'specific.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'unspecific1.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'unspecific2.prompt.md', + contents: 'oh hi, rabot!', + }, + { + name: 'readme.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + }, + { + name: '/Users/legomushroom/repos/prompts', + children: [ + { + name: 'general', + children: [ + { + name: 'common.prompt.md', + contents: 'oh hi, bot!', + }, + { + name: 'uncommon-10.prompt.md', + contents: 'oh hi, robot!', + }, + { + name: 'license.md', + contents: 'non prompt file', + }, + ], + } + ], + }, + ], + ); + + assert.deepStrictEqual( + (await locator.listFiles()) + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/gen/text/my.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/specific.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific1.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested/unspecific2.prompt.md').fsPath, + // - + createURI('/Users/legomushroom/repos/prompts/general/common.prompt.md').fsPath, + createURI('/Users/legomushroom/repos/prompts/general/uncommon-10.prompt.md').fsPath, + ], + 'Must find correct prompts.', + ); + }); + } }); }); }); }); + + suite('• isValidGlob', () => { + test('• valid patterns', () => { + const globs = [ + '**', + '\*', + '\**', + '**/*', + '**/*.prompt.md', + '/Users/legomushroom/**/*.prompt.md', + '/Users/legomushroom/*.prompt.md', + '/Users/legomushroom/*', + '/Users/legomushroom/repos/{repo1,test}', + '/Users/legomushroom/repos/{repo1,test}/**', + '/Users/legomushroom/repos/{repo1,test}/*', + '/Users/legomushroom/**/{repo1,test}/**', + '/Users/legomushroom/**/{repo1,test}', + '/Users/legomushroom/**/{repo1,test}/*', + '/Users/legomushroom/**/repo[1,2,3]', + '/Users/legomushroom/**/repo[1,2,3]/**', + '/Users/legomushroom/**/repo[1,2,3]/*', + '/Users/legomushroom/**/repo[1,2,3]/**/*.prompt.md', + 'repo[1,2,3]/**/*.prompt.md', + 'repo[[1,2,3]/**/*.prompt.md', + '{repo1,test}/*.prompt.md', + '{repo1,test}/*', + '/{repo1,test}/*', + '/{repo1,test}}/*', + ]; + + for (const glob of globs) { + assert( + (isValidGlob(glob) === true), + `'${glob}' must be a 'valid' glob pattern.`, + ); + } + }); + + test('• invalid patterns', () => { + const globs = [ + '.', + '\\*', + '\\?', + '\\*\\?\\*', + 'repo[1,2,3', + 'repo1,2,3]', + 'repo\\[1,2,3]', + 'repo[1,2,3\\]', + 'repo\\[1,2,3\\]', + '{repo1,repo2', + 'repo1,repo2}', + '\\{repo1,repo2}', + '{repo1,repo2\\}', + '\\{repo1,repo2\\}', + '/Users/legomushroom/repos', + '/Users/legomushroom/repo[1,2,3', + '/Users/legomushroom/repo1,2,3]', + '/Users/legomushroom/repo\\[1,2,3]', + '/Users/legomushroom/repo[1,2,3\\]', + '/Users/legomushroom/repo\\[1,2,3\\]', + '/Users/legomushroom/{repo1,repo2', + '/Users/legomushroom/repo1,repo2}', + '/Users/legomushroom/\\{repo1,repo2}', + '/Users/legomushroom/{repo1,repo2\\}', + '/Users/legomushroom/\\{repo1,repo2\\}', + ]; + + for (const glob of globs) { + assert( + (isValidGlob(glob) === false), + `'${glob}' must be an 'invalid' glob pattern.`, + ); + } + }); + }); + + suite('• getConfigBasedSourceFolders', () => { + test('• gets unambiguous list of folders', async () => { + const locator = await createPromptsLocator( + { + '.github/prompts': true, + '/Users/**/repos/**': true, + 'gen/text/**': true, + 'gen/text/nested/*.prompt.md': true, + 'general/*': true, + '/Users/legomushroom/repos/vscode/my-prompts': true, + '/Users/legomushroom/repos/vscode/your-prompts/*.md': true, + '/Users/legomushroom/repos/prompts/shared-prompts/*': true, + }, + [ + '/Users/legomushroom/repos/vscode', + '/Users/legomushroom/repos/prompts', + ], + [], + ); + + assert.deepStrictEqual( + locator.getConfigBasedSourceFolders() + .map((file) => file.fsPath), + [ + createURI('/Users/legomushroom/repos/vscode/.github/prompts').fsPath, + createURI('/Users/legomushroom/repos/prompts/.github/prompts').fsPath, + createURI('/Users/legomushroom/repos/vscode/gen/text/nested').fsPath, + createURI('/Users/legomushroom/repos/prompts/gen/text/nested').fsPath, + createURI('/Users/legomushroom/repos/vscode/general').fsPath, + createURI('/Users/legomushroom/repos/prompts/general').fsPath, + createURI('/Users/legomushroom/repos/vscode/my-prompts').fsPath, + createURI('/Users/legomushroom/repos/vscode/your-prompts').fsPath, + createURI('/Users/legomushroom/repos/prompts/shared-prompts').fsPath, + ], + 'Must find correct prompts.', + ); + }); + }); }); 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 0005bca0..4e95ff4b 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(); } @@ -76,7 +76,6 @@ suite('VoiceChat', () => { getAgents(): IChatAgent[] { return agents; } getDefaultAgent(): IChatAgent | undefined { throw new Error(); } getContributedDefaultAgent(): IChatAgentData | undefined { throw new Error(); } - getSecondaryAgent(): IChatAgent | undefined { throw new Error(); } registerAgent(id: string, data: IChatAgentData): IDisposable { throw new Error('Method not implemented.'); } getAgent(id: string): IChatAgentData | undefined { throw new Error('Method not implemented.'); } getAgentsByName(name: string): IChatAgentData[] { throw new Error('Method not implemented.'); } @@ -86,10 +85,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 6c657fb9..9a24f11c 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 540a6dee..e8aedfe3 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 ff2710dc..4bfaeb31 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -31,7 +31,7 @@ import { IConfigurationService } from '../../../../../platform/configuration/com import { SEMANTIC_HIGHLIGHTING_SETTING_ID, IEditorSemanticHighlightingOptions } from '../../../../../editor/contrib/semanticTokens/common/semanticTokensConfig.js'; import { Schemas } from '../../../../../base/common/network.js'; import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; -import { ITreeSitterParserService } from '../../../../../editor/common/services/treeSitterParserService.js'; +import { ITextModelTreeSitter, ITreeSitterParserService } from '../../../../../editor/common/services/treeSitterParserService.js'; import type * as Parser from '@vscode/tree-sitter-wasm'; const $ = dom.$; @@ -254,7 +254,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { if (this._isDisposed) { return; } - this._compute(grammar, semanticTokens, tree?.tree, position); + this._compute(grammar, semanticTokens, tree, position); this._domNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`; this._editor.layoutContentWidget(this); }, (err) => { @@ -275,7 +275,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { return this._themeService.getColorTheme().semanticHighlighting; } - private _compute(grammar: IGrammar | null, semanticTokens: SemanticTokensResult | null, tree: Parser.Tree | undefined, position: Position) { + private _compute(grammar: IGrammar | null, semanticTokens: SemanticTokensResult | null, tree: ITextModelTreeSitter | undefined, position: Position) { const textMateTokenInfo = grammar && this._getTokensAtPosition(grammar, position); const semanticTokenInfo = semanticTokens && this._getSemanticTokenAtPosition(semanticTokens, position); const treeSitterTokenInfo = tree && this._getTreeSitterTokenAtPosition(tree, position); @@ -400,26 +400,28 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { } if (treeSitterTokenInfo) { + const lastTokenInfo = treeSitterTokenInfo[treeSitterTokenInfo.length - 1]; dom.append(this._domNode, $('hr.tiw-metadata-separator')); const table = dom.append(this._domNode, $('table.tiw-metadata-table')); const tbody = dom.append(table, $('tbody')); dom.append(tbody, $('tr', undefined, - $('td.tiw-metadata-key', undefined, `tree-sitter token ${treeSitterTokenInfo.id}` as string), - $('td.tiw-metadata-value', undefined, `${treeSitterTokenInfo.text}`) + $('td.tiw-metadata-key', undefined, `tree-sitter token ${lastTokenInfo.id}` as string), + $('td.tiw-metadata-value', undefined, `${lastTokenInfo.text}`) )); const scopes = new Array(); - let node = treeSitterTokenInfo; - while (node.parent) { + let i = treeSitterTokenInfo.length - 1; + let node = treeSitterTokenInfo[i]; + while (node.parent || i > 0) { scopes.push(node.type); - node = node.parent; + node = node.parent ?? treeSitterTokenInfo[--i]; if (node) { scopes.push($('br')); } } 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 +430,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(' ')), )); } } @@ -662,10 +664,23 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { return lastGoodNode; } - private _getTreeSitterTokenAtPosition(tree: Parser.Tree, pos: Position): Parser.Node | null { - const cursor = tree.walk(); - - return this._walkTreeforPosition(cursor, pos); + private _getTreeSitterTokenAtPosition(textModelTreeSitter: ITextModelTreeSitter, pos: Position): Parser.Node[] | null { + let tree = textModelTreeSitter.parseResult; + if (!tree?.tree) { + return null; + } + const nodes: Parser.Node[] = []; + do { + const cursor = tree.tree.walk(); + const node = this._walkTreeforPosition(cursor, pos); + if (node) { + nodes.push(node); + tree = textModelTreeSitter.getInjection(node.startIndex, tree.languageId); + } else { + tree = undefined; + } + } while (tree?.tree); + return nodes.length > 0 ? nodes : null; } private _renderTokenStyleDefinition(definition: TokenStyleDefinition | undefined, property: keyof TokenStyleData): Array { diff --git a/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts b/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts index 3579b82c..b74d2331 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { WorkerDescriptor } from '../../../../base/browser/defaultWorkerFactory.js'; +import { WebWorkerDescriptor } from '../../../../base/browser/webWorkerFactory.js'; +import { FileAccess } from '../../../../base/common/network.js'; import { EditorWorkerService } from '../../../../editor/browser/services/editorWorkerService.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; @@ -19,7 +20,7 @@ export class WorkbenchEditorWorkerService extends EditorWorkerService { @ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { - const workerDescriptor = new WorkerDescriptor('vs/editor/common/services/editorSimpleWorker', 'TextEditorWorker'); + const workerDescriptor = new WebWorkerDescriptor(FileAccess.asBrowserUri('vs/editor/common/services/editorWebWorkerMain.js'), 'TextEditorWorker'); super(workerDescriptor, modelService, configurationService, logService, languageConfigurationService, languageFeaturesService); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index 309745ca..550288cb 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/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index e664efbc..0039a403 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -157,7 +157,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThreadDisposables = []; this.create(); - this._globalToDispose.add(this.themeService.onDidColorThemeChange(e => this._applyTheme(e.theme))); + this._globalToDispose.add(this.themeService.onDidColorThemeChange(this._applyTheme, this)); this._globalToDispose.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.fontInfo)) { this._applyTheme(this.themeService.getColorTheme()); diff --git a/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts b/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts index c18066cb..3efc58ca 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 016ead15..41ed56ce 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/configExporter/electron-sandbox/configurationExportHelper.contribution.ts b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts deleted file mode 100644 index 678bf198..00000000 --- a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.ts +++ /dev/null @@ -1,26 +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 { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; -import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-sandbox/environmentService.js'; -import { DefaultConfigurationExportHelper } from './configurationExportHelper.js'; - -export class ExtensionPoints implements IWorkbenchContribution { - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService - ) { - // Config Exporter - if (environmentService.args['export-default-configuration']) { - instantiationService.createInstance(DefaultConfigurationExportHelper); - } - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts deleted file mode 100644 index ae0bcfca..00000000 --- a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts +++ /dev/null @@ -1,120 +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 { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-sandbox/environmentService.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from '../../../../platform/configuration/common/configurationRegistry.js'; -import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { VSBuffer } from '../../../../base/common/buffer.js'; -import { URI } from '../../../../base/common/uri.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; - -interface IExportedConfigurationNode { - name: string; - description: string; - default: any; - type?: string | string[]; - enum?: any[]; - enumDescriptions?: string[]; -} - -interface IConfigurationExport { - settings: IExportedConfigurationNode[]; - buildTime: number; - commit?: string; - buildNumber?: number; -} - -export class DefaultConfigurationExportHelper { - - constructor( - @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, - @IExtensionService private readonly extensionService: IExtensionService, - @ICommandService private readonly commandService: ICommandService, - @IFileService private readonly fileService: IFileService, - @IProductService private readonly productService: IProductService - ) { - const exportDefaultConfigurationPath = environmentService.args['export-default-configuration']; - if (exportDefaultConfigurationPath) { - this.writeConfigModelAndQuit(URI.file(exportDefaultConfigurationPath)); - } - } - - private async writeConfigModelAndQuit(target: URI): Promise { - try { - await this.extensionService.whenInstalledExtensionsRegistered(); - await this.writeConfigModel(target); - } finally { - this.commandService.executeCommand('workbench.action.quit'); - } - } - - private async writeConfigModel(target: URI): Promise { - const config = this.getConfigModel(); - - const resultString = JSON.stringify(config, undefined, ' '); - await this.fileService.writeFile(target, VSBuffer.fromString(resultString)); - } - - private getConfigModel(): IConfigurationExport { - const configRegistry = Registry.as(Extensions.Configuration); - const configurations = configRegistry.getConfigurations().slice(); - const settings: IExportedConfigurationNode[] = []; - const processedNames = new Set(); - - const processProperty = (name: string, prop: IConfigurationPropertySchema) => { - if (processedNames.has(name)) { - console.warn('Setting is registered twice: ' + name); - return; - } - - processedNames.add(name); - const propDetails: IExportedConfigurationNode = { - name, - description: prop.description || prop.markdownDescription || '', - default: prop.default, - type: prop.type - }; - - if (prop.enum) { - propDetails.enum = prop.enum; - } - - if (prop.enumDescriptions || prop.markdownEnumDescriptions) { - propDetails.enumDescriptions = prop.enumDescriptions || prop.markdownEnumDescriptions; - } - - settings.push(propDetails); - }; - - const processConfig = (config: IConfigurationNode) => { - if (config.properties) { - for (const name in config.properties) { - processProperty(name, config.properties[name]); - } - } - - config.allOf?.forEach(processConfig); - }; - - configurations.forEach(processConfig); - - const excludedProps = configRegistry.getExcludedConfigurationProperties(); - for (const name in excludedProps) { - processProperty(name, excludedProps[name]); - } - - const result: IConfigurationExport = { - settings: settings.sort((a, b) => a.name.localeCompare(b.name)), - buildTime: Date.now(), - commit: this.productService.commit, - buildNumber: this.productService.settingsSearchBuildId - }; - - return result; - } -} diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index f3b3fb7f..d246fb9f 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 f1879eac..0965dcaf 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 9a0a631e..cc2513d2 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/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 34ef1b08..8f67f94b 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'; @@ -170,7 +170,7 @@ export function createInlineValueDecoration(lineNumber: number, contentText: str } function replaceWsWithNoBreakWs(str: string): string { - return str.replace(/[ \t]/g, strings.noBreakWhitespace); + return str.replace(/[ \t\n]/g, strings.noBreakWhitespace); } function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray, ranges: Range[], model: ITextModel, wordToLineNumbersMap: Map) { @@ -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 ddd5e8bd..2d2a3ea5 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 879fc915..9a359f4f 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 107ead5e..3e63e489 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 41f28eab..b9ea3a41 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -32,7 +32,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { widgetBorder, widgetShadow } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; -import { getTitleBarStyle, TitlebarStyle } from '../../../../platform/window/common/window.js'; +import { getWindowControlsStyle, WindowControlsStyle } from '../../../../platform/window/common/window.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { EditorTabsMode, IWorkbenchLayoutService, LayoutSettings, Parts } from '../../../services/layout/browser/layoutService.js'; import { CONTEXT_DEBUG_STATE, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_IN_DEBUG_MODE, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, IDebugConfiguration, IDebugService, State, VIEWLET_ID } from '../common/debug.js'; @@ -80,12 +80,12 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); // Note: changes to this setting require a restart, so no need to listen to it. - const customTitleBar = getTitleBarStyle(this.configurationService) === TitlebarStyle.CUSTOM; + const customWindowControls = getWindowControlsStyle(this.configurationService) === WindowControlsStyle.CUSTOM; // Do not allow the widget to overflow or underflow window controls. // Use CSS calculations to avoid having to force layout with `.clientWidth` - const controlsOnLeft = customTitleBar && platform === Platform.Mac; - const controlsOnRight = customTitleBar && (platform === Platform.Windows || platform === Platform.Linux); + const controlsOnLeft = customWindowControls && platform === Platform.Mac; + const controlsOnRight = customWindowControls && (platform === Platform.Windows || platform === Platform.Linux); this.$el.style.transform = `translate( min( max(${controlsOnLeft ? '60px' : '0px'}, calc(-50% + (100vw * var(--x-position)))), @@ -94,8 +94,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { var(--y-position) )`; - - this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container')); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 95c740dc..c98e3515 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -775,8 +775,8 @@ class InstructionRenderer extends Disposable implements ITableRenderer { - this._topStackFrameColor = e.theme.getColor(topStackFrameColor); - this._focusedStackFrameColor = e.theme.getColor(focusedStackFrameColor); + this._topStackFrameColor = e.getColor(topStackFrameColor); + this._focusedStackFrameColor = e.getColor(focusedStackFrameColor); })); } diff --git a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts index 9f8d2657..35479f2c 100644 --- a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts @@ -10,7 +10,7 @@ import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWi import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from '../common/debug.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; -import { IThemeService, IColorTheme, IThemeChangeEvent } from '../../../../platform/theme/common/themeService.js'; +import { IThemeService, IColorTheme } from '../../../../platform/theme/common/themeService.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { Color } from '../../../../base/common/color.js'; import { registerColor } from '../../../../platform/theme/common/colorRegistry.js'; @@ -41,7 +41,7 @@ export class ExceptionWidget extends ZoneWidget { super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' }); this.applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(this.onDidColorThemeChange.bind(this))); + this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this))); this.create(); const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50); @@ -49,10 +49,6 @@ export class ExceptionWidget extends ZoneWidget { this._disposables.add(onDidLayoutChangeScheduler); } - private onDidColorThemeChange(e: IThemeChangeEvent): void { - this.applyTheme(e.theme); - } - private applyTheme(theme: IColorTheme): void { this.backgroundColor = theme.getColor(debugExceptionWidgetBackground); const frameColor = theme.getColor(debugExceptionWidgetBorder); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 8df25793..6ce5e3e3 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/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index add32108..8d3c433d 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/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 70f1b37e..1bb5eb33 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { localize } from '../../../../nls.js'; -import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { IFileService } from '../../../../platform/files/common/files.js'; @@ -15,7 +15,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { createSyncHeaders, IAuthenticationProvider, IResourceRefHandle } from '../../../../platform/userDataSync/common/userDataSync.js'; import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsStorageService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService, SyncResource } from '../common/editSessions.js'; +import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsStorageService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService, SyncResource, EDIT_SESSIONS_PENDING_KEY } from '../common/editSessions.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { getCurrentAuthenticationSessionInfo } from '../../../services/authentication/browser/authenticationService.js'; @@ -91,6 +91,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes // If another window changes the preferred session storage, reset our cached auth state in memory this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EditSessionsWorkbenchService.CACHED_SESSION_STORAGE_KEY, this._store)(() => this.onDidChangeStorage())); + this.registerSignInAction(); this.registerResetAuthenticationAction(); this.signedInContext = EDIT_SESSIONS_SIGNED_IN.bindTo(this.contextKeyService); @@ -453,6 +454,46 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } } + private registerSignInAction() { + if (!this.serverConfiguration?.url) { + return; + } + const that = this; + const id = 'workbench.editSessions.actions.signIn'; + const when = ContextKeyExpr.and(ContextKeyExpr.equals(EDIT_SESSIONS_PENDING_KEY, false), ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false)); + this._register(registerAction2(class ResetEditSessionAuthenticationAction extends Action2 { + constructor() { + super({ + id, + title: localize('sign in', 'Turn on Cloud Changes...'), + category: EDIT_SESSION_SYNC_CATEGORY, + precondition: when, + menu: [{ + id: MenuId.CommandPalette, + }, + { + id: MenuId.AccountsContext, + group: '2_editSessions', + when, + }] + }); + } + + async run() { + return await that.initialize('write', false); + } + })); + + this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, { + group: '2_editSessions', + command: { + id, + title: localize('sign in badge', 'Turn on Cloud Changes... (1)'), + }, + when: ContextKeyExpr.and(ContextKeyExpr.equals(EDIT_SESSIONS_PENDING_KEY, true), ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false)) + })); + } + private registerResetAuthenticationAction() { const that = this; this._register(registerAction2(class ResetEditSessionAuthenticationAction extends Action2 { diff --git a/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts b/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts index 0068c223..46acc992 100644 --- a/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts +++ b/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts @@ -48,6 +48,7 @@ class NullEnablementService implements IUserDataSyncEnablementService { canToggleEnablement(): boolean { return true; } setEnablement(_enabled: boolean): void { } isResourceEnabled(_resource: SyncResource): boolean { return true; } + isResourceEnablementConfigured(_resource: SyncResource): boolean { return false; } setResourceEnablement(_resource: SyncResource, _enabled: boolean): void { } getResourceSyncStateVersion(_resource: SyncResource): string | undefined { return undefined; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 8542167a..aaaf4865 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -30,7 +30,7 @@ import { localize } from '../../../../nls.js'; import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKey, IContextKeyService, IScopedContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { computeSize, IExtensionGalleryService, IGalleryExtension, ILocalExtension } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { computeSize, FilterType, IExtensionGalleryService, IGalleryExtension, ILocalExtension } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; import { ExtensionType, IExtensionManifest } from '../../../../platform/extensions/common/extensions.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; @@ -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,23 +72,21 @@ 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'; import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; +import { IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; function toDateString(date: Date) { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}, ${date.toLocaleTimeString(language, { hourCycle: 'h23' })}`; @@ -161,11 +158,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 +249,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 +290,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 resource = append(append(subtitle, $('.subtitle-entry.resource')), $('', { tabIndex: 0 })); - resource.setAttribute('role', 'button'); + const publisherContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(publisherContainer); + const publisherWidget = this.instantiationService.createInstance(PublisherWidget, publisherContainer, 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 extensionKindContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(extensionKindContainer); + const extensionKindWidget = this.instantiationService.createInstance(ExtensionKindIndicatorWidget, extensionKindContainer, 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 installCountContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(installCountContainer); + const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCountContainer, false); - const sponsorWidget = this.instantiationService.createInstance(SponsorWidget, append(subtitle, $('.subtitle-entry'))); + const ratingsContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(ratingsContainer); + const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratingsContainer, false); + + 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 +432,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 +556,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; @@ -1064,6 +1031,7 @@ class AdditionalDetailsWidget extends Disposable { @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService, ) { super(); this.render(extension); @@ -1093,10 +1061,17 @@ class AdditionalDetailsWidget extends Disposable { const categoriesContainer = append(container, $('.categories-container.additional-details-element')); append(categoriesContainer, $('.additional-details-title', undefined, localize('categories', "Categories"))); const categoriesElement = append(categoriesContainer, $('.categories')); - for (const category of extension.categories) { - this.disposables.add(onClick(append(categoriesElement, $('span.category', { tabindex: '0' }, category)), - () => this.extensionsWorkbenchService.openSearch(`@category:"${category}"`))); - } + this.extensionGalleryManifestService.getExtensionGalleryManifest() + .then(manifest => { + const hasCategoryFilter = manifest?.capabilities.extensionQuery.filtering?.some(({ name }) => name === FilterType.Category); + for (const category of extension.categories) { + const categoryElement = append(categoriesElement, $('span.category', { tabindex: '0' }, category)); + if (hasCategoryFilter) { + categoryElement.classList.add('clickable'); + this.disposables.add(onClick(categoryElement, () => this.extensionsWorkbenchService.openSearch(`@category:"${category}"`))); + } + } + }); } } @@ -1105,7 +1080,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 +1090,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 */ } @@ -1333,14 +1308,12 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = const buttonHoverBackgroundColor = theme.getColor(buttonHoverBackground); if (buttonHoverBackgroundColor) { - collector.addRule(`.monaco-workbench .extension-editor .content > .details > .additional-details-container .categories-container > .categories > .category:hover { background-color: ${buttonHoverBackgroundColor}; border-color: ${buttonHoverBackgroundColor}; }`); - collector.addRule(`.monaco-workbench .extension-editor .content > .details > .additional-details-container .tags-container > .tags > .tag:hover { background-color: ${buttonHoverBackgroundColor}; border-color: ${buttonHoverBackgroundColor}; }`); + collector.addRule(`.monaco-workbench .extension-editor .content > .details > .additional-details-container .categories-container > .categories > .category.clickable:hover { background-color: ${buttonHoverBackgroundColor}; border-color: ${buttonHoverBackgroundColor}; }`); } const buttonForegroundColor = theme.getColor(buttonForeground); if (buttonForegroundColor) { - collector.addRule(`.monaco-workbench .extension-editor .content > .details > .additional-details-container .categories-container > .categories > .category:hover { color: ${buttonForegroundColor}; }`); - collector.addRule(`.monaco-workbench .extension-editor .content > .details > .additional-details-container .tags-container > .tags > .tag:hover { color: ${buttonForegroundColor}; }`); + collector.addRule(`.monaco-workbench .extension-editor .content > .details > .additional-details-container .categories-container > .categories > .category.clickable:hover { color: ${buttonForegroundColor}; }`); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts b/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts index 9d0f876a..0b90d652 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 e3016394..34a5326a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -8,7 +8,7 @@ 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'; @@ -80,6 +80,7 @@ import { IConfigurationMigrationRegistry, Extensions as ConfigurationMigrationEx 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 { ExtensionGalleryResourceType, ExtensionGalleryServiceUrlConfigKey, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */); @@ -279,6 +280,18 @@ Registry.as(ConfigurationExtensions.Configuration) scope: ConfigurationScope.APPLICATION, tags: ['onExp', 'usesOnlineServices'] }, + [ExtensionGalleryServiceUrlConfigKey]: { + type: 'string', + description: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"), + default: '', + scope: ConfigurationScope.APPLICATION, + tags: ['usesOnlineServices'], + included: false, + policy: { + name: 'ExtensionGalleryServiceUrl', + minimumVersion: '1.99', + }, + }, [AllowedExtensionsConfigKey]: { // Note: Type is set only to object because to support policies generation during build time, where single type is expected. type: 'object', @@ -297,6 +310,7 @@ Registry.as(ConfigurationExtensions.Configuration) policy: { name: 'AllowedExtensions', minimumVersion: '1.96', + description: localize('extensions.allowed.policy', "Specify a list of extensions that are allowed to use. This helps maintain a secure and consistent development environment by restricting the use of unauthorized extensions. More information: https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions"), }, additionalProperties: false, patternProperties: { @@ -546,6 +560,10 @@ 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); +const CONTEXT_GALLERY_HAS_EXTENSION_LINK = new RawContextKey('galleryHasExtensionLink', false); async function runAction(action: IAction): Promise { try { @@ -567,7 +585,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, @@ -597,11 +616,23 @@ 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); + CONTEXT_GALLERY_HAS_EXTENSION_LINK.bindTo(this.contextKeyService).set(!!(extensionGalleryManifest && getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionDetailsViewUri))); + } + private registerQuickAccessProvider(): void { if (this.extensionManagementServerService.localExtensionManagementServer || this.extensionManagementServerService.remoteExtensionManagementServer @@ -905,7 +936,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() @@ -915,7 +947,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() @@ -925,7 +958,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."), [] ); } @@ -992,16 +1025,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, }], @@ -1072,7 +1106,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, }); @@ -1191,19 +1225,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), @@ -1519,7 +1554,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) => { @@ -1641,14 +1676,12 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '1_copy', - when: ContextKeyExpr.has('isGalleryExtension'), + when: ContextKeyExpr.and(ContextKeyExpr.has('isGalleryExtension'), CONTEXT_GALLERY_HAS_EXTENSION_LINK), }, - 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); } } }); @@ -1670,7 +1703,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) => { @@ -1683,7 +1716,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 8b062d1d..572bb260 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; } @@ -2598,11 +2610,13 @@ export class ExtensionStatusAction extends ExtensionAction { return; } - // Extension is disabled by its dependency - const result = this.allowedExtensionsService.isAllowed(this.extension.local); - if (result !== true) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('disabled - not allowed', "This extension is disabled because {0}", result.value)) }, true); - return; + // Extension is disabled by allowed list + if (this.extension.enablementState === EnablementState.DisabledByAllowlist) { + const result = this.allowedExtensionsService.isAllowed(this.extension.local); + if (result !== true) { + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('disabled - not allowed', "This extension is disabled because {0}", result.value)) }, true); + return; + } } // Extension is disabled by environment diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts index f7871ef4..ea1bc04e 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 8e2e5ab9..97be5462 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 ee75bba1..7f02128f 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,16 +58,16 @@ 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'; import { Codicon } from '../../../../base/common/codicons.js'; +import { IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; export const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); export const ExtensionsSortByContext = new RawContextKey('extensionsSortByValue', ''); @@ -505,6 +504,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchBox: SuggestEnabledInput | undefined; private notificationContainer: HTMLElement | undefined; private readonly searchViewletState: MementoObject; + private extensionGalleryManifest: IExtensionGalleryManifest | null = null; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -512,6 +512,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IProgressService private readonly progressService: IProgressService, @IInstantiationService instantiationService: IInstantiationService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @INotificationService private readonly notificationService: INotificationService, @@ -553,6 +554,15 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this._register(this.paneCompositeService.onDidPaneCompositeOpen(e => { if (e.viewContainerLocation === ViewContainerLocation.Sidebar) { this.onViewletOpen(e.composite); } }, this)); this._register(extensionsWorkbenchService.onReset(() => this.refresh())); this.searchViewletState = this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + + extensionGalleryManifestService.getExtensionGalleryManifest() + .then(galleryManifest => { + this.extensionGalleryManifest = galleryManifest; + this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifest(galleryManifest => { + this.extensionGalleryManifest = galleryManifest; + this.refresh(); + })); + }); } get searchValue(): string | undefined { @@ -583,7 +593,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE else if (/sort:/.test(item)) { return 'c'; } else { return 'd'; } }, - provideResults: (query: string) => Query.suggestions(query) + provideResults: (query: string) => Query.suggestions(query, this.extensionGalleryManifest) }, placeholder, 'extensions:searchinput', { placeholderText: placeholder, value: searchValue })); this.notificationContainer = append(this.header, $('.notification-container.hidden', { 'tabindex': '0' })); @@ -972,14 +982,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 +996,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 cbb4d0cb..dd378644 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 17bba8cc..7a2dd56b 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())); + } + + private clear(): void { + this.container.innerText = ''; + this.disposables.clear(); } render(): void { - this.container.innerText = ''; + this.clear(); if (!this.extension) { return; @@ -96,15 +109,20 @@ export class InstallCountWidget extends ExtensionWidget { return; } - append(this.container, $('span' + ThemeIcon.asCSSSelector(installCountIcon))); - const count = append(this.container, $('span.count')); + 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 { const installCount = extension.installCount; - if (installCount === undefined) { + if (!installCount) { return undefined; } @@ -129,12 +147,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 +163,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())); + } + + private clear(): void { + this.container.innerText = ''; + this.disposables.clear(); } render(): void { - this.container.innerText = ''; + this.clear(); if (!this.extension) { return; @@ -167,51 +191,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())); + } + + private clear(): void { + this.element?.remove(); + this.disposables.clear(); } render(): void { - reset(this.container); - this.disposables.clear(); - if (!this.extension?.publisherDomain?.verified) { + this.clear(); + if (!this.extension) { return; } @@ -223,20 +267,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)); - 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.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))); + } + + if (this.extension.url) { + this.disposables.add(onClick(this.element, () => this.extensionsWorkbenchService.openSearch(`publisher:"${this.extension?.publisherDisplayName}"`))); + } } } + } export class SponsorWidget extends ExtensionWidget { @@ -244,7 +313,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 +514,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 +691,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 +731,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 +760,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 +1002,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 +1018,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 74f8e88f..bcf99d67 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -21,7 +21,9 @@ import { TargetPlatformToString, IAllowedExtensionsService, AllowedExtensionsConfigKey, - EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT + EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT, + ExtensionManagementError, + ExtensionManagementErrorCode } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, DefaultIconPath, IResourceExtension } from '../../../services/extensionManagement/common/extensionManagement.js'; import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, groupByExtension, getGalleryExtensionId, isMalicious } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; @@ -66,6 +68,7 @@ import { ShowCurrentReleaseNotesActionId } from '../../update/common/update.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 { ExtensionGalleryResourceType, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; interface IExtensionStateProvider { (extension: Extension): T; @@ -196,11 +199,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 { @@ -215,6 +214,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; } @@ -228,11 +231,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 { @@ -295,7 +294,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 { @@ -310,6 +313,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) { @@ -549,7 +556,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()); } @@ -626,8 +633,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) { @@ -652,7 +659,7 @@ class Extensions extends Disposable { const extensions = await this.mapInstalledExtensionWithCompatibleGalleryExtension(galleryExtensions, productVersion); for (const [extension, gallery] of extensions) { // update metadata of the extension if it does not exist - if (extension.local && !extension.local.identifier.uuid) { + if (extension.local && extension.local.identifier.uuid !== gallery.identifier.uuid) { extension.local = await this.updateMetadata(extension.local, gallery); } if (!extension.gallery || extension.gallery.version !== gallery.version || extension.gallery.properties.targetPlatform !== gallery.properties.targetPlatform) { @@ -882,8 +889,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 }); } } } @@ -938,6 +945,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, @@ -1057,13 +1065,13 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } if (e.affectsConfiguration(AutoCheckUpdatesConfigurationKey)) { if (this.isAutoCheckUpdatesEnabled()) { - this.checkForUpdates(); + this.checkForUpdates(`Enabled auto check updates`); } } })); this._register(this.extensionEnablementService.onEnablementChanged(platformExtensions => { if (this.getAutoUpdateValue() === 'onlyEnabledExtensions' && platformExtensions.some(e => this.extensionEnablementService.isEnabled(e))) { - this.checkForUpdates(); + this.checkForUpdates('Extension enablement changed'); } })); this._register(Event.debounce(this.onChange, () => undefined, 100)(() => this.hasOutdatedExtensionsContextKey.set(this.outdated.length > 0))); @@ -1074,14 +1082,14 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension comment: 'Report when update check is triggered on product update'; }>('extensions:updatecheckonproductupdate'); if (this.isAutoCheckUpdatesEnabled()) { - this.checkForUpdates(); + this.checkForUpdates('Product update'); } } })); this._register(this.allowedExtensionsService.onDidChangeAllowedExtensionsConfigValue(() => { if (this.isAutoCheckUpdatesEnabled()) { - this.checkForUpdates(); + this.checkForUpdates('Allowed extensions changed'); } })); @@ -1819,7 +1827,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return ExtensionState.Uninstalled; } - async checkForUpdates(onlyBuiltin?: boolean): Promise { + async checkForUpdates(reason?: string, onlyBuiltin?: boolean): Promise { + if (reason) { + this.logService.info(`[Extensions]: Checking for updates. Reason: ${reason}`); + } else { + this.logService.trace(`[Extensions]: Checking for updates`); + } if (!this.galleryService.isEnabled()) { return; } @@ -1864,6 +1877,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.telemetryService.publicLog2('galleryService:checkingForUpdates', { count: infos.length, }); + this.logService.trace(`Checking updates for extensions`, infos.map(e => e.id).join(', ')); const galleryExtensions = await this.galleryService.getExtensions(infos, { targetPlatform, compatible: true, productVersion: this.getProductVersion(), preferResourceApi: true }, CancellationToken.None); if (galleryExtensions.length) { await this.syncInstalledExtensionsWithGallery(galleryExtensions); @@ -1955,6 +1969,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } await Promise.allSettled(extensions.map(extensions => extensions.syncInstalledExtensionsWithGallery(gallery, this.getProductVersion()))); if (this.outdated.length) { + this.logService.info(`Auto updating outdated extensions.`, this.outdated.map(e => e.identifier.id).join(', ')); this.eventuallyAutoUpdateExtensions(); } } @@ -1986,7 +2001,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } private async autoUpdateBuiltinExtensions(): Promise { - await this.checkForUpdates(true); + await this.checkForUpdates(undefined, true); const toUpdate = this.outdated.filter(e => e.isBuiltin); await Promises.settled(toUpdate.map(e => this.install(e, e.local?.preRelease ? { installPreReleaseVersion: true } : undefined))); } @@ -2010,9 +2025,11 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension const toUpdate: IExtension[] = []; for (const extension of this.outdated) { if (!this.shouldAutoUpdateExtension(extension)) { + this.logService.info('Auto update disabled for extension', extension.identifier.id); continue; } if (await this.shouldRequireConsentToUpdate(extension)) { + this.logService.info('Auto update consent required for extension', extension.identifier.id); continue; } toUpdate.push(extension); @@ -2023,7 +2040,10 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } const productVersion = this.getProductVersion(); - await Promises.settled(toUpdate.map(e => this.install(e, e.local?.preRelease ? { installPreReleaseVersion: true, productVersion } : { productVersion }))); + await Promises.settled(toUpdate.map(e => { + this.logService.info('Auto updating extension', e.identifier.id); + return this.install(e, e.local?.preRelease ? { installPreReleaseVersion: true, productVersion } : { productVersion }); + })); } private getProductVersion(): IProductVersion { @@ -2278,7 +2298,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.")); } @@ -2383,10 +2403,15 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (!installable) { if (!gallery) { const id = isString(arg) ? arg : (arg).identifier.id; + const manifest = await this.extensionGalleryManifestService.getExtensionGalleryManifest(); + const reportIssueUri = manifest ? getExtensionGalleryManifestResourceUri(manifest, ExtensionGalleryResourceType.ReportIssueUri) : undefined; + const reportIssueMessage = reportIssueUri ? nls.localize('report issue', "If this issue persists, please report it at {0}", reportIssueUri.toString()) : ''; if (installOptions.version) { - throw new Error(nls.localize('not found version', "Unable to install extension '{0}' because the requested version '{1}' is not found.", id, installOptions.version)); + const message = nls.localize('not found version', "The extension '{0}' cannot be installed because the requested version '{1}' was not found.", id, installOptions.version); + throw new ExtensionManagementError(reportIssueMessage ? `${message} ${reportIssueMessage}` : message, ExtensionManagementErrorCode.NotFound); } else { - throw new Error(nls.localize('not found', "Unable to install extension '{0}' because it is not found.", id)); + const message = nls.localize('not found', "The extension '{0}' cannot be installed because it was not found.", id); + throw new ExtensionManagementError(reportIssueMessage ? `${message} ${reportIssueMessage}` : message, ExtensionManagementErrorCode.NotFound); } } installable = gallery; @@ -2466,7 +2491,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return extension; } - async installInServer(extension: IExtension, server: IExtensionManagementServer): Promise { + async installInServer(extension: IExtension, server: IExtensionManagementServer, installOptions?: InstallOptions): Promise { await this.doInstall(extension, async () => { const local = extension.local; if (!local) { @@ -2476,7 +2501,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension extension = (await this.getExtensions([{ ...extension.identifier, preRelease: local.preRelease }], CancellationToken.None))[0] ?? extension; } if (extension.gallery) { - return server.extensionManagementService.installFromGallery(extension.gallery, { installPreReleaseVersion: local.preRelease }); + return server.extensionManagementService.installFromGallery(extension.gallery, { installPreReleaseVersion: local.preRelease, ...installOptions }); } const targetPlatform = await server.extensionManagementService.getTargetPlatform(); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css index 30bbba04..df7aaffb 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; - display: flex; - align-items: center; line-height: 24px; } -.extension-list-item > .details > .footer > .author > .publisher-name { +.extension-list-item > .details > .footer .publisher { + display: flex; + align-items: center; +} + +.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 03023ddf..12ea3c20 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 { @@ -472,15 +477,13 @@ padding-bottom: 15px; } -.extension-editor > .body > .content > .details > .additional-details-container .categories-container > .categories > .category, -.extension-editor > .body > .content > .details > .additional-details-container .tags-container > .tags > .tag { +.extension-editor > .body > .content > .details > .additional-details-container .categories-container > .categories > .category { display: inline-block; border: 1px solid rgba(136, 136, 136, 0.45); padding: 2px 4px; border-radius: 2px; font-size: 90%; margin: 0px 6px 3px 0px; - cursor: pointer; } .extension-editor > .body > .content > .details > .additional-details-container .resources-container > .resources > .resource { @@ -734,7 +737,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 6a7ab84a..c5d81e6c 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/extensionQuery.ts b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts index 68320b14..cd74a11b 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IExtensionGalleryManifest } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; +import { FilterType, SortBy } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { EXTENSION_CATEGORIES } from '../../../../platform/extensions/common/extensions.js'; export class Query { @@ -11,11 +13,32 @@ export class Query { this.value = value.trim(); } - static suggestions(query: string): string[] { - const commands = ['installed', 'updates', 'enabled', 'disabled', 'builtin', 'featured', 'popular', 'recommended', 'recentlyPublished', 'workspaceUnsupported', 'deprecated', 'sort', 'category', 'tag', 'ext', 'id', 'outdated', 'recentlyUpdated'] as const; + static suggestions(query: string, galleryManifest: IExtensionGalleryManifest | null): string[] { + + const commands = ['installed', 'updates', 'enabled', 'disabled', 'builtin']; + if (galleryManifest?.capabilities.extensionQuery?.filtering?.some(c => c.name === FilterType.Featured)) { + commands.push('featured'); + } + + commands.push(...['popular', 'recommended', 'recentlyPublished', 'workspaceUnsupported', 'deprecated', 'sort']); + const isCategoriesEnabled = galleryManifest?.capabilities.extensionQuery?.filtering?.some(c => c.name === FilterType.Category); + if (isCategoriesEnabled) { + commands.push('category'); + } + + commands.push(...['tag', 'ext', 'id', 'outdated', 'recentlyUpdated']); + const sortCommands = []; + if (galleryManifest?.capabilities.extensionQuery?.sorting?.some(c => c.name === SortBy.InstallCount)) { + sortCommands.push('installs'); + } + if (galleryManifest?.capabilities.extensionQuery?.sorting?.some(c => c.name === SortBy.WeightedRating)) { + sortCommands.push('rating'); + } + sortCommands.push('name', 'publishedDate', 'updateDate'); + const subcommands = { - 'sort': ['installs', 'rating', 'name', 'publishedDate', 'updateDate'], - 'category': EXTENSION_CATEGORIES.map(c => `"${c.toLowerCase()}"`), + 'sort': sortCommands, + 'category': isCategoriesEnabled ? EXTENSION_CATEGORIES.map(c => `"${c.toLowerCase()}"`) : [], 'tag': [''], 'ext': [''], 'id': [''] diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 97e8d4b6..ce233779 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -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; @@ -141,7 +143,7 @@ export interface IExtensionsWorkbenchService { install(id: string, installOptions?: InstallExtensionOptions, progressLocation?: ProgressLocation | string): Promise; install(vsix: URI, installOptions?: InstallExtensionOptions, progressLocation?: ProgressLocation | string): Promise; install(extension: IExtension, installOptions?: InstallExtensionOptions, progressLocation?: ProgressLocation | string): Promise; - installInServer(extension: IExtension, server: IExtensionManagementServer): Promise; + installInServer(extension: IExtension, server: IExtensionManagementServer, installOptions?: InstallOptions): Promise; downloadVSIX(extension: string, prerelease: boolean): Promise; uninstall(extension: IExtension): Promise; togglePreRelease(extension: IExtension): Promise; @@ -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 d7ab04b6..e78c2df7 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 9bdf631a..03228337 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 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'; @@ -11,6 +12,7 @@ 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 { 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'; @@ -25,18 +27,58 @@ import { IAuthenticationService } from '../../../services/authentication/common/ import { IExtensionManagementServerService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { IExtensionManifestPropertiesService } from '../../../services/extensions/common/extensionManifestPropertiesService.js'; import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; +import { IExtensionsWorkbenchService } from '../common/extensions.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 + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { + this.installExtensionsIfInstalledLocallyInRemote(); this.installFailedRemoteExtensions(); } + private async installExtensionsIfInstalledLocallyInRemote(): 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; + } + + const alreadyInstalledLocally = await this.extensionsWorkbenchService.queryLocal(this.extensionManagementServerService.localExtensionManagementServer); + const alreadyInstalledRemotely = await this.extensionsWorkbenchService.queryLocal(this.extensionManagementServerService.remoteExtensionManagementServer); + const extensionsToInstall = alreadyInstalledLocally + .filter(ext => settingValue.some(id => areSameExtensions(ext.identifier, { id }))) + .filter(ext => !alreadyInstalledRemotely.some(e => areSameExtensions(e.identifier, ext.identifier))); + + + if (!extensionsToInstall.length) { + return; + } + + await Promise.allSettled(extensionsToInstall.map(ext => { + this.extensionsWorkbenchService.installInServer(ext, this.extensionManagementServerService.remoteExtensionManagementServer!, { donotIncludePackAndDependencies: true }); + })); + } + private async installFailedRemoteExtensions(): Promise { if (!this.remoteAgentService.getConnection()) { return; diff --git a/src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts b/src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts index 61457a1e..6d31b784 100644 --- a/src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts +++ b/src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts @@ -140,10 +140,10 @@ suite('Extension query', () => { }); test('autocomplete', () => { - Query.suggestions('@sort:in').some(x => x === '@sort:installs '); - Query.suggestions('@sort:installs').every(x => x !== '@sort:rating '); + Query.suggestions('@sort:in', null).some(x => x === '@sort:installs '); + Query.suggestions('@sort:installs', null).every(x => x !== '@sort:rating '); - Query.suggestions('@category:blah').some(x => x === '@category:"extension packs" '); - Query.suggestions('@category:"extension packs"').every(x => x !== '@category:formatters '); + Query.suggestions('@category:blah', null).some(x => x === '@category:"extension packs" '); + Query.suggestions('@category:"extension packs"', null).every(x => x !== '@category:formatters '); }); }); diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index addaf290..c8c1f5e3 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 cb60e6d4..61f0ac0a 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/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 69cfd27a..267b609b 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -234,11 +234,10 @@ async function resourcesToClipboard(resources: URI[], relative: boolean, clipboa const lineDelimiter = isWindows ? '\r\n' : '\n'; let separator: '/' | '\\' | undefined = undefined; - if (relative) { - const relativeSeparator = configurationService.getValue('explorer.copyRelativePathSeparator'); - if (relativeSeparator === '/' || relativeSeparator === '\\') { - separator = relativeSeparator; - } + const copyRelativeOrFullPathSeparatorSection = relative ? 'explorer.copyRelativePathSeparator' : 'explorer.copyPathSeparator'; + const copyRelativeOrFullPathSeparator: '/' | '\\' | undefined = configurationService.getValue(copyRelativeOrFullPathSeparatorSection); + if (copyRelativeOrFullPathSeparator === '/' || copyRelativeOrFullPathSeparator === '\\') { + separator = copyRelativeOrFullPathSeparator; } const text = resources.map(resource => labelService.getUriLabel(resource, { relative, noPrefix: true, separator })).join(lineDelimiter); diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 606dcf8e..b15c9c52 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -584,6 +584,21 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('copyRelativePathSeparator', "The path separation character used when copying relative file paths."), 'default': 'auto' }, + 'explorer.copyPathSeparator': { + 'type': 'string', + 'enum': [ + '/', + '\\', + 'auto' + ], + 'enumDescriptions': [ + nls.localize('copyPathSeparator.slash', "Use slash as path separation character."), + nls.localize('copyPathSeparator.backslash', "Use backslash as path separation character."), + nls.localize('copyPathSeparator.auto', "Uses operating system specific path separation character."), + ], + 'description': nls.localize('copyPathSeparator', "The path separation character used when copying file paths."), + 'default': 'auto' + }, 'explorer.excludeGitIgnore': { type: 'boolean', markdownDescription: nls.localize('excludeGitignore', "Controls whether entries in .gitignore should be parsed and excluded from the Explorer. Similar to {0}.", '`#files.exclude#`'), diff --git a/src/vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty.ts b/src/vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty.ts index 0494623f..ab3d1dab 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 7fa126ec..dc31d83b 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'; @@ -577,20 +577,27 @@ 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', + when: CTX_INLINE_CHAT_HAS_AGENT2 + } }); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index b42f3ded..6ce57174 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -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', @@ -1237,8 +1237,7 @@ export class InlineChatController2 implements IEditorContribution { { enableWorkingSet: 'implicit', rendererOptions: { - renderCodeBlockPills: true, - renderTextEditsAsSummary: uri => isEqual(uri, _editor.getModel()?.uri) + renderTextEditsAsSummary: _uri => true } }, this._editor @@ -1423,9 +1422,9 @@ export async function reviewEdits(accessor: ServicesAccessor, editor: ICodeEdito const chatEditingService = accessor.get(IChatEditingService); const uri = editor.getModel().uri; - const chatModel = chatService.startSession(ChatAgentLocation.Editor, token); + const chatModel = chatService.startSession(ChatAgentLocation.Editor, token, false); - const editSession = await chatEditingService.createEditingSession(chatModel.sessionId); + const editSession = await chatEditingService.createEditingSession(chatModel); const store = new DisposableStore(); store.add(chatModel); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index 0e6b7f98..c17f655b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -27,7 +27,7 @@ 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'; @@ -39,6 +39,7 @@ 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")); @@ -227,7 +228,7 @@ export class InlineChatHintsController extends Disposable implements IEditorCont const ghostState = ghostCtrl?.model.read(r)?.state.read(r); const textFocus = editorObs.isTextFocused.read(r); - const position = editorObs.cursorPosition.read(r); + let position = editorObs.cursorPosition.read(r); const model = editorObs.model.read(r); const kb = keyObs.read(r); @@ -240,12 +241,16 @@ 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); + // position can be wrong + position = model.validatePosition(position); + 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/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index 5f06c2da..97b36199 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -5,11 +5,16 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../base/common/map.js'; import { Schemas } from '../../../../base/common/network.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { isEqual } from '../../../../base/common/resources.js'; +import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { IActiveCodeEditor, ICodeEditor, isCodeEditor, isCompositeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; import { Range } from '../../../../editor/common/core/range.js'; +import { ILanguageService } from '../../../../editor/common/languages/language.js'; import { IValidEditOperation } from '../../../../editor/common/model.js'; import { createTextBufferFactoryFromSnapshot } from '../../../../editor/common/model/textModel.js'; import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; @@ -20,20 +25,17 @@ 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 { 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'; +import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { UntitledTextEditorInput } from '../../../services/untitled/common/untitledTextEditorInput.js'; +import { IChatWidgetService } from '../../chat/browser/chat.js'; +import { IChatAgentService } from '../../chat/common/chatAgents.js'; +import { WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; +import { IChatService } from '../../chat/common/chatService.js'; +import { ChatAgentLocation } from '../../chat/common/constants.js'; +import { CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_AGENT2, CTX_INLINE_CHAT_POSSIBLE } from '../common/inlineChat.js'; import { HunkData, Session, SessionWholeRange, StashedSession, TelemetryData, TelemetryDataClassification } from './inlineChatSession.js'; import { IInlineChatSession2, IInlineChatSessionEndEvent, IInlineChatSessionEvent, IInlineChatSessionService, ISessionKeyComputer } from './inlineChatSessionService.js'; -import { isEqual } from '../../../../base/common/resources.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; -import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; -import { assertType } from '../../../../base/common/types.js'; -import { autorun } from '../../../../base/common/observable.js'; -import { ResourceMap } from '../../../../base/common/map.js'; type SessionData = { @@ -84,7 +86,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { @ILanguageService private readonly _languageService: ILanguageService, @IChatService private readonly _chatService: IChatService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, - @IChatEditingService private readonly _chatEditingService: IChatEditingService, + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, ) { } dispose() { @@ -162,7 +164,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { })); store.add(this._chatAgentService.onDidChangeAgents(e => { - if (e === undefined && (!this._chatAgentService.getAgent(agent.id) || !this._chatAgentService.getActivatedAgents().includes(agent))) { + if (e === undefined && (!this._chatAgentService.getAgent(agent.id) || !this._chatAgentService.getActivatedAgents().map(agent => agent.id).includes(agent.id))) { this._logService.trace(`[IE] provider GONE for ${editor.getId()}, ${agent.extensionId}`); this._releaseSession(session, true); } @@ -335,10 +337,11 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { this._onWillStartSession.fire(editor as IActiveCodeEditor); - const chatModel = this._chatService.startSession(ChatAgentLocation.EditingSession, token); + const chatModel = this._chatService.startSession(ChatAgentLocation.EditingSession, token, false); - const editingSession = await this._chatEditingService.createEditingSession(chatModel.sessionId); - editingSession.addFileToWorkingSet(uri); + const editingSession = await chatModel.editingSessionObs?.promise!; + const widget = this._chatWidgetService.getWidgetBySessionId(chatModel.sessionId); + await widget?.attachmentModel.addFile(uri); const store = new DisposableStore(); store.add(toDisposable(() => { @@ -347,7 +350,6 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { this._sessions2.delete(uri); this._onDidChangeSessions.fire(this); })); - store.add(editingSession); store.add(chatModel); store.add(autorun(r => { @@ -359,10 +361,11 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { const allSettled = entries.every(entry => { const state = entry.state.read(r); - return state === WorkingSetEntryState.Accepted || state === WorkingSetEntryState.Rejected; + return (state === WorkingSetEntryState.Accepted || state === WorkingSetEntryState.Rejected) + && !entry.isCurrentlyBeingModifiedBy.read(r); }); - if (allSettled) { + if (allSettled && !chatModel.requestInProgress) { // self terminate store.dispose(); } @@ -381,7 +384,19 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { } getSession2(uri: URI): IInlineChatSession2 | undefined { - return this._sessions2.get(uri); + let result = this._sessions2.get(uri); + if (!result) { + // no direct session, try to find an editing session which has a file entry for the uri + for (const [_, candidate] of this._sessions2) { + const entry = candidate.editingSession.getEntry(uri); + if (entry) { + result = candidate; + break; + } + } + } + + return result; } } @@ -406,10 +421,10 @@ export class InlineChatEnabler { const updateAgent = () => { const agent = chatAgentService.getDefaultAgent(ChatAgentLocation.Editor); - if (agent?.locations.length === 1) { + if (agent?.id === 'github.copilot.editor' || agent?.id === 'setup.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 138b5191..83836042 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 d2a96c1d..3b6979b8 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 185476fb..49aa4074 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 f2d8cc86..5e202422 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/inlineChat/test/browser/testWorkerService.ts b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts index b8e2b138..416e3cf3 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts @@ -9,7 +9,7 @@ import { IModelService } from '../../../../../editor/common/services/model.js'; import { assertType } from '../../../../../base/common/types.js'; import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from '../../../../../editor/common/services/editorWorker.js'; import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../../../../editor/common/diff/documentDiffProvider.js'; -import { BaseEditorSimpleWorker } from '../../../../../editor/common/services/editorSimpleWorker.js'; +import { EditorWorker } from '../../../../../editor/common/services/editorWebWorker.js'; import { LineRange } from '../../../../../editor/common/core/lineRange.js'; import { MovedText } from '../../../../../editor/common/diff/linesDiffComputer.js'; import { LineRangeMapping, DetailedLineRangeMapping, RangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; @@ -18,7 +18,7 @@ import { TextEdit } from '../../../../../editor/common/languages.js'; export class TestWorkerService extends mock() { - private readonly _worker = new BaseEditorSimpleWorker(); + private readonly _worker = new EditorWorker(); constructor(@IModelService private readonly _modelService: IModelService) { super(); diff --git a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts index 4f8c7c47..75930b38 100644 --- a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts +++ b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts @@ -69,7 +69,7 @@ export class InlineCompletionLanguageStatusBarContribution extends Disposable { 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 38da042c..c5074ca0 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 bf42337b..c587880a 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 fbd7fbb3..e739f448 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 14ee09be..23f3be9a 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 a2805613..0c1effb4 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.feedback.enabled')) { + 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 4ff3e642..547921c9 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.feedback.enabled')) { + 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 e6be6f94..bb27e35e 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 c88a95a9..aebbe980 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 c2022340..f6ae1519 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/limitIndicator/browser/limitIndicator.contribution.ts b/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts index 538e67d5..0640e070 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 af8e6eaf..3a10bafb 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 6e639a98..4ed27e35 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 00000000..1e5cef15 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { 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 { CursorWorkspaceMcpDiscoveryAdapter } from '../common/discovery/workspaceMcpDiscoveryAdapter.js'; +import { IMcpConfigPathsService, McpConfigPathsService } from '../common/mcpConfigPathsService.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, EditStoredInput, InstallFromActivation, ListMcpServerCommand, MCPServerActionRendering, McpServerOptionsCommand, RemoveStoredInput, ResetMcpCachedTools, ResetMcpTrustCommand, RestartServer, ShowOutput, StartServer, StopServer } from './mcpCommands.js'; +import { McpDiscovery } from './mcpDiscovery.js'; +import { McpLanguageFeatures } from './mcpLanguageFeatures.js'; +import { McpUrlHandler } from './mcpUrlHandler.js'; + +registerSingleton(IMcpRegistry, McpRegistry, InstantiationType.Delayed); +registerSingleton(IMcpService, McpService, InstantiationType.Delayed); +registerSingleton(IMcpConfigPathsService, McpConfigPathsService, InstantiationType.Delayed); + +mcpDiscoveryRegistry.register(new SyncDescriptor(RemoteNativeMpcDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(ConfigMcpDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(ExtensionMcpDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(CursorWorkspaceMcpDiscoveryAdapter)); + +registerWorkbenchContribution2('mcpDiscovery', McpDiscovery, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2('mcpContextKeys', McpContextKeysController, WorkbenchPhase.BlockRestore); +registerWorkbenchContribution2('mcpLanguageFeatures', McpLanguageFeatures, WorkbenchPhase.Eventually); +registerWorkbenchContribution2('mcpUrlHandler', McpUrlHandler, WorkbenchPhase.BlockRestore); + +registerAction2(ListMcpServerCommand); +registerAction2(McpServerOptionsCommand); +registerAction2(ResetMcpTrustCommand); +registerAction2(ResetMcpCachedTools); +registerAction2(AddConfigurationAction); +registerAction2(RemoveStoredInput); +registerAction2(EditStoredInput); +registerAction2(StartServer); +registerAction2(StopServer); +registerAction2(ShowOutput); +registerAction2(InstallFromActivation); +registerAction2(RestartServer); + +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 00000000..6f536d3a --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts @@ -0,0 +1,559 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { assertNever } from '../../../../base/common/assert.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 { URI } from '../../../../base/common/uri.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 { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js'; +import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { spinningLoading } from '../../../../platform/theme/common/iconRegistry.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { ActiveEditorContext, ResourceContextKey } from '../../../common/contextkeys.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { IEditorService } from '../../../services/editor/common/editorService.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'; +import { McpUrlHandler } from './mcpUrlHandler.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.placeholder = 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); + const firstRun = pick.items.length === 0; + pick.items = [ + { id: '$add', label: localize('mcp.addServer', 'Add Server'), description: localize('mcp.addServer.description', 'Add a new server configuration'), alwaysShow: true, iconClass: ThemeIcon.asClassName(Codicon.add) }, + ...Object.values(servers).filter(s => s.length).flatMap((servers): (ItemType | IQuickPickSeparator)[] => [ + { 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)), + })), + ]), + ]; + + if (firstRun && pick.items.length > 3) { + pick.activeItems = pick.items.slice(2, 3) as ItemType[]; // select the first server by default + } + })); + + + 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) { + // no-op + } else if (picked.id === '$add') { + commandService.executeCommand(AddConfigurationAction.ID); + } else { + 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 mcpRegistry = accessor.get(IMcpRegistry); + const editorService = accessor.get(IEditorService); + const server = mcpService.servers.get().find(s => s.definition.id === id); + if (!server) { + return; + } + + + const collection = mcpRegistry.collections.get().find(c => c.id === server.collection.id); + const serverDefinition = collection?.serverDefinitions.get().find(s => s.id === server.definition.id); + + interface ActionItem extends IQuickPickItem { + action: 'start' | 'stop' | 'restart' | 'showOutput' | 'config'; + } + + 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: localize('mcp.start', 'Start Server'), + action: 'start' + }); + } else { + items.push({ + label: localize('mcp.stop', 'Stop Server'), + action: 'stop' + }); + items.push({ + label: localize('mcp.restart', 'Restart Server'), + action: 'restart' + }); + } + + items.push({ + label: localize('mcp.showOutput', 'Show Output'), + action: 'showOutput' + }); + + const configTarget = serverDefinition?.presentation?.origin || collection?.presentation?.origin; + if (configTarget) { + items.push({ + label: localize('mcp.config', 'Show Configuration'), + action: 'config', + }); + } + + 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; + case 'config': + editorService.openEditor({ + resource: URI.isUri(configTarget) ? configTarget : configTarget!.uri, + options: { selection: URI.isUri(configTarget) ? undefined : configTarget!.range } + }); + break; + default: + assertNever(pick.action); + } + } +} + +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(); + } +} + + +export class RemoveStoredInput extends Action2 { + static readonly ID = 'workbench.mcp.removeStoredInput'; + + constructor() { + super({ + id: RemoveStoredInput.ID, + title: localize2('mcp.resetCachedTools', "Reset Cached Tools"), + category, + f1: false, + }); + } + + run(accessor: ServicesAccessor, scope: StorageScope, id?: string): void { + accessor.get(IMcpRegistry).clearSavedInputs(scope, id); + } +} + +export class EditStoredInput extends Action2 { + static readonly ID = 'workbench.mcp.editStoredInput'; + + constructor() { + super({ + id: EditStoredInput.ID, + title: localize2('mcp.editStoredInput', "Edit Stored Input"), + category, + f1: false, + }); + } + + run(accessor: ServicesAccessor, inputId: string, uri: URI | undefined, configSection: string, target: ConfigurationTarget): void { + const workspaceFolder = uri && accessor.get(IWorkspaceContextService).getWorkspaceFolder(uri); + accessor.get(IMcpRegistry).editSavedInput(inputId, workspaceFolder || undefined, configSection, target); + } +} + +export class ShowOutput extends Action2 { + static readonly ID = 'workbench.mcp.showOutput'; + + constructor() { + super({ + id: ShowOutput.ID, + title: localize2('mcp.command.showOutput', "Show Output"), + category, + f1: false, + }); + } + + run(accessor: ServicesAccessor, serverId: string): void { + accessor.get(IMcpService).servers.get().find(s => s.definition.id === serverId)?.showOutput(); + } +} + +export class RestartServer extends Action2 { + static readonly ID = 'workbench.mcp.restartServer'; + + constructor() { + super({ + id: RestartServer.ID, + title: localize2('mcp.command.restartServer', "Restart Server"), + category, + f1: false, + }); + } + + async run(accessor: ServicesAccessor, serverId: string) { + const s = accessor.get(IMcpService).servers.get().find(s => s.definition.id === serverId); + s?.showOutput(); + await s?.stop(); + await s?.start(); + } +} + +export class StartServer extends Action2 { + static readonly ID = 'workbench.mcp.startServer'; + + constructor() { + super({ + id: StartServer.ID, + title: localize2('mcp.command.startServer', "Start Server"), + category, + f1: false, + }); + } + + async run(accessor: ServicesAccessor, serverId: string) { + const s = accessor.get(IMcpService).servers.get().find(s => s.definition.id === serverId); + await s?.start(); + } +} + +export class StopServer extends Action2 { + static readonly ID = 'workbench.mcp.stopServer'; + + constructor() { + super({ + id: StopServer.ID, + title: localize2('mcp.command.stopServer', "Stop Server"), + category, + f1: false, + }); + } + + async run(accessor: ServicesAccessor, serverId: string) { + const s = accessor.get(IMcpService).servers.get().find(s => s.definition.id === serverId); + await s?.stop(); + } +} + +export class InstallFromActivation extends Action2 { + static readonly ID = 'workbench.mcp.installFromActivation'; + + constructor() { + super({ + id: InstallFromActivation.ID, + title: localize2('mcp.command.installFromActivation', "Install..."), + category, + f1: false, + menu: { + id: MenuId.EditorContent, + when: ContextKeyExpr.equals('resourceScheme', McpUrlHandler.scheme) + } + }); + } + + async run(accessor: ServicesAccessor, uri: URI) { + const addConfigHelper = accessor.get(IInstantiationService).createInstance(McpAddConfigurationCommand, undefined); + addConfigHelper.pickForUrlHandler(uri); + } +} 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 00000000..f61417a6 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts @@ -0,0 +1,498 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { mapFindFirst } from '../../../../base/common/arraysFind.js'; +import { assertNever } from '../../../../base/common/assert.js'; +import { disposableTimeout } from '../../../../base/common/async.js'; +import { parse as parseJsonc } from '../../../../base/common/jsonc.js'; +import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { basename } from '../../../../base/common/resources.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 { IFileService } from '../../../../platform/files/common/files.js'; +import { IMcpConfiguration, IMcpConfigurationSSE, McpConfigurationServer } from '../../../../platform/mcp/common/mcpPlatformTypes.js'; +import { INotificationService } from '../../../../platform/notification/common/notification.js'; +import { IQuickInputService, IQuickPickItem, QuickPickInput } from '../../../../platform/quickinput/common/quickInput.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { EditorsOrder } from '../../../common/editor.js'; +import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js'; +import { ConfiguredInput } from '../../../services/configurationResolver/common/configurationResolver.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { IMcpConfigurationStdio, mcpConfigurationSection, mcpStdioServerSchema } from '../common/mcpConfiguration.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; +import { McpServerOptionsCommand } from './mcpCommands.js'; + +const enum AddConfigurationType { + Stdio, + SSE, + + NpmPackage, + PipPackage, + DockerImage, +} + +type AssistedConfigurationType = AddConfigurationType.NpmPackage | AddConfigurationType.PipPackage | AddConfigurationType.DockerImage; + +const assistedTypes = { + [AddConfigurationType.NpmPackage]: { + title: localize('mcp.npm.title', "Enter NPM Package Name"), + placeholder: localize('mcp.npm.placeholder', "Package name (e.g., @org/package)"), pickLabel: localize('mcp.serverType.npm', "NPM Package"), + pickDescription: localize('mcp.serverType.npm.description', "Install from an NPM package name") + }, + [AddConfigurationType.PipPackage]: { + title: localize('mcp.pip.title', "Enter Pip Package Name"), + placeholder: localize('mcp.pip.placeholder', "Package name (e.g., package-name)"), + pickLabel: localize('mcp.serverType.pip', "Pip Package"), + pickDescription: localize('mcp.serverType.pip.description', "Install from a Pip package name") + }, + [AddConfigurationType.DockerImage]: { + title: localize('mcp.docker.title', "Enter Docker Image Name"), + placeholder: localize('mcp.docker.placeholder', "Image name (e.g., mcp/imagename)"), + pickLabel: localize('mcp.serverType.docker', "Docker Image"), + pickDescription: localize('mcp.serverType.docker.description', "Install from a Docker image") + }, +}; + +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 }; + +type AddServerData = { + packageType: string; +}; +type AddServerClassification = { + owner: 'digitarald'; + comment: 'Generic details for adding a new MCP server'; + packageType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of MCP server package' }; +}; +type AddServerCompletedData = { + packageType: string; + serverType: string | undefined; + target: string; +}; +type AddServerCompletedClassification = { + owner: 'digitarald'; + comment: 'Generic details for successfully adding model-assisted MCP server'; + packageType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of MCP server package' }; + serverType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of MCP server' }; + target: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The target of the MCP server configuration' }; +}; + +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, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IEditorService private readonly _openerService: IEditorService, + @IEditorService private readonly _editorService: IEditorService, + @IFileService private readonly _fileService: IFileService, + @INotificationService private readonly _notificationService: INotificationService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + ) { } + + 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") }, + ...Object.entries(assistedTypes).map(([type, { pickLabel, pickDescription }]) => ({ + kind: Number(type) as AddConfigurationType, + label: pickLabel, + description: pickDescription, + })) + ); + } + + const result = await this._quickInputService.pick<{ kind: AddConfigurationType } & IQuickPickItem>(items, { + 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; + } + + this._telemetryService.publicLog2('mcp.addserver', { + packageType: 'stdio' + }); + + // 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; + } + + this._telemetryService.publicLog2('mcp.addserver', { + packageType: 'sse' + }); + + 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: AssistedConfigurationType): Promise<{ name: string; config: McpConfigurationServer } | undefined> { + const packageName = await this._quickInputService.input({ + ignoreFocusLost: true, + title: assistedTypes[type].title, + placeHolder: assistedTypes[type].placeholder, + }); + + 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; + + const packageType = this.getPackageType(type); + + this._telemetryService.publicLog2('mcp.addserver', { + packageType: packageType! + }); + + this._commandService.executeCommand( + AddConfigurationCopilotCommand.ValidatePackage, + { + type: packageType, + 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, + type: packageType + } + ); + + if (!configWithName) { + return undefined; + } + + const { name, ...config } = configWithName; + return { name, config }; + } + + /** Shows the location of a server config once it's discovered. */ + private showOnceDiscovered(name: string) { + const store = new DisposableStore(); + store.add(autorun(reader => { + const colls = this._mcpRegistry.collections.read(reader); + const match = mapFindFirst(colls, collection => mapFindFirst(collection.serverDefinitions.read(reader), + server => server.label === name ? { server, collection } : undefined)); + if (match) { + if (match.collection.presentation?.origin) { + this._openerService.openEditor({ + resource: match.collection.presentation.origin, + options: { + selection: match.server.presentation?.origin?.range, + preserveFocus: true, + } + }); + } else { + this._commandService.executeCommand(McpServerOptionsCommand.id, name); + } + + store.dispose(); + } + })); + + store.add(disposableTimeout(() => store.dispose(), 5000)); + } + + private writeToUserSetting(name: string, config: McpConfigurationServer, target: ConfigurationTarget, inputs?: ConfiguredInput[]) { + const settings: IMcpConfiguration = { ...getConfigValueInTarget(this._configurationService.inspect(mcpConfigurationSection), target) }; + settings.servers = { ...settings.servers, [name]: config }; + if (inputs) { + settings.inputs = [...(settings.inputs || []), ...inputs]; + } + return this._configurationService.updateValue(mcpConfigurationSection, settings, target); + } + + 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: + case AddConfigurationType.PipPackage: + case AddConfigurationType.DockerImage: { + const r = await this.getAssistedConfig(serverType); + 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 { + await this.writeToUserSetting(serverId, serverConfig, target!); + } + + const packageType = this.getPackageType(serverType); + if (packageType) { + this._telemetryService.publicLog2('mcp.addserver.completed', { + packageType, + serverType: serverConfig.type, + target: target === ConfigurationTarget.WORKSPACE ? 'workspace' : 'user' + }); + } + + this.showOnceDiscovered(serverId); + } + + public async pickForUrlHandler(resource: URI, showIsPrimary = false): Promise { + const name = decodeURIComponent(basename(resource)).replace(/\.json$/, ''); + const placeHolder = localize('install.title', 'Install MCP server {0}', name); + + const items: IQuickPickItem[] = [ + { id: 'install', label: localize('install.start', 'Install Server'), description: localize('install.description', 'Install in your user settings') }, + { id: 'show', label: localize('install.show', 'Show Configuration', name) }, + { id: 'rename', label: localize('install.rename', 'Rename "{0}"', name) }, + { id: 'cancel', label: localize('cancel', 'Cancel') }, + ]; + if (showIsPrimary) { + [items[0], items[1]] = [items[1], items[0]]; + } + + const pick = await this._quickInputService.pick(items, { placeHolder, ignoreFocusLost: true }); + const getEditors = () => this._editorService.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE) + .filter(e => e.editor.resource?.toString() === resource.toString()); + + switch (pick?.id) { + case 'show': + await this._editorService.openEditor({ resource }); + break; + case 'install': + await this._editorService.save(getEditors()); + try { + const contents = await this._fileService.readFile(resource); + const { inputs, ...config }: McpConfigurationServer & { inputs?: ConfiguredInput[] } = parseJsonc(contents.value.toString()); + await this.writeToUserSetting(name, config, ConfigurationTarget.USER_LOCAL, inputs); + this._editorService.closeEditors(getEditors()); + this.showOnceDiscovered(name); + } catch (e) { + this._notificationService.error(localize('install.error', 'Error installing MCP server {0}: {1}', name, e.message)); + await this._editorService.openEditor({ resource }); + } + break; + case 'rename': { + const newName = await this._quickInputService.input({ placeHolder: localize('install.newName', 'Enter new name'), value: name }); + if (newName) { + const newURI = resource.with({ path: `/${encodeURIComponent(newName)}.json` }); + await this._editorService.save(getEditors()); + await this._fileService.move(resource, newURI); + return this.pickForUrlHandler(newURI, showIsPrimary); + } + break; + } + } + } + + private getPackageType(serverType: AddConfigurationType): string | undefined { + switch (serverType) { + case AddConfigurationType.NpmPackage: + return 'npm'; + case AddConfigurationType.PipPackage: + return 'pip'; + case AddConfigurationType.DockerImage: + return 'docker'; + case AddConfigurationType.Stdio: + return 'stdio'; + case AddConfigurationType.SSE: + return 'sse'; + default: + return undefined; + } + } +} 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 00000000..dc07ad2a --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * 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 } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; +import { mcpEnabledSection } from '../common/mcpConfiguration.js'; + +export class McpDiscovery extends Disposable implements IWorkbenchContribution { + public static readonly ID = 'workbench.contrib.mcp.discovery'; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(); + + const enabled = observableConfigValue(mcpEnabledSection, true, configurationService); + const store = this._register(new DisposableStore()); + + this._register(autorun(reader => { + if (enabled.read(reader)) { + for (const discovery of mcpDiscoveryRegistry.getAll()) { + const inst = store.add(instantiationService.createInstance(discovery)); + inst.start(); + } + } else { + store.clear(); + } + })); + } +} diff --git a/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts b/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts new file mode 100644 index 00000000..8f81edd8 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts @@ -0,0 +1,372 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { computeLevenshteinDistance } from '../../../../base/common/diff/diff.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { markdownCommandLink, MarkdownString } from '../../../../base/common/htmlContent.js'; +import { findNodeAtLocation, Node, parseTree } from '../../../../base/common/json.js'; +import { Disposable, DisposableStore, dispose, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; +import { IObservable } from '../../../../base/common/observable.js'; +import { isEqual } from '../../../../base/common/resources.js'; +import { Range } from '../../../../editor/common/core/range.js'; +import { CodeLensList, CodeLensProvider, InlayHint, InlayHintList } from '../../../../editor/common/languages.js'; +import { ITextModel } from '../../../../editor/common/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { localize } from '../../../../nls.js'; +import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js'; +import { ConfigurationResolverExpression, IResolvedValue } from '../../../services/configurationResolver/common/configurationResolverExpression.js'; +import { IMcpConfigPath, IMcpConfigPathsService } from '../common/mcpConfigPathsService.js'; +import { mcpConfigurationSection } from '../common/mcpConfiguration.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; +import { IMcpService, McpConnectionState } from '../common/mcpTypes.js'; +import { EditStoredInput, RemoveStoredInput, RestartServer, ShowOutput, StartServer, StopServer } from './mcpCommands.js'; + +const diagnosticOwner = 'vscode.mcp'; + +export class McpLanguageFeatures extends Disposable implements IWorkbenchContribution { + private readonly _cachedMcpSection = this._register(new MutableDisposable<{ model: ITextModel; inConfig: IMcpConfigPath; tree: Node } & IDisposable>()); + + constructor( + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IMcpConfigPathsService private readonly _mcpConfigPathsService: IMcpConfigPathsService, + @IMcpService private readonly _mcpService: IMcpService, + @IMarkerService private readonly _markerService: IMarkerService, + @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, + ) { + super(); + + const patterns = [ + { pattern: '**/.vscode/mcp.json' }, + { pattern: '**/settings.json' }, + { pattern: '**/workspace.json' }, + ]; + + const onDidChangeCodeLens = this._register(new Emitter()); + const codeLensProvider: CodeLensProvider = { + onDidChange: onDidChangeCodeLens.event, + provideCodeLenses: (model, range) => this._provideCodeLenses(model, () => onDidChangeCodeLens.fire(codeLensProvider)), + }; + this._register(languageFeaturesService.codeLensProvider.register(patterns, codeLensProvider)); + + this._register(languageFeaturesService.inlayHintsProvider.register(patterns, { + onDidChangeInlayHints: _mcpRegistry.onDidChangeInputs, + provideInlayHints: (model, range) => this._provideInlayHints(model, range), + })); + } + + /** Simple mechanism to avoid extra json parsing for hints+lenses */ + private _parseModel(model: ITextModel) { + if (this._cachedMcpSection.value?.model === model) { + return this._cachedMcpSection.value; + } + + const uri = model.uri; + const inConfig = this._mcpConfigPathsService.paths.get().find(u => isEqual(u.uri, uri)); + if (!inConfig) { + return undefined; + } + + const value = model.getValue(); + const tree = parseTree(value); + const listeners = [ + model.onDidChangeContent(() => this._cachedMcpSection.clear()), + model.onWillDispose(() => this._cachedMcpSection.clear()), + ]; + this._addDiagnostics(model, value, tree, inConfig); + + return this._cachedMcpSection.value = { + model, + tree, + inConfig, + dispose: () => { + this._markerService.remove(diagnosticOwner, [uri]); + dispose(listeners); + } + }; + } + + private _addDiagnostics(tm: ITextModel, value: string, tree: Node, inConfig: IMcpConfigPath) { + const serversNode = findNodeAtLocation(tree, inConfig.section ? [...inConfig.section, 'servers'] : ['servers']); + if (!serversNode) { + return; + } + + const getClosestMatchingVariable = (name: string) => { + let bestValue = ''; + let bestDistance = Infinity; + for (const variable of this._configurationResolverService.resolvableVariables) { + const distance = computeLevenshteinDistance(name, variable); + if (distance < bestDistance) { + bestDistance = distance; + bestValue = variable; + } + } + return bestValue; + }; + + const diagnostics: IMarkerData[] = []; + forEachPropertyWithReplacement(serversNode, node => { + const expr = ConfigurationResolverExpression.parse(node.value); + + for (const { id, name, arg } of expr.unresolved()) { + if (!this._configurationResolverService.resolvableVariables.has(name)) { + const position = value.indexOf(id, node.offset); + if (position === -1) { continue; } // unreachable? + + const start = tm.getPositionAt(position); + const end = tm.getPositionAt(position + id.length); + diagnostics.push({ + severity: MarkerSeverity.Warning, + message: localize('mcp.variableNotFound', 'Variable `{0}` not found, did you mean ${{1}}?', name, getClosestMatchingVariable(name) + (arg ? `:${arg}` : '')), + startLineNumber: start.lineNumber, + startColumn: start.column, + endLineNumber: end.lineNumber, + endColumn: end.column, + modelVersionId: tm.getVersionId(), + }); + } + } + }); + + if (diagnostics.length) { + this._markerService.changeOne(diagnosticOwner, tm.uri, diagnostics); + } else { + this._markerService.remove(diagnosticOwner, [tm.uri]); + } + } + + private _provideCodeLenses(model: ITextModel, onDidChangeCodeLens: () => void): CodeLensList | undefined { + const parsed = this._parseModel(model); + if (!parsed) { + return undefined; + } + + const { tree, inConfig } = parsed; + const serversNode = findNodeAtLocation(tree, inConfig.section ? [...inConfig.section, 'servers'] : ['servers']); + if (!serversNode) { + return undefined; + } + + const store = new DisposableStore(); + const lenses: CodeLensList = { lenses: [], dispose: () => store.dispose() }; + const read = (observable: IObservable): T => { + store.add(Event.fromObservableLight(observable)(onDidChangeCodeLens)); + return observable.get(); + }; + + const collection = read(this._mcpRegistry.collections).find(c => isEqual(c.presentation?.origin, model.uri)); + if (!collection) { + return lenses; + } + + const mcpServers = read(this._mcpService.servers).filter(s => s.collection.id === collection.id); + for (const node of serversNode.children || []) { + if (node.type !== 'property' || node.children?.[0]?.type !== 'string') { + continue; + } + + const name = node.children[0].value as string; + const server = mcpServers.find(s => s.definition.label === name); + if (!server) { + continue; + } + + const range = Range.fromPositions(model.getPositionAt(node.children[0].offset)); + switch (read(server.connectionState).state) { + case McpConnectionState.Kind.Error: + lenses.lenses.push({ + range, + command: { + id: ShowOutput.ID, + title: '$(error) ' + localize('server.error', 'Error'), + arguments: [server.definition.id], + }, + }, { + range, + command: { + id: RestartServer.ID, + title: localize('mcp.restart', "Restart"), + arguments: [server.definition.id], + }, + }); + break; + case McpConnectionState.Kind.Starting: + lenses.lenses.push({ + range, + command: { + id: ShowOutput.ID, + title: '$(loading~spin) ' + localize('server.starting', 'Starting'), + arguments: [server.definition.id], + }, + }, { + range, + command: { + id: StopServer.ID, + title: localize('cancel', "Cancel"), + arguments: [server.definition.id], + }, + }); + break; + case McpConnectionState.Kind.Running: + lenses.lenses.push({ + range, + command: { + id: ShowOutput.ID, + title: '$(check) ' + localize('server.running', 'Running'), + arguments: [server.definition.id], + }, + }, { + range, + command: { + id: StopServer.ID, + title: localize('mcp.stop', "Stop"), + arguments: [server.definition.id], + }, + }, { + range, + command: { + id: RestartServer.ID, + title: localize('mcp.restart', "Restart"), + arguments: [server.definition.id], + }, + }, { + range, + command: { + id: '', + title: localize('server.toolCount', '{0} tools', read(server.tools).length), + }, + }); + break; + case McpConnectionState.Kind.Stopped: { + lenses.lenses.push({ + range, + command: { + id: StartServer.ID, + title: '$(debug-start) ' + localize('mcp.start', "Start"), + arguments: [server.definition.id], + }, + }); + const toolCount = read(server.tools).length; + if (toolCount) { + lenses.lenses.push({ + range, + command: { + id: '', + title: localize('server.toolCountCached', '{0} cached tools', toolCount), + } + }); + } + } + } + } + + return lenses; + } + + private async _provideInlayHints(model: ITextModel, range: Range): Promise { + const parsed = this._parseModel(model); + if (!parsed) { + return undefined; + } + + const { tree, inConfig } = parsed; + const mcpSection = inConfig.section ? findNodeAtLocation(tree, [...inConfig.section]) : tree; + if (!mcpSection) { + return undefined; + } + + const inputsNode = findNodeAtLocation(mcpSection, ['inputs']); + if (!inputsNode) { + return undefined; + } + + const inputs = await this._mcpRegistry.getSavedInputs(inConfig.scope); + const hints: InlayHint[] = []; + + const serversNode = findNodeAtLocation(mcpSection, ['servers']); + if (serversNode) { + annotateServers(serversNode); + } + annotateInputs(inputsNode); + + return { hints, dispose: () => { } }; + + function annotateServers(servers: Node) { + forEachPropertyWithReplacement(servers, node => { + const expr = ConfigurationResolverExpression.parse(node.value); + for (const { id } of expr.unresolved()) { + const saved = inputs[id]; + if (saved) { + pushAnnotation(id, node.offset + node.value.indexOf(id) + id.length, saved); + } + } + }); + } + + function annotateInputs(node: Node) { + if (node.type !== 'array' || !node.children) { + return; + } + + for (const input of node.children) { + if (input.type !== 'object' || !input.children) { + continue; + } + + const idProp = input.children.find(c => c.type === 'property' && c.children?.[0].value === 'id'); + if (!idProp) { + continue; + } + + const id = idProp.children![1]; + if (!id || id.type !== 'string' || !id.value) { + continue; + } + + const savedId = '${input:' + id.value + '}'; + const saved = inputs[savedId]; + if (saved) { + pushAnnotation(savedId, id.offset + 1 + id.length, saved); + } + } + } + + function pushAnnotation(savedId: string, offset: number, saved: IResolvedValue): InlayHint { + const tooltip = new MarkdownString([ + markdownCommandLink({ id: EditStoredInput.ID, title: localize('edit', 'Edit'), arguments: [savedId, model.uri, mcpConfigurationSection, inConfig!.target] }), + markdownCommandLink({ id: RemoveStoredInput.ID, title: localize('clear', 'Clear'), arguments: [inConfig!.scope, savedId] }), + markdownCommandLink({ id: RemoveStoredInput.ID, title: localize('clearAll', 'Clear All'), arguments: [inConfig!.scope] }), + ].join(' | '), { isTrusted: true }); + + const hint: InlayHint = { + label: '= ' + (saved.input?.type === 'promptString' && saved.input.password ? '*'.repeat(10) : (saved.value || '')), + position: model.getPositionAt(offset), + tooltip, + paddingLeft: true, + }; + + hints.push(hint); + return hint; + } + } +} + + + +function forEachPropertyWithReplacement(node: Node, callback: (node: Node) => void) { + if (node.type === 'string' && typeof node.value === 'string' && node.value.includes(ConfigurationResolverExpression.VARIABLE_LHS)) { + callback(node); + } else if (node.type === 'property') { + // skip the property name + node.children?.slice(1).forEach(n => forEachPropertyWithReplacement(n, callback)); + } else { + node.children?.forEach(n => forEachPropertyWithReplacement(n, callback)); + } +} + + diff --git a/src/vs/workbench/contrib/mcp/browser/mcpUrlHandler.ts b/src/vs/workbench/contrib/mcp/browser/mcpUrlHandler.ts new file mode 100644 index 00000000..b6221244 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpUrlHandler.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 { VSBuffer } from '../../../../base/common/buffer.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { InMemoryFileSystemProvider } from '../../../../platform/files/common/inMemoryFilesystemProvider.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { McpConfigurationServer } from '../../../../platform/mcp/common/mcpPlatformTypes.js'; +import { IOpenURLOptions, IURLHandler, IURLService } from '../../../../platform/url/common/url.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { McpAddConfigurationCommand } from './mcpCommandsAddConfiguration.js'; + +const providerScheme = 'mcp-install'; + +export class McpUrlHandler extends Disposable implements IWorkbenchContribution, IURLHandler { + public static readonly scheme = providerScheme; + + + private readonly _fileSystemProvider = new Lazy(() => { + return this._instaService.invokeFunction(accessor => { + const fileService = accessor.get(IFileService); + const filesystem = new InMemoryFileSystemProvider(); + this._register(fileService.registerProvider(providerScheme, filesystem)); + return providerScheme; + }); + }); + + constructor( + @IURLService urlService: IURLService, + @IInstantiationService private readonly _instaService: IInstantiationService, + @IFileService private readonly _fileService: IFileService, + ) { + super(); + this._register(urlService.registerHandler(this)); + } + + async handleURL(uri: URI, options?: IOpenURLOptions): Promise { + if (uri.path !== 'mcp/install') { + return false; + } + + let parsed: McpConfigurationServer & { name: string }; + try { + parsed = JSON.parse(decodeURIComponent(uri.query)); + } catch (e) { + return false; + } + + const { name, ...rest } = parsed; + + const scheme = this._fileSystemProvider.value; + const fileUri = URI.from({ scheme, path: `/${encodeURIComponent(name)}.json` }); + + await this._fileService.writeFile( + fileUri, + VSBuffer.fromString(JSON.stringify(rest, null, '\t')), + ); + + const addConfigHelper = this._instaService.createInstance(McpAddConfigurationCommand, undefined); + addConfigHelper.pickForUrlHandler(fileUri, true); + + return Promise.resolve(true); + } +} 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 00000000..70969d39 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Throttler } from '../../../../../base/common/async.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { autorunDelta, ISettableObservable, observableValue } from '../../../../../base/common/observable.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { Location } from '../../../../../editor/common/languages.js'; +import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { getMcpServerMapping } from '../mcpConfigFileUtils.js'; +import { IMcpConfigPath, IMcpConfigPathsService } from '../mcpConfigPathsService.js'; +import { IMcpConfiguration, mcpConfigurationSection } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpServerDefinition, McpServerTransportType } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; + +interface ConfigSource { + path: IMcpConfigPath; + serverDefinitions: ISettableObservable; + disposable: MutableDisposable; + getServerToLocationMapping(uri: URI): Promise>; +} + +/** + * Discovers MCP servers based on various config sources. + */ +export class ConfigMcpDiscovery extends Disposable implements IMcpDiscovery { + private configSources: ConfigSource[] = []; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @ITextModelService private readonly _textModelService: ITextModelService, + @IMcpConfigPathsService private readonly _mcpConfigPathsService: IMcpConfigPathsService, + ) { + super(); + } + + public start() { + const throttler = this._register(new Throttler()); + + const addPath = (path: IMcpConfigPath) => { + this.configSources.push({ + path, + serverDefinitions: observableValue(this, []), + disposable: this._register(new MutableDisposable()), + getServerToLocationMapping: (uri) => this._getServerIdMapping(uri, path.section ? [...path.section, 'servers'] : ['servers']), + }); + }; + + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(mcpConfigurationSection)) { + throttler.queue(() => this.sync()); + } + })); + + this._register(autorunDelta(this._mcpConfigPathsService.paths, ({ lastValue, newValue }) => { + for (const last of lastValue || []) { + if (!newValue.includes(last)) { + const idx = this.configSources.findIndex(src => src.path.id === last.id); + if (idx !== -1) { + this.configSources[idx].disposable.dispose(); + this.configSources.splice(idx, 1); + } + } + } + + for (const next of newValue) { + if (!lastValue || !lastValue.includes(next)) { + addPath(next); + } + } + + this.sync(); + })); + } + + private async _getServerIdMapping(resource: URI, pathToServers: string[]): Promise> { + const store = new DisposableStore(); + try { + const ref = await this._textModelService.createModelReference(resource); + store.add(ref); + const serverIdMapping = getMcpServerMapping({ model: ref.object.textEditorModel, pathToServers }); + return serverIdMapping; + } catch { + return new Map(); + } finally { + store.dispose(); + } + } + + private async sync() { + const configurationKey = this._configurationService.inspect(mcpConfigurationSection); + const configMappings = await Promise.all(this.configSources.map(src => { + const uri = src.path.uri; + return uri && src.getServerToLocationMapping(uri); + })); + + for (const [index, src] of this.configSources.entries()) { + const collectionId = `mcp.config.${src.path.id}`; + // inspect() will give the first workspace folder, and must be + // asked for explicitly for other folders. + let value = src.path.workspaceFolder + ? this._configurationService.inspect(mcpConfigurationSection, { resource: src.path.workspaceFolder.uri })[src.path.key] + : configurationKey[src.path.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.path.target, { donotNotifyError: true }); + } + + const configMapping = configMappings[index]; + 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 || {}, + envFile: value.envFile, + cwd: undefined, + }, + roots: src.path.workspaceFolder ? [src.path.workspaceFolder.uri] : [], + variableReplacement: { + folder: src.path.workspaceFolder, + section: mcpConfigurationSection, + target: src.path.target, + }, + presentation: { + order: src.path.order, + origin: configMapping?.get(name), + } + })); + + 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.path.label, + presentation: { order: src.path.order, origin: src.path.uri }, + remoteAuthority: src.path.remoteAuthority || null, + serverDefinitions: src.serverDefinitions, + isTrustedByDefault: true, + scope: src.path.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 00000000..1c3a96b5 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { isFalsyOrWhitespace } from '../../../../../base/common/strings.js'; +import { localize } from '../../../../../nls.js'; +import { IMcpCollectionContribution } from '../../../../../platform/extensions/common/extensions.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) { + + if (!ExtensionMcpDiscovery._validate(collections)) { + continue; + } + + 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())); + } + + private static _validate(user: extensionsRegistry.IExtensionPointUser): boolean { + + if (!Array.isArray(user.value)) { + user.collector.error(localize('invalidData', "Expected an array of MCP collections")); + return false; + } + + for (const contribution of user.value) { + if (typeof contribution.id !== 'string' || isFalsyOrWhitespace(contribution.id)) { + user.collector.error(localize('invalidId', "Expected 'id' to be a non-empty string.")); + return false; + } + if (typeof contribution.label !== 'string' || isFalsyOrWhitespace(contribution.label)) { + user.collector.error(localize('invalidLabel', "Expected 'label' to be a non-empty string.")); + return false; + } + } + + return true; + } +} 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 00000000..a6a091cf --- /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 00000000..c2a7e649 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { VSBuffer } from '../../../../../base/common/buffer.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { autorunWithStore, IObservable, IReader, ISettableObservable, 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 { DiscoverySource, discoverySourceLabel, mcpDiscoverySection } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpCollectionDefinition, McpCollectionSortOrder, McpServerDefinition } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; +import { ClaudeDesktopMpcDiscoveryAdapter, CursorDesktopMpcDiscoveryAdapter, NativeMpcDiscoveryAdapter, WindsurfDesktopMpcDiscoveryAdapter } from './nativeMcpDiscoveryAdapters.js'; + +export type WritableMcpCollectionDefinition = McpCollectionDefinition & { serverDefinitions: ISettableObservable }; + +export abstract class FilesystemMcpDiscovery extends Disposable { + protected readonly _fsDiscoveryEnabled: IObservable; + + constructor( + @IConfigurationService configurationService: IConfigurationService, + @IFileService private readonly _fileService: IFileService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + ) { + super(); + + this._fsDiscoveryEnabled = observableConfigValue(mcpDiscoverySection, true, configurationService); + } + + protected _isDiscoveryEnabled(reader: IReader, discoverySource: DiscoverySource | undefined): boolean { + const fsDiscovery = this._fsDiscoveryEnabled.read(reader); + if (typeof fsDiscovery === 'boolean') { + return fsDiscovery; + } + if (discoverySource && fsDiscovery[discoverySource] === false) { + return false; + } + return true; + } + + protected watchFile( + file: URI, + collection: WritableMcpCollectionDefinition, + discoverySource: DiscoverySource | undefined, + adaptFile: (contents: VSBuffer) => McpServerDefinition[] | undefined, + ): IDisposable { + const store = new DisposableStore(); + const collectionRegistration = store.add(new MutableDisposable()); + const updateFile = async () => { + let definitions: McpServerDefinition[] = []; + try { + const contents = await this._fileService.readFile(file); + definitions = adaptFile(contents.value) || []; + } catch { + // ignored + } + if (!definitions.length) { + collectionRegistration.clear(); + } else { + collection.serverDefinitions.set(definitions, undefined); + if (!collectionRegistration.value) { + collectionRegistration.value = this._mcpRegistry.registerCollection(collection); + } + } + }; + + store.add(autorunWithStore((reader, store) => { + if (!this._isDiscoveryEnabled(reader, discoverySource)) { + 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(); + })); + + return store; + } +} + +/** + * Base class that discovers MCP servers on a filesystem, outside of the ones + * defined in VS Code settings. + */ +export abstract class NativeFilesystemMcpDiscovery extends FilesystemMcpDiscovery implements IMcpDiscovery { + private readonly adapters: readonly NativeMpcDiscoveryAdapter[]; + private suffix = ''; + + constructor( + remoteAuthority: string | null, + @ILabelService labelService: ILabelService, + @IFileService fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(configurationService, fileService, mcpRegistry); + if (remoteAuthority) { + this.suffix = ' ' + localize('onRemoteLabel', ' on {0}', labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority)); + } + + this.adapters = [ + instantiationService.createInstance(ClaudeDesktopMpcDiscoveryAdapter, remoteAuthority), + instantiationService.createInstance(CursorDesktopMpcDiscoveryAdapter, remoteAuthority), + instantiationService.createInstance(WindsurfDesktopMpcDiscoveryAdapter, 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: WritableMcpCollectionDefinition = { + id: adapter.id, + label: discoverySourceLabel[adapter.discoverySource] + this.suffix, + remoteAuthority: adapter.remoteAuthority, + scope: StorageScope.PROFILE, + isTrustedByDefault: false, + serverDefinitions: observableValue(this, []), + presentation: { + origin: file, + order: adapter.order + (adapter.remoteAuthority ? McpCollectionSortOrder.RemoteBoost : 0), + }, + }; + + this._register(this.watchFile(file, collection, adapter.discoverySource, contents => adapter.adaptFile(contents, details))); + } + } +} 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 00000000..0abe6cd9 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Mutable } from '../../../../../base/common/types.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { INativeMcpDiscoveryData } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { DiscoverySource } from '../mcpConfiguration.js'; +import { McpCollectionSortOrder, McpServerDefinition, McpServerTransportType } from '../mcpTypes.js'; + +export interface NativeMpcDiscoveryAdapter { + readonly remoteAuthority: string | null; + readonly id: string; + readonly order: number; + readonly discoverySource: DiscoverySource; + + getFilePath(details: INativeMcpDiscoveryData): URI | undefined; + adaptFile(contents: VSBuffer, details: INativeMcpDiscoveryData): McpServerDefinition[] | undefined; +} + +export function claudeConfigToServerDefinition(idPrefix: string, contents: VSBuffer, cwd?: URI) { + let parsed: { + mcpServers: Record; + url?: string; + }>; + }; + + try { + parsed = JSON.parse(contents.toString()); + } catch { + return; + } + + return Object.entries(parsed.mcpServers).map(([name, server]): Mutable => { + return { + id: `${idPrefix}.${name}`, + label: name, + launch: server.url ? { + type: McpServerTransportType.SSE, + uri: URI.parse(server.url), + headers: [], + } : { + type: McpServerTransportType.Stdio, + args: server.args || [], + command: server.command, + env: server.env || {}, + envFile: undefined, + cwd, + } + }; + }); +} + +export class ClaudeDesktopMpcDiscoveryAdapter implements NativeMpcDiscoveryAdapter { + public id: string; + public readonly order = McpCollectionSortOrder.Filesystem; + public readonly discoverySource: DiscoverySource = DiscoverySource.ClaudeDesktop; + + 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 { + return claudeConfigToServerDefinition(this.id, contents, homedir); + } +} + +export class WindsurfDesktopMpcDiscoveryAdapter extends ClaudeDesktopMpcDiscoveryAdapter { + public override readonly discoverySource: DiscoverySource = DiscoverySource.Windsurf; + + constructor(remoteAuthority: string | null) { + super(remoteAuthority); + this.id = `windsurf.${this.remoteAuthority}`; + } + + override getFilePath({ homedir }: INativeMcpDiscoveryData): URI | undefined { + return URI.joinPath(homedir, '.codeium', 'windsurf', 'mcp_config.json'); + } +} + +export class CursorDesktopMpcDiscoveryAdapter extends ClaudeDesktopMpcDiscoveryAdapter { + public override readonly discoverySource: DiscoverySource = DiscoverySource.CursorGlobal; + + constructor(remoteAuthority: string | null) { + super(remoteAuthority); + this.id = `cursor.${this.remoteAuthority}`; + } + + override getFilePath({ homedir }: INativeMcpDiscoveryData): URI | undefined { + return URI.joinPath(homedir, '.cursor', 'mcp.json'); + } +} 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 00000000..a2473c98 --- /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 { NativeFilesystemMcpDiscovery } from './nativeMcpDiscoveryAbstract.js'; + +/** + * Discovers MCP servers on the remote filesystem, if any. + */ +export class RemoteNativeMpcDiscovery extends NativeFilesystemMcpDiscovery { + 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/discovery/workspaceMcpDiscoveryAdapter.ts b/src/vs/workbench/contrib/mcp/common/discovery/workspaceMcpDiscoveryAdapter.ts new file mode 100644 index 00000000..4701f10c --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/workspaceMcpDiscoveryAdapter.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableMap, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../../base/common/observable.js'; +import { joinPath } from '../../../../../base/common/resources.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js'; +import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js'; +import { DiscoverySource } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpCollectionSortOrder } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; +import { FilesystemMcpDiscovery, WritableMcpCollectionDefinition } from './nativeMcpDiscoveryAbstract.js'; +import { claudeConfigToServerDefinition } from './nativeMcpDiscoveryAdapters.js'; + +export class CursorWorkspaceMcpDiscoveryAdapter extends FilesystemMcpDiscovery implements IMcpDiscovery { + private readonly _collections = this._register(new DisposableMap()); + + constructor( + @IFileService fileService: IFileService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IMcpRegistry mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + ) { + super(configurationService, fileService, mcpRegistry); + } + + start(): void { + this._register(this._workspaceContextService.onDidChangeWorkspaceFolders(e => { + for (const removed of e.removed) { + this._collections.deleteAndDispose(removed.uri.toString()); + } + for (const added of e.added) { + this.watchFolder(added); + } + })); + + for (const folder of this._workspaceContextService.getWorkspace().folders) { + this.watchFolder(folder); + } + } + + private watchFolder(folder: IWorkspaceFolder) { + const configFile = joinPath(folder.uri, '.cursor', 'mcp.json'); + const collection: WritableMcpCollectionDefinition = { + id: `cursor-workspace.${folder.index}`, + label: `${folder.name}/.cursor/mcp.json`, + remoteAuthority: this._remoteAgentService.getConnection()?.remoteAuthority || null, + scope: StorageScope.WORKSPACE, + isTrustedByDefault: false, + serverDefinitions: observableValue(this, []), + presentation: { + origin: configFile, + order: McpCollectionSortOrder.WorkspaceFolder + 1, + }, + }; + + this._collections.set(folder.uri.toString(), this.watchFile( + URI.joinPath(folder.uri, '.cursor', 'mcp.json'), + collection, + DiscoverySource.CursorWorkspace, + contents => { + const defs = claudeConfigToServerDefinition(collection.id, contents, folder.uri); + defs?.forEach(d => d.roots = [folder.uri]); + return defs; + } + )); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts b/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts new file mode 100644 index 00000000..641782c5 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.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 { findNodeAtLocation, parseTree as jsonParseTree } from '../../../../base/common/json.js'; +import { Location } from '../../../../editor/common/languages.js'; +import { ITextModel } from '../../../../editor/common/model.js'; + +export const getMcpServerMapping = (opts: { + model: ITextModel; + // Path to MCP servers in the config. + pathToServers: string[]; +}): Map => { + const tree = jsonParseTree(opts.model.getValue()); + const servers = findNodeAtLocation(tree, opts.pathToServers); + if (!servers || servers.type !== 'object') { + return new Map(); + } + + const result = new Map(); + for (const node of servers.children || []) { + if (node.type !== 'property' || node.children?.[0]?.type !== 'string') { + continue; + } + + const start = opts.model.getPositionAt(node.offset); + const end = opts.model.getPositionAt(node.offset + node.length); + result.set(node.children[0].value, { + uri: opts.model.uri, + range: { + startLineNumber: start.lineNumber, + startColumn: start.column, + endLineNumber: end.lineNumber, + endColumn: end.column, + } + }); + } + + return result; +}; diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfigPathsService.ts b/src/vs/workbench/contrib/mcp/common/mcpConfigPathsService.ts new file mode 100644 index 00000000..e86c822b --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpConfigPathsService.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 { Disposable } from '../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { IObservable, ISettableObservable, observableValue } from '../../../../base/common/observable.js'; +import { basename } from '../../../../base/common/resources.js'; +import { isDefined } from '../../../../base/common/types.js'; +import { URI } from '../../../../base/common/uri.js'; +import { localize } from '../../../../nls.js'; +import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.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 { IWorkspaceContextService, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { FOLDER_SETTINGS_PATH, IPreferencesService } from '../../../services/preferences/common/preferences.js'; +import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; +import { mcpConfigurationSection } from './mcpConfiguration.js'; +import { McpCollectionSortOrder } from './mcpTypes.js'; + +export interface IMcpConfigPath { + /** Short, unique ID for this config. */ + id: string; + /** Configuration scope that maps to this path. */ + key: 'userLocalValue' | 'userRemoteValue' | 'workspaceValue' | 'workspaceFolderValue'; + /** Display name */ + label: string; + /** Storage where associated data should be stored. */ + scope: StorageScope; + /** Configuration target that correspond to this file */ + target: ConfigurationTarget; + /** Order in which the configuration should be displayed */ + order: number; + /** Config's remote authority */ + remoteAuthority?: string; + /** Config file URI. */ + uri: URI | undefined; + /** When MCP config is nested in a config file, the parent nested key. */ + section?: string[]; + /** Workspace folder, when the config refers to a workspace folder value. */ + workspaceFolder?: IWorkspaceFolder; +} + +export interface IMcpConfigPathsService { + _serviceBrand: undefined; + + readonly paths: IObservable; +} + +export const IMcpConfigPathsService = createDecorator('IMcpConfigPathsService'); + +export class McpConfigPathsService extends Disposable implements IMcpConfigPathsService { + _serviceBrand: undefined; + + private readonly _paths: ISettableObservable; + + public get paths(): IObservable { + return this._paths; + } + + constructor( + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, + @IProductService productService: IProductService, + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IPreferencesService preferencesService: IPreferencesService, + ) { + super(); + + const workspaceConfig = workspaceContextService.getWorkspace().configuration; + const initialPaths: (IMcpConfigPath | undefined | null)[] = [ + { + id: 'usrlocal', + key: 'userLocalValue', + target: ConfigurationTarget.USER_LOCAL, + label: localize('mcp.configuration.userLocalValue', 'Global in {0}', productService.nameShort), + scope: StorageScope.PROFILE, + order: McpCollectionSortOrder.User, + uri: preferencesService.userSettingsResource, + section: [mcpConfigurationSection], + }, + workspaceConfig && { + id: 'workspace', + key: 'workspaceValue', + target: ConfigurationTarget.WORKSPACE, + label: basename(workspaceConfig), + scope: StorageScope.WORKSPACE, + order: McpCollectionSortOrder.Workspace, + remoteAuthority: _environmentService.remoteAuthority, + uri: workspaceConfig, + section: ['settings', mcpConfigurationSection], + }, + ...workspaceContextService.getWorkspace() + .folders + .map(wf => this._fromWorkspaceFolder(wf)) + ]; + + this._paths = observableValue('mcpConfigPaths', initialPaths.filter(isDefined)); + + remoteAgentService.getEnvironment().then((env) => { + const label = _environmentService.remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, _environmentService.remoteAuthority) : 'Remote'; + + this._paths.set([ + ...this.paths.get(), + { + id: 'usrremote', + key: 'userRemoteValue', + target: ConfigurationTarget.USER_REMOTE, + label, + scope: StorageScope.PROFILE, + order: McpCollectionSortOrder.User + McpCollectionSortOrder.RemoteBoost, + uri: env?.settingsPath, + remoteAuthority: _environmentService.remoteAuthority, + section: [mcpConfigurationSection], + } + ], undefined); + }); + + this._register(workspaceContextService.onDidChangeWorkspaceFolders(e => { + const next = this._paths.get().slice(); + for (const folder of e.added) { + next.push(this._fromWorkspaceFolder(folder)); + } + for (const folder of e.removed) { + const idx = next.findIndex(c => c.workspaceFolder === folder); + if (idx !== -1) { + next.splice(idx, 1); + } + } + this._paths.set(next, undefined); + })); + } + + private _fromWorkspaceFolder(workspaceFolder: IWorkspaceFolder): IMcpConfigPath { + return { + id: `wf${workspaceFolder.index}`, + key: 'workspaceFolderValue', + target: ConfigurationTarget.WORKSPACE_FOLDER, + label: `${workspaceFolder.name}/.vscode/mcp.json`, + scope: StorageScope.WORKSPACE, + remoteAuthority: this._environmentService.remoteAuthority, + order: McpCollectionSortOrder.WorkspaceFolder, + uri: URI.joinPath(workspaceFolder.uri, FOLDER_SETTINGS_PATH, '../mcp.json'), + workspaceFolder, + }; + } +} 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 00000000..cea67890 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * 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 enum DiscoverySource { + ClaudeDesktop = 'claude-desktop', + Windsurf = 'windsurf', + CursorGlobal = 'cursor-global', + CursorWorkspace = 'cursor-workspace', +} + +export const allDiscoverySources = Object.keys({ + [DiscoverySource.ClaudeDesktop]: true, + [DiscoverySource.Windsurf]: true, + [DiscoverySource.CursorGlobal]: true, + [DiscoverySource.CursorWorkspace]: true, +} satisfies Record) as DiscoverySource[]; + +export const discoverySourceLabel: Record = { + [DiscoverySource.ClaudeDesktop]: localize('mcp.discovery.source.claude-desktop', "Claude Desktop"), + [DiscoverySource.Windsurf]: localize('mcp.discovery.source.windsurf', "Windsurf"), + [DiscoverySource.CursorGlobal]: localize('mcp.discovery.source.cursor-global', "Cursor (Global)"), + [DiscoverySource.CursorWorkspace]: localize('mcp.discovery.source.cursor-workspace', "Cursor (Workspace)"), +}; + +export const mcpConfigurationSection = 'mcp'; +export const mcpDiscoverySection = 'chat.mcp.discovery.enabled'; +export const mcpEnabledSection = 'chat.mcp.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' + }, + }, + envFile: { + type: 'string', + description: localize('app.mcp.envFile.command', "Path to a file containing environment variables for the server."), + examples: ['${workspaceFolder}/.env'], + }, + 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 00000000..0d0aff9a --- /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 00000000..f7577bcc --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts @@ -0,0 +1,372 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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 { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { INotificationService, Severity } from '../../../../platform/notification/common/notification.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 { IWorkspaceFolderData } from '../../../../platform/workspace/common/workspace.js'; +import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js'; +import { ConfigurationResolverExpression, IResolvedValue } from '../../../services/configurationResolver/common/configurationResolverExpression.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { McpRegistryInputStorage } from './mcpRegistryInputStorage.js'; +import { IMcpHostDelegate, IMcpRegistry, IMcpResolveConnectionOptions } from './mcpRegistryTypes.js'; +import { McpServerConnection } from './mcpServerConnection.js'; +import { IMcpServerConnection, LazyCollectionState, McpCollectionDefinition, McpCollectionReference, McpServerDefinition, McpServerLaunch } 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; + } + + private readonly _onDidChangeInputs = this._register(new Emitter()); + public readonly onDidChangeInputs = this._onDidChangeInputs.event; + + 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, + @INotificationService private readonly _notificationService: INotificationService, + @IEditorService private readonly _editorService: IEditorService, + ) { + super(); + } + + public registerDelegate(delegate: IMcpHostDelegate): IDisposable { + this._delegates.push(delegate); + this._delegates.sort((a, b) => b.priority - a.priority); + + 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; + } + + private _getInputStorage(scope: StorageScope): McpRegistryInputStorage { + return scope === StorageScope.WORKSPACE ? this._workspaceStorage.value : this._profileStorage.value; + } + + private _getInputStorageInConfigTarget(configTarget: ConfigurationTarget): McpRegistryInputStorage { + return this._getInputStorage( + configTarget === ConfigurationTarget.WORKSPACE || configTarget === ConfigurationTarget.WORKSPACE_FOLDER + ? StorageScope.WORKSPACE + : StorageScope.PROFILE + ); + } + + public async clearSavedInputs(scope: StorageScope, inputId?: string) { + const storage = this._getInputStorage(scope); + if (inputId) { + await storage.clear(inputId); + } else { + storage.clearAll(); + } + + this._onDidChangeInputs.fire(); + } + + public async editSavedInput(inputId: string, folderData: IWorkspaceFolderData | undefined, configSection: string, target: ConfigurationTarget): Promise { + const storage = this._getInputStorageInConfigTarget(target); + const expr = ConfigurationResolverExpression.parse(inputId); + + const stored = await storage.getMap(); + const previous = stored[inputId].value; + await this._configurationResolverService.resolveWithInteraction(folderData, expr, configSection, previous ? { [inputId.slice(2, -1)]: previous } : {}, target); + await this._updateStorageWithExpressionInputs(storage, expr); + } + + public getSavedInputs(scope: StorageScope): Promise<{ [id: string]: IResolvedValue }> { + return this._getInputStorage(scope).getMap(); + } + + 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 originURI = collection.presentation?.origin; + const labelWithOrigin = originURI ? `[\`${basename(originURI)}\`](${originURI})` : 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; + } + + private async _updateStorageWithExpressionInputs(inputStorage: McpRegistryInputStorage, expr: ConfigurationResolverExpression): Promise { + const secrets: Record = {}; + const inputs: Record = {}; + for (const [replacement, resolved] of expr.resolved()) { + if (resolved.input?.type === 'promptString' && resolved.input.password) { + secrets[replacement.id] = resolved; + } else { + inputs[replacement.id] = resolved; + } + } + + inputStorage.setPlainText(inputs); + await inputStorage.setSecrets(secrets); + this._onDidChangeInputs.fire(); + } + + private async _replaceVariablesInLaunch(definition: McpServerDefinition, launch: McpServerLaunch) { + if (!definition.variableReplacement) { + return launch; + } + + const { section, target, folder } = definition.variableReplacement; + const inputStorage = this._getInputStorageInConfigTarget(target); + const previouslyStored = await inputStorage.getMap(); + + // pre-fill the variables we already resolved to avoid extra prompting + const expr = ConfigurationResolverExpression.parse(launch); + for (const replacement of expr.unresolved()) { + if (previouslyStored.hasOwnProperty(replacement.id)) { + expr.resolve(replacement, previouslyStored[replacement.id]); + } + } + + // resolve variables requiring user input + await this._configurationResolverService.resolveWithInteraction(folder, expr, section, undefined, target); + + await this._updateStorageWithExpressionInputs(inputStorage, expr); + + // resolve other non-interactive variables, returning the final object + return await this._configurationResolverService.resolveAsync(folder, expr); + } + + public async resolveConnection({ collectionRef, definitionRef, forceTrust, logger }: 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: McpServerLaunch | undefined; + try { + launch = await this._replaceVariablesInLaunch(definition, definition.launch); + } catch (e) { + this._notificationService.notify({ + severity: Severity.Error, + message: localize('mcp.launchError', 'Error starting {0}: {1}', definition.label, String(e)), + actions: { + primary: collection.presentation?.origin && [ + { + id: 'mcp.launchError.openConfig', + class: undefined, + enabled: true, + tooltip: '', + label: localize('mcp.launchError.openConfig', 'Open Configuration'), + run: () => this._editorService.openEditor({ + resource: collection.presentation!.origin, + options: { selection: definition.presentation?.origin?.range } + }), + } + ] + } + }); + return; + } + + return this._instantiationService.createInstance( + McpServerConnection, + collection, + definition, + delegate, + launch, + logger, + ); + } +} 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 00000000..852c34a2 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { IResolvedValue } from '../../../services/configurationResolver/common/configurationResolverExpression.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() { + const key = await this._getEncryptionKey.value; + return this._secretsSealerSequencer.queue(async () => { + if (!this._record.value.unsealedSecrets || isEmptyObject(this._record.value.unsealedSecrets)) { + this._record.value.secrets = undefined; + return; + } + + const toSeal = JSON.stringify(this._record.value.unsealedSecrets); + const iv = crypto.getRandomValues(new Uint8Array(MCP_ENCRYPTION_IV_LENGTH)); + 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))); + this._record.value.secrets = { iv: encodeBase64(VSBuffer.wrap(iv)), 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 00000000..07de8bc1 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogger, LogLevel } from '../../../../platform/log/common/log.js'; +import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { IWorkspaceFolderData } from '../../../../platform/workspace/common/workspace.js'; +import { IResolvedValue } from '../../../services/configurationResolver/common/configurationResolverExpression.js'; +import { IMcpServerConnection, LazyCollectionState, McpCollectionDefinition, McpCollectionReference, McpConnectionState, McpDefinitionReference, McpServerDefinition, McpServerLaunch } 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<{ level: LogLevel; message: string }>; + readonly onDidReceiveMessage: Event; + send(message: MCP.JSONRPCMessage): void; + stop(): void; +} + +export interface IMcpHostDelegate { + /** Priority for this delegate, delegates are tested in descending priority order */ + readonly priority: number; + waitForInitialProviderPromises(): Promise; + canStart(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition): boolean; + start(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition, resolvedLaunch: McpServerLaunch): IMcpMessageTransport; +} + +export interface IMcpResolveConnectionOptions { + logger: ILogger; + 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; + + /** Fired when the user provides more inputs when creating a connection. */ + readonly onDidChangeInputs: Event; + + readonly collections: IObservable; + readonly delegates: readonly IMcpHostDelegate[]; + /** Whether there are new collections that can be resolved with a discover() call */ + readonly lazyCollectionState: IObservable; + + /** Gets the prefix that should be applied to a collection's tools in order to avoid ID conflicts */ + collectionToolPrefix(collection: McpCollectionReference): 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 input, or globally. */ + clearSavedInputs(scope: StorageScope, inputId?: string): Promise; + /** Edits a previously-saved input. */ + editSavedInput(inputId: string, folderData: IWorkspaceFolderData | undefined, configSection: string, target: ConfigurationTarget): Promise; + /** Gets saved inputs from storage. */ + getSavedInputs(scope: StorageScope): Promise<{ [id: string]: IResolvedValue }>; + /** 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 00000000..e565f38c --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServer.ts @@ -0,0 +1,512 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as json from '../../../../base/common/json.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 { basename } from '../../../../base/common/resources.js'; +import { URI } from '../../../../base/common/uri.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { ILogger, ILoggerService } from '../../../../platform/log/common/log.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { IOutputService } from '../../../services/output/common/output.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'; +import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; +import { localize } from '../../../../nls.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; + +type ServerBootData = { + supportsLogging: boolean; + supportsPrompts: boolean; + supportsResources: boolean; + toolCount: number; +}; +type ServerBootClassification = { + owner: 'connor4312'; + comment: 'Details the capabilities of the MCP server'; + supportsLogging: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the server supports logging' }; + supportsPrompts: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the server supports prompts' }; + supportsResources: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the server supports resource' }; + toolCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of tools the server advertises' }; +}; + +type ServerBootState = { + state: string; + time: number; +}; +type ServerBootStateClassification = { + owner: 'connor4312'; + comment: 'Details the capabilities of the MCP server'; + state: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The server outcome' }; + time: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Duration in milliseconds to reach that state' }; +}; + +interface IToolCacheEntry { + /** Cached tools so we can show what's available before it's started */ + readonly tools: readonly IValidatedMcpTool[]; +} + +interface IServerCacheEntry { + readonly servers: readonly McpServerDefinition.Serialized[]; +} + +const toolInvalidCharRe = /[^a-z0-9_-]/gi; + +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 IValidatedMcpTool[] | undefined { + return this.cache.get(definitionId)?.tools; + } + + /** Sets cached tools for a server */ + storeTools(definitionId: string, tools: readonly IValidatedMcpTool[]): 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; + } +} + +interface IValidatedMcpTool extends MCP.Tool { + /** + * Tool name as published by the MCP server. This may + * be different than the one in {@link definition} due to name normalization + * in {@link McpServer._getValidatedTools}. + */ + serverToolName: string; +} + +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; + }); + + private readonly _loggerId: string; + private readonly _logger: ILogger; + + public get trusted() { + return this._mcpRegistry.getTrust(this.collection); + } + + constructor( + public readonly collection: McpCollectionReference, + public readonly definition: McpDefinitionReference, + explicitRoots: URI[] | undefined, + private readonly _requiresExtensionActivation: boolean | undefined, + private readonly _toolCache: McpServerMetadataCache, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IWorkspaceContextService workspacesService: IWorkspaceContextService, + @IExtensionService private readonly _extensionService: IExtensionService, + @ILoggerService private readonly _loggerService: ILoggerService, + @IOutputService private readonly _outputService: IOutputService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICommandService private readonly _commandService: ICommandService, + @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}` })); + // If the logger is disposed but not deregistered, then the disposed instance + // is reused and no-ops. todo@sandy081 this seems like a bug. + this._register(toDisposable(() => _loggerService.deregisterLogger(this._loggerId))); + + // 1. Reflect workspaces into the MCP roots + const workspaces = explicitRoots + ? observableValue(this, explicitRoots.map(uri => ({ uri, name: basename(uri) }))) + : 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._loggerService.setVisibility(this._loggerId, true); + this._outputService.showChannel(this._loggerId); + } + + 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({ + logger: this._logger, + 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); + } + + const start = Date.now(); + const state = await connection.start(); + this._telemetryService.publicLog2('mcp/serverBootState', { + state: McpConnectionState.toKindString(state.state), + time: Date.now() - start, + }); + + return state; + }); + } + + public stop(): Promise { + return this._connection.get()?.stop() || Promise.resolve(); + } + + private resetLiveData() { + transaction(tx => { + this.toolsFromServerPromise.set(undefined, tx); + }); + } + + private async _normalizeTool(originalTool: MCP.Tool): Promise { + const tool: IValidatedMcpTool = { ...originalTool, serverToolName: originalTool.name }; + if (!tool.description) { + // Ensure a description is provided for each tool, #243919 + this._logger.warn(`Tool ${tool.name} does not have a description. Tools must be accurately described to be called`); + tool.description = ''; + } + + if (toolInvalidCharRe.test(tool.name)) { + this._logger.warn(`Tool ${JSON.stringify(tool.name)} is invalid. Tools names may only contain [a-z0-9_-]`); + tool.name = tool.name.replace(toolInvalidCharRe, '_'); + } + + type JsonDiagnostic = { message: string; range: { line: number; character: number }[] }; + + let diagnostics: JsonDiagnostic[] = []; + const toolJson = JSON.stringify(tool.inputSchema); + try { + const schemaUri = URI.parse('https://json-schema.org/draft-07/schema'); + diagnostics = await this._commandService.executeCommand('json.validate', schemaUri, toolJson) || []; + } catch (e) { + // ignored (error in json extension?); + } + + if (!diagnostics.length) { + return tool; + } + + // because it's all one line from JSON.stringify, we can treat characters as offsets. + const tree = json.parseTree(toolJson); + const messages = diagnostics.map(d => { + const node = json.findNodeAtOffset(tree, d.range[0].character); + const path = node && `/${json.getNodePath(node).join('/')}`; + return d.message + (path ? ` (at ${path})` : ''); + }); + + return { error: messages }; + } + + private async _getValidatedTools(handler: McpServerRequestHandler, tools: MCP.Tool[]): Promise { + let error = ''; + + const validations = await Promise.all(tools.map(t => this._normalizeTool(t))); + const validated: IValidatedMcpTool[] = []; + for (const [i, result] of validations.entries()) { + if ('error' in result) { + error += localize('mcpBadSchema.tool', 'Tool `{0}` has invalid JSON parameters:', tools[i].name) + '\n'; + for (const message of result.error) { + error += `\t- ${message}\n`; + } + error += `\t- Schema: ${JSON.stringify(tools[i].inputSchema)}\n\n`; + } else { + validated.push(result); + } + } + + if (error) { + handler.logger.warn(`${tools.length - validated.length} tools have invalid JSON schemas and will be omitted`); + warnInvalidTools(this._instantiationService, this.definition.label, error); + } + + return validated; + } + + 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) => { + const toolPromise = handler.capabilities.tools ? handler.listTools({}, cts.token) : Promise.resolve([]); + const toolPromiseSafe = toolPromise.then(async tools => { + handler.logger.info(`Discovered ${tools.length} tools`); + return this._getValidatedTools(handler, tools); + }); + this.toolsFromServerPromise.set(new ObservablePromise(toolPromiseSafe), tx); + + return [toolPromise]; + }; + + store.add(handler.onDidChangeToolList(() => { + handler.logger.info('Tool list changed, refreshing tools...'); + updateTools(undefined); + })); + + let promises: ReturnType; + transaction(tx => { + promises = updateTools(tx); + }); + + Promise.all(promises!).then(([tools]) => { + this._telemetryService.publicLog2('mcp/serverBoot', { + supportsLogging: !!handler.capabilities.logging, + supportsPrompts: !!handler.capabilities.prompts, + supportsResources: !!handler.capabilities.resources, + toolCount: tools.length, + }); + }); + } + + /** + * 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; + + public get definition(): MCP.Tool { return this._definition; } + + constructor( + private readonly _server: McpServer, + idPrefix: string, + private readonly _definition: IValidatedMcpTool, + ) { + this.id = (idPrefix + _definition.name).replaceAll('.', '_'); + } + + call(params: Record, token?: CancellationToken): Promise { + // serverToolName is always set now, but older cache entries (from 1.99-Insiders) may not have it. + const name = this._definition.serverToolName ?? this._definition.name; + return this._server.callOn(h => h.callTool({ name, arguments: params }), token); + } +} + +function warnInvalidTools(instaService: IInstantiationService, serverName: string, errorText: string) { + instaService.invokeFunction((accessor) => { + const notificationService = accessor.get(INotificationService); + const editorService = accessor.get(IEditorService); + notificationService.notify({ + severity: Severity.Warning, + message: localize('mcpBadSchema', 'MCP server `{0}` has tools with invalid parameters which will be omitted.', serverName), + actions: { + primary: [{ + class: undefined, + enabled: true, + id: 'mcpBadSchema.show', + tooltip: '', + label: localize('mcpBadSchema.show', 'Show'), + run: () => { + editorService.openEditor({ + resource: undefined, + contents: errorText, + }); + } + }] + } + }); + }); +} 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 00000000..f367162f --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * 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, log } from '../../../../platform/log/common/log.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from './mcpRegistryTypes.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { IMcpServerConnection, McpCollectionDefinition, McpConnectionState, McpServerDefinition, 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; + + constructor( + private readonly _collection: McpCollectionDefinition, + public readonly definition: McpServerDefinition, + private readonly _delegate: IMcpHostDelegate, + public readonly launchDefinition: McpServerLaunch, + private readonly _logger: ILogger, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + } + + /** @inheritdoc */ + public async start(): Promise { + const currentState = this._state.get(); + if (!McpConnectionState.canBeStarted(currentState.state)) { + return this._waitForState(McpConnectionState.Kind.Running, McpConnectionState.Kind.Error); + } + + 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); + 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): IReference { + const store = new DisposableStore(); + const cts = new CancellationTokenSource(); + + store.add(toDisposable(() => cts.dispose(true))); + store.add(launch); + store.add(launch.onDidLog(({ level, message }) => { + log(this._logger, level, message); + })); + + 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 (!store.isDisposed) { + this._requestHandler.set(handler, undefined); + } else { + handler.dispose(); + } + }, + err => { + store.dispose(); + if (!store.isDisposed) { + 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._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._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 00000000..8c0fa08c --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts @@ -0,0 +1,486 @@ +/*--------------------------------------------------------------------------------------------- + * 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, IntervalTimer } from '../../../../base/common/async.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { CancellationError } from '../../../../base/common/errors.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { canLog, ILogger, LogLevel } 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); + const store = new DisposableStore(); + try { + const timer = store.add(new IntervalTimer()); + timer.cancelAndSet(() => { + logger.info('Waiting for server to respond to `initialize` request...'); + }, 5000); + + 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; + } finally { + store.dispose(); + } + } + + protected constructor( + private readonly launch: IMcpMessageTransport, + public 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 { + if (this._store.isDisposed) { + return Promise.reject(new CancellationError()); + } + + 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(); + } + cancelListener.dispose(); + }); + + // Send the request + this.send(jsonRpcRequest); + const ret = promise.p.finally(() => { + cancelListener.dispose(); + this._pendingRequests.delete(id); + }); + + return ret as Promise; + } + + private send(mcp: MCP.JSONRPCMessage) { + if (canLog(this.logger.getLevel(), LogLevel.Debug)) { // avoid building the string if we don't need to + this.logger.debug(`[editor -> server] ${JSON.stringify(mcp)}`); + } + + this.launch.send(mcp); + } + + /** + * 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.send({ ...notification, jsonrpc: MCP.JSONRPC_VERSION }); + } + + /** + * Handle incoming messages from the server + */ + private handleMessage(message: MCP.JSONRPCMessage): void { + if (canLog(this.logger.getLevel(), LogLevel.Debug)) { // avoid building the string if we don't need to + this.logger.debug(`[server <- editor] ${JSON.stringify(message)}`); + } + + // 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.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.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 00000000..38666d83 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpService.ts @@ -0,0 +1,275 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Codicon } from '../../../../base/common/codicons.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, + @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 collection = this._mcpRegistry.collections.get().find(c => c.id === server.collection.id); + const toolData: IToolData = { + id: tool.id, + source: { type: 'mcp', collectionId: server.collection.id, definitionId: server.definition.id }, + icon: Codicon.tools, + displayName: tool.definition.name, + toolReferenceName: tool.definition.name, + modelDescription: tool.definition.description ?? '', + userDescription: tool.definition.description ?? '', + inputSchema: tool.definition.inputSchema, + canBeReferencedInPrompt: true, + supportsToolPicker: true, + runsInWorkspace: collection?.scope === StorageScope.WORKSPACE || !!collection?.remoteAuthority, + tags: ['mcp'], + }; + + 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, + def.serverDefinition.roots, + !!def.collectionDefinition.lazy, + 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): Promise { + const tool = this._tool; + const server = this._server; + + const mcpToolWarning = localize( + 'mcp.tool.warning', + "{0} This tool is from \'{1}\' (MCP Server). Note that MCP servers or malicious conversation content may attempt to misuse '{2}' through tools. Please carefully review any requested actions.", + '$(info)', + server.definition.label, + this._productService.nameShort + ); + + return { + confirmationMessages: { + title: localize('msg.title', "Run `{0}`", tool.definition.name, server.definition.label), + message: new MarkdownString(localize('msg.msg', "{0}\n\n {1}", tool.definition.description, mcpToolWarning), { supportThemeIcons: true }), + allowAutoConfirm: true, + }, + 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)), + toolSpecificData: { + kind: 'input', + rawInput: parameters + } + }; + } + + async invoke(invocation: IToolInvocation, _countTokens: CountTokensCallback, token: CancellationToken) { + + const result: IToolResult = { + content: [] + }; + + const outputParts: string[] = []; + + 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 + }); + + outputParts.push(item.text); + } else { + // TODO@jrieken handle different item types + } + } + + result.toolResultDetails = { + input: JSON.stringify(invocation.parameters, undefined, 2), + output: outputParts.join('\n') + }; + + 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 00000000..6c4a2540 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts @@ -0,0 +1,413 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { equals as arraysEqual } from '../../../../base/common/arrays.js'; +import { IObservable } from '../../../../base/common/observable.js'; +import { URI, UriComponents } from '../../../../base/common/uri.js'; +import { Location } from '../../../../editor/common/languages.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 collection is configured, used in workspace trust prompts and "show config" */ + readonly origin?: URI; + }; +} + +export const enum McpCollectionSortOrder { + WorkspaceFolder = 0, + Workspace = 100, + User = 200, + Extension = 300, + Filesystem = 400, + + RemoteBoost = -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; + /** Explicit roots. If undefined, all workspace folders. */ + readonly roots?: URI[] | undefined; + /** If set, allows configuration variables to be resolved in the {@link launch} with the given context */ + readonly variableReplacement?: McpServerDefinitionVariableReplacement; + + readonly presentation?: { + /** Sort order of the definition. */ + readonly order?: number; + /** Place where this server is configured, used in workspace trust prompts and "show config" */ + readonly origin?: Location; + }; +} + +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 + && arraysEqual(a.roots, b.roots, (a, b) => a.toString() === b.toString()) + && objectsEqual(a.launch, b.launch) + && objectsEqual(a.presentation, b.presentation) + && objectsEqual(a.variableReplacement, b.variableReplacement); + } +} + + +export interface McpServerDefinitionVariableReplacement { + section?: string; // e.g. 'mcp' + folder?: IWorkspaceFolderData; + target: ConfigurationTarget; +} + +export namespace McpServerDefinitionVariableReplacement { + export interface Serialized { + target: ConfigurationTarget; + section?: string; + folder?: { name: string; index: number; uri: UriComponents }; + } + + 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; + readonly envFile: string | undefined; +} + +/** + * 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; envFile: string | undefined }; + + 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, + envFile: launch.envFile, + }; + } + } +} + +/** + * 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; + + /** + * 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); + } + }; + + export const toKindString = (s: McpConnectionState.Kind): string => { + switch (s) { + case Kind.Stopped: + return 'stopped'; + case Kind.Starting: + return 'starting'; + case Kind.Running: + return 'running'; + case Kind.Error: + return 'error'; + 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; + + /** Gets whether the state is a running state. */ + export const isRunning = (s: McpConnectionState) => !canBeStarted(s.state); + + 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 00000000..582e69ea --- /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 00000000..efdf3ee1 --- /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 00000000..df53ead1 --- /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 { NativeFilesystemMcpDiscovery } from '../common/discovery/nativeMcpDiscoveryAbstract.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; + +export class NativeMcpDiscovery extends NativeFilesystemMcpDiscovery { + 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 00000000..8f4cbb70 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts @@ -0,0 +1,566 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { timeout } from '../../../../../base/common/async.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 { ILogger, ILoggerService, NullLogger } from '../../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../../platform/product/common/productService.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 { ConfigurationResolverExpression } from '../../../../services/configurationResolver/common/configurationResolverExpression.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 { + const parsed = ConfigurationResolverExpression.parse(value); + for (const variable of parsed.unresolved()) { + const resolved = this.resolvedVariables.get(variable.inner); + if (resolved) { + parsed.resolve(variable, resolved); + } + } + + return Promise.resolve(parsed.toObject()); + } + + resolveWithInteraction(folder: any, config: any, section?: string, variables?: Record, target?: ConfigurationTarget): Promise | undefined> { + const parsed = ConfigurationResolverExpression.parse(config); + // 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 + for (const [k, v] of result.entries()) { + parsed.resolve({ id: '${' + k + '}' } as any, v); + } + + return Promise.resolve(result); + } +} + +class TestMcpHostDelegate implements IMcpHostDelegate { + priority = 0; + + 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; + let logger: ILogger; + + 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, {}], + ); + + logger = new NullLogger(); + + 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: {}, + envFile: undefined, + 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}' + }, + envFile: undefined, + cwd: URI.parse('file:///test') + }, + variableReplacement: { + section: 'mcp', + target: ConfigurationTarget.WORKSPACE, + } + }; + + 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, logger }) 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, logger }) as McpServerConnection; + + assert.ok(connection2); + assert.strictEqual((connection2.launchDefinition as any).env.PATH, 'interactiveValue0'); + connection2.dispose(); + + registry.clearSavedInputs(StorageScope.WORKSPACE); + + const connection3 = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition, logger }) 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, logger }); + + 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({ + logger, + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection); + assert.strictEqual(testDialogService.promptSpy.called, true); + connection?.dispose(); + + testDialogService.promptSpy.resetHistory(); + const connection2 = await registry.resolveConnection({ + logger, + 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({ + logger, + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection, undefined); + assert.strictEqual(testDialogService.promptSpy.called, true); + + testDialogService.promptSpy.resetHistory(); + const connection2 = await registry.resolveConnection({ + logger, + 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({ + logger, + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection1, undefined); + + testDialogService.promptSpy.resetHistory(); + testDialogService.setPromptResult(true); + + const connection2 = await registry.resolveConnection({ + logger, + 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({ + logger, + 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 00000000..36cc0ca4 --- /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': { value: 'value1' }, + 'key2': { value: 'value2' } + }; + + await mcpInputStorage.setPlainText(values); + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1.value, 'value1'); + assert.strictEqual(result.key2.value, 'value2'); + }); + + test('setSecrets stores encrypted values that can be retrieved with getMap', async () => { + const secrets = { + 'secretKey1': { value: 'secretValue1' }, + 'secretKey2': { value: 'secretValue2' } + }; + + await mcpInputStorage.setSecrets(secrets); + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.secretKey1.value, 'secretValue1'); + assert.strictEqual(result.secretKey2.value, 'secretValue2'); + }); + + test('getMap returns combined plain text and secret values', async () => { + await mcpInputStorage.setPlainText({ + 'plainKey': { value: 'plainValue' } + }); + + await mcpInputStorage.setSecrets({ + 'secretKey': { value: 'secretValue' } + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.plainKey.value, 'plainValue'); + assert.strictEqual(result.secretKey.value, 'secretValue'); + }); + + test('clear removes specific values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': { value: 'value1' }, + 'key2': { value: 'value2' } + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': { value: 'secretValue1' }, + 'secretKey2': { value: '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.value, 'value2'); + assert.strictEqual(result.secretKey1, undefined); + assert.strictEqual(result.secretKey2.value, 'secretValue2'); + }); + + test('clearAll removes all values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': { value: 'value1' } + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': { value: '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': { value: 'value1' }, + 'key2': { value: 'value2' } + }); + + await mcpInputStorage.setPlainText({ + 'key1': { value: 'updatedValue1' } + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1.value, 'updatedValue1'); + assert.strictEqual(result.key2.value, 'value2'); + }); + + test('updates to secret values overwrite existing values', async () => { + await mcpInputStorage.setSecrets({ + 'secretKey1': { value: 'secretValue1' }, + 'secretKey2': { value: 'secretValue2' } + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': { value: 'updatedSecretValue1' } + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.secretKey1.value, 'updatedSecretValue1'); + assert.strictEqual(result.secretKey2.value, 'secretValue2'); + }); + + test('storage persists values across instances', async () => { + // Set values on first instance + await mcpInputStorage.setPlainText({ + 'key1': { value: 'value1' } + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': { value: '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.value, 'value1'); + assert.strictEqual(result.secretKey1.value, '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 00000000..36ce1367 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { LogLevel } from '../../../../../platform/log/common/log.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<{ level: LogLevel; message: string }>()); + 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({ level: LogLevel.Info, 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 00000000..bcc62930 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.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 * 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, LogLevel, NullLogger } 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; + + priority = 0; + + 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: {}, + envFile: undefined, + 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, + new NullLogger(), + ); + 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, + new NullLogger(), + ); + 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, + new NullLogger(), + ); + 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, + new NullLogger(), + ); + 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, + new NullLogger(), + ); + 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)); + + connection.dispose(); + }); + + test('should clean up when disposed', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch, + new NullLogger(), + ); + + // 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('should log transport messages', async () => { + // Track logged messages + const loggedMessages: string[] = []; + + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch, + { + getLevel: () => LogLevel.Debug, + info: (message: string) => { + loggedMessages.push(message); + }, + error: () => { }, + dispose: () => { } + } as Partial as ILogger, + ); + 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')); + + connection.dispose(); + await timeout(10); + }); + + test('should correctly handle transitions to and from error state', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch, + new NullLogger(), + ); + 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, + new NullLogger(), + ); + 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 00000000..205465a0 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts @@ -0,0 +1,398 @@ +/*--------------------------------------------------------------------------------------------- + * 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; + + priority = 0; + + 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/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index c0f8bdf3..bbf7bf34 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -22,6 +22,8 @@ import { MergeEditorInput } from './mergeEditorInput.js'; import { MergeEditor, MergeEditorOpenHandlerContribution, MergeEditorResolverContribution } from './view/mergeEditor.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { MergeEditorSerializer } from './mergeEditorSerializer.js'; +import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +import { MergeEditorAccessibilityHelpProvider } from './mergeEditorAccessibilityHelp.js'; Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( @@ -95,3 +97,5 @@ Registry .registerWorkbenchContribution(MergeEditorOpenHandlerContribution, LifecyclePhase.Restored); registerWorkbenchContribution2(MergeEditorResolverContribution.ID, MergeEditorResolverContribution, WorkbenchPhase.BlockStartup /* only registers an editor resolver */); + +AccessibleViewRegistry.register(new MergeEditorAccessibilityHelpProvider()); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorAccessibilityHelp.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorAccessibilityHelp.ts new file mode 100644 index 00000000..2ead17be --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorAccessibilityHelp.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 { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; +import { localize } from '../../../../nls.js'; +import { AccessibleContentProvider, AccessibleViewProviderId, AccessibleViewType } from '../../../../platform/accessibility/browser/accessibleView.js'; +import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +import { ContextKeyEqualsExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js'; + + +export class MergeEditorAccessibilityHelpProvider implements IAccessibleViewImplementation { + readonly name = 'mergeEditor'; + readonly type = AccessibleViewType.Help; + readonly priority = 125; + readonly when = ContextKeyEqualsExpr.create('isMergeEditor', true); + getProvider(accessor: ServicesAccessor) { + const codeEditorService = accessor.get(ICodeEditorService); + + const codeEditor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); + if (!codeEditor) { + return; + } + + const content = [ + localize('msg1', "You are in a merge editor."), + localize('msg2', "Navigate between merge conflicts using the commands Go to Next Unhandled Conflict{0} and Go to Previous Unhandled Conflict{1}.", '', ''), + localize('msg3', "Run the command Merge Editor: Accept All Changes from the Left{0} and Merge Editor: Accept All Changes from the Right{1}", '', ''), + ]; + + return new AccessibleContentProvider( + AccessibleViewProviderId.MergeEditor, + { type: AccessibleViewType.Help }, + () => content.join('\n'), + () => codeEditor.focus(), + AccessibilityVerbositySettingId.MergeEditor, + ); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts index c0c4acc6..a110faf6 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 d60d64bc..308db886 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 32b12772..666ad3f3 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 00b45c45..584fc890 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 0d7c0c3d..f10ea922 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 992fcddb..184c94db 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 6b8387bd..00000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/contribution.ts +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { INotebookModelSynchronizerFactory, NotebookModelSynchronizerFactory } from './notebookSynchronizer.js'; -import { INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js'; -import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; -import { InstantiationType, registerSingleton } from '../../../../../../platform/instantiation/common/extensions.js'; -import { INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory } from './notebookOriginalCellModelFactory.js'; -import { NotebookChatEditorControllerContrib } from './notebookChatEditController.js'; - - -registerNotebookContribution(NotebookChatEditorControllerContrib.ID, NotebookChatEditorControllerContrib); -registerSingleton(INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory, InstantiationType.Delayed); -registerSingleton(INotebookModelSynchronizerFactory, NotebookModelSynchronizerFactory, InstantiationType.Delayed); -registerSingleton(INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts deleted file mode 100644 index ec0d2f43..00000000 --- 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 5669e5b1..00000000 --- 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/notebookChatEditContext.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts deleted file mode 100644 index ff706d88..00000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts +++ /dev/null @@ -1,9 +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 { RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js'; - -export const ctxNotebookHasEditorModification = new RawContextKey('chat.hasNotebookEditorModifications', undefined, localize('chat.hasNotebookEditorModifications', "The current Notebook editor contains chat modifications")); 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 32cce7d9..00000000 --- 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 dbebccbe..00000000 --- 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 5d788c4c..00000000 --- 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/debug/notebookDebugDecorations.ts b/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts index 3185f926..43215bff 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts @@ -10,7 +10,7 @@ import { IConfigurationService } from '../../../../../../platform/configuration/ import { debugIconBreakpointForeground } from '../../../../debug/browser/breakpointEditorContribution.js'; import { focusedStackFrameColor, topStackFrameColor } from '../../../../debug/browser/callStackEditorContribution.js'; import { IDebugService, IStackFrame } from '../../../../debug/common/debug.js'; -import { INotebookCellDecorationOptions, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, NotebookOverviewRulerLane } from '../../notebookBrowser.js'; +import { INotebookCellDecorationOptions, INotebookDeltaCellDecoration, INotebookEditor, INotebookEditorContribution, NotebookOverviewRulerLane } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; import { runningCellRulerDecorationColor } from '../../notebookEditorWidget.js'; import { CellUri, NotebookCellExecutionState } from '../../../common/notebookCommon.js'; @@ -96,7 +96,7 @@ export class PausedCellDecorationContribution extends Disposable implements INot } private setTopFrameDecoration(handlesAndRanges: ICellAndRange[]): void { - const newDecorations = handlesAndRanges.map(({ handle, range }) => { + const newDecorations: INotebookDeltaCellDecoration[] = handlesAndRanges.map(({ handle, range }) => { const options: INotebookCellDecorationOptions = { overviewRuler: { color: topStackFrameColor, @@ -105,14 +105,17 @@ export class PausedCellDecorationContribution extends Disposable implements INot position: NotebookOverviewRulerLane.Full } }; - return { handle, options }; + return { + handle, + options + }; }); this._currentTopDecorations = this._notebookEditor.deltaCellDecorations(this._currentTopDecorations, newDecorations); } private setFocusedFrameDecoration(focusedFrameCellAndRange: ICellAndRange | undefined): void { - let newDecorations: INotebookDeltaDecoration[] = []; + let newDecorations: INotebookDeltaCellDecoration[] = []; if (focusedFrameCellAndRange) { const options: INotebookCellDecorationOptions = { overviewRuler: { @@ -122,14 +125,17 @@ export class PausedCellDecorationContribution extends Disposable implements INot position: NotebookOverviewRulerLane.Full } }; - newDecorations = [{ handle: focusedFrameCellAndRange.handle, options }]; + newDecorations = [{ + handle: focusedFrameCellAndRange.handle, + options + }]; } this._currentOtherDecorations = this._notebookEditor.deltaCellDecorations(this._currentOtherDecorations, newDecorations); } private setExecutingCellDecorations(handles: number[]): void { - const newDecorations = handles.map(handle => { + const newDecorations: INotebookDeltaCellDecoration[] = handles.map(handle => { const options: INotebookCellDecorationOptions = { overviewRuler: { color: runningCellRulerDecorationColor, @@ -138,7 +144,10 @@ export class PausedCellDecorationContribution extends Disposable implements INot position: NotebookOverviewRulerLane.Left } }; - return { handle, options }; + return { + handle, + options + }; }); this._executingCellDecorations = this._notebookEditor.deltaCellDecorations(this._executingCellDecorations, newDecorations); @@ -180,7 +189,7 @@ export class NotebookBreakpointDecorations extends Disposable implements INotebo } }; return { handle: parsed.handle, options }; - }).filter(x => !!x) as INotebookDeltaDecoration[] + }).filter(x => !!x) as INotebookDeltaCellDecoration[] : []; this._currentDecorations = this._notebookEditor.deltaCellDecorations(this._currentDecorations, newDecorations); } 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 8ce231cb..116d5d3f 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/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 43b0f248..db11bd0f 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -176,7 +176,7 @@ registerAction2(class OpenCellOutputInEditorAction extends Action2 { if (outputViewModel?.model.outputId && notebookEditor.textModel?.uri) { // reserve notebook document reference since the active notebook editor might not be pinned so it can be replaced by the output editor const ref = await notebookModelService.resolve(notebookEditor.textModel.uri); - await openerService.open(CellUri.generateCellOutputUri(notebookEditor.textModel.uri, outputViewModel.model.outputId)); + await openerService.open(CellUri.generateCellOutputUriWithId(notebookEditor.textModel.uri, outputViewModel.model.outputId)); ref.dispose(); } } 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 fd5ac883..1875c730 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,19 +23,33 @@ 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 { IChatRequestPasteVariableEntry } from '../../../../chat/common/chatModel.js'; +import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; +import { ChatContextKeys } from '../../../../chat/common/chatContextKeys.js'; +import { IBaseChatRequestVariableEntry } 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'; import { INotebookKernelService } from '../../../common/notebookKernelService.js'; -import { getNotebookEditorFromEditorPane, ICellOutputViewModel, INotebookEditor } from '../../notebookBrowser.js'; +import { getNotebookEditorFromEditorPane, ICellOutputViewModel, INotebookEditor, ICellViewModel } from '../../notebookBrowser.js'; import * as icons from '../../notebookIcons.js'; import { getOutputViewModelFromId } from '../cellOutputActions.js'; import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from '../coreActions.js'; +import { CellUri } from '../../../common/notebookCommon.js'; import './cellChatActions.js'; import { CTX_NOTEBOOK_CHAT_HAS_AGENT } from './notebookChatContext.js'; const NotebookKernelVariableKey = 'kernelVariable'; +const NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT_CONST = ['text/plain', 'text/html', + 'application/vnd.code.notebook.error', + 'application/vnd.code.notebook.stdout', + 'application/x.notebook.stdout', + 'application/x.notebook.stream', + 'application/vnd.code.notebook.stderr', + 'application/x.notebook.stderr', + 'image/png', + 'image/jpeg', + 'image/svg', +]; class NotebookChatContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.notebookChatContribution'; @@ -101,7 +115,7 @@ class NotebookChatContribution extends Disposable implements IWorkbenchContribut })); // output context - NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT.bindTo(contextKeyService).set(['image/png']); + NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT.bindTo(contextKeyService).set(NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT_CONST); } private async addKernelVariableCompletion(widget: IChatWidget, result: CompletionList, info: { insert: Range; replace: Range; varWord: IWordAtPosition | null }, token: CancellationToken) { @@ -249,6 +263,7 @@ registerAction2(class CopyCellOutputAction extends Action2 { }, category: NOTEBOOK_ACTIONS_CATEGORY, icon: icons.copyIcon, + precondition: ChatContextKeys.enabled }); } @@ -295,42 +310,60 @@ registerAction2(class CopyCellOutputAction extends Action2 { const mimeType = outputViewModel.pickedMimeType?.mimeType; - if (mimeType === 'image/png') { - const chatWidgetService = accessor.get(IChatWidgetService); - - const widget = chatWidgetService.lastFocusedWidget; - if (!widget) { + const chatWidgetService = accessor.get(IChatWidgetService); + let widget = chatWidgetService.lastFocusedWidget; + if (!widget) { + const widgets = chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Panel); + if (widgets.length === 0) { return; } + widget = widgets[0]; + } + if (mimeType && NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT_CONST.includes(mimeType)) { - const imageOutput = outputViewModel.model.outputs.find(output => output.mime === mimeType); - if (!imageOutput) { + // get the cell index + const cellFromViewModelHandle = outputViewModel.cellViewModel.handle; + const cell: ICellViewModel | undefined = notebookEditor.getCellByHandle(cellFromViewModelHandle); + if (!cell) { return; } + // uri of the cell + const cellUri = cell.uri; + + // get the output index + const outputId = outputViewModel?.model.outputId; + let outputIndex: number = 0; + if (outputId !== undefined) { + // find the output index + + outputIndex = cell.outputsViewModels.findIndex(output => { + return output.model.outputId === outputId; + }); - const attachedVariables = widget.attachmentModel.attachments; - const displayName = localize('cellOutputDisplayname', 'Notebook Cell Output Image'); - let tempDisplayName = displayName; - for (let appendValue = 2; attachedVariables.some(attachment => attachment.name === tempDisplayName); appendValue++) { - tempDisplayName = `${displayName} ${appendValue}`; } + // get URI of notebook + let notebookUri = notebookEditor.textModel?.uri; + if (!notebookUri) { + // if the notebook is not found, try to parse the cell uri + const parsedCellUri = CellUri.parse(cellUri); + notebookUri = parsedCellUri?.notebook; + if (!notebookUri) { + return; + } + } + // construct the URI using the cell uri and output index + const outputCellUri = CellUri.generateCellOutputUriWithIndex(notebookUri, cellUri, outputIndex); - const imageData = imageOutput.data; - const variableEntry: IChatRequestPasteVariableEntry = { - kind: 'paste', - code: '', - language: '', - pastedLines: '', - fileName: 'notebook-cell-output-image-' + outputViewModel.model.outputId, - copiedFrom: undefined, - id: 'notebook-cell-output-image-' + outputViewModel.model.outputId, - name: tempDisplayName, - isImage: true, - value: imageData.buffer, + + + const l: IBaseChatRequestVariableEntry = { + value: outputCellUri, + id: outputCellUri.toString(), + name: outputCellUri.toString(), + isFile: true, }; - - widget.attachmentModel.addContext(variableEntry); + widget.attachmentModel.addContext(l); } } 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 fd3cd47d..2296896d 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 57fc0cfc..b3e1fcda 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 1a31b09e..8da4d2c2 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 2d9733f6..540f3080 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts @@ -13,13 +13,14 @@ import { NotebookCellTextModel } from '../../../common/model/notebookCellTextMod import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; import { DefaultLineHeight } from '../diffElementViewModel.js'; import { CellDiffInfo } from '../notebookDiffViewModel.js'; -import { INotebookEditor } from '../../notebookBrowser.js'; +import { INotebookEditor, NotebookOverviewRulerLane } from '../../notebookBrowser.js'; import * as DOM from '../../../../../../base/browser/dom.js'; import { MenuWorkbenchToolBar, HiddenItemStrategy } from '../../../../../../platform/actions/browser/toolbar.js'; import { MenuId } from '../../../../../../platform/actions/common/actions.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../../../../../platform/instantiation/common/serviceCollection.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { overviewRulerDeletedForeground } from '../../../../scm/common/quickDiff.js'; const ttPolicy = createTrustedTypesPolicy('notebookRenderer', { createHTML: value => value }); @@ -46,6 +47,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 +70,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 }]); } @@ -139,6 +144,16 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo const id = accessor.addZone(notebookViewZone); accessor.layoutZone(id); this.createdViewZones.set(index, id); + + const deletedCellOverviewRulereDecorationIds = this._notebookEditor.deltaCellDecorations([], [{ + viewZoneId: id, + options: { + overviewRuler: { + color: overviewRulerDeletedForeground, + position: NotebookOverviewRulerLane.Center, + } + } + }]); this.zoneRemover.add(toDisposable(() => { if (this.createdViewZones.get(index) === id) { this.createdViewZones.delete(index); @@ -148,6 +163,8 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo accessor.removeZone(id); dispose(widgets); }); + + this._notebookEditor.deltaCellDecorations(deletedCellOverviewRulereDecorationIds, []); } })); }); 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 0b1668d0..c0973dd6 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/notebookInsertedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.ts index 99649731..7d3121d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.ts @@ -5,7 +5,8 @@ import { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { CellDiffInfo } from '../notebookDiffViewModel.js'; -import { INotebookEditor } from '../../notebookBrowser.js'; +import { INotebookEditor, NotebookOverviewRulerLane } from '../../notebookBrowser.js'; +import { overviewRulerAddedForeground } from '../../../../scm/common/quickDiff.js'; export class NotebookInsertedCellDecorator extends Disposable { private readonly decorators = this._register(new DisposableStore()); @@ -23,7 +24,14 @@ export class NotebookInsertedCellDecorator extends Disposable { const cells = diffInfo.filter(diff => diff.type === 'insert').map((diff) => model.cells[diff.modifiedCellIndex]); const ids = this.notebookEditor.deltaCellDecorations([], cells.map(cell => ({ handle: cell.handle, - options: { className: 'nb-insertHighlight', outputClassName: 'nb-insertHighlight' } + options: { + className: 'nb-insertHighlight', outputClassName: 'nb-insertHighlight', overviewRuler: { + color: overviewRulerAddedForeground, + modelRanges: [], + includeOutput: true, + position: NotebookOverviewRulerLane.Full + } + } }))); this.clear(); this.decorators.add(toDisposable(() => { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts index f2993cbb..7475ec97 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts @@ -5,9 +5,9 @@ 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 { INotebookEditor, NotebookOverviewRulerLane } from '../../notebookBrowser.js'; import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; +import { overviewRulerModifiedForeground } from '../../../../scm/common/quickDiff.js'; export class NotebookModifiedCellDecorator extends Disposable { private readonly decorators = this._register(new DisposableStore()); @@ -23,19 +23,24 @@ export class NotebookModifiedCellDecorator extends Disposable { return; } - const modifiedMarkdownCells: NotebookCellTextModel[] = []; + const modifiedCells: NotebookCellTextModel[] = []; for (const diff of diffInfo) { if (diff.type === 'modified') { const cell = model.cells[diff.modifiedCellIndex]; - if (cell.cellKind === CellKind.Markup) { - modifiedMarkdownCells.push(cell); - } + modifiedCells.push(cell); } } - const ids = this.notebookEditor.deltaCellDecorations([], modifiedMarkdownCells.map(cell => ({ + const ids = this.notebookEditor.deltaCellDecorations([], modifiedCells.map(cell => ({ handle: cell.handle, - options: { outputClassName: 'nb-insertHighlight' } + options: { + overviewRuler: { + color: overviewRulerModifiedForeground, + modelRanges: [], + includeOutput: true, + position: NotebookOverviewRulerLane.Full + } + } }))); this.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 639e2fcd..45f4aa68 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -929,9 +929,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]) { if (diffSide === DiffSide.Original) { - this._originalWebview?.deltaCellContainerClassNames(cellId, added, removed); + this._originalWebview?.deltaCellOutputContainerClassNames(cellId, added, removed); } else { - this._modifiedWebview?.deltaCellContainerClassNames(cellId, added, removed); + this._modifiedWebview?.deltaCellOutputContainerClassNames(cellId, added, removed); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 37a6a4d2..5cd62062 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -511,14 +511,14 @@ export class NotebookTextDiffList extends WorkbenchList div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -551,7 +551,7 @@ export class NotebookTextDiffList extends WorkbenchList div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts index 912e7709..f24aabe7 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts @@ -57,7 +57,7 @@ export class NotebookDiffOverviewRuler extends Themable { })); this._register(this.themeService.onDidColorThemeChange(e => { - const colorChanged = this.applyColors(e.theme); + const colorChanged = this.applyColors(e); if (colorChanged) { this._scheduleRender(); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts index 8ec5ec76..6ef165a4 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/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 6c2f0f8c..38042ed1 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -334,10 +334,6 @@ flex-direction: row-reverse; } -.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row .codicon:not(.suggest-icon) { - color: var(--vscode-icon-foreground); -} - .monaco-workbench .notebookOverlay > .cell-list-container .notebook-overview-ruler-container { position: absolute; top: 0; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index c1cb8947..10a50d99 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/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index b37aaca0..cf385680 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -332,11 +332,33 @@ export interface INotebookCellDecorationOptions { }; } -export interface INotebookDeltaDecoration { +export interface INotebookViewZoneDecorationOptions { + overviewRuler?: { + color: string; + position: NotebookOverviewRulerLane; + }; +} + +export interface INotebookDeltaCellDecoration { readonly handle: number; readonly options: INotebookCellDecorationOptions; } +export interface INotebookDeltaViewZoneDecoration { + readonly viewZoneId: string; + readonly options: INotebookViewZoneDecorationOptions; +} + +export function isNotebookCellDecoration(obj: unknown): obj is INotebookDeltaCellDecoration { + return !!obj && typeof (obj as INotebookDeltaCellDecoration).handle === 'number'; +} + +export function isNotebookViewZoneDecoration(obj: unknown): obj is INotebookDeltaViewZoneDecoration { + return !!obj && typeof (obj as INotebookDeltaViewZoneDecoration).viewZoneId === 'string'; +} + +export type INotebookDeltaDecoration = INotebookDeltaCellDecoration | INotebookDeltaViewZoneDecoration; + export interface INotebookDeltaCellStatusBarItems { readonly handle: number; readonly items: readonly INotebookCellStatusBarItem[]; @@ -442,6 +464,17 @@ export interface INotebookViewZoneChangeAccessor { layoutZone(id: string): void; } +export interface INotebookCellOverlay { + cell: ICellViewModel; + domNode: HTMLElement; +} + +export interface INotebookCellOverlayChangeAccessor { + addOverlay(overlay: INotebookCellOverlay): string; + removeOverlay(id: string): void; + layoutOverlay(id: string): void; +} + export type NotebookViewCellsSplice = [ number /* start */, number /* delete count */, @@ -464,6 +497,7 @@ export interface INotebookViewModel { getNearestVisibleCellIndexUpwards(index: number): number; getTrackedRange(id: string): ICellRange | null; setTrackedRange(id: string | null, newRange: ICellRange | null, newStickiness: TrackedRangeStickiness): string | null; + getOverviewRulerDecorations(): INotebookDeltaViewZoneDecoration[]; getSelections(): ICellRange[]; getCellIndex(cell: ICellViewModel): number; getMostRecentlyExecutedCell(): ICellViewModel | undefined; @@ -737,6 +771,10 @@ export interface INotebookEditor { changeViewZones(callback: (accessor: INotebookViewZoneChangeAccessor) => void): void; + changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void; + + getViewZoneLayoutInfo(id: string): { top: number; height: number } | null; + /** * Get a contribution of this editor. * @id Unique identifier of the contribution. @@ -807,7 +845,7 @@ export interface INotebookEditorDelegate extends INotebookEditor { * Hide the inset in the webview layer without removing it */ hideInset(output: IDisplayOutputViewModel): void; - deltaCellContainerClassNames(cellId: string, added: string[], removed: string[]): void; + deltaCellContainerClassNames(cellId: string, added: string[], removed: string[], cellKind: CellKind): void; } export interface IActiveNotebookEditorDelegate extends INotebookEditorDelegate { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 7234b9b4..b289cdb6 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 1c6d1eed..c6d652b6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -55,7 +55,7 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { contrastBorder, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, transparent } from '../../../../platform/theme/common/colorRegistry.js'; import { EDITOR_PANE_BACKGROUND, PANEL_BORDER, SIDE_BAR_BACKGROUND } from '../../../common/theme.js'; import { debugIconStartForeground } from '../../debug/browser/debugColors.js'; -import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, CellRevealRangeType, CellRevealType, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewZoneChangeAccessor, INotebookWebviewMessage, RenderOutputType, ScrollToRevealBehavior } from './notebookBrowser.js'; +import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, CellRevealRangeType, CellRevealType, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookCellOverlayChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewZoneChangeAccessor, INotebookWebviewMessage, RenderOutputType, ScrollToRevealBehavior } from './notebookBrowser.js'; import { NotebookEditorExtensionsRegistry } from './notebookEditorExtensions.js'; import { INotebookEditorService } from './services/notebookEditorService.js'; import { notebookDebug } from './notebookLogger.js'; @@ -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; } `); @@ -1043,6 +1043,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._onDidScroll.fire(); this.clearActiveCellWidgets(); } + + if (e.scrollTop === e.oldScrollTop && e.scrollHeightChanged) { + this._onDidChangeLayout.fire(); + } })); this._focusTracker = this._register(DOM.trackFocus(this.getDomNode())); @@ -1617,21 +1621,21 @@ 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], []); + this.deltaCellContainerClassNames(cell.id, [options.className], [], cell.cellKind); } if (options.outputClassName) { - this.deltaCellContainerClassNames(cell.id, [options.outputClassName], []); + this.deltaCellContainerClassNames(cell.id, [options.outputClassName], [], cell.cellKind); } }); e.removed.forEach(options => { if (options.className) { - this.deltaCellContainerClassNames(cell.id, [], [options.className]); + this.deltaCellContainerClassNames(cell.id, [], [options.className], cell.cellKind); } if (options.outputClassName) { - this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName]); + this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName], cell.cellKind); } }); })); @@ -2285,8 +2289,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return ret; } - deltaCellContainerClassNames(cellId: string, added: string[], removed: string[]) { - this._webview?.deltaCellContainerClassNames(cellId, added, removed); + deltaCellContainerClassNames(cellId: string, added: string[], removed: string[], cellkind: CellKind): void { + if (cellkind === CellKind.Markup) { + this._webview?.deltaMarkupPreviewClassNames(cellId, added, removed); + } else { + this._webview?.deltaCellOutputContainerClassNames(cellId, added, removed); + } } changeModelDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null { @@ -2298,6 +2306,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD //#region View Zones changeViewZones(callback: (accessor: INotebookViewZoneChangeAccessor) => void): void { this._list.changeViewZones(callback); + this._onDidChangeLayout.fire(); + } + + getViewZoneLayoutInfo(id: string): { top: number; height: number } | null { + return this._list.getViewZoneLayoutInfo(id); + } + //#endregion + + //#region Overlay + changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void { + this._list.changeCellOverlays(callback); } //#endregion @@ -2343,6 +2362,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); @@ -2375,6 +2395,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } + const pendingLayout = this._pendingLayouts?.get(cell); this._pendingLayouts?.delete(cell); if (!this.hasEditorFocus()) { @@ -2393,15 +2414,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(); } @@ -3267,6 +3294,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 1bc15e00..20199190 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -48,6 +48,7 @@ import { NotebookMultiDiffEditorInput } from '../diff/notebookMultiDiffEditorInp import { SnapshotContext } from '../../../../services/workingCopy/common/fileWorkingCopy.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { CancellationError } from '../../../../../base/common/errors.js'; +import { ICellRange } from '../../common/notebookRange.js'; export class NotebookProviderInfoStore extends Disposable { @@ -180,19 +181,38 @@ export class NotebookProviderInfoStore extends Disposable { }; const notebookEditorOptions = { canHandleDiff: () => !!this._configurationService.getValue(NotebookSetting.textDiffEditorPreview) && !this._accessibilityService.isScreenReaderOptimized(), - canSupportResource: (resource: URI) => resource.scheme === Schemas.untitled || resource.scheme === Schemas.vscodeNotebookCell || this._fileService.hasProvider(resource) + canSupportResource: (resource: URI) => { + if (resource.scheme === Schemas.vscodeNotebookCellOutput) { + const params = new URLSearchParams(resource.query); + return params.get('openIn') === 'notebook'; + } + return resource.scheme === Schemas.untitled || resource.scheme === Schemas.vscodeNotebookCell || this._fileService.hasProvider(resource); + } }; - const notebookEditorInputFactory: EditorInputFactoryFunction = ({ resource, options }) => { - const data = CellUri.parse(resource); + const notebookEditorInputFactory: EditorInputFactoryFunction = async ({ resource, options }) => { + let data; + if (resource.scheme === Schemas.vscodeNotebookCellOutput) { + const outputUriData = CellUri.parseCellOutputUri(resource); + if (!outputUriData || !outputUriData.notebook || outputUriData.cellHandle === undefined) { + throw new Error('Invalid cell output uri'); + } + + data = { + notebook: outputUriData.notebook, + handle: outputUriData.cellHandle + }; + + } else { + data = CellUri.parse(resource); + } + let notebookUri: URI; let cellOptions: IResourceEditorInput | undefined; - let preferredResource = resource; if (data) { // resource is a notebook cell notebookUri = this.uriIdentService.asCanonicalUri(data.notebook); - preferredResource = data.notebook; cellOptions = { resource, options }; } else { notebookUri = this.uriIdentService.asCanonicalUri(resource); @@ -202,8 +222,38 @@ export class NotebookProviderInfoStore extends Disposable { cellOptions = (options as INotebookEditorOptions | undefined)?.cellOptions; } - const notebookOptions: INotebookEditorOptions = { ...options, cellOptions, viewState: undefined }; - const editor = NotebookEditorInput.getOrCreate(this._instantiationService, notebookUri, preferredResource, notebookProviderInfo.id); + let notebookOptions: INotebookEditorOptions; + + if (resource.scheme === Schemas.vscodeNotebookCellOutput) { + if (data?.handle === undefined || !data?.notebook) { + throw new Error('Invalid cell handle'); + } + + const cellUri = CellUri.generate(data.notebook, data.handle); + + cellOptions = { resource: cellUri, options }; + + const cellIndex = await this._notebookEditorModelResolverService.resolve(notebookUri) + .then(model => model.object.notebook.cells.findIndex(cell => cell.handle === data?.handle)) + .then(index => index >= 0 ? index : 0); + + const cellIndexesToRanges: ICellRange[] = [{ start: cellIndex, end: cellIndex + 1 }]; + + notebookOptions = { + ...options, + cellOptions, + viewState: undefined, + cellSelections: cellIndexesToRanges + }; + } else { + notebookOptions = { + ...options, + cellOptions, + viewState: undefined, + }; + } + const preferredResourceParam = cellOptions?.resource; + const editor = NotebookEditorInput.getOrCreate(this._instantiationService, notebookUri, preferredResourceParam, notebookProviderInfo.id); return { editor, options: notebookOptions }; }; @@ -212,7 +262,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(); }); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts index 4a052791..80864318 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts @@ -5,17 +5,17 @@ import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { IWorkerClient, Proxied } from '../../../../../base/common/worker/simpleWorker.js'; -import { createWebWorker } from '../../../../../base/browser/defaultWorkerFactory.js'; +import { IWebWorkerClient, Proxied } from '../../../../../base/common/worker/webWorker.js'; +import { createWebWorker } from '../../../../../base/browser/webWorkerFactory.js'; import { NotebookCellTextModel } from '../../common/model/notebookCellTextModel.js'; import { CellUri, IMainCellDto, INotebookDiffResult, NotebookCellsChangeType, NotebookRawContentEventDto } from '../../common/notebookCommon.js'; import { INotebookService } from '../../common/notebookService.js'; -import { NotebookEditorSimpleWorker } from '../../common/services/notebookSimpleWorker.js'; +import { NotebookWorker } from '../../common/services/notebookWebWorker.js'; import { INotebookEditorWorkerService } from '../../common/services/notebookWorkerService.js'; import { IModelService } from '../../../../../editor/common/services/model.js'; import { ITextModel } from '../../../../../editor/common/model.js'; import { TextModel } from '../../../../../editor/common/model/textModel.js'; -import { Schemas } from '../../../../../base/common/network.js'; +import { FileAccess, Schemas } from '../../../../../base/common/network.js'; import { isEqual } from '../../../../../base/common/resources.js'; export class NotebookEditorWorkerServiceImpl extends Disposable implements INotebookEditorWorkerService { @@ -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); } @@ -75,7 +76,7 @@ class NotebookEditorModelManager extends Disposable { private _syncedModelsLastUsedTime: { [modelUrl: string]: number } = Object.create(null); constructor( - private readonly _proxy: Proxied, + private readonly _proxy: Proxied, private readonly _notebookService: INotebookService, private readonly _modelService: IModelService, ) { @@ -235,7 +236,7 @@ class NotebookEditorModelManager extends Disposable { } class NotebookWorkerClient extends Disposable { - private _worker: IWorkerClient | null; + private _worker: IWebWorkerClient | null; private _modelManager: NotebookEditorModelManager | null; @@ -256,29 +257,27 @@ class NotebookWorkerClient extends Disposable { return proxy.$canPromptRecommendation(modelUri.toString()); } - private _getOrCreateModelManager(proxy: Proxied): NotebookEditorModelManager { + private _getOrCreateModelManager(proxy: Proxied): NotebookEditorModelManager { if (!this._modelManager) { this._modelManager = this._register(new NotebookEditorModelManager(proxy, this._notebookService, this._modelService)); } return this._modelManager; } - protected _ensureSyncedResources(resources: URI[]): Proxied { + protected _ensureSyncedResources(resources: URI[]): Proxied { const proxy = this._getOrCreateWorker().proxy; this._getOrCreateModelManager(proxy).ensureSyncedResources(resources); return proxy; } - private _getOrCreateWorker(): IWorkerClient { + private _getOrCreateWorker(): IWebWorkerClient { if (!this._worker) { try { - this._worker = this._register(createWebWorker( - 'vs/workbench/contrib/notebook/common/services/notebookSimpleWorker', + this._worker = this._register(createWebWorker( + FileAccess.asBrowserUri('vs/workbench/contrib/notebook/common/services/notebookWebWorkerMain.js'), 'NotebookEditorWorker' )); } catch (err) { - // logOnceWebWorkerWarning(err); - // this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null)); throw (err); } } 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 9068b002..4ae75347 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts @@ -72,11 +72,11 @@ export class CellDecorations extends CellContentPart { this.currentCell.getCellDecorations().forEach(options => { if (options.className && this.currentCell) { this.rootContainer.classList.add(options.className); - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.className], []); + this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.className], [], this.currentCell.cellKind); } if (options.outputClassName && this.currentCell) { - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.outputClassName], []); + this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.outputClassName], [], this.currentCell.cellKind); } }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index dfeb1406..84b71097 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -774,7 +774,7 @@ export class CellOutputContainer extends CellContentPart { actionHandler: { callback: (content) => { if (content === 'command:workbench.action.openLargeOutput') { - this.openerService.open(CellUri.generateCellOutputUri(this.notebookEditor.textModel!.uri)); + this.openerService.open(CellUri.generateCellOutputUriWithId(this.notebookEditor.textModel!.uri)); } return; 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 f58ff0aa..a23d2f8b 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/cellParts/cellToolbarStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts index 548fae02..aee39b19 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from '../../../../../../base/common/lifecycle.js'; +import { combinedDisposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; import { clamp } from '../../../../../../base/common/numbers.js'; import { ICellViewModel, INotebookEditor } from '../../notebookBrowser.js'; @@ -27,5 +27,11 @@ export function registerCellToolbarStickyScroll(notebookEditor: INotebookEditor, }; updateForScroll(); - return notebookEditor.onDidScroll(() => updateForScroll()); + const disposables: IDisposable[] = []; + disposables.push( + notebookEditor.onDidScroll(() => updateForScroll()), + notebookEditor.onDidChangeLayout(() => updateForScroll()) + ); + + return combinedDisposable(...disposables); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index e86ed7e3..b595e863 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -201,7 +201,7 @@ export class CodeCell extends Disposable { const cts = new CancellationTokenSource(); this._register({ dispose() { cts.dispose(true); } }); raceCancellation(this.viewCell.resolveTextModel(), cts.token).then(model => { - if (this._isDisposed) { + if (this._isDisposed || model?.isDisposed()) { return; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index fdeaf59a..508e9f18 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -19,7 +19,7 @@ import { PrefixSumComputer } from '../../../../../editor/common/model/prefixSumC import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IListService, IWorkbenchListOptions, WorkbenchList } from '../../../../../platform/list/browser/listService.js'; -import { CursorAtBoundary, ICellViewModel, CellEditState, ICellOutputViewModel, CellRevealType, CellRevealRangeType, CursorAtLineBoundary, INotebookViewZoneChangeAccessor } from '../notebookBrowser.js'; +import { CursorAtBoundary, ICellViewModel, CellEditState, ICellOutputViewModel, CellRevealType, CellRevealRangeType, CursorAtLineBoundary, INotebookViewZoneChangeAccessor, INotebookCellOverlayChangeAccessor } from '../notebookBrowser.js'; import { CellViewModel, NotebookViewModel } from '../viewModel/notebookViewModelImpl.js'; import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType, NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY } from '../../common/notebookCommon.js'; import { ICellRange, cellRangesToIndexes, reduceCellRanges, cellRangesEqual } from '../../common/notebookRange.js'; @@ -36,6 +36,7 @@ import { NotebookOptions } from '../notebookOptions.js'; import { INotebookExecutionStateService } from '../../common/notebookExecutionStateService.js'; import { NotebookCellAnchor } from './notebookCellAnchor.js'; import { NotebookViewZones } from '../viewParts/notebookViewZones.js'; +import { NotebookCellOverlays } from '../viewParts/notebookCellOverlays.js'; const enum CellRevealPosition { Top, @@ -79,6 +80,7 @@ function validateWebviewBoundary(element: HTMLElement) { export class NotebookCellList extends WorkbenchList implements IDisposable, IStyleController, INotebookCellList { protected override readonly view!: NotebookCellListView; private viewZones!: NotebookViewZones; + private cellOverlays!: NotebookCellOverlays; get onWillScroll(): Event { return this.view.onWillScroll; } get rowsContainer(): HTMLElement { @@ -289,6 +291,7 @@ export class NotebookCellList extends WorkbenchList implements ID protected override createListView(container: HTMLElement, virtualDelegate: IListVirtualDelegate, renderers: IListRenderer[], viewOptions: IListViewOptions): IListView { const listView = new NotebookCellListView(container, virtualDelegate, renderers, viewOptions); this.viewZones = new NotebookViewZones(listView, this); + this.cellOverlays = new NotebookCellOverlays(listView); return listView; } @@ -340,6 +343,7 @@ export class NotebookCellList extends WorkbenchList implements ID // update whitespaces which are anchored to the model indexes this.viewZones.onCellsChanged(e); + this.cellOverlays.onCellsChanged(e); const currentRanges = this._hiddenRangeIds.map(id => this._viewModel!.getTrackedRange(id)).filter(range => range !== null) as ICellRange[]; const newVisibleViewCells: CellViewModel[] = getVisibleCells(this._viewModel!.viewCells as CellViewModel[], currentRanges); @@ -448,6 +452,8 @@ export class NotebookCellList extends WorkbenchList implements ID this._updateHiddenRangePrefixSum(newRanges); this.viewZones.onHiddenRangesChange(); this.viewZones.layout(); + this.cellOverlays.onHiddenRangesChange(); + this.cellOverlays.layout(); return false; } } @@ -461,12 +467,14 @@ export class NotebookCellList extends WorkbenchList implements ID this._updateHiddenRangePrefixSum(newRanges); // Update view zone positions after hidden ranges change this.viewZones.onHiddenRangesChange(); + this.cellOverlays.onHiddenRangesChange(); if (triggerViewUpdate) { this.updateHiddenAreasInView(oldRanges, newRanges); } this.viewZones.layout(); + this.cellOverlays.layout(); return true; } @@ -541,6 +549,7 @@ export class NotebookCellList extends WorkbenchList implements ID } this.viewZones.layout(); + this.cellOverlays.layout(); } getModelIndex(cell: CellViewModel): number | undefined { @@ -1036,7 +1045,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 +1177,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); } } @@ -1231,12 +1241,14 @@ export class NotebookCellList extends WorkbenchList implements ID } this.view.updateElementHeight(index, size, anchorElementIndex); this.viewZones.layout(); + this.cellOverlays.layout(); return; } if (anchorElementIndex !== null) { this.view.updateElementHeight(index, size, anchorElementIndex); this.viewZones.layout(); + this.cellOverlays.layout(); return; } @@ -1250,12 +1262,14 @@ export class NotebookCellList extends WorkbenchList implements ID if (this._notebookCellAnchor.shouldAnchor(this.view, focus, heightDelta, this.element(index))) { this.view.updateElementHeight(index, size, focus); this.viewZones.layout(); + this.cellOverlays.layout(); return; } } this.view.updateElementHeight(index, size, null); this.viewZones.layout(); + this.cellOverlays.layout(); return; } @@ -1265,6 +1279,16 @@ export class NotebookCellList extends WorkbenchList implements ID } } + changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void { + if (this.cellOverlays.changeCellOverlays(callback)) { + this.cellOverlays.layout(); + } + } + + getViewZoneLayoutInfo(viewZoneId: string): { height: number; top: number } | null { + return this.viewZones.getViewZoneLayoutInfo(viewZoneId); + } + // override override domFocus() { const focused = this.getFocusedElements()[0]; @@ -1346,14 +1370,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 +1410,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; } `); } @@ -1438,6 +1462,7 @@ export class NotebookCellList extends WorkbenchList implements ID this._localDisposableStore.dispose(); this._notebookCellAnchor.dispose(); this.viewZones.dispose(); + this.cellOverlays.dispose(); super.dispose(); // un-ref diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts index 7034d6bb..8d64212d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts @@ -306,18 +306,11 @@ export class NotebookCellListView extends ListView { removeWhitespace(id: string): void { const scrollTop = this.scrollTop; const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); - const currentPosition = this.notebookRangeMap.getWhitespacePosition(id); - - if (currentPosition > scrollTop) { - this.notebookRangeMap.removeWhitespace(id); - this.render(previousRenderRange, scrollTop, this.lastRenderHeight, undefined, undefined, false); - this._rerender(scrollTop, this.renderHeight, false); - this.eventuallyUpdateScrollDimensions(); - } else { - this.notebookRangeMap.removeWhitespace(id); - this.eventuallyUpdateScrollDimensions(); - } + this.notebookRangeMap.removeWhitespace(id); + this.render(previousRenderRange, scrollTop, this.lastRenderHeight, undefined, undefined, false); + this._rerender(scrollTop, this.renderHeight, false); + this.eventuallyUpdateScrollDimensions(); } getWhitespacePosition(id: string): number { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 94ced5b8..0d1adcd6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -16,7 +16,7 @@ import { Selection } from '../../../../../editor/common/core/selection.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IWorkbenchListOptionsUpdate } from '../../../../../platform/list/browser/listService.js'; -import { CellRevealRangeType, CellRevealType, ICellOutputViewModel, ICellViewModel, INotebookViewZoneChangeAccessor } from '../notebookBrowser.js'; +import { CellRevealRangeType, CellRevealType, ICellOutputViewModel, ICellViewModel, INotebookCellOverlayChangeAccessor, INotebookViewZoneChangeAccessor } from '../notebookBrowser.js'; import { CellPartsCollection } from './cellPart.js'; import { CellViewModel, NotebookViewModel } from '../viewModel/notebookViewModelImpl.js'; import { ICellRange } from '../../common/notebookRange.js'; @@ -66,6 +66,8 @@ export interface INotebookCellList extends ICoordinatesConverter { revealOffsetInCenterIfOutsideViewport(offset: number): void; setHiddenAreas(_ranges: ICellRange[], triggerViewUpdate: boolean): boolean; changeViewZones(callback: (accessor: INotebookViewZoneChangeAccessor) => void): void; + changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void; + getViewZoneLayoutInfo(viewZoneId: string): { height: number; top: number } | null; domElementOfElement(element: ICellViewModel): HTMLElement | null; focusView(): void; triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent): void; 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 26e9dbba..b6c10143 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -762,7 +762,7 @@ export class BackLayerWebView extends Themable { } } - this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId)); + this.openerService.open(CellUri.generateCellOutputUriWithId(this.documentUri, outputId)); return; } if (uri.path === 'cellOutput.enableScrolling') { @@ -794,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; @@ -1854,14 +1855,24 @@ export class BackLayerWebView extends Themable { } - deltaCellContainerClassNames(cellId: string, added: string[], removed: string[]) { + deltaCellOutputContainerClassNames(cellId: string, added: string[], removed: string[]) { this._sendMessageToWebview({ type: 'decorations', cellId, addedClassNames: added, removedClassNames: removed }); + } + deltaMarkupPreviewClassNames(cellId: string, added: string[], removed: string[]) { + if (this.markupPreviewMapping.get(cellId)) { + this._sendMessageToWebview({ + type: 'markupDecorations', + cellId, + addedClassNames: added, + removedClassNames: removed + }); + } } updateOutputRenderers() { 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 6c0a4f51..84589a5e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -84,7 +84,7 @@ export class NotebookCellListDelegate extends Disposable implements IListVirtual } } -abstract class AbstractCellRenderer { +abstract class AbstractCellRenderer extends Disposable { protected readonly editorOptions: CellEditorOptions; constructor( @@ -99,11 +99,12 @@ abstract class AbstractCellRenderer { language: string, protected dndController: CellDragAndDropController | undefined ) { - this.editorOptions = new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(language), this.notebookEditor.notebookOptions, configurationService); + super(); + this.editorOptions = this._register(new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(language), this.notebookEditor.notebookOptions, configurationService)); } - dispose() { - this.editorOptions.dispose(); + override dispose() { + super.dispose(); this.dndController = undefined; } } @@ -199,7 +200,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen editorContainer, foldingIndicator, templateDisposables, - elementDisposables: new DisposableStore(), + elementDisposables: templateDisposables.add(new DisposableStore()), cellParts, toJSON: () => { return {}; } }; @@ -225,7 +226,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { - templateData.elementDisposables.dispose(); templateData.templateDisposables.dispose(); } @@ -366,7 +366,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende outputShowMoreContainer, editor, templateDisposables, - elementDisposables: new DisposableStore(), + elementDisposables: templateDisposables.add(new DisposableStore()), cellParts, toJSON: () => { return {}; } }; @@ -397,7 +397,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } disposeTemplate(templateData: CodeCellRenderTemplate): void { - templateData.templateDisposables.clear(); + templateData.templateDisposables.dispose(); } disposeElement(element: ICellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 073737f5..26149b87 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -305,7 +305,7 @@ export interface IUpdateRenderersMessage { } export interface IUpdateDecorationsMessage { - readonly type: 'decorations'; + readonly type: 'decorations' | 'markupDecorations'; readonly cellId: string; readonly addedClassNames: readonly string[]; readonly removedClassNames: readonly string[]; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 5b7077bf..ac3d98fd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1780,6 +1780,16 @@ async function webviewPreloads(ctx: PreloadContext) { outputContainer?.classList.remove(...event.data.removedClassNames); break; } + case 'markupDecorations': { + const markupCell = window.document.getElementById(event.data.cellId); + // The cell may not have been added yet if it is out of view. + // Decorations will be added when the cell is shown. + if (markupCell) { + markupCell?.classList.add(...event.data.addedClassNames); + markupCell?.classList.remove(...event.data.removedClassNames); + } + break; + } case 'customKernelMessage': onDidReceiveKernelMessage.fire(event.data.message); break; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 8b9ca509..56a7c140 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -23,7 +23,7 @@ import { FoldingRegions } from '../../../../../editor/contrib/folding/browser/fo import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { CellFindMatchModel } from '../contrib/find/findModel.js'; -import { CellEditState, CellFindMatchWithIndex, CellFoldingState, EditorFoldingStateDelegate, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, IModelDecorationsChangeAccessor, INotebookDeltaCellStatusBarItems, INotebookDeltaDecoration, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewModel } from '../notebookBrowser.js'; +import { CellEditState, CellFindMatchWithIndex, CellFoldingState, EditorFoldingStateDelegate, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, IModelDecorationsChangeAccessor, INotebookDeltaCellStatusBarItems, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewModel, INotebookDeltaDecoration, isNotebookCellDecoration, INotebookDeltaViewZoneDecoration } from '../notebookBrowser.js'; import { NotebookLayoutInfo, NotebookMetadataChangedEvent } from '../notebookViewEvents.js'; import { NotebookCellSelectionCollection } from './cellSelectionCollection.js'; import { CodeCellViewModel } from './codeCellViewModel.js'; @@ -189,6 +189,9 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD private _decorationIdToCellMap = new Map(); private _statusBarItemIdToCellMap = new Map(); + private _lastOverviewRulerDecorationId: number = 0; + private _overviewRulerDecorations = new Map(); + constructor( public viewType: string, private _notebook: NotebookTextModel, @@ -495,6 +498,10 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._hiddenRanges; } + getOverviewRulerDecorations(): INotebookDeltaViewZoneDecoration[] { + return Array.from(this._overviewRulerDecorations.values()); + } + getCellByHandle(handle: number) { return this._handleToViewCellMapping.get(handle); } @@ -723,18 +730,29 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD cell?.deltaCellDecorations([id], []); this._decorationIdToCellMap.delete(id); } + + if (this._overviewRulerDecorations.has(id)) { + this._overviewRulerDecorations.delete(id); + } }); const result: string[] = []; newDecorations.forEach(decoration => { - const cell = this.getCellByHandle(decoration.handle); - const ret = cell?.deltaCellDecorations([], [decoration.options]) || []; - ret.forEach(id => { - this._decorationIdToCellMap.set(id, decoration.handle); - }); + if (isNotebookCellDecoration(decoration)) { + const cell = this.getCellByHandle(decoration.handle); + const ret = cell?.deltaCellDecorations([], [decoration.options]) || []; + ret.forEach(id => { + this._decorationIdToCellMap.set(id, decoration.handle); + }); + result.push(...ret); + } else { + const id = ++this._lastOverviewRulerDecorationId; + const decorationId = `_overview_${this.id};${id}`; + this._overviewRulerDecorations.set(decorationId, decoration); + result.push(decorationId); + } - result.push(...ret); }); return result; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookCellOverlays.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookCellOverlays.ts new file mode 100644 index 00000000..82c37b94 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookCellOverlays.ts @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createFastDomNode, FastDomNode } from '../../../../../base/browser/fastDomNode.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { localize2 } from '../../../../../nls.js'; +import { Categories } from '../../../../../platform/action/common/actionCommonCategories.js'; +import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { IsDevelopmentContext } from '../../../../../platform/contextkey/common/contextkeys.js'; +import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IEditorService } from '../../../../services/editor/common/editorService.js'; +import { CellKind } from '../../common/notebookCommon.js'; +import { getNotebookEditorFromEditorPane, INotebookCellOverlay, INotebookCellOverlayChangeAccessor, INotebookViewCellsUpdateEvent } from '../notebookBrowser.js'; +import { NotebookCellListView } from '../view/notebookCellListView.js'; +import { CellViewModel } from '../viewModel/notebookViewModelImpl.js'; + +interface INotebookCellOverlayWidget { + overlayId: string; + overlay: INotebookCellOverlay; + domNode: FastDomNode; +} + +export class NotebookCellOverlays extends Disposable { + private _lastOverlayId = 0; + public domNode: FastDomNode; + private _overlays: { [key: string]: INotebookCellOverlayWidget } = Object.create(null); + + constructor( + private readonly listView: NotebookCellListView + ) { + super(); + this.domNode = createFastDomNode(document.createElement('div')); + this.domNode.setClassName('cell-overlays'); + this.domNode.setPosition('absolute'); + this.domNode.setAttribute('role', 'presentation'); + this.domNode.setAttribute('aria-hidden', 'true'); + this.domNode.setWidth('100%'); + + this.listView.containerDomNode.appendChild(this.domNode.domNode); + } + + changeCellOverlays(callback: (changeAccessor: INotebookCellOverlayChangeAccessor) => void): boolean { + let overlaysHaveChanged = false; + const changeAccessor: INotebookCellOverlayChangeAccessor = { + addOverlay: (overlay: INotebookCellOverlay): string => { + overlaysHaveChanged = true; + return this._addOverlay(overlay); + }, + removeOverlay: (id: string): void => { + overlaysHaveChanged = true; + this._removeOverlay(id); + }, + layoutOverlay: (id: string): void => { + overlaysHaveChanged = true; + this._layoutOverlay(id); + } + }; + + callback(changeAccessor); + + return overlaysHaveChanged; + } + + onCellsChanged(e: INotebookViewCellsUpdateEvent): void { + this.layout(); + } + + onHiddenRangesChange() { + this.layout(); + } + + layout() { + for (const id in this._overlays) { + this._layoutOverlay(id); + } + } + + private _addOverlay(overlay: INotebookCellOverlay): string { + const overlayId = `${++this._lastOverlayId}`; + + const overlayWidget = { + overlayId, + overlay, + domNode: createFastDomNode(overlay.domNode) + }; + + this._overlays[overlayId] = overlayWidget; + overlayWidget.domNode.setClassName('cell-overlay'); + overlayWidget.domNode.setPosition('absolute'); + this.domNode.appendChild(overlayWidget.domNode); + + return overlayId; + } + + private _removeOverlay(id: string): void { + const overlay = this._overlays[id]; + if (overlay) { + // overlay.overlay.dispose(); + try { + this.domNode.removeChild(overlay.domNode); + } catch { + // no op + } + + delete this._overlays[id]; + } + } + + private _layoutOverlay(id: string): void { + const overlay = this._overlays[id]; + if (!overlay) { + return; + } + + const isInHiddenRanges = this._isInHiddenRanges(overlay); + if (isInHiddenRanges) { + overlay.domNode.setDisplay('none'); + return; + } + + overlay.domNode.setDisplay('block'); + const index = this.listView.indexOf(overlay.overlay.cell as CellViewModel); + if (index === -1) { + // should not happen + return; + } + + const top = this.listView.elementTop(index); + overlay.domNode.setTop(top); + } + + private _isInHiddenRanges(zone: INotebookCellOverlayWidget) { + const index = this.listView.indexOf(zone.overlay.cell as CellViewModel); + if (index === -1) { + return true; + } + + return false; + } +} + + + +class ToggleNotebookCellOverlaysDeveloperAction extends Action2 { + static cellOverlayIds: string[] = []; + constructor() { + super({ + id: 'notebook.developer.addCellOverlays', + title: localize2('workbench.notebook.developer.addCellOverlays', "Toggle Notebook Cell Overlays"), + category: Categories.Developer, + precondition: IsDevelopmentContext, + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane); + + if (!editor) { + return; + } + + if (ToggleNotebookCellOverlaysDeveloperAction.cellOverlayIds.length > 0) { + // remove all view zones + editor.changeCellOverlays(accessor => { + ToggleNotebookCellOverlaysDeveloperAction.cellOverlayIds.forEach(id => { + accessor.removeOverlay(id); + }); + ToggleNotebookCellOverlaysDeveloperAction.cellOverlayIds = []; + }); + } else { + editor.changeCellOverlays(accessor => { + const cells = editor.getCellsInRange(); + if (cells.length === 0) { + return; + } + + const cellOverlayIds: string[] = []; + for (let i = 0; i < cells.length; i++) { + if (cells[i].cellKind !== CellKind.Markup) { + continue; + } + const domNode = document.createElement('div'); + domNode.innerText = `Cell Overlay ${i}`; + domNode.style.top = '10px'; + domNode.style.right = '10px'; + domNode.style.backgroundColor = 'rgba(0, 255, 0, 0.5)'; + const overlayId = accessor.addOverlay({ + cell: cells[i], + domNode: domNode, + }); + + cellOverlayIds.push(overlayId); + } + ToggleNotebookCellOverlaysDeveloperAction.cellOverlayIds = cellOverlayIds; + }); + } + } +} + +registerAction2(ToggleNotebookCellOverlaysDeveloperAction); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index d59ee4f4..ea8227a1 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -47,7 +47,6 @@ export class NotebookStickyLine extends Disposable { this.toggleFoldRange(currentFoldingState); } })); - } private toggleFoldRange(currentState: CellFoldingState) { @@ -212,13 +211,17 @@ export class NotebookStickyScroll extends Disposable { await notebookCellOutline.computeFullSymbols(CancellationToken.None); // Initial content update - this.updateContent(computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight())); + const computed = computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight()); + this.updateContent(computed); // Set up outline change listener this._disposables.add(notebookCellOutline.onDidChange(() => { - const recompute = computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight()); - if (!this.compareStickyLineMaps(recompute, this.currentStickyLines)) { - this.updateContent(recompute); + const computed = computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight()); + if (!this.compareStickyLineMaps(computed, this.currentStickyLines)) { + this.updateContent(computed); + } else { + // if we don't end up updating the content, we need to avoid leaking the map + this.disposeStickyLineMap(computed); } })); @@ -226,21 +229,36 @@ export class NotebookStickyScroll extends Disposable { this._disposables.add(this.notebookEditor.onDidAttachViewModel(async () => { // ensure recompute symbols when view model changes -- could be missed if outline is closed await notebookCellOutline.computeFullSymbols(CancellationToken.None); - this.updateContent(computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight())); + + const computed = computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight()); + this.updateContent(computed); })); this._disposables.add(this.notebookEditor.onDidScroll(() => { const d = new Delayer(100); d.trigger(() => { d.dispose(); - const recompute = computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight()); - if (!this.compareStickyLineMaps(recompute, this.currentStickyLines)) { - this.updateContent(recompute); + + const computed = computeContent(this.notebookEditor, this.notebookCellList, notebookCellOutline.entries, this.getCurrentStickyHeight()); + if (!this.compareStickyLineMaps(computed, this.currentStickyLines)) { + this.updateContent(computed); + } else { + // if we don't end up updating the content, we need to avoid leaking the map + this.disposeStickyLineMap(computed); } }); })); } + // Add helper method to dispose a map of sticky lines + private disposeStickyLineMap(map: Map) { + map.forEach(value => { + if (value.line) { + value.line.dispose(); + } + }); + } + // take in an cell index, and get the corresponding outline entry static getVisibleOutlineEntry(visibleIndex: number, notebookOutlineEntries: OutlineEntry[]): OutlineEntry | undefined { let left = 0; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts index d2467be2..0cee6535 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts @@ -72,6 +72,7 @@ export class NotebookEditorContextKeys { dispose(): void { this._disposables.dispose(); this._viewModelDisposables.dispose(); + this._selectedKernelDisposables.dispose(); this._notebookKernelCount.reset(); this._notebookKernelSourceCount.reset(); this._interruptibleKernel.reset(); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts index 3d2c44c6..3e08af27 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts @@ -33,7 +33,8 @@ import { URI } from '../../../../../base/common/uri.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { INotebookTextModel } from '../../common/notebookCommon.js'; import { SELECT_KERNEL_ID } from '../controller/coreActions.js'; -import { EnablementState } from '../../../../services/extensionManagement/common/extensionManagement.js'; +import { EnablementState, IExtensionManagementServerService } from '../../../../services/extensionManagement/common/extensionManagement.js'; +import { areSameExtensions } from '../../../../../platform/extensionManagement/common/extensionManagementUtil.js'; type KernelPick = IQuickPickItem & { kernel: INotebookKernel }; function isKernelPick(item: QuickPickInput): item is KernelPick { @@ -105,7 +106,8 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { protected readonly _logService: ILogService, protected readonly _extensionWorkbenchService: IExtensionsWorkbenchService, protected readonly _extensionService: IExtensionService, - protected readonly _commandService: ICommandService + protected readonly _commandService: ICommandService, + protected readonly _extensionManagementServerService: IExtensionManagementServerService ) { } async showQuickPick(editor: IActiveNotebookEditor, wantedId?: string, skipAutoRun?: boolean): Promise { @@ -254,6 +256,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { await this._showKernelExtension( this._extensionWorkbenchService, this._extensionService, + this._extensionManagementServerService, editor.textModel.viewType, [] ); @@ -262,6 +265,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { await this._showKernelExtension( this._extensionWorkbenchService, this._extensionService, + this._extensionManagementServerService, editor.textModel.viewType, pick.extensionIds, this._productService.quality !== 'stable' @@ -281,33 +285,48 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { protected async _showKernelExtension( extensionWorkbenchService: IExtensionsWorkbenchService, extensionService: IExtensionService, + extensionManagementServerService: IExtensionManagementServerService, viewType: string, extIds: string[], isInsiders?: boolean ) { // If extension id is provided attempt to install the extension as the user has requested the suggested ones be installed const extensionsToInstall: IExtension[] = []; + const extensionsToInstallOnRemote: IExtension[] = []; const extensionsToEnable: IExtension[] = []; for (const extId of extIds) { const extension = (await extensionWorkbenchService.getExtensions([{ id: extId }], CancellationToken.None))[0]; if (extension.enablementState === EnablementState.DisabledGlobally || extension.enablementState === EnablementState.DisabledWorkspace || extension.enablementState === EnablementState.DisabledByEnvironment) { extensionsToEnable.push(extension); - } else { + } else if (!extensionWorkbenchService.installed.some(e => areSameExtensions(e.identifier, extension.identifier))) { + // Install this extension only if it hasn't already been installed. const canInstall = await extensionWorkbenchService.canInstall(extension); if (canInstall === true) { extensionsToInstall.push(extension); } + } else if (extensionManagementServerService.remoteExtensionManagementServer) { + // already installed, check if it should be installed on remote since we are not getting any kernels or kernel providers. + if (extensionWorkbenchService.installed.some(e => areSameExtensions(e.identifier, extension.identifier) && e.server === extensionManagementServerService.remoteExtensionManagementServer)) { + // extension exists on remote server. should not happen + continue; + } else { + // extension doesn't exist on remote server + const canInstall = await extensionWorkbenchService.canInstall(extension); + if (canInstall) { + extensionsToInstallOnRemote.push(extension); + } + } } } - if (extensionsToInstall.length || extensionsToEnable.length) { + if (extensionsToInstall.length || extensionsToEnable.length || extensionsToInstallOnRemote.length) { await Promise.all([...extensionsToInstall.map(async extension => { await extensionWorkbenchService.install( extension, { installPreReleaseVersion: isInsiders ?? false, - context: { skipWalkthrough: true } + context: { skipWalkthrough: true }, }, ProgressLocation.Notification ); @@ -325,6 +344,8 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { default: break; } + }), ...extensionsToInstallOnRemote.map(async extension => { + await extensionWorkbenchService.installInServer(extension, this._extensionManagementServerService.remoteExtensionManagementServer!); })]); await extensionService.activateByEvent(`onNotebook:${viewType}`); @@ -435,6 +456,7 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase { @ILogService _logService: ILogService, @IExtensionsWorkbenchService _extensionWorkbenchService: IExtensionsWorkbenchService, @IExtensionService _extensionService: IExtensionService, + @IExtensionManagementServerService _extensionManagementServerService: IExtensionManagementServerService, @ICommandService _commandService: ICommandService, @INotebookKernelHistoryService private readonly _notebookKernelHistoryService: INotebookKernelHistoryService, @IOpenerService private readonly _openerService: IOpenerService @@ -449,6 +471,7 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase { _extensionWorkbenchService, _extensionService, _commandService, + _extensionManagementServerService, ); } @@ -609,6 +632,7 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase { await this._showKernelExtension( this._extensionWorkbenchService, this._extensionService, + this._extensionManagementServerService, editor.textModel.viewType, [] ); @@ -617,6 +641,7 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase { await this._showKernelExtension( this._extensionWorkbenchService, this._extensionService, + this._extensionManagementServerService, editor.textModel.viewType, selectedKernelPickItem.extensionIds, this._productService.quality !== 'stable' diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts index 2b2bd8bd..b4c6ca0c 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts @@ -140,11 +140,13 @@ export class NotebooKernelActionViewItem extends ActionViewItem { @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, @INotebookKernelHistoryService private readonly _notebookKernelHistoryService: INotebookKernelHistoryService, ) { + const action = new Action('fakeAction', undefined, ThemeIcon.asClassName(selectKernelIcon), true, (event) => actualAction.run(event)); super( undefined, - new Action('fakeAction', undefined, ThemeIcon.asClassName(selectKernelIcon), true, (event) => actualAction.run(event)), + action, { ...options, label: false, icon: true } ); + this._register(action); this._register(_editor.onDidChangeModel(this._update, this)); this._register(_notebookKernelService.onDidAddKernel(this._update, this)); this._register(_notebookKernelService.onDidRemoveKernel(this._update, this)); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler.ts index 4085b53a..1bab34e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler.ts @@ -111,6 +111,45 @@ export class NotebookOverviewRuler extends Themable { currentFrom += cellHeight; } + + const overviewRulerDecorations = viewModel.getOverviewRulerDecorations(); + + for (let i = 0; i < overviewRulerDecorations.length; i++) { + const decoration = overviewRulerDecorations[i]; + if (!decoration.options.overviewRuler) { + continue; + } + const viewZoneInfo = this.notebookEditor.getViewZoneLayoutInfo(decoration.viewZoneId); + + if (!viewZoneInfo) { + continue; + } + + const fillStyle = this.getColor(decoration.options.overviewRuler.color) ?? '#000000'; + let x = 0; + switch (decoration.options.overviewRuler.position) { + case NotebookOverviewRulerLane.Left: + x = 0; + break; + case NotebookOverviewRulerLane.Center: + x = laneWidth; + break; + case NotebookOverviewRulerLane.Right: + x = laneWidth * 2; + break; + default: + break; + } + + const width = decoration.options.overviewRuler.position === NotebookOverviewRulerLane.Full ? laneWidth * 3 : laneWidth; + + ctx.fillStyle = fillStyle; + + const viewZoneHeight = (viewZoneInfo.height / scrollHeight) * ratio * height; + const viewZoneTop = (viewZoneInfo.top / scrollHeight) * ratio * height; + + ctx.fillRect(x, viewZoneTop, width, viewZoneHeight); + } } } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookViewZones.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookViewZones.ts index 8645d66d..b0ade627 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookViewZones.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookViewZones.ts @@ -6,7 +6,13 @@ import { FastDomNode, createFastDomNode } from '../../../../../base/browser/fastDomNode.js'; import { onUnexpectedError } from '../../../../../base/common/errors.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { INotebookViewCellsUpdateEvent, INotebookViewZone, INotebookViewZoneChangeAccessor } from '../notebookBrowser.js'; +import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; +import { localize2 } from '../../../../../nls.js'; +import { Categories } from '../../../../../platform/action/common/actionCommonCategories.js'; +import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { IsDevelopmentContext } from '../../../../../platform/contextkey/common/contextkeys.js'; +import { IEditorService } from '../../../../services/editor/common/editorService.js'; +import { getNotebookEditorFromEditorPane, INotebookViewCellsUpdateEvent, INotebookViewZone, INotebookViewZoneChangeAccessor } from '../notebookBrowser.js'; import { NotebookCellListView } from '../view/notebookCellListView.js'; import { ICoordinatesConverter } from '../view/notebookRenderingCommon.js'; import { CellViewModel } from '../viewModel/notebookViewModelImpl.js'; @@ -66,6 +72,16 @@ export class NotebookViewZones extends Disposable { return zonesHaveChanged; } + getViewZoneLayoutInfo(viewZoneId: string): { height: number; top: number } | null { + const zoneWidget = this._zones[viewZoneId]; + if (!zoneWidget) { + return null; + } + const top = this.listView.getWhitespacePosition(zoneWidget.whitespaceId); + const height = zoneWidget.zone.heightInPx; + return { height: height, top: top }; + } + onCellsChanged(e: INotebookViewCellsUpdateEvent): void { const splices = e.splices.slice().reverse(); splices.forEach(splice => { @@ -141,6 +157,16 @@ export class NotebookViewZones extends Disposable { private _removeZone(id: string): void { this.listView.removeWhitespace(id); + const zoneWidget = this._zones[id]; + if (zoneWidget) { + // safely remove the dom node from its parent + try { + this.domNode.removeChild(zoneWidget.domNode); + } catch { + // ignore the error + } + } + delete this._zones[id]; } @@ -186,3 +212,59 @@ function safeInvoke1Arg(func: Function, arg1: any): void { onUnexpectedError(e); } } + +class ToggleNotebookViewZoneDeveloperAction extends Action2 { + static viewZoneIds: string[] = []; + constructor() { + super({ + id: 'notebook.developer.addViewZones', + title: localize2('workbench.notebook.developer.addViewZones', "Toggle Notebook View Zones"), + category: Categories.Developer, + precondition: IsDevelopmentContext, + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane); + + if (!editor) { + return; + } + + if (ToggleNotebookViewZoneDeveloperAction.viewZoneIds.length > 0) { + // remove all view zones + editor.changeViewZones(accessor => { + // remove all view zones in reverse order, to follow how we handle this in the prod code + ToggleNotebookViewZoneDeveloperAction.viewZoneIds.reverse().forEach(id => { + accessor.removeZone(id); + }); + ToggleNotebookViewZoneDeveloperAction.viewZoneIds = []; + }); + } else { + editor.changeViewZones(accessor => { + const cells = editor.getCellsInRange(); + if (cells.length === 0) { + return; + } + + const viewZoneIds: string[] = []; + for (let i = 0; i < cells.length; i++) { + const domNode = document.createElement('div'); + domNode.innerText = `View Zone ${i}`; + domNode.style.backgroundColor = 'rgba(0, 255, 0, 0.5)'; + const viewZoneId = accessor.addZone({ + afterModelPosition: i, + heightInPx: 200, + domNode: domNode, + }); + viewZoneIds.push(viewZoneId); + } + ToggleNotebookViewZoneDeveloperAction.viewZoneIds = viewZoneIds; + }); + } + } +} + +registerAction2(ToggleNotebookViewZoneDeveloperAction); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 8f84a01b..3970dd6b 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -209,9 +209,6 @@ export class NotebookCellTextModel extends Disposable implements ICell { this._outputs = outputs.map(op => new NotebookCellOutputTextModel(op)); this._metadata = metadata ?? {}; this._internalMetadata = internalMetadata ?? {}; - this._internalMetadata.cellId = this._internalMetadata.cellId ?? - this._metadata.id as string ?? - uri.fragment; } enableAutoLanguageDetection() { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index a7f1c786..237e0ff8 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -31,6 +31,7 @@ import { NotebookCellTextModel } from './notebookCellTextModel.js'; class StackOperation implements IWorkspaceUndoRedoElement { type: UndoRedoElementType.Workspace; + tag = 'notebookUndoRedoElement'; public get code() { return this._operations.length === 1 ? this._operations[0].code : 'undoredo.notebooks.stackOperation'; @@ -121,6 +122,7 @@ class StackOperation implements IWorkspaceUndoRedoElement { class NotebookOperationManager { private _pendingStackOperation: StackOperation | null = null; + private _isAppending: boolean = false; constructor( private readonly _textModel: NotebookTextModel, private _undoService: IUndoRedoService, @@ -136,14 +138,28 @@ class NotebookOperationManager { pushStackElement(alternativeVersionId: string, selectionState: ISelectionState | undefined) { if (this._pendingStackOperation && !this._pendingStackOperation.isEmpty) { this._pendingStackOperation.pushEndState(alternativeVersionId, selectionState); - this._undoService.pushElement(this._pendingStackOperation, this._pendingStackOperation.undoRedoGroup); + if (!this._isAppending) { + this._undoService.pushElement(this._pendingStackOperation, this._pendingStackOperation.undoRedoGroup); + } } + this._isAppending = false; this._pendingStackOperation = null; } + private _getOrCreateEditStackElement(beginSelectionState: ISelectionState | undefined, undoRedoGroup: UndoRedoGroup | undefined, alternativeVersionId: string) { return this._pendingStackOperation ??= new StackOperation(this._textModel, undoRedoGroup, this._pauseableEmitter, this._postUndoRedo, beginSelectionState, alternativeVersionId || ''); } + appendPreviousOperation(): boolean { + const previous = this._undoService.getLastElement(this._textModel.uri) as StackOperation; + if (previous && previous.tag === 'notebookUndoRedoElement') { + this._pendingStackOperation = previous; + this._isAppending = true; + return true; + } + return false; + } + pushEditOperation(element: IUndoRedoElement, beginSelectionState: ISelectionState | undefined, resultSelectionState: ISelectionState | undefined, alternativeVersionId: string, undoRedoGroup: UndoRedoGroup | undefined) { const pendingStackOperation = this._getOrCreateEditStackElement(beginSelectionState, undoRedoGroup, alternativeVersionId); pendingStackOperation.pushEditOperation(element, beginSelectionState, resultSelectionState, alternativeVersionId); @@ -582,10 +598,40 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel return result; } + private newCellsFromLastEdit = new Set(); + private isOnlyEditingMetadataOnNewCells(rawEdits: ICellEditOperation[]): boolean { + for (const edit of rawEdits) { + if (edit.editType === CellEditType.PartialInternalMetadata) { + continue; + } + if (edit.editType !== CellEditType.Metadata && edit.editType !== CellEditType.PartialMetadata) { + return false; + } + + if (('index' in edit) && !this.newCellsFromLastEdit.has(this.cells[edit.index].handle)) { + return false; + } + if ('handle' in edit && !this.newCellsFromLastEdit.has(edit.handle)) { + return false; + } + } + + return true; + } + applyEdits(rawEdits: ICellEditOperation[], synchronous: boolean, beginSelectionState: ISelectionState | undefined, endSelectionsComputer: () => ISelectionState | undefined, undoRedoGroup: UndoRedoGroup | undefined, computeUndoRedo: boolean): boolean { this._pauseableEmitter.pause(); this._operationManager.pushStackElement(this._alternativeVersionId, undefined); + if (computeUndoRedo && this.isOnlyEditingMetadataOnNewCells(rawEdits)) { + if (!this._operationManager.appendPreviousOperation()) { + // we can't append the previous operation, so just don't compute undo/redo + computeUndoRedo = false; + } + } else if (computeUndoRedo) { + this.newCellsFromLastEdit.clear(); + } + try { this._doApplyEdits(rawEdits, synchronous, computeUndoRedo, beginSelectionState, undoRedoGroup); return true; @@ -816,6 +862,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel const dirtyStateListener = cell.onDidChangeContent((e) => { this._bindCellContentHandler(cell, e); }); + + this.newCellsFromLastEdit.add(cell.handle); this._cellListeners.set(cell.handle, dirtyStateListener); this._register(cell); return cell; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index d87906fa..40c4bd34 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -19,7 +19,6 @@ import { ISplice } from '../../../../base/common/sequence.js'; import { ThemeColor } from '../../../../base/common/themables.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; import { Range } from '../../../../editor/common/core/range.js'; -import { ILineChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; import * as editorCommon from '../../../../editor/common/editorCommon.js'; import { Command, WorkspaceEditMetadata } from '../../../../editor/common/languages.js'; import { IReadonlyTextBuffer, ITextModel } from '../../../../editor/common/model.js'; @@ -34,7 +33,7 @@ import { ICellExecutionError } from './notebookExecutionStateService.js'; import { INotebookTextModelLike } from './notebookKernelService.js'; import { ICellRange } from './notebookRange.js'; import { RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js'; -import { generateMetadataUri, generate as generateUri, parseMetadataUri, parse as parseUri } from '../../../services/notebook/common/notebookDocumentService.js'; +import { generateMetadataUri, generate as generateUri, extractCellOutputDetails, parseMetadataUri, parse as parseUri } from '../../../services/notebook/common/notebookDocumentService.js'; import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from '../../../services/workingCopy/common/workingCopy.js'; import { SnapshotContext } from '../../../services/workingCopy/common/fileWorkingCopy.js'; @@ -121,7 +120,12 @@ export interface NotebookCellMetadata { } export interface NotebookCellInternalMetadata { - cellId?: string; + /** + * Used only for diffing of Notebooks. + * This is not persisted and generally useful only when diffing two notebooks. + * Useful only after we've manually matched a few cells together so we know which cells are matching. + */ + internalId?: string; executionId?: string; executionOrder?: number; lastRunSuccess?: boolean; @@ -613,32 +617,37 @@ export namespace CellUri { return parseUri(cell); } - export function generateCellOutputUri(notebook: URI, outputId?: string) { + /** + * Generates a URI for a cell output in a notebook using the output ID. + * Used when URI should be opened as text in the editor. + */ + export function generateCellOutputUriWithId(notebook: URI, outputId?: string) { return notebook.with({ scheme: Schemas.vscodeNotebookCellOutput, - fragment: `op${outputId ?? ''},${notebook.scheme !== Schemas.file ? notebook.scheme : ''}` + query: new URLSearchParams({ + openIn: 'editor', + outputId: outputId ?? '', + notebookScheme: notebook.scheme !== Schemas.file ? notebook.scheme : '', + }).toString() + }); + } + /** + * Generates a URI for a cell output in a notebook using the output index. + * Used when URI should be opened in notebook editor. + */ + export function generateCellOutputUriWithIndex(notebook: URI, cellUri: URI, outputIndex: number): URI { + return notebook.with({ + scheme: Schemas.vscodeNotebookCellOutput, + fragment: cellUri.fragment, + query: new URLSearchParams({ + openIn: 'notebook', + outputIndex: String(outputIndex), + }).toString() }); } - export function parseCellOutputUri(uri: URI): { notebook: URI; outputId?: string } | undefined { - if (uri.scheme !== Schemas.vscodeNotebookCellOutput) { - return; - } - - const match = /^op([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?\,(.*)$/i.exec(uri.fragment); - if (!match) { - return undefined; - } - - const outputId = (match[1] && match[1] !== '') ? match[1] : undefined; - const scheme = match[2]; - return { - outputId, - notebook: uri.with({ - scheme: scheme || Schemas.file, - fragment: null - }) - }; + export function parseCellOutputUri(uri: URI): { notebook: URI; openIn: string; outputId?: string; cellFragment?: string; outputIndex?: number; cellHandle?: number } | undefined { + return extractCellOutputDetails(uri); } export function generateCellPropertyUri(notebook: URI, handle: number, scheme: string): URI { @@ -955,7 +964,6 @@ export interface INotebookCellStatusBarItemProvider { export interface INotebookDiffResult { cellsDiff: IDiffResult; metadataChanged: boolean; - linesDiff?: { originalCellhandle: number; modifiedCellhandle: number; lineChanges: ILineChange[] }[]; } export interface INotebookCellStatusBarItem { diff --git a/src/vs/workbench/contrib/notebook/common/notebookDiff.ts b/src/vs/workbench/contrib/notebook/common/notebookDiff.ts new file mode 100644 index 00000000..fb8844cf --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/notebookDiff.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDiffChange } from '../../../../base/common/diff/diff.js'; +import { CellKind, INotebookDiffResult } from './notebookCommon.js'; + +export type CellDiffInfo = { + originalCellIndex: number; + modifiedCellIndex: number; + type: 'unchanged' | 'modified'; +} | +{ + originalCellIndex: number; + type: 'delete'; +} | +{ + modifiedCellIndex: number; + type: 'insert'; +}; + +interface ICell { + cellKind: CellKind; + getHashValue(): number; + equal(cell: ICell): boolean; +} +// interface INotebookDiffResult { +// cellsDiff: IDiffResult; +// metadataChanged: boolean; +// } + +export function computeDiff(originalModel: { readonly cells: readonly ICell[] }, modifiedModel: { readonly cells: readonly ICell[] }, 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 computeModifiedLCS(change: IDiffChange, originalModel: { readonly cells: readonly ICell[] }, modifiedModel: { readonly cells: readonly ICell[] }) { + const result: CellDiffInfo[] = []; + // modified cells + const modifiedLen = Math.min(change.originalLength, change.modifiedLength); + + for (let j = 0; j < modifiedLen; j++) { + const originalCell = originalModel.cells[change.originalStart + j]; + const modifiedCell = modifiedModel.cells[change.modifiedStart + j]; + if (originalCell.cellKind !== modifiedCell.cellKind) { + result.push({ + originalCellIndex: change.originalStart + j, + type: 'delete' + }); + result.push({ + modifiedCellIndex: change.modifiedStart + j, + type: 'insert' + }); + } else { + const isTheSame = originalCell.equal(modifiedCell); + 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; +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 68b112f2..d0e9724f 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -27,7 +27,6 @@ import { IFileWorkingCopyManager } from '../../../services/workingCopy/common/fi import { IStoredFileWorkingCopy, IStoredFileWorkingCopyModel, IStoredFileWorkingCopyModelContentChangedEvent, IStoredFileWorkingCopyModelFactory, IStoredFileWorkingCopySaveEvent, StoredFileWorkingCopyState } from '../../../services/workingCopy/common/storedFileWorkingCopy.js'; import { IUntitledFileWorkingCopy, IUntitledFileWorkingCopyModel, IUntitledFileWorkingCopyModelContentChangedEvent, IUntitledFileWorkingCopyModelFactory } from '../../../services/workingCopy/common/untitledFileWorkingCopy.js'; import { WorkingCopyCapabilities } from '../../../services/workingCopy/common/workingCopy.js'; -import { INotebookSynchronizerService } from './notebookSynchronizerService.js'; //#region --- simple content provider @@ -56,7 +55,6 @@ export class SimpleNotebookEditorModel extends EditorModel implements INotebookE private readonly _workingCopyManager: IFileWorkingCopyManager, scratchpad: boolean, @IFilesConfigurationService private readonly _filesConfigurationService: IFilesConfigurationService, - @INotebookSynchronizerService private readonly _notebookSynchronizerService: INotebookSynchronizerService ) { super(); @@ -122,9 +120,6 @@ export class SimpleNotebookEditorModel extends EditorModel implements INotebookE async revert(options?: IRevertOptions): Promise { assertType(this.isResolved()); - if (this._workingCopy) { - await this._notebookSynchronizerService.revert(this._workingCopy); - } return this._workingCopy!.revert(options); } @@ -141,7 +136,7 @@ export class SimpleNotebookEditorModel extends EditorModel implements INotebookE } else { this._workingCopy = await this._workingCopyManager.resolve({ untitledResource: this.resource, isScratchpad: this.scratchPad }); } - this._workingCopy.onDidRevert(() => this._onDidRevertUntitled.fire()); + this._register(this._workingCopy.onDidRevert(() => this._onDidRevertUntitled.fire())); } else { this._workingCopy = await this._workingCopyManager.resolve(this.resource, { limits: options?.limits, diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts index a21a9575..862ca633 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts @@ -61,6 +61,15 @@ class NotebookModelReferenceCollection extends ReferenceCollection { // Untrack as being disposed this.modelsToDispose.delete(key); @@ -186,7 +195,7 @@ export class NotebookModelResolverServiceImpl implements INotebookEditorModelRes const suffix = NotebookProviderInfo.possibleFileEnding(info.selectors) ?? ''; for (let counter = 1; ; counter++) { const candidate = URI.from({ scheme: Schemas.untitled, path: `Untitled-${counter}${suffix}`, query: notebookType }); - if (!this._notebookService.getNotebookTextModel(candidate)) { + if (!this._notebookService.getNotebookTextModel(candidate) && !this._data.isListeningToModel(candidate)) { return candidate; } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookSynchronizerService.ts b/src/vs/workbench/contrib/notebook/common/notebookSynchronizerService.ts deleted file mode 100644 index 4fc50465..00000000 --- a/src/vs/workbench/contrib/notebook/common/notebookSynchronizerService.ts +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IStoredFileWorkingCopy } from '../../../services/workingCopy/common/storedFileWorkingCopy.js'; -import { IUntitledFileWorkingCopy } from '../../../services/workingCopy/common/untitledFileWorkingCopy.js'; -import { NotebookFileWorkingCopyModel } from './notebookEditorModel.js'; - -export const INotebookSynchronizerService = createDecorator('notebookSynchronizerService'); - -export interface INotebookSynchronizerService { - readonly _serviceBrand: undefined; - revert(workingCopy: IStoredFileWorkingCopy | IUntitledFileWorkingCopy): Promise; -} diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookCellMatching.ts b/src/vs/workbench/contrib/notebook/common/services/notebookCellMatching.ts new file mode 100644 index 00000000..0df0bab4 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/services/notebookCellMatching.ts @@ -0,0 +1,344 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { computeLevenshteinDistance } from '../../../../../base/common/diff/diff.js'; +import { CellKind } from '../notebookCommon.js'; + + +type EditCount = number; +type OriginalIndex = number; +type ModifiedIndex = number; +type CellEditCountCache = { + modifiedToOriginal: Map>; + originalToModified: Map>; +}; + +type ICell = { + internalMetadata?: { + internalId?: string; + }; + getValue(): string; + getLinesContent(): string[]; + cellKind: CellKind; +}; + +/** + * Given a set of modified cells and original cells, this function will attempt to match the modified cells with the original cells. + * E.g. Assume you have (original on left and modified on right): + * ================= + * Cell A | Cell a + * Cell B | Cell b + * Cell C | Cell d + * Cell D | Cell e + * ================= + * Here we know that `Cell C` has been removed and `Cell e` has been added. + * The mapping from modified to original will be as follows: + * Cell a => Cell A + * Cell b => Cell B + * Cell d => Cell D + * Cell e => + * Cell C in original was not matched, hence it was deleted. + * + * Thus the return value is as follows: + * [ + * { modified: 0, original: 0 }, + * { modified: 1, original: 1 }, + * { modified: 2, original: 3 }, + * { modified: 3, original: -1 }, + * ] + * @returns + */ +export function matchCellBasedOnSimilarties(modifiedCells: ICell[], originalCells: ICell[]): { modified: number; original: number; percentage: number }[] { + const cache: CellEditCountCache = { + modifiedToOriginal: new Map>(), + originalToModified: new Map>(), + }; + const results: { modified: number; original: number; dist: number; percentage: number; possibleOriginal: number }[] = []; + const mappedOriginalCellToModifiedCell = new Map(); + const mappedModifiedIndexes = new Set(); + const originalIndexWithMostEdits = new Map(); + const canOriginalIndexBeMappedToModifiedIndex = (originalIndex: number, value: { editCount: EditCount }) => { + if (mappedOriginalCellToModifiedCell.has(originalIndex)) { + return false; + } + const existingEdits = originalIndexWithMostEdits.get(originalIndex)?.dist ?? Number.MAX_SAFE_INTEGER; + return value.editCount < existingEdits; + }; + const trackMappedIndexes = (modifiedIndex: number, originalIndex: number) => { + mappedOriginalCellToModifiedCell.set(originalIndex, modifiedIndex); + mappedModifiedIndexes.add(modifiedIndex); + }; + + for (let i = 0; i < modifiedCells.length; i++) { + const modifiedCell = modifiedCells[i]; + const { index, editCount: dist, percentage } = computeClosestCell({ cell: modifiedCell, index: i }, originalCells, true, cache, canOriginalIndexBeMappedToModifiedIndex); + if (index >= 0 && dist === 0) { + trackMappedIndexes(i, index); + results.push({ modified: i, original: index, dist, percentage, possibleOriginal: index }); + } else { + originalIndexWithMostEdits.set(index, { dist: dist, modifiedIndex: i }); + results.push({ modified: i, original: -1, dist: dist, percentage, possibleOriginal: index }); + } + } + + results.forEach((result, i) => { + if (result.original >= 0) { + return; + } + + /** + * I.e. Assume you have the following + * ================= + * A a (this has ben matched) + * B b + * C c + * D d (these two have been matched) + * e e + * f f + * ================= + * Just match A => a, B => b, C => c + */ + // Find the next cell that has been matched. + const previousMatchedCell = i > 0 ? results.slice(0, i).reverse().find(r => r.original >= 0) : undefined; + const previousMatchedOriginalIndex = previousMatchedCell?.original ?? -1; + const previousMatchedModifiedIndex = previousMatchedCell?.modified ?? -1; + const matchedCell = results.slice(i + 1).find(r => r.original >= 0); + const unavailableIndexes = new Set(); + const nextMatchedModifiedIndex = results.findIndex((item, idx) => idx > i && item.original >= 0); + const nextMatchedOriginalIndex = nextMatchedModifiedIndex >= 0 ? results[nextMatchedModifiedIndex].original : -1; + // Find the available indexes that we can match with. + // We are only interested in b and c (anything after d is of no use). + originalCells.forEach((_, i) => { + if (mappedOriginalCellToModifiedCell.has(i)) { + unavailableIndexes.add(i); + return; + } + if (matchedCell && i >= matchedCell.original) { + unavailableIndexes.add(i); + } + if (nextMatchedOriginalIndex >= 0 && i > nextMatchedOriginalIndex) { + unavailableIndexes.add(i); + } + }); + + + const modifiedCell = modifiedCells[i]; + /** + * I.e. Assume you have the following + * ================= + * A a (this has ben matched) + * B b + * C c + * D d (these two have been matched) + * e e + * f f + * ================= + * Given that we have a probable match for B => b, we can match it. + */ + if (result.original === -1 && result.possibleOriginal >= 0 && !unavailableIndexes.has(result.possibleOriginal) && canOriginalIndexBeMappedToModifiedIndex(result.possibleOriginal, { editCount: result.dist })) { + trackMappedIndexes(i, result.possibleOriginal); + result.original = result.possibleOriginal; + return; + } + + + /** + * I.e. Assume you have the following + * ================= + * A a (this has ben matched) + * B b + * C c + * D d (these two have been matched) + * ================= + * Its possible that B matches better with c and C matches better with b. + * However given the fact that we have matched A => a and D => d. + * & if the indexes are an exact match. + * I.e. index of D in Modified === index of d in Original, and index of A in Modified === index of a in Original. + * Then this means there are absolutely no modifications. + * Hence we can just assign the indexes as is. + * + * NOTE: For this, we must ensure we have exactly the same number of items on either side. + * I.e. we have B, C remaining in Modified, and b, c remaining in Original. + * Thats 2 Modified items === 2 Original Items. + * If its not the same, then this means something has been deleted/inserted, and we cannot blindly map the indexes. + */ + if (previousMatchedOriginalIndex > 0 && previousMatchedModifiedIndex > 0 && previousMatchedOriginalIndex === previousMatchedModifiedIndex) { + if ((nextMatchedModifiedIndex >= 0 ? nextMatchedModifiedIndex : modifiedCells.length - 1) === (nextMatchedOriginalIndex >= 0 ? nextMatchedOriginalIndex : originalCells.length - 1) && !unavailableIndexes.has(i) && i < originalCells.length) { + const remainingModifiedItems = (nextMatchedModifiedIndex >= 0 ? nextMatchedModifiedIndex : modifiedCells.length) - previousMatchedModifiedIndex; + const remainingOriginalItems = (nextMatchedOriginalIndex >= 0 ? nextMatchedOriginalIndex : originalCells.length) - previousMatchedOriginalIndex; + if (remainingModifiedItems === remainingOriginalItems && modifiedCell.cellKind === originalCells[i].cellKind) { + trackMappedIndexes(i, i); + result.original = i; + return; + } + } + } + /** + * I.e. Assume you have the following + * ================= + * A a (this has ben matched) + * B b + * C c + * D d (these two have been matched) + * e e + * f f + * ================= + * We can now try to match B with b and c and figure out which is best. + * RULE 1. Its possible that B will match best with c, howevber C matches better with c, meaning we should match B with b. + * To do this, we need to see if c has a better match with something else. + */ + // RULE 1 + // Try to find the next best match, but exclucde items that have a better match. + const { index, percentage } = computeClosestCell({ cell: modifiedCell, index: i }, originalCells, false, cache, (originalIndex: number, originalValue: { editCount: EditCount }) => { + if (unavailableIndexes.has(originalIndex)) { + return false; + } + + if (nextMatchedModifiedIndex > 0 || previousMatchedOriginalIndex > 0) { + // See if we have a beter match for this. + const matchesForThisOriginalIndex = cache.originalToModified.get(originalIndex); + if (matchesForThisOriginalIndex && previousMatchedOriginalIndex < originalIndex) { + const betterMatch = Array.from(matchesForThisOriginalIndex).find(([modifiedIndex, value]) => { + if (modifiedIndex === i) { + // This is the same modifeid entry. + return false; + } + if (modifiedIndex >= nextMatchedModifiedIndex) { + // We're only interested in matches that are before the next matched index. + return false; + } + if (mappedModifiedIndexes.has(i)) { + // This has already been matched. + return false; + } + return value.editCount < originalValue.editCount; + }); + if (betterMatch) { + // We do have a better match for this, hence do not use this. + return false; + } + } + } + return !unavailableIndexes.has(originalIndex); + }); + + /** + * I.e. Assume you have the following + * ================= + * A a (this has ben matched) + * B bbbbbbbbbbbbbb + * C cccccccccccccc + * D d (these two have been matched) + * e e + * f f + * ================= + * RULE 1 . Now when attempting to match `bbbbbbbbbbbb` with B, the number of edits is very high and the percentage is also very high. + * Basically majority of the text needs to be changed. + * However if the indexes line up perfectly well, and this is the best match, then use it. + * + * Similarly its possible we're trying to match b with `BBBBBBBBBBBB` and the number of edits is very high, but the indexes line up perfectly well. + * + * RULE 2. However it is also possible that there's a better match with another cell + * Assume we have + * ================= + * AAAA a (this has been matched) + * bbbbbbbb b + * bbbb c + * dddd d (these two have been matched) + * ================= + * In this case if we use the algorithm of (1) above, we'll end up matching bbbb with b, and bbbbbbbb with c. + * But we're not really sure if this is the best match. + * In such cases try to match with the same cell index. + * + */ + // RULE 1 (got a match and the indexes line up perfectly well, use it regardless of the number of edits). + if (index >= 0 && i > 0 && results[i - 1].original === index - 1) { + trackMappedIndexes(i, index); + results[i].original = index; + return; + } + + // RULE 2 + // Here we know that `AAAA => a` + // Check if the previous cell has been matched. + // And if the next modified and next original cells are a match. + const nextOriginalCell = (i > 0 && originalCells.length > results[i - 1].original) ? results[i - 1].original + 1 : -1; + const nextOriginalCellValue = i > 0 && nextOriginalCell >= 0 && nextOriginalCell < originalCells.length ? originalCells[nextOriginalCell].getValue() : undefined; + if (index >= 0 && i > 0 && typeof nextOriginalCellValue === 'string' && !mappedOriginalCellToModifiedCell.has(nextOriginalCell)) { + if (modifiedCell.getValue().includes(nextOriginalCellValue) || nextOriginalCellValue.includes(modifiedCell.getValue())) { + trackMappedIndexes(i, nextOriginalCell); + results[i].original = nextOriginalCell; + return; + } + } + + if (percentage < 90 || (i === 0 && results.length === 1)) { + trackMappedIndexes(i, index); + results[i].original = index; + return; + } + }); + + return results; +} + +function computeClosestCell({ cell, index: cellIndex }: { cell: ICell; index: number }, arr: readonly ICell[], ignoreEmptyCells: boolean, cache: CellEditCountCache, canOriginalIndexBeMappedToModifiedIndex: (originalIndex: number, value: { editCount: EditCount }) => boolean): { index: number; editCount: number; percentage: number } { + let min_edits = Infinity; + let min_index = -1; + + // Always give preference to internal Cell Id if found. + const internalId = cell.internalMetadata?.internalId; + if (internalId) { + const internalIdIndex = arr.findIndex(cell => cell.internalMetadata?.internalId === internalId); + if (internalIdIndex >= 0) { + return { index: internalIdIndex, editCount: 0, percentage: Number.MAX_SAFE_INTEGER }; + } + } + + for (let i = 0; i < arr.length; i++) { + // Skip cells that are not of the same kind. + if (arr[i].cellKind !== cell.cellKind) { + continue; + } + const str = arr[i].getValue(); + const cacheEntry = cache.modifiedToOriginal.get(cellIndex) ?? new Map(); + const value = cacheEntry.get(i) ?? { editCount: computeNumberOfEdits(cell, arr[i]), }; + cacheEntry.set(i, value); + cache.modifiedToOriginal.set(cellIndex, cacheEntry); + + const originalCacheEntry = cache.originalToModified.get(i) ?? new Map(); + originalCacheEntry.set(cellIndex, value); + cache.originalToModified.set(i, originalCacheEntry); + + if (!canOriginalIndexBeMappedToModifiedIndex(i, value)) { + continue; + } + if (str.length === 0 && ignoreEmptyCells) { + continue; + } + if (str === cell.getValue() && cell.getValue().length > 0) { + return { index: i, editCount: 0, percentage: 0 }; + } + + if (value.editCount < min_edits) { + min_edits = value.editCount; + min_index = i; + } + } + + if (min_index === -1) { + return { index: -1, editCount: Number.MAX_SAFE_INTEGER, percentage: Number.MAX_SAFE_INTEGER }; + } + const percentage = !cell.getValue().length && !arr[min_index].getValue().length ? 0 : (cell.getValue().length ? (min_edits * 100 / cell.getValue().length) : Number.MAX_SAFE_INTEGER); + return { index: min_index, editCount: min_edits, percentage }; +} + +function computeNumberOfEdits(modified: ICell, original: ICell) { + if (modified.getValue() === original.getValue()) { + return 0; + } + + return computeLevenshteinDistance(modified.getValue(), original.getValue()); +} diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts deleted file mode 100644 index c5b12612..00000000 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts +++ /dev/null @@ -1,370 +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 { IDiffResult, ISequence, LcsDiff } from '../../../../../base/common/diff/diff.js'; -import { doHash, numberHash } from '../../../../../base/common/hash.js'; -import { IDisposable } from '../../../../../base/common/lifecycle.js'; -import { URI } from '../../../../../base/common/uri.js'; -import { IRequestHandler, IWorkerServer } from '../../../../../base/common/worker/simpleWorker.js'; -import { PieceTreeTextBufferBuilder } from '../../../../../editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; -import { CellKind, IMainCellDto, INotebookDiffResult, IOutputDto, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookDocumentMetadata, TransientDocumentMetadata } from '../notebookCommon.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { SearchParams } from '../../../../../editor/common/model/textModelSearch.js'; -import { MirrorModel } from '../../../../../editor/common/services/textModelSync/textModelSync.impl.js'; -import { DefaultEndOfLine } from '../../../../../editor/common/model.js'; -import { IModelChangedEvent } from '../../../../../editor/common/model/mirrorTextModel.js'; -import { filter } from '../../../../../base/common/objects.js'; - -class MirrorCell { - private readonly textModel: MirrorModel; - private _hash?: Promise; - public get eol() { - return this._eol === '\r\n' ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF; - } - constructor( - public readonly handle: number, - uri: URI, - source: string[], - private readonly _eol: string, - versionId: number, - public language: string, - public cellKind: CellKind, - public outputs: IOutputDto[], - public metadata?: NotebookCellMetadata, - public internalMetadata?: NotebookCellInternalMetadata, - - ) { - this.textModel = new MirrorModel(uri, source, _eol, versionId); - } - - onEvents(e: IModelChangedEvent) { - this.textModel.onEvents(e); - this._hash = undefined; - } - getValue(): string { - return this.textModel.getValue(); - } - - async getComparisonValue(): Promise { - return this._hash ??= this._getHash(); - } - - private async _getHash() { - let hashValue = numberHash(104579, 0); - - hashValue = doHash(this.language, hashValue); - hashValue = doHash(this.getValue(), hashValue); - hashValue = doHash(this.metadata, hashValue); - hashValue = doHash({ ...this.internalMetadata, 'cellId': '' }, hashValue); - for (const op of this.outputs) { - hashValue = doHash(op.metadata, hashValue); - for (const output of op.outputs) { - hashValue = doHash(output.mime, hashValue); - } - } - - // note: hash has not updated within the Promise.all since we must retain order - const digests = await Promise.all(this.outputs.flatMap(op => - op.outputs.map(o => crypto.subtle.digest('sha-1', o.data.buffer)) - )); - for (const digest of digests) { - hashValue = numberHash(new Int32Array(digest)[0], hashValue); - } - - - return hashValue; - } -} - -class MirrorNotebookDocument { - constructor( - readonly uri: URI, - public cells: MirrorCell[], - public metadata: NotebookDocumentMetadata, - public transientDocumentMetadata: TransientDocumentMetadata, - ) { - } - - acceptModelChanged(event: NotebookCellsChangedEventDto) { - // note that the cell content change is not applied to the MirrorCell - // but it's fine as if a cell content is modified after the first diff, its position will not change any more - // TODO@rebornix, but it might lead to interesting bugs in the future. - event.rawEvents.forEach(e => { - if (e.kind === NotebookCellsChangeType.ModelChange) { - this._spliceNotebookCells(e.changes); - } else if (e.kind === NotebookCellsChangeType.Move) { - const cells = this.cells.splice(e.index, 1); - this.cells.splice(e.newIdx, 0, ...cells); - } else if (e.kind === NotebookCellsChangeType.Output) { - const cell = this.cells[e.index]; - cell.outputs = e.outputs; - } else if (e.kind === NotebookCellsChangeType.ChangeCellLanguage) { - this._assertIndex(e.index); - const cell = this.cells[e.index]; - cell.language = e.language; - } else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) { - this._assertIndex(e.index); - const cell = this.cells[e.index]; - cell.metadata = e.metadata; - } else if (e.kind === NotebookCellsChangeType.ChangeCellInternalMetadata) { - this._assertIndex(e.index); - const cell = this.cells[e.index]; - cell.internalMetadata = e.internalMetadata; - } else if (e.kind === NotebookCellsChangeType.ChangeDocumentMetadata) { - this.metadata = e.metadata; - } - }); - } - - private _assertIndex(index: number): void { - if (index < 0 || index >= this.cells.length) { - throw new Error(`Illegal index ${index}. Cells length: ${this.cells.length}`); - } - } - - _spliceNotebookCells(splices: NotebookCellTextModelSplice[]) { - splices.reverse().forEach(splice => { - const cellDtos = splice[2]; - const newCells = cellDtos.map(cell => { - return new MirrorCell( - cell.handle, - URI.parse(cell.url), - cell.source, - cell.eol, - cell.versionId, - cell.language, - cell.cellKind, - cell.outputs, - cell.metadata, - ); - }); - - this.cells.splice(splice[0], splice[1], ...newCells); - }); - } -} - -class CellSequence implements ISequence { - - static async create(textModel: MirrorNotebookDocument) { - const hashValue = new Int32Array(textModel.cells.length); - await Promise.all(textModel.cells.map(async (c, i) => { - hashValue[i] = await c.getComparisonValue(); - })); - return new CellSequence(hashValue); - } - - static async createWithCellId(textModel: MirrorNotebookDocument): Promise> { - const hashValue = new Map(); - await Promise.all(textModel.cells.map(async (c, i) => { - const value = await c.getComparisonValue(); - const id: string = (c.metadata?.id || '') as string; - hashValue.set(id, value); - })); - return hashValue; - } - - constructor(readonly hashValue: Int32Array) { } - - getElements(): string[] | number[] | Int32Array { - return this.hashValue; - } -} - -export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable { - _requestHandlerBrand: any; - - private _models: { [uri: string]: MirrorNotebookDocument }; - - constructor() { - this._models = Object.create(null); - } - dispose(): void { - } - - public $acceptNewModel(uri: string, metadata: NotebookDocumentMetadata, transientDocumentMetadata: TransientDocumentMetadata, cells: IMainCellDto[]): void { - this._models[uri] = new MirrorNotebookDocument(URI.parse(uri), cells.map(dto => new MirrorCell( - dto.handle, - URI.parse(dto.url), - dto.source, - dto.eol, - dto.versionId, - dto.language, - dto.cellKind, - dto.outputs, - dto.metadata, - dto.internalMetadata - )), metadata, transientDocumentMetadata); - } - - public $acceptModelChanged(strURL: string, event: NotebookCellsChangedEventDto) { - const model = this._models[strURL]; - model?.acceptModelChanged(event); - } - - public $acceptCellModelChanged(strURL: string, handle: number, event: IModelChangedEvent) { - const model = this._models[strURL]; - model.cells.find(cell => cell.handle === handle)?.onEvents(event); - } - - public $acceptRemovedModel(strURL: string): void { - if (!this._models[strURL]) { - return; - } - delete this._models[strURL]; - } - - async $computeDiff(originalUrl: string, modifiedUrl: string): Promise { - const original = this._getModel(originalUrl); - const modified = this._getModel(modifiedUrl); - - const [originalSeq, modifiedSeq] = await Promise.all([ - CellSequence.create(original), - CellSequence.create(modified), - ]); - - const diff = new LcsDiff(originalSeq, modifiedSeq); - const diffResult = diff.ComputeDiff(false); - - const originalMetadata = filter(original.metadata, key => !original.transientDocumentMetadata[key]); - const modifiedMetadata = filter(modified.metadata, key => !modified.transientDocumentMetadata[key]); - return { - metadataChanged: JSON.stringify(originalMetadata) !== JSON.stringify(modifiedMetadata), - cellsDiff: await this.$computeDiffWithCellIds(original, modified) || diffResult, - // linesDiff: cellLineChanges - }; - } - - async $computeDiffWithCellIds(original: MirrorNotebookDocument, modified: MirrorNotebookDocument): Promise { - const originalCellIndexIds = original.cells.map((cell, index) => ({ index, id: (cell.metadata?.id || '') as string })); - const modifiedCellIndexIds = modified.cells.map((cell, index) => ({ index, id: (cell.metadata?.id || '') as string })); - const originalCellIds = originalCellIndexIds.map(c => c.id); - const modifiedCellIds = modifiedCellIndexIds.map(c => c.id); - const orderOrOriginalCellIds = originalCellIds.filter(id => modifiedCellIds.includes(id)).join(','); - const orderOrModifiedCellIds = modifiedCellIds.filter(id => originalCellIds.includes(id)).join(','); - if (originalCellIndexIds.some(c => !c.id) || modifiedCellIndexIds.some(c => !c.id) || orderOrOriginalCellIds !== orderOrModifiedCellIds) { - return; - } - - const diffResult: IDiffResult = { changes: [], quitEarly: false, }; - - const computeCellHashesById = async (notebook: MirrorNotebookDocument) => { - const hashValue = new Map(); - await Promise.all(notebook.cells.map(async (c, i) => { - const value = await c.getComparisonValue(); - // Verified earlier that these cannot be empty. - const id: string = (c.metadata?.id || '') as string; - hashValue.set(id, value); - })); - return hashValue; - }; - - const [originalSeq, modifiedSeq] = await Promise.all([computeCellHashesById(original), computeCellHashesById(modified)]); - - while (modifiedCellIndexIds.length) { - const modifiedCell = modifiedCellIndexIds.shift()!; - const originalCell = originalCellIndexIds.find(c => c.id === modifiedCell.id); - if (originalCell) { - // Everything before this cell is a deletion - const index = originalCellIndexIds.indexOf(originalCell); - const deletedFromOriginal = originalCellIndexIds.splice(0, index + 1); - - if (deletedFromOriginal.length === 1) { - if (originalSeq.get(originalCell.id) === modifiedSeq.get(originalCell.id)) { - // Cell contents are the same. - // No changes, hence ignore this cell. - } - else { - diffResult.changes.push({ - originalStart: originalCell.index, - originalLength: 1, - modifiedStart: modifiedCell.index, - modifiedLength: 1 - }); - } - } else { - // This means we have some cells before this and they were removed. - diffResult.changes.push({ - originalStart: deletedFromOriginal[0].index, - originalLength: deletedFromOriginal.length - 1, - modifiedStart: modifiedCell.index, - modifiedLength: 0 - }); - } - continue; - } - else { - // This is a new cell. - diffResult.changes.push({ - originalStart: originalCellIndexIds.length ? originalCellIndexIds[0].index : original.cells.length, - originalLength: 0, - modifiedStart: modifiedCell.index, - modifiedLength: 1 - }); - } - } - - // If we still have some original cells, then those have been removed. - if (originalCellIndexIds.length) { - diffResult.changes.push({ - originalStart: originalCellIndexIds[0].index, - originalLength: originalCellIndexIds.length, - modifiedStart: modifiedCellIndexIds.length, - modifiedLength: 0 - }); - } - - return diffResult; - } - - $canPromptRecommendation(modelUrl: string): boolean { - const model = this._getModel(modelUrl); - const cells = model.cells; - - for (let i = 0; i < cells.length; i++) { - const cell = cells[i]; - if (cell.cellKind === CellKind.Markup) { - continue; - } - - if (cell.language !== 'python') { - continue; - } - - const searchParams = new SearchParams('import\\s*pandas|from\\s*pandas', true, false, null); - const searchData = searchParams.parseSearchRequest(); - - if (!searchData) { - continue; - } - - const builder = new PieceTreeTextBufferBuilder(); - builder.acceptChunk(cell.getValue()); - const bufferFactory = builder.finish(true); - const textBuffer = bufferFactory.create(cell.eol).textBuffer; - - const lineCount = textBuffer.getLineCount(); - const maxLineCount = Math.min(lineCount, 20); - const range = new Range(1, 1, maxLineCount, textBuffer.getLineLength(maxLineCount) + 1); - const cellMatches = textBuffer.findMatchesLineByLine(range, searchData, true, 1); - if (cellMatches.length > 0) { - return true; - } - } - - return false; - } - - protected _getModel(uri: string): MirrorNotebookDocument { - return this._models[uri]; - } -} - -/** - * Defines the worker entry point. Must be exported and named `create`. - * @skipMangle - */ -export function create(workerServer: IWorkerServer): IRequestHandler { - return new NotebookEditorSimpleWorker(); -} diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts new file mode 100644 index 00000000..d95e43c5 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts @@ -0,0 +1,576 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IDiffChange, ISequence, LcsDiff } from '../../../../../base/common/diff/diff.js'; +import { doHash, hash, numberHash } from '../../../../../base/common/hash.js'; +import { IDisposable } from '../../../../../base/common/lifecycle.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { IWebWorkerServerRequestHandler } from '../../../../../base/common/worker/webWorker.js'; +import { PieceTreeTextBufferBuilder } from '../../../../../editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; +import { CellKind, IMainCellDto, INotebookDiffResult, IOutputDto, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookDocumentMetadata, TransientDocumentMetadata } from '../notebookCommon.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { SearchParams } from '../../../../../editor/common/model/textModelSearch.js'; +import { MirrorModel } from '../../../../../editor/common/services/textModelSync/textModelSync.impl.js'; +import { DefaultEndOfLine } from '../../../../../editor/common/model.js'; +import { IModelChangedEvent } from '../../../../../editor/common/model/mirrorTextModel.js'; +import { filter } from '../../../../../base/common/objects.js'; +import { matchCellBasedOnSimilarties } from './notebookCellMatching.js'; +import { generateUuid } from '../../../../../base/common/uuid.js'; +import { DiffChange } from '../../../../../base/common/diff/diffChange.js'; +import { computeDiff } from '../notebookDiff.js'; + +const PREFIX_FOR_UNMATCHED_ORIGINAL_CELLS = `unmatchedOriginalCell`; + +class MirrorCell { + private readonly textModel: MirrorModel; + private _hash?: number; + public get eol() { + return this._eol === '\r\n' ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF; + } + constructor( + public readonly handle: number, + uri: URI, + source: string[], + private readonly _eol: string, + versionId: number, + public language: string, + public cellKind: CellKind, + public outputs: IOutputDto[], + public metadata?: NotebookCellMetadata, + public internalMetadata?: NotebookCellInternalMetadata, + + ) { + this.textModel = new MirrorModel(uri, source, _eol, versionId); + } + + onEvents(e: IModelChangedEvent) { + this.textModel.onEvents(e); + this._hash = undefined; + } + getValue(): string { + return this.textModel.getValue(); + } + + getLinesContent(): string[] { + return this.textModel.getLinesContent(); + } + getComparisonValue(): number { + return this._hash ??= this._getHash(); + } + + private _getHash() { + let hashValue = numberHash(104579, 0); + + hashValue = doHash(this.language, hashValue); + hashValue = doHash(this.getValue(), hashValue); + hashValue = doHash(this.metadata, hashValue); + // For purpose of diffing only cellId matters, rest do not + hashValue = doHash(this.internalMetadata?.internalId || '', hashValue); + for (const op of this.outputs) { + hashValue = doHash(op.metadata, hashValue); + for (const output of op.outputs) { + hashValue = doHash(output.mime, hashValue); + } + } + + const digests = this.outputs.flatMap(op => + op.outputs.map(o => hash(Array.from(o.data.buffer))) + ); + for (const digest of digests) { + hashValue = numberHash(digest, hashValue); + } + + return hashValue; + } +} + +class MirrorNotebookDocument { + constructor( + readonly uri: URI, + public cells: MirrorCell[], + public metadata: NotebookDocumentMetadata, + public transientDocumentMetadata: TransientDocumentMetadata, + ) { + } + + acceptModelChanged(event: NotebookCellsChangedEventDto) { + // note that the cell content change is not applied to the MirrorCell + // but it's fine as if a cell content is modified after the first diff, its position will not change any more + // TODO@rebornix, but it might lead to interesting bugs in the future. + event.rawEvents.forEach(e => { + if (e.kind === NotebookCellsChangeType.ModelChange) { + this._spliceNotebookCells(e.changes); + } else if (e.kind === NotebookCellsChangeType.Move) { + const cells = this.cells.splice(e.index, 1); + this.cells.splice(e.newIdx, 0, ...cells); + } else if (e.kind === NotebookCellsChangeType.Output) { + const cell = this.cells[e.index]; + cell.outputs = e.outputs; + } else if (e.kind === NotebookCellsChangeType.ChangeCellLanguage) { + this._assertIndex(e.index); + const cell = this.cells[e.index]; + cell.language = e.language; + } else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) { + this._assertIndex(e.index); + const cell = this.cells[e.index]; + cell.metadata = e.metadata; + } else if (e.kind === NotebookCellsChangeType.ChangeCellInternalMetadata) { + this._assertIndex(e.index); + const cell = this.cells[e.index]; + cell.internalMetadata = e.internalMetadata; + } else if (e.kind === NotebookCellsChangeType.ChangeDocumentMetadata) { + this.metadata = e.metadata; + } + }); + } + + private _assertIndex(index: number): void { + if (index < 0 || index >= this.cells.length) { + throw new Error(`Illegal index ${index}. Cells length: ${this.cells.length}`); + } + } + + _spliceNotebookCells(splices: NotebookCellTextModelSplice[]) { + splices.reverse().forEach(splice => { + const cellDtos = splice[2]; + const newCells = cellDtos.map(cell => { + return new MirrorCell( + cell.handle, + URI.parse(cell.url), + cell.source, + cell.eol, + cell.versionId, + cell.language, + cell.cellKind, + cell.outputs, + cell.metadata, + ); + }); + + this.cells.splice(splice[0], splice[1], ...newCells); + }); + } +} + +class CellSequence implements ISequence { + + static create(textModel: MirrorNotebookDocument) { + const hashValue = textModel.cells.map(c => c.getComparisonValue()); + return new CellSequence(hashValue); + } + static createWithCellId(cells: MirrorCell[], includeCellContents?: boolean) { + const hashValue = cells.map((c) => { + if (includeCellContents) { + return `${doHash(c.internalMetadata?.internalId, numberHash(104579, 0))}#${c.getComparisonValue()}`; + } else { + return `${doHash(c.internalMetadata?.internalId, numberHash(104579, 0))}}`; + } + }); + return new CellSequence(hashValue); + } + + constructor(readonly hashValue: number[] | string[]) { } + + getElements(): string[] | number[] | Int32Array { + return this.hashValue; + } +} + +export class NotebookWorker implements IWebWorkerServerRequestHandler, IDisposable { + _requestHandlerBrand: any; + + private _models: { [uri: string]: MirrorNotebookDocument }; + + constructor() { + this._models = Object.create(null); + } + dispose(): void { + } + + public $acceptNewModel(uri: string, metadata: NotebookDocumentMetadata, transientDocumentMetadata: TransientDocumentMetadata, cells: IMainCellDto[]): void { + this._models[uri] = new MirrorNotebookDocument(URI.parse(uri), cells.map(dto => new MirrorCell( + dto.handle, + URI.parse(dto.url), + dto.source, + dto.eol, + dto.versionId, + dto.language, + dto.cellKind, + dto.outputs, + dto.metadata, + dto.internalMetadata + )), metadata, transientDocumentMetadata); + } + + public $acceptModelChanged(strURL: string, event: NotebookCellsChangedEventDto) { + const model = this._models[strURL]; + model?.acceptModelChanged(event); + } + + public $acceptCellModelChanged(strURL: string, handle: number, event: IModelChangedEvent) { + const model = this._models[strURL]; + model.cells.find(cell => cell.handle === handle)?.onEvents(event); + } + + public $acceptRemovedModel(strURL: string): void { + if (!this._models[strURL]) { + return; + } + delete this._models[strURL]; + } + + async $computeDiff(originalUrl: string, modifiedUrl: string): Promise { + const original = this._getModel(originalUrl); + const modified = this._getModel(modifiedUrl); + + const originalModel = new NotebookTextModelFacade(original); + const modifiedModel = new NotebookTextModelFacade(modified); + + const originalMetadata = filter(original.metadata, key => !original.transientDocumentMetadata[key]); + const modifiedMetadata = filter(modified.metadata, key => !modified.transientDocumentMetadata[key]); + const metadataChanged = JSON.stringify(originalMetadata) !== JSON.stringify(modifiedMetadata); + // TODO@DonJayamanne + // In the future we might want to avoid computing LCS of outputs + // That will make this faster. + const originalDiff = new LcsDiff(CellSequence.create(original), CellSequence.create(modified)).ComputeDiff(false); + if (originalDiff.changes.length === 0) { + return { + metadataChanged, + cellsDiff: originalDiff + }; + } + + // This will return the mapping of the cells and what cells were inserted/deleted. + // We do not care much about accuracy of the diff, but care about the mapping of unmodified cells. + // That can be used as anchor points to find the cells that have changed. + // And on cells that have changed, we can use similarity algorithms to find the mapping. + // Eg as mentioned earlier, its possible after similarity algorithms we find that cells weren't inserted/deleted but were just modified. + const cellMapping = computeDiff(originalModel, modifiedModel, { cellsDiff: { changes: originalDiff.changes, quitEarly: false }, metadataChanged: false, }).cellDiffInfo; + + // If we have no insertions/deletions, then this is a good diffing. + if (cellMapping.every(c => c.type === 'modified')) { + return { + metadataChanged, + cellsDiff: originalDiff + }; + } + + let diffUsingCellIds = this.canComputeDiffWithCellIds(original, modified); + if (!diffUsingCellIds) { + /** + * Assume we have cells as follows + * Original Modified + * A A + * B B + * C e + * D F + * E + * F + * + * Using LCS we know easily that A, B cells match. + * Using LCS it would look like C changed to e + * Using LCS D & E were removed. + * + * A human would be able to tell that cell C, D were removed. + * A human can tell that E changed to e because the code in the cells are very similar. + * Note the words `similar`, humans try to match cells based on certain heuristics. + * & the most obvious one is the similarity of the code in the cells. + * + * LCS has no notion of similarity, it only knows about equality. + * We can use other algorithms to find similarity. + * So if we eliminate A, B, we are left with C, D, E, F and we need to find what they map to in `e, F` in modifed document. + * We can use a similarity algorithm to find that. + * + * The purpose of using LCS first is to find the cells that have not changed. + * This avoids the need to use similarity algorithms on all cells. + * + * At the end of the day what we need is as follows + * A <=> A + * B <=> B + * C => Deleted + * D => Deleted + * E => e + * F => F + */ + + + + // Note, if cells are swapped, then this compilicates things + // Trying to solve diff manually is not easy. + // Lets instead use LCS find the cells that haven't changed, + // & the cells that have. + // For the range of cells that have change, lets see if we can get better results using similarity algorithms. + // Assume we have + // Code Cell = print("Hello World") + // Code Cell = print("Foo Bar") + // We now change this to + // MD Cell = # Description + // Code Cell = print("Hello WorldZ") + // Code Cell = print("Foo BarZ") + // LCS will tell us that everything changed. + // But using similarity algorithms we can tell that the first cell is new and last 2 changed. + + + + // Lets try the similarity algorithms on all cells. + // We might fare better. + const result = matchCellBasedOnSimilarties(modified.cells, original.cells); + // If we have at least one match, then great. + if (result.some(c => c.original !== -1)) { + // We have managed to find similarities between cells. + // Now we can definitely find what cell is new/removed. + this.updateCellIdsBasedOnMappings(result, original.cells, modified.cells); + diffUsingCellIds = true; + } + } + + if (!diffUsingCellIds) { + return { + metadataChanged, + cellsDiff: originalDiff + }; + } + + // At this stage we can use internalMetadata.cellId for tracking changes. + // I.e. we compute LCS diff and the hashes of some cells from original will be equal to that in modified as we're using cellId. + // Thus we can find what cells are new/deleted. + // After that we can find whether the contents of the cells changed. + const cellsInsertedOrDeletedDiff = new LcsDiff(CellSequence.createWithCellId(original.cells), CellSequence.createWithCellId(modified.cells)).ComputeDiff(false); + const cellDiffInfo = computeDiff(originalModel, modifiedModel, { cellsDiff: { changes: cellsInsertedOrDeletedDiff.changes, quitEarly: false }, metadataChanged: false, }).cellDiffInfo; + + let processedIndex = 0; + const changes: IDiffChange[] = []; + cellsInsertedOrDeletedDiff.changes.forEach(change => { + if (!change.originalLength && change.modifiedLength) { + // Inserted. + // Find all modified cells before this. + const changeIndex = cellDiffInfo.findIndex(c => c.type === 'insert' && c.modifiedCellIndex === change.modifiedStart); + cellDiffInfo.slice(processedIndex, changeIndex).forEach(c => { + if (c.type === 'unchanged' || c.type === 'modified') { + const originalCell = original.cells[c.originalCellIndex]; + const modifiedCell = modified.cells[c.modifiedCellIndex]; + const changed = c.type === 'modified' || originalCell.getComparisonValue() !== modifiedCell.getComparisonValue(); + if (changed) { + changes.push(new DiffChange(c.originalCellIndex, 1, c.modifiedCellIndex, 1)); + } + } + }); + changes.push(change); + processedIndex = changeIndex + 1; + } else if (change.originalLength && !change.modifiedLength) { + // Deleted. + // Find all modified cells before this. + const changeIndex = cellDiffInfo.findIndex(c => c.type === 'delete' && c.originalCellIndex === change.originalStart); + cellDiffInfo.slice(processedIndex, changeIndex).forEach(c => { + if (c.type === 'unchanged' || c.type === 'modified') { + const originalCell = original.cells[c.originalCellIndex]; + const modifiedCell = modified.cells[c.modifiedCellIndex]; + const changed = c.type === 'modified' || originalCell.getComparisonValue() !== modifiedCell.getComparisonValue(); + if (changed) { + changes.push(new DiffChange(c.originalCellIndex, 1, c.modifiedCellIndex, 1)); + } + } + }); + changes.push(change); + processedIndex = changeIndex + 1; + } else { + // This could be a situation where a cell has been deleted on left and inserted on the right. + // E.g. markdown cell deleted and code cell inserted. + // But LCS shows them as a modification. + const changeIndex = cellDiffInfo.findIndex(c => (c.type === 'delete' && c.originalCellIndex === change.originalStart) || (c.type === 'insert' && c.modifiedCellIndex === change.modifiedStart)); + cellDiffInfo.slice(processedIndex, changeIndex).forEach(c => { + if (c.type === 'unchanged' || c.type === 'modified') { + const originalCell = original.cells[c.originalCellIndex]; + const modifiedCell = modified.cells[c.modifiedCellIndex]; + const changed = c.type === 'modified' || originalCell.getComparisonValue() !== modifiedCell.getComparisonValue(); + if (changed) { + changes.push(new DiffChange(c.originalCellIndex, 1, c.modifiedCellIndex, 1)); + } + } + }); + changes.push(change); + processedIndex = changeIndex + 1; + } + }); + cellDiffInfo.slice(processedIndex).forEach(c => { + if (c.type === 'unchanged' || c.type === 'modified') { + const originalCell = original.cells[c.originalCellIndex]; + const modifiedCell = modified.cells[c.modifiedCellIndex]; + const changed = c.type === 'modified' || originalCell.getComparisonValue() !== modifiedCell.getComparisonValue(); + if (changed) { + changes.push(new DiffChange(c.originalCellIndex, 1, c.modifiedCellIndex, 1)); + } + } + }); + + return { + metadataChanged, + cellsDiff: { + changes, + quitEarly: false + } + }; + } + + canComputeDiffWithCellIds(original: MirrorNotebookDocument, modified: MirrorNotebookDocument): boolean { + return this.canComputeDiffWithCellInternalIds(original, modified) || this.canComputeDiffWithCellMetadataIds(original, modified); + } + + canComputeDiffWithCellInternalIds(original: MirrorNotebookDocument, modified: MirrorNotebookDocument): boolean { + const originalCellIndexIds = original.cells.map((cell, index) => ({ index, id: (cell.internalMetadata?.internalId || '') as string })); + const modifiedCellIndexIds = modified.cells.map((cell, index) => ({ index, id: (cell.internalMetadata?.internalId || '') as string })); + // If we have a cell without an id, do not use metadata.id for diffing. + if (originalCellIndexIds.some(c => !c.id) || modifiedCellIndexIds.some(c => !c.id)) { + return false; + } + // If none of the ids in original can be found in modified, then we can't use metadata.id for diffing. + // I.e. everything is new, no point trying. + return originalCellIndexIds.some(c => modifiedCellIndexIds.find(m => m.id === c.id)); + } + + canComputeDiffWithCellMetadataIds(original: MirrorNotebookDocument, modified: MirrorNotebookDocument): boolean { + const originalCellIndexIds = original.cells.map((cell, index) => ({ index, id: (cell.metadata?.id || '') as string })); + const modifiedCellIndexIds = modified.cells.map((cell, index) => ({ index, id: (cell.metadata?.id || '') as string })); + // If we have a cell without an id, do not use metadata.id for diffing. + if (originalCellIndexIds.some(c => !c.id) || modifiedCellIndexIds.some(c => !c.id)) { + return false; + } + // If none of the ids in original can be found in modified, then we can't use metadata.id for diffing. + // I.e. everything is new, no point trying. + if (originalCellIndexIds.every(c => !modifiedCellIndexIds.find(m => m.id === c.id))) { + return false; + } + + // Internally we use internalMetadata.cellId for diffing, hence update the internalMetadata.cellId + original.cells.map((cell, index) => { + cell.internalMetadata = cell.internalMetadata || {}; + cell.internalMetadata.internalId = cell.metadata?.id as string || ''; + }); + modified.cells.map((cell, index) => { + cell.internalMetadata = cell.internalMetadata || {}; + cell.internalMetadata.internalId = cell.metadata?.id as string || ''; + }); + return true; + } + + + isOriginalCellMatchedWithModifiedCell(originalCell: MirrorCell) { + return (originalCell.internalMetadata?.internalId as string || '').startsWith(PREFIX_FOR_UNMATCHED_ORIGINAL_CELLS); + } + updateCellIdsBasedOnMappings(mappings: { modified: number; original: number }[], originalCells: MirrorCell[], modifiedCells: MirrorCell[]): boolean { + const uuids = new Map(); + originalCells.map((cell, index) => { + cell.internalMetadata = cell.internalMetadata || { internalId: '' }; + cell.internalMetadata.internalId = `${PREFIX_FOR_UNMATCHED_ORIGINAL_CELLS}${generateUuid()}`; + const found = mappings.find(r => r.original === index); + if (found) { + // Do not use the indexes as ids. + // If we do, then the hashes will be very similar except for last digit. + cell.internalMetadata.internalId = generateUuid(); + uuids.set(found.modified, cell.internalMetadata.internalId as string); + } + }); + modifiedCells.map((cell, index) => { + cell.internalMetadata = cell.internalMetadata || { internalId: '' }; + cell.internalMetadata.internalId = uuids.get(index) ?? generateUuid(); + }); + return true; + } + + $canPromptRecommendation(modelUrl: string): boolean { + const model = this._getModel(modelUrl); + const cells = model.cells; + + for (let i = 0; i < cells.length; i++) { + const cell = cells[i]; + if (cell.cellKind === CellKind.Markup) { + continue; + } + + if (cell.language !== 'python') { + continue; + } + + const searchParams = new SearchParams('import\\s*pandas|from\\s*pandas', true, false, null); + const searchData = searchParams.parseSearchRequest(); + + if (!searchData) { + continue; + } + + const builder = new PieceTreeTextBufferBuilder(); + builder.acceptChunk(cell.getValue()); + const bufferFactory = builder.finish(true); + const textBuffer = bufferFactory.create(cell.eol).textBuffer; + + const lineCount = textBuffer.getLineCount(); + const maxLineCount = Math.min(lineCount, 20); + const range = new Range(1, 1, maxLineCount, textBuffer.getLineLength(maxLineCount) + 1); + const cellMatches = textBuffer.findMatchesLineByLine(range, searchData, true, 1); + if (cellMatches.length > 0) { + return true; + } + } + + return false; + } + + protected _getModel(uri: string): MirrorNotebookDocument { + return this._models[uri]; + } +} + +export function create(): IWebWorkerServerRequestHandler { + return new NotebookWorker(); +} + +export type CellDiffInfo = { + originalCellIndex: number; + modifiedCellIndex: number; + type: 'unchanged' | 'modified'; +} | +{ + originalCellIndex: number; + type: 'delete'; +} | +{ + modifiedCellIndex: number; + type: 'insert'; +}; + +interface ICell { + cellKind: CellKind; + getHashValue(): number; + equal(cell: ICell): boolean; +} + +class NotebookTextModelFacade { + public readonly cells: readonly ICell[]; + constructor( + readonly notebook: MirrorNotebookDocument + ) { + + this.cells = notebook.cells.map(cell => new NotebookCellTextModelFacade(cell)); + } + +} +class NotebookCellTextModelFacade implements ICell { + get cellKind(): CellKind { + return this.cell.cellKind; + } + constructor( + private readonly cell: MirrorCell + ) { + } + getHashValue(): number { + return this.cell.getComparisonValue(); + } + equal(cell: ICell): boolean { + if (cell.cellKind !== this.cellKind) { + return false; + } + return this.getHashValue() === cell.getHashValue(); + } + +} diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorkerMain.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWebWorkerMain.ts similarity index 65% rename from src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorkerMain.ts rename to src/vs/workbench/contrib/notebook/common/services/notebookWebWorkerMain.ts index edebb727..80021b49 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorkerMain.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWebWorkerMain.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { bootstrapSimpleWorker } from '../../../../../base/common/worker/simpleWorkerBootstrap.js'; -import { create } from './notebookSimpleWorker.js'; +import { bootstrapWebWorker } from '../../../../../base/common/worker/webWorkerBootstrap.js'; +import { create } from './notebookWebWorker.js'; -bootstrapSimpleWorker(create); +bootstrapWebWorker(create); diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookCellDiagnostics.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookCellDiagnostics.test.ts index 73ddc03a..5c218da6 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookCellDiagnostics.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookCellDiagnostics.test.ts @@ -14,13 +14,14 @@ import { IConfigurationService } from '../../../../../../platform/configuration/ import { TestConfigurationService } from '../../../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IMarkerData, IMarkerService } from '../../../../../../platform/markers/common/markers.js'; -import { ChatAgentLocation, IChatAgent, IChatAgentData, IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { IChatAgent, IChatAgentData, IChatAgentService } from '../../../../chat/common/chatAgents.js'; import { CellDiagnostics } from '../../../browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.js'; import { CodeCellViewModel } from '../../../browser/viewModel/codeCellViewModel.js'; import { CellKind, NotebookSetting } from '../../../common/notebookCommon.js'; import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookCellExecution, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; import { setupInstantiationService, TestNotebookExecutionStateService, withTestNotebook } from '../testNotebookEditor.js'; import { nullExtensionDescription } from '../../../../../services/extensions/common/extensions.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; suite('notebookCellDiagnostics', () => { @@ -71,7 +72,7 @@ suite('notebookCellDiagnostics', () => { extensionPublisherId: '', name: 'testEditorAgent', isDefault: true, - locations: [ChatAgentLocation.Editor], + locations: [ChatAgentLocation.Notebook], metadata: {}, slashCommands: [], disambiguation: [], diff --git a/src/vs/workbench/contrib/notebook/test/browser/diff/notebookDiffService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/diff/notebookDiffService.test.ts new file mode 100644 index 00000000..29a71805 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/diff/notebookDiffService.test.ts @@ -0,0 +1,14750 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { Mimes } from '../../../../../../base/common/mime.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; +import { CellKind, IMainCellDto, IOutputDto, NotebookCellMetadata } from '../../../common/notebookCommon.js'; +import { matchCellBasedOnSimilarties } from '../../../common/services/notebookCellMatching.js'; +import { NotebookWorker } from '../../../common/services/notebookWebWorker.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { IDiffChange } from '../../../../../../base/common/diff/diff.js'; + + +suite('NotebookDiff Diff Service', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + let worker: NotebookWorker; + suiteSetup(() => { + worker = new NotebookWorker(); + }); + suiteTeardown(() => { + worker.dispose(); + }); + + test('No changes', async () => { + const { mapping, diff } = await mapCells([ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ], [ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes.length, 0); + }); + + test('diff different source', async () => { + const { mapping, diff } = await mapCells([ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ], [ + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange + ]); + }); + + test('diff different source (2 cells)', async () => { + const { mapping, diff } = await mapCells([ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ], [ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['z'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 1, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange + ]); + }); + + test('diff different source (first cell changed)', async () => { + const { mapping, diff } = await mapCells([ + [['import sys'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ], [ + [['import pandas'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange + ]); + }); + + + test('diff test small source', async () => { + const { mapping, diff } = await mapCells([ + [['123456789'], 'javascript', CellKind.Code, [], {}] + ], [ + [['987654321'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange + ]); + }); + + test('diff test data single cell', async () => { + const { mapping, diff } = await mapCells([ + [[ + '# This version has a bug\n', + 'def mult(a, b):\n', + ' return a / b' + ], 'javascript', CellKind.Code, [], {}] + ], [ + [[ + 'def mult(a, b):\n', + ' \'This version is debugged.\'\n', + ' return a * b' + ], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange + ]); + }); + + test('diff foo/foe (swapped cells)', async () => { + const { mapping, diff } = await mapCells([ + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([6])) }] }], { metadata: { collapsed: false }, executionOrder: 5 }], + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([2])) }] }], { metadata: { collapsed: false }, executionOrder: 6 }], + [[''], 'javascript', CellKind.Code, [], {}] + ], [ + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([2])) }] }], { metadata: { collapsed: false }, executionOrder: 6 }], + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([6])) }] }], { metadata: { collapsed: false }, executionOrder: 5 }], + [[''], 'javascript', CellKind.Code, [], {}] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 1 }, + { modified: 1, original: 0 }, + { modified: 2, original: 2 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 1, originalLength: 1, modifiedStart: 2, modifiedLength: 0 } satisfies IDiffChange, + // { originalStart: 1, originalLength: 1, modifiedStart: 2, modifiedLength: 0 } satisfies IDiffChange + ]); + }); + + test('diff foo/foe (swapped cells with changes in output)', async () => { + const { mapping, diff } = await mapCells([ + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([6])) }] }], { metadata: { collapsed: false }, executionOrder: 5 }], + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([2])) }] }], { metadata: { collapsed: false }, executionOrder: 6 }], + [[''], 'javascript', CellKind.Code, [], {}] + ], [ + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([6])) }] }], { metadata: { collapsed: false }, executionOrder: 5 }], + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([2])) }] }], { metadata: { collapsed: false }, executionOrder: 6 }], + [[''], 'javascript', CellKind.Code, [], {}] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 1 }, + { modified: 1, original: 0 }, + { modified: 2, original: 2 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 0, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 1, originalLength: 1, modifiedStart: 2, modifiedLength: 0 } satisfies IDiffChange, + ]); + }); + + test('diff markdown', async () => { + const { mapping, diff } = await mapCells([ + [['This is a test notebook with only markdown cells'], 'markdown', CellKind.Markup, [], {}], + [['Lorem ipsum dolor sit amet'], 'markdown', CellKind.Markup, [], {}], + [['In other news'], 'markdown', CellKind.Markup, [], {}], + ], [ + [['This is a test notebook with markdown cells only'], 'markdown', CellKind.Markup, [], {}], + [['Lorem ipsum dolor sit amet'], 'markdown', CellKind.Markup, [], {}], + [['In the news'], 'markdown', CellKind.Markup, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('diff insert 1 at the top', async () => { + const { mapping, diff } = await mapCells([ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}] + ], [ + [['var h = 8;'], 'javascript', CellKind.Code, [], {}], + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: -1 }, + { modified: 1, original: 0 }, + { modified: 2, original: 1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('diff insert 1 at the top with lots of cells', async () => { + + const { mapping, diff } = await mapCells([ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ], [ + [['var h = 8;'], 'javascript', CellKind.Code, [], {}], + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: -1 }, + { modified: 1, original: 0 }, + { modified: 2, original: 1 }, + { modified: 3, original: 2 }, + { modified: 4, original: 3 }, + { modified: 5, original: 4 }, + { modified: 6, original: 5 }, + { modified: 7, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('diff insert 3 at the top with lots of cells', async () => { + + const { mapping, diff } = await mapCells([ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ], [ + [['var h = 8;'], 'javascript', CellKind.Code, [], {}], + [['var i = 8;'], 'javascript', CellKind.Code, [], {}], + [['var j = 8;'], 'javascript', CellKind.Code, [], {}], + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: -1 }, + { modified: 1, original: -1 }, + { modified: 2, original: -1 }, + { modified: 3, original: 0 }, + { modified: 4, original: 1 }, + { modified: 5, original: 2 }, + { modified: 6, original: 3 }, + { modified: 7, original: 4 }, + { modified: 8, original: 5 }, + { modified: 9, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 3 } satisfies IDiffChange, + ]); + }); + + test('diff insert 1 in the middle', async () => { + + const { mapping, diff } = await mapCells([ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ], [ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var h = 8;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: -1 }, + { modified: 5, original: 4 }, + { modified: 6, original: 5 }, + { modified: 7, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 4, originalLength: 0, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('LCS (swap cells with outputs)', async () => { + const { mapping, diff } = await mapCells([ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }] + ], [ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 1, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('LCS (outputs changed)', async () => { + const { mapping, diff } = await mapCells([ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }], + [['y'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }] + ], [ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true } }], + [['y'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Swap cells with output and with same code in each cell', async () => { + const { mapping, diff } = await mapCells([ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }], + [['x = 5'], 'javascript', CellKind.Code, [], {}], + [['x'], 'javascript', CellKind.Code, [], {}], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([5])) }] }], {}], + ], [ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }], + [['x = 5'], 'javascript', CellKind.Code, [], {}], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([5])) }] }], {}], + [['x'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 1, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 6, originalLength: 1, modifiedStart: 6, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Change outputs of cells', async () => { + const { mapping, diff } = await mapCells([ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }], + [['y'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }], + [['x = 5'], 'javascript', CellKind.Code, [], {}], + [['x'], 'javascript', CellKind.Code, [], {}], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([5])) }] }], {}], + ], [ + [['# Description'], 'markdown', CellKind.Markup, [], { metadata: {} }], + [['x = 3'], 'javascript', CellKind.Code, [], { metadata: { collapsed: true }, executionOrder: 1 }], + [['x'], 'javascript', CellKind.Code, [], { metadata: { collapsed: false } }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 1 }], + [['x = 5'], 'javascript', CellKind.Code, [], {}], + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([5])) }] }], {}], + [['y'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 1, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 6, originalLength: 1, modifiedStart: 6, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Insert a new cell', async () => { + const { mapping, diff } = await mapCells([ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ], [ + [['var a = 1;'], 'javascript', CellKind.Code, [], {}], + [['var b = 2;'], 'javascript', CellKind.Code, [], {}], + [['var c = 3;'], 'javascript', CellKind.Code, [], {}], + [['var d = 4;'], 'javascript', CellKind.Code, [], {}], + [['var h = 8;'], 'javascript', CellKind.Code, [], {}], + [['var e = 5;'], 'javascript', CellKind.Code, [], {}], + [['var f = 6;'], 'javascript', CellKind.Code, [], {}], + [['var g = 7;'], 'javascript', CellKind.Code, [], {}], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: -1 },// + { modified: 5, original: 4 }, + { modified: 6, original: 5 }, + { modified: 7, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 4, originalLength: 0, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('diff output', async () => { + const { mapping, diff } = await mapCells([ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([4])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ], [ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([5])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 1, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('diff output fast check', async () => { + const { mapping, diff } = await mapCells([ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([4])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ], [ + [['x'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([3])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + [['y'], 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: Mimes.text, data: VSBuffer.wrap(new Uint8Array([5])) }] }], { metadata: { collapsed: false }, executionOrder: 3 }], + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 1, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('No cells', async () => { + const { mapping, diff } = await mapCells([ + ], [ + ]); + + assert.deepStrictEqual(mapping, [ + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + ]); + }); + test('No cells and add 1 cell', async () => { + const { mapping, diff } = await mapCells([ + ], [ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: -1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('One cells and delete 1 cell', async () => { + const { mapping, diff } = await mapCells([ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], [ + ]); + + assert.deepStrictEqual(mapping, [ + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 0 } satisfies IDiffChange, + ]); + }); + test('No changes with 1 cell', async () => { + const { mapping, diff } = await mapCells([ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], [ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + // { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 0 } satisfies IDiffChange, + ]); + }); + test('One changes with 1 cell', async () => { + const { mapping, diff } = await mapCells([ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], + [ + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('No changes with 2 cells', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + // { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('One changes with 2 cells, 1st cell changed', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], [ + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('2 cells, 1st cell deleted', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], [ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 0 } satisfies IDiffChange, + ]); + }); + test('2 cells, inserted a cell in the middle', async () => { + const { mapping, diff } = await mapCells([ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined] + ], [ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("Bar Baz")'], 'python', CellKind.Code, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: -1 }, + { modified: 2, original: 1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 1, originalLength: 0, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('One changes with 2 cells, 2nd cell changed', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ], [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined] + ]); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 1, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Two cells, all cells changed', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined] + ] + , [ + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined] + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 2, modifiedStart: 0, modifiedLength: 2 } satisfies IDiffChange, + ]); + }); + test('Insert a md cell and modify the next code cell (few cells)', async () => { + const { mapping, diff } = await mapCells([ + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Description'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello worldz")'], 'python', CellKind.Code, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: -1 }, + { modified: 1, original: 0 }, + { modified: 2, original: 1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 0, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Insert a md cell and modify the next code cell', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz1234")'], 'python', CellKind.Code, [], undefined] + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['# New Markdown cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo baz")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz1234")'], 'python', CellKind.Code, [], undefined] + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: -1 }, + { modified: 4, original: 3 }, + { modified: 5, original: 4 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 3, originalLength: 0, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 1, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Delete code cell and insert MD cell, both will not match', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['sys.executable'], 'python', CellKind.Code, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['# Header'], 'python', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: -1 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Delete MD cell and insert code cell, both will not match', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['sys.executable'], 'python', CellKind.Code, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['# Header'], 'python', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: -1 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Insert a md cell and modify the next 2 code cells', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz")'], 'python', CellKind.Code, [], undefined] + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['# New Markdown cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("Foo BaZ")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz Modified")'], 'python', CellKind.Code, [], undefined] + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: -1 }, + { modified: 4, original: 3 }, + { modified: 5, original: 4 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 3, originalLength: 0, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 1, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 4, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Insert a md cell, delete a cell and modify the next 2 code cells', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz")'], 'python', CellKind.Code, [], undefined], + [['print("Fox Trot")'], 'python', CellKind.Code, [], undefined] + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['# New Markdown cell'], 'markdown', CellKind.Markup, [], undefined], + // [['print("Foo BaZ")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz Modified")'], 'python', CellKind.Code, [], undefined], + [['print("Fox Trots")'], 'python', CellKind.Code, [], undefined] + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: -1 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 3, originalLength: 1, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 4, originalLength: 1, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Insert a md cell, delete a code cell and modify the next 2 code cells (deleted and inserted lineup)', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['print("Foo Bar")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz")'], 'python', CellKind.Code, [], undefined], + [['print("Fox Trot")'], 'python', CellKind.Code, [], undefined] + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['sys.executable'], 'python', CellKind.Code, [], undefined], + // [['print("Foo BaZ")'], 'python', CellKind.Code, [], undefined], + [['print("bar baz Modified")'], 'python', CellKind.Code, [], undefined], + [['print("Fox Trot")'], 'python', CellKind.Code, [], undefined] + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 3, originalLength: 1, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 4, originalLength: 1, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + + test('Insert a few md cells and modify the next few code cells', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Yet another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# New 1'], 'markdown', CellKind.Markup, [], undefined], + [['# New 2'], 'markdown', CellKind.Markup, [], undefined], + [['# Another MD cell (modified1)'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell (modified)'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: -1 }, + { modified: 5, original: -1 }, + { modified: 6, original: 4 }, + { modified: 7, original: 5 }, + { modified: 8, original: 6 }, + { modified: 9, original: 7 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 4, originalLength: 0, modifiedStart: 4, modifiedLength: 2 } satisfies IDiffChange, + { originalStart: 4, originalLength: 1, modifiedStart: 6, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 6, originalLength: 1, modifiedStart: 8, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Insert & delete a few md cells and modify the next few code cells', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Yet another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# New 1'], 'markdown', CellKind.Markup, [], undefined], + [['# New 2'], 'markdown', CellKind.Markup, [], undefined], + [['# Another MD cell (modified1)'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell (modified)'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: -1 }, + { modified: 5, original: -1 }, + { modified: 6, original: 4 }, + { modified: 7, original: 5 }, + { modified: 8, original: 6 }, + { modified: 9, original: 7 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 4, originalLength: 0, modifiedStart: 4, modifiedLength: 2 } satisfies IDiffChange, + { originalStart: 4, originalLength: 1, modifiedStart: 6, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 6, originalLength: 1, modifiedStart: 8, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Modify, then insert ', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Yet another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo BaZ'], 'markdown', CellKind.Markup, [], undefined], + [['# Header'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Yet another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: -1 }, + { modified: 4, original: 3 }, + { modified: 5, original: 4 }, + { modified: 6, original: 5 }, + { modified: 7, original: 6 }, + { modified: 8, original: 7 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 0, modifiedStart: 3, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 6, originalLength: 1, modifiedStart: 8, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Modify, then delete ', async () => { + const { mapping, diff } = await mapCells([ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo Bar'], 'markdown', CellKind.Markup, [], undefined], + [['print("foo bar")'], 'python', CellKind.Code, [], undefined], + [['# Another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['# Yet another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['sys.executable'], 'python', CellKind.Code, [], undefined], + ] + , [ + [['# Hello World'], 'markdown', CellKind.Markup, [], undefined], + [['print("Hello world")'], 'python', CellKind.Code, [], undefined], + [['# Foo BaZ'], 'markdown', CellKind.Markup, [], undefined], + [['# Another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['import sys'], 'python', CellKind.Code, [], undefined], + [['# Yet another MD cell'], 'markdown', CellKind.Markup, [], undefined], + [['sys.executable'], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 4 }, + { modified: 4, original: 5 }, + { modified: 5, original: 6 }, + { modified: 6, original: 7 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 3, originalLength: 1, modifiedStart: 3, modifiedLength: 0 } satisfies IDiffChange, + // { originalStart: 6, originalLength: 1, modifiedStart: 8, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Update code cell', async () => { + const { mapping, diff } = await mapCells([ + [ + [ + "# This is a simple notebook\n", + "\n", + "There's nothing special here, I am just writing some text and plotting a function" + ], 'markdown', CellKind.Markup, [], undefined], + [ + [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "%matplotlib inline" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "x = np.linspace(0, 4*np.pi,50)\n", + "y = np.sin(x)\n", + "\n", + "plt.plot(x, y)" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "df = pd.DataFrame({f\"column_{c}\": np.random.normal(size=100) for c in range(100)})\n", + "df" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "You could actually do some neat stuff with it, pandas like changing the colors (values above 1 should be green)" + ], 'python', CellKind.Markup, [], undefined], + [ + [ + "df.style.applymap(lambda x: \"background-color: green\" if x>1 else \"background-color: white\")" + ], 'python', CellKind.Code, [], undefined], + [ + [], 'python', CellKind.Code, [], undefined], + ] + , [ + [ + [ + "# This is a simple notebook\n", + "\n", + "There's nothing special here, I am just writing some text and plotting a function" + ], 'markdown', CellKind.Markup, [], undefined], + [ + [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "%matplotlib inline" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "x = np.linspace(0, 4*np.pi,50)\n", + "y = 2 * np.sin(x)\n", + "\n", + "plt.plot(x, y)" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "df = pd.DataFrame({f\"column_{c}\": np.random.normal(size=100) for c in range(100)})\n", + "df" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "You could actually do some neat stuff with it, pandas like changing the colors (values above 1 should be green)" + ], 'python', CellKind.Markup, [], undefined], + [ + [ + "df.style.applymap(lambda x: \"background-color: green\" if x>1 else \"background-color: white\")" + ], 'python', CellKind.Code, [], undefined], + [ + [], 'python', CellKind.Code, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + ]); + + }); + test('Update code cell 1', async () => { + const { mapping, diff } = await mapCells([ + [ + [ + "# Hello World" + ], 'markdown', CellKind.Markup, [], undefined], + [ + [ + "import os\n", + "from pprint import pprint \n", + "from sklearn.preprocessing import LabelEncoder\n", + "import ast\n", + "import pandas as pd\n", + "from clipper import clipper\n", + "import ffmpeg" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "data_dir = '../../../app/data/'\n", + "output_dir = 'output/'\n", + "video_dir = 'videos/'\n", + "\n", + "file_path = data_dir+output_dir+data_file\n", + "video_path = data_dir+video_dir+video_file\n", + "\n", + "df = pd.read_csv(file_path)\n", + "df.head()" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "# convert the string representation of results to a list\n", + "df['list_categories'] = df['categories'].apply(ast.literal_eval)\n", + "\n", + "# get most likely label from each list\n", + "df['label'] = df['list_categories'].apply(lambda x: x[0] if x else None)" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "# initialize the LabelEncoder\n", + "label_encoder = LabelEncoder()\n", + "\n", + "# fit and transform the label to a numerical value\n", + "numerical_data = label_encoder.fit_transform(df['label'])\n", + "\n", + "df['label_value'] = numerical_data" + ], 'python', CellKind.Markup, [], undefined], + ] + , [ + [ + [ + "# Updated markdown cell" + ], 'markdown', CellKind.Markup, [], undefined], + [ + [ + "from sklearn.preprocessing import LabelEncoder\n", + "import ast\n", + "import pandas as pd\n", + "from clipper import clipper\n", + "import ffmpeg" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "data_dir = '../../../app/data/'\n", + "output_dir = 'output/'\n", + "video_dir = 'videos/'\n", + "\n", + "file_path = data_dir+output_dir+data_file\n", + "video_path = data_dir+video_dir+video_file\n", + "\n", + "df = pd.read_csv(file_path)\n", + "df.head()" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "# convert the string representation of results to a list\n", + "df['list_categories'] = df['categories'].apply(ast.literal_eval)\n", + "\n", + "# get most likely label from each list\n", + "df['label'] = df['list_categories'].apply(lambda x: x[0] if x else None)" + ], 'python', CellKind.Code, [], undefined], + [ + [ + "# initialize the LabelEncoder\n", + "label_encoder = LabelEncoder()\n", + "\n", + "# fit and transform the label to a numerical value\n", + "numerical_data = label_encoder.fit_transform(df['label'])\n", + "\n", + "df['label_value'] = numerical_data" + ], 'python', CellKind.Markup, [], undefined], + ] + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 1, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 1, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + ]); + }); + test('Detect modification and insertion of cells', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "source": [ + "# Live 3D Human Pose Estimation with OpenVINO\n", + "\n", + "This notebook demonstrates live 3D Human Pose Estimation with OpenVINO via a webcam. We utilize the model [human-pose-estimation-3d-0001](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001) from [Open Model Zoo](https://github.com/openvinotoolkit/open_model_zoo/). At the end of this notebook, you will see live inference results from your webcam (if available). Alternatively, you can also upload a video file to test out the algorithms.\n", + "**Make sure you have properly installed the [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) and been using JupyterLab to run the demo as suggested in the `README.md`**\n", + "\n", + "> **NOTE**: _To use a webcam, you must run this Jupyter notebook on a computer with a webcam. If you run on a remote server, the webcam will not work. However, you can still do inference on a video file in the final step. This demo utilizes the Python interface in `Three.js` integrated with WebGL to process data from the model inference. These results are processed and displayed in the notebook._\n", + "\n", + "_To ensure that the results are displayed correctly, run the code in a recommended browser on one of the following operating systems:_\n", + "_Ubuntu, Windows: Chrome_\n", + "_macOS: Safari_\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Imports](#Imports)\n", + "- [The model](#The-model)\n", + " - [Download the model](#Download-the-model)\n", + " - [Convert Model to OpenVINO IR format](#Convert-Model-to-OpenVINO-IR-format)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Load the model](#Load-the-model)\n", + "- [Processing](#Processing)\n", + " - [Model Inference](#Model-Inference)\n", + " - [Draw 2D Pose Overlays](#Draw-2D-Pose-Overlays)\n", + " - [Main Processing Function](#Main-Processing-Function)\n", + "- [Run](#Run)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "Make sure your [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) is working properly.\n", + "To avoid errors that may arise from the version of the dependency package, it is recommended to use the **JupyterLab** instead of the Jupyter notebook to display image results.\n", + "```\n", + "- pip install --upgrade pip && pip install -r requirements.txt\n", + "- jupyter labextension install --no-build @jupyter-widgets/jupyterlab-manager\n", + "- jupyter labextension install --no-build jupyter-datawidgets/extension\n", + "- jupyter labextension install jupyter-threejs\n", + "- jupyter labextension list\n", + "```\n", + "\n", + "You should see:\n", + "```\n", + "JupyterLab v...\n", + " ...\n", + " jupyterlab-datawidgets v... enabled OK\n", + " @jupyter-widgets/jupyterlab-manager v... enabled OK\n", + " jupyter-threejs v... enabled OK\n", + "```\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "**The `pythreejs` extension may not display properly when using a Jupyter Notebook release. Therefore, it is recommended to use Jupyter Lab instead.**" + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install pythreejs \"openvino-dev>=2024.0.0\" \"opencv-python\" \"torch\" \"onnx<1.16.2\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import collections\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import cv2\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "from IPython.display import clear_output, display\n", + "import openvino as ov\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "with open(\"notebook_utils.py\", \"w\") as f:\n", + " f.write(r.text)\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/engine3js.py\",\n", + ")\n", + "with open(\"engine3js.py\", \"w\") as f:\n", + " f.write(r.text)\n", + "\n", + "import notebook_utils as utils\n", + "import engine3js as engine" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## The model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Download the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We use `omz_downloader`, which is a command line tool from the `openvino-dev` package. `omz_downloader` automatically creates a directory structure and downloads the selected model." + ] + }, + { + "cell_type": "code", + "source": [ + "# directory where model will be downloaded\n", + "base_model_dir = \"model\"\n", + "\n", + "# model name as named in Open Model Zoo\n", + "model_name = \"human-pose-estimation-3d-0001\"\n", + "# selected precision (FP32, FP16)\n", + "precision = \"FP32\"\n", + "\n", + "BASE_MODEL_NAME = f\"{base_model_dir}/public/{model_name}/{model_name}\"\n", + "model_path = Path(BASE_MODEL_NAME).with_suffix(\".pth\")\n", + "onnx_path = Path(BASE_MODEL_NAME).with_suffix(\".onnx\")\n", + "\n", + "ir_model_path = Path(f\"model/public/{model_name}/{precision}/{model_name}.xml\")\n", + "model_weights_path = Path(f\"model/public/{model_name}/{precision}/{model_name}.bin\")\n", + "\n", + "if not model_path.exists():\n", + " download_command = f\"omz_downloader \" f\"--name {model_name} \" f\"--output_dir {base_model_dir}\"\n", + " ! $download_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Convert Model to OpenVINO IR format\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The selected model comes from the public directory, which means it must be converted into OpenVINO Intermediate Representation (OpenVINO IR). We use `omz_converter` to convert the ONNX format model to the OpenVINO IR format." + ] + }, + { + "cell_type": "code", + "source": [ + "if not onnx_path.exists():\n", + " convert_command = (\n", + " f\"omz_converter \" f\"--name {model_name} \" f\"--precisions {precision} \" f\"--download_dir {base_model_dir} \" f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $convert_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "device = utils.device_widget()\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Converted models are located in a fixed structure, which indicates vendor, model name and precision.\n", + "\n", + "First, initialize the inference engine, OpenVINO Runtime. Then, read the network architecture and model weights from the `.bin` and `.xml` files to compile for the desired device. An inference request is then created to infer the compiled model." + ] + }, + { + "cell_type": "code", + "source": [ + "# initialize inference engine\n", + "core = ov.Core()\n", + "# read the network and corresponding weights from file\n", + "model = core.read_model(model=ir_model_path, weights=model_weights_path)\n", + "# load the model on the specified device\n", + "compiled_model = core.compile_model(model=model, device_name=device.value)\n", + "infer_request = compiled_model.create_infer_request()\n", + "input_tensor_name = model.inputs[0].get_any_name()\n", + "\n", + "# get input and output names of nodes\n", + "input_layer = compiled_model.input(0)\n", + "output_layers = list(compiled_model.outputs)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The input for the model is data from the input image and the outputs are heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "input_layer.any_name, [o.any_name for o in output_layers]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Processing\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Frames captured from video files or the live webcam are used as the input for the 3D model. This is how you obtain the output heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "def model_infer(scaled_img, stride):\n", + " \"\"\"\n", + " Run model inference on the input image\n", + "\n", + " Parameters:\n", + " scaled_img: resized image according to the input size of the model\n", + " stride: int, the stride of the window\n", + " \"\"\"\n", + "\n", + " # Remove excess space from the picture\n", + " img = scaled_img[\n", + " 0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride),\n", + " 0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride),\n", + " ]\n", + "\n", + " img = np.transpose(img, (2, 0, 1))[None,]\n", + " infer_request.infer({input_tensor_name: img})\n", + " # A set of three inference results is obtained\n", + " results = {name: infer_request.get_tensor(name).data[:] for name in {\"features\", \"heatmaps\", \"pafs\"}}\n", + " # Get the results\n", + " results = (results[\"features\"][0], results[\"heatmaps\"][0], results[\"pafs\"][0])\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Draw 2D Pose Overlays\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We need to define some connections between the joints in advance, so that we can draw the structure of the human body in the resulting image after obtaining the inference results.\n", + "Joints are drawn as circles and limbs are drawn as lines. The code is based on the [3D Human Pose Estimation Demo](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/human_pose_estimation_3d_demo/python) from Open Model Zoo." + ] + }, + { + "cell_type": "code", + "source": [ + "# 3D edge index array\n", + "body_edges = np.array(\n", + " [\n", + " [0, 1],\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [1, 15],\n", + " [15, 16], # nose - l_eye - l_ear\n", + " [1, 17],\n", + " [17, 18], # nose - r_eye - r_ear\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "body_edges_2d = np.array(\n", + " [\n", + " [0, 1], # neck - nose\n", + " [1, 16],\n", + " [16, 18], # nose - l_eye - l_ear\n", + " [1, 15],\n", + " [15, 17], # nose - r_eye - r_ear\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "def draw_poses(frame, poses_2d, scaled_img, use_popup):\n", + " \"\"\"\n", + " Draw 2D pose overlays on the image to visualize estimated poses.\n", + " Joints are drawn as circles and limbs are drawn as lines.\n", + "\n", + " :param frame: the input image\n", + " :param poses_2d: array of human joint pairs\n", + " \"\"\"\n", + " for pose in poses_2d:\n", + " pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose()\n", + " was_found = pose[2] > 0\n", + "\n", + " pose[0], pose[1] = (\n", + " pose[0] * frame.shape[1] / scaled_img.shape[1],\n", + " pose[1] * frame.shape[0] / scaled_img.shape[0],\n", + " )\n", + "\n", + " # Draw joints.\n", + " for edge in body_edges_2d:\n", + " if was_found[edge[0]] and was_found[edge[1]]:\n", + " cv2.line(\n", + " frame,\n", + " tuple(pose[0:2, edge[0]].astype(np.int32)),\n", + " tuple(pose[0:2, edge[1]].astype(np.int32)),\n", + " (255, 255, 0),\n", + " 4,\n", + " cv2.LINE_AA,\n", + " )\n", + " # Draw limbs.\n", + " for kpt_id in range(pose.shape[1]):\n", + " if pose[2, kpt_id] != -1:\n", + " cv2.circle(\n", + " frame,\n", + " tuple(pose[0:2, kpt_id].astype(np.int32)),\n", + " 3,\n", + " (0, 255, 255),\n", + " -1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Main Processing Function\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run 3D pose estimation on the specified source. It could be either a webcam feed or a video file." + ] + }, + { + "cell_type": "code", + "source": [ + "def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0):\n", + " \"\"\"\n", + " 2D image as input, using OpenVINO as inference backend,\n", + " get joints 3D coordinates, and draw 3D human skeleton in the scene\n", + "\n", + " :param source: The webcam number to feed the video stream with primary webcam set to \"0\", or the video path.\n", + " :param flip: To be used by VideoPlayer function for flipping capture image.\n", + " :param use_popup: False for showing encoded frames over this notebook, True for creating a popup window.\n", + " :param skip_frames: Number of frames to skip at the beginning of the video.\n", + " \"\"\"\n", + "\n", + " focal_length = -1 # default\n", + " stride = 8\n", + " player = None\n", + " skeleton_set = None\n", + "\n", + " try:\n", + " # create video player to play with target fps video_path\n", + " # get the frame from camera\n", + " # You can skip first N frames to fast forward video. change 'skip_first_frames'\n", + " player = utils.VideoPlayer(source, flip=flip, fps=30, skip_first_frames=skip_frames)\n", + " # start capturing\n", + " player.start()\n", + "\n", + " input_image = player.next()\n", + " # set the window size\n", + " resize_scale = 450 / input_image.shape[1]\n", + " windows_width = int(input_image.shape[1] * resize_scale)\n", + " windows_height = int(input_image.shape[0] * resize_scale)\n", + "\n", + " # use visualization library\n", + " engine3D = engine.Engine3js(grid=True, axis=True, view_width=windows_width, view_height=windows_height)\n", + "\n", + " if use_popup:\n", + " # display the 3D human pose in this notebook, and origin frame in popup window\n", + " display(engine3D.renderer)\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE)\n", + " else:\n", + " # set the 2D image box, show both human pose and image in the notebook\n", + " imgbox = widgets.Image(format=\"jpg\", height=windows_height, width=windows_width)\n", + " display(widgets.HBox([engine3D.renderer, imgbox]))\n", + "\n", + " skeleton = engine.Skeleton(body_edges=body_edges)\n", + "\n", + " processing_times = collections.deque()\n", + "\n", + " while True:\n", + " # grab the frame\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + "\n", + " # resize image and change dims to fit neural network input\n", + " # (see https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001)\n", + " scaled_img = cv2.resize(frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2]))\n", + "\n", + " if focal_length < 0: # Focal length is unknown\n", + " focal_length = np.float32(0.8 * scaled_img.shape[1])\n", + "\n", + " # inference start\n", + " start_time = time.time()\n", + " # get results\n", + " inference_result = model_infer(scaled_img, stride)\n", + "\n", + " # inference stop\n", + " stop_time = time.time()\n", + " processing_times.append(stop_time - start_time)\n", + " # Process the point to point coordinates of the data\n", + " poses_3d, poses_2d = engine.parse_poses(inference_result, 1, stride, focal_length, True)\n", + "\n", + " # use processing times from last 200 frames\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + "\n", + " if len(poses_3d) > 0:\n", + " # From here, you can rotate the 3D point positions using the function \"draw_poses\",\n", + " # or you can directly make the correct mapping below to properly display the object image on the screen\n", + " poses_3d_copy = poses_3d.copy()\n", + " x = poses_3d_copy[:, 0::4]\n", + " y = poses_3d_copy[:, 1::4]\n", + " z = poses_3d_copy[:, 2::4]\n", + " poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = (\n", + " -z + np.ones(poses_3d[:, 2::4].shape) * 200,\n", + " -y + np.ones(poses_3d[:, 2::4].shape) * 100,\n", + " -x,\n", + " )\n", + "\n", + " poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3]\n", + " people = skeleton(poses_3d=poses_3d)\n", + "\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " except Exception:\n", + " pass\n", + "\n", + " engine3D.scene_add(people)\n", + " skeleton_set = people\n", + "\n", + " # draw 2D\n", + " frame = draw_poses(frame, poses_2d, scaled_img, use_popup)\n", + "\n", + " else:\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " skeleton_set = None\n", + " except Exception:\n", + " pass\n", + "\n", + " cv2.putText(\n", + " frame,\n", + " f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " (10, 30),\n", + " cv2.FONT_HERSHEY_COMPLEX,\n", + " 0.7,\n", + " (0, 0, 255),\n", + " 1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " if use_popup:\n", + " cv2.imshow(title, frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27, use ESC to exit\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # encode numpy array to jpg\n", + " imgbox.value = cv2.imencode(\n", + " \".jpg\",\n", + " frame,\n", + " params=[cv2.IMWRITE_JPEG_QUALITY, 90],\n", + " )[1].tobytes()\n", + "\n", + " engine3D.renderer.render(engine3D.scene, engine3D.cam)\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " clear_output()\n", + " if player is not None:\n", + " # stop capturing\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()\n", + " if skeleton_set:\n", + " engine3D.scene_remove(skeleton_set)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Run\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run, using a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + "> **NOTE**:\n", + ">\n", + "> *1. To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a server (e.g. Binder), the webcam will not work.*\n", + ">\n", + "> *2. Popup mode may not work if you run this notebook on a remote computer (e.g. Binder).*\n", + "\n", + "If you do not have a webcam, you can still run this demo with a video file. Any [format supported by OpenCV](https://docs.opencv.org/4.5.1/dd/d43/tutorial_py_video_display.html) will work." + ] + }, + { + "cell_type": "markdown", + "source": [ + "Using the following method, you can click and move your mouse over the picture on the left to interact." + ] + }, + { + "cell_type": "code", + "source": [ + "USE_WEBCAM = False\n", + "\n", + "cam_id = 0\n", + "video_path = \"https://storage.openvinotoolkit.org/data/test_data/videos/face-demographics-walking.mp4\"\n", + "\n", + "source = cam_id if USE_WEBCAM else video_path\n", + "\n", + "run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)" + ] + } + ].map(fromJupyterCell) + , [ + { + "cell_type": "markdown", + "source": [ + "# Live 3D Human Pose Estimation with OpenVINO\n", + "\n", + "This notebook demonstrates live 3D Human Pose Estimation with OpenVINO via a webcam. We utilize the model [human-pose-estimation-3d-0001](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001) from [Open Model Zoo](https://github.com/openvinotoolkit/open_model_zoo/). At the end of this notebook, you will see live inference results from your webcam (if available). Alternatively, you can also upload a video file to test out the algorithms.\n", + "**Make sure you have properly installed the [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) and been using JupyterLab to run the demo as suggested in the `README.md`**\n", + "\n", + "> **NOTE**: _To use a webcam, you must run this Jupyter notebook on a computer with a webcam. If you run on a remote server, the webcam will not work. However, you can still do inference on a video file in the final step. This demo utilizes the Python interface in `Three.js` integrated with WebGL to process data from the model inference. These results are processed and displayed in the notebook._\n", + "\n", + "_To ensure that the results are displayed correctly, run the code in a recommended browser on one of the following operating systems:_\n", + "_Ubuntu, Windows: Chrome_\n", + "_macOS: Safari_\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Imports](#Imports)\n", + "- [The model](#The-model)\n", + " - [Download the model](#Download-the-model)\n", + " - [Convert Model to OpenVINO IR format](#Convert-Model-to-OpenVINO-IR-format)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Load the model](#Load-the-model)\n", + "- [Processing](#Processing)\n", + " - [Model Inference](#Model-Inference)\n", + " - [Draw 2D Pose Overlays](#Draw-2D-Pose-Overlays)\n", + " - [Main Processing Function](#Main-Processing-Function)\n", + "- [Run](#Run)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "Make sure your [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) is working properly.\n", + "To avoid errors that may arise from the version of the dependency package, it is recommended to use the **JupyterLab** instead of the Jupyter notebook to display image results.\n", + "```\n", + "- pip install --upgrade pip && pip install -r requirements.txt\n", + "- jupyter labextension install --no-build @jupyter-widgets/jupyterlab-manager\n", + "- jupyter labextension install --no-build jupyter-datawidgets/extension\n", + "- jupyter labextension install jupyter-threejs\n", + "- jupyter labextension list\n", + "```\n", + "\n", + "You should see:\n", + "```\n", + "JupyterLab v...\n", + " ...\n", + " jupyterlab-datawidgets v... enabled OK\n", + " @jupyter-widgets/jupyterlab-manager v... enabled OK\n", + " jupyter-threejs v... enabled OK\n", + "```\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "**The `pythreejs` extension may not display properly when using a Jupyter Notebook release. Therefore, it is recommended to use Jupyter Lab instead.**" + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install pythreejs \"openvino>=2024.4.0\" \"opencv-python\" \"torch\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import collections\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import cv2\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "from IPython.display import clear_output, display\n", + "import openvino as ov\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "with open(\"notebook_utils.py\", \"w\") as f:\n", + " f.write(r.text)\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/engine3js.py\",\n", + ")\n", + "with open(\"engine3js.py\", \"w\") as f:\n", + " f.write(r.text)\n", + "\n", + "import notebook_utils as utils\n", + "import engine3js as engine" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## The model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Download the model\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "source": [ + "from notebook_utils import download_file\n", + "import tarfile\n", + "\n", + "\n", + "# directory where model will be downloaded\n", + "base_model_dir = Path(\"model\")\n", + "\n", + "download_file(\"https://storage.openvinotoolkit.org/repositories/open_model_zoo/public/2022.1/human-pose-estimation-3d-0001/human-pose-estimation-3d.tar.gz\", directory=base_model_dir)\n", + "\n", + "ckpt_file = base_model_dir / \"human-pose-estimation-3d-0001.pth\"\n", + "\n", + "if not ckpt_file.exists():\n", + " with tarfile.open(base_model_dir / \"human-pose-estimation-3d.tar.gz\") as f:\n", + " f.extractall(base_model_dir)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Convert Model to OpenVINO IR format\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "source": [ + "import torch\n", + "import openvino as ov\n", + "\n", + "ov_model_path = Path(base_model_dir) / \"human-pose-estimation-3d-0001.xml\"\n", + "\n", + "if not ov_model_path.exists():\n", + " from model.model import PoseEstimationWithMobileNet\n", + "\n", + " pose_estimation_model = PoseEstimationWithMobileNet(is_convertible_by_mo=True)\n", + " pose_estimation_model.loas_state_dict(torch.load(ckpt_file, map_location=\"cpu\"))\n", + " pose_estimation_model.eval()\n", + "\n", + " with torch.no_grad():\n", + " ov_model = ov.convert_model(pose_estimation_model, example_input=torch.zeros([1, 3, 256, 448]), input=[1, 3, 256, 448])\n", + " ov.save_model(ov_model, ov_model_path)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "device = utils.device_widget()\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Converted models are located in a fixed structure, which indicates vendor, model name and precision.\n", + "\n", + "First, initialize the inference engine, OpenVINO Runtime. Then, read the network architecture and model weights from the `.bin` and `.xml` files to compile for the desired device. An inference request is then created to infer the compiled model." + ] + }, + { + "cell_type": "code", + "source": [ + "# initialize inference engine\n", + "core = ov.Core()\n", + "# read the network and corresponding weights from file\n", + "model = core.read_model(ov_model_path)\n", + "# load the model on the specified device\n", + "compiled_model = core.compile_model(model=model, device_name=device.value)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Processing\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Frames captured from video files or the live webcam are used as the input for the 3D model. This is how you obtain the output heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "def model_infer(scaled_img, stride):\n", + " \"\"\"\n", + " Run model inference on the input image\n", + "\n", + " Parameters:\n", + " scaled_img: resized image according to the input size of the model\n", + " stride: int, the stride of the window\n", + " \"\"\"\n", + "\n", + " # Remove excess space from the picture\n", + " img = scaled_img[\n", + " 0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride),\n", + " 0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride),\n", + " ]\n", + "\n", + " mean_value = 128.0\n", + " scale_value = 255.0\n", + "\n", + " img = (img - mean_value) / scale_value\n", + "\n", + " img = np.transpose(img, (2, 0, 1))[None,]\n", + " result = compiled_model(img)\n", + " # Get the results\n", + " results = (result[0][0], results[1][0], results[2][0])\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Draw 2D Pose Overlays\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We need to define some connections between the joints in advance, so that we can draw the structure of the human body in the resulting image after obtaining the inference results.\n", + "Joints are drawn as circles and limbs are drawn as lines. The code is based on the [3D Human Pose Estimation Demo](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/human_pose_estimation_3d_demo/python) from Open Model Zoo." + ] + }, + { + "cell_type": "code", + "source": [ + "# 3D edge index array\n", + "body_edges = np.array(\n", + " [\n", + " [0, 1],\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [1, 15],\n", + " [15, 16], # nose - l_eye - l_ear\n", + " [1, 17],\n", + " [17, 18], # nose - r_eye - r_ear\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "body_edges_2d = np.array(\n", + " [\n", + " [0, 1], # neck - nose\n", + " [1, 16],\n", + " [16, 18], # nose - l_eye - l_ear\n", + " [1, 15],\n", + " [15, 17], # nose - r_eye - r_ear\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "def draw_poses(frame, poses_2d, scaled_img, use_popup):\n", + " \"\"\"\n", + " Draw 2D pose overlays on the image to visualize estimated poses.\n", + " Joints are drawn as circles and limbs are drawn as lines.\n", + "\n", + " :param frame: the input image\n", + " :param poses_2d: array of human joint pairs\n", + " \"\"\"\n", + " for pose in poses_2d:\n", + " pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose()\n", + " was_found = pose[2] > 0\n", + "\n", + " pose[0], pose[1] = (\n", + " pose[0] * frame.shape[1] / scaled_img.shape[1],\n", + " pose[1] * frame.shape[0] / scaled_img.shape[0],\n", + " )\n", + "\n", + " # Draw joints.\n", + " for edge in body_edges_2d:\n", + " if was_found[edge[0]] and was_found[edge[1]]:\n", + " cv2.line(\n", + " frame,\n", + " tuple(pose[0:2, edge[0]].astype(np.int32)),\n", + " tuple(pose[0:2, edge[1]].astype(np.int32)),\n", + " (255, 255, 0),\n", + " 4,\n", + " cv2.LINE_AA,\n", + " )\n", + " # Draw limbs.\n", + " for kpt_id in range(pose.shape[1]):\n", + " if pose[2, kpt_id] != -1:\n", + " cv2.circle(\n", + " frame,\n", + " tuple(pose[0:2, kpt_id].astype(np.int32)),\n", + " 3,\n", + " (0, 255, 255),\n", + " -1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Main Processing Function\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run 3D pose estimation on the specified source. It could be either a webcam feed or a video file." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0):\n", + " \"\"\"\n", + " 2D image as input, using OpenVINO as inference backend,\n", + " get joints 3D coordinates, and draw 3D human skeleton in the scene\n", + "\n", + " :param source: The webcam number to feed the video stream with primary webcam set to \"0\", or the video path.\n", + " :param flip: To be used by VideoPlayer function for flipping capture image.\n", + " :param use_popup: False for showing encoded frames over this notebook, True for creating a popup window.\n", + " :param skip_frames: Number of frames to skip at the beginning of the video.\n", + " \"\"\"\n", + "\n", + " focal_length = -1 # default\n", + " stride = 8\n", + " player = None\n", + " skeleton_set = None\n", + "\n", + " try:\n", + " # create video player to play with target fps video_path\n", + " # get the frame from camera\n", + " # You can skip first N frames to fast forward video. change 'skip_first_frames'\n", + " player = utils.VideoPlayer(source, flip=flip, fps=30, skip_first_frames=skip_frames)\n", + " # start capturing\n", + " player.start()\n", + "\n", + " input_image = player.next()\n", + " # set the window size\n", + " resize_scale = 450 / input_image.shape[1]\n", + " windows_width = int(input_image.shape[1] * resize_scale)\n", + " windows_height = int(input_image.shape[0] * resize_scale)\n", + "\n", + " # use visualization library\n", + " engine3D = engine.Engine3js(grid=True, axis=True, view_width=windows_width, view_height=windows_height)\n", + "\n", + " if use_popup:\n", + " # display the 3D human pose in this notebook, and origin frame in popup window\n", + " display(engine3D.renderer)\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE)\n", + " else:\n", + " # set the 2D image box, show both human pose and image in the notebook\n", + " imgbox = widgets.Image(format=\"jpg\", height=windows_height, width=windows_width)\n", + " display(widgets.HBox([engine3D.renderer, imgbox]))\n", + "\n", + " skeleton = engine.Skeleton(body_edges=body_edges)\n", + "\n", + " processing_times = collections.deque()\n", + "\n", + " while True:\n", + " # grab the frame\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + "\n", + " # resize image and change dims to fit neural network input\n", + " # (see https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001)\n", + " scaled_img = cv2.resize(frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2]))\n", + "\n", + " if focal_length < 0: # Focal length is unknown\n", + " focal_length = np.float32(0.8 * scaled_img.shape[1])\n", + "\n", + " # inference start\n", + " start_time = time.time()\n", + " # get results\n", + " inference_result = model_infer(scaled_img, stride)\n", + "\n", + " # inference stop\n", + " stop_time = time.time()\n", + " processing_times.append(stop_time - start_time)\n", + " # Process the point to point coordinates of the data\n", + " poses_3d, poses_2d = engine.parse_poses(inference_result, 1, stride, focal_length, True)\n", + "\n", + " # use processing times from last 200 frames\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + "\n", + " if len(poses_3d) > 0:\n", + " # From here, you can rotate the 3D point positions using the function \"draw_poses\",\n", + " # or you can directly make the correct mapping below to properly display the object image on the screen\n", + " poses_3d_copy = poses_3d.copy()\n", + " x = poses_3d_copy[:, 0::4]\n", + " y = poses_3d_copy[:, 1::4]\n", + " z = poses_3d_copy[:, 2::4]\n", + " poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = (\n", + " -z + np.ones(poses_3d[:, 2::4].shape) * 200,\n", + " -y + np.ones(poses_3d[:, 2::4].shape) * 100,\n", + " -x,\n", + " )\n", + "\n", + " poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3]\n", + " people = skeleton(poses_3d=poses_3d)\n", + "\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " except Exception:\n", + " pass\n", + "\n", + " engine3D.scene_add(people)\n", + " skeleton_set = people\n", + "\n", + " # draw 2D\n", + " frame = draw_poses(frame, poses_2d, scaled_img, use_popup)\n", + "\n", + " else:\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " skeleton_set = None\n", + " except Exception:\n", + " pass\n", + "\n", + " cv2.putText(\n", + " frame,\n", + " f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " (10, 30),\n", + " cv2.FONT_HERSHEY_COMPLEX,\n", + " 0.7,\n", + " (0, 0, 255),\n", + " 1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " if use_popup:\n", + " cv2.imshow(title, frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27, use ESC to exit\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # encode numpy array to jpg\n", + " imgbox.value = cv2.imencode(\n", + " \".jpg\",\n", + " frame,\n", + " params=[cv2.IMWRITE_JPEG_QUALITY, 90],\n", + " )[1].tobytes()\n", + "\n", + " engine3D.renderer.render(engine3D.scene, engine3D.cam)\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " clear_output()\n", + " if player is not None:\n", + " # stop capturing\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()\n", + " if skeleton_set:\n", + " engine3D.scene_remove(skeleton_set)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Run\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run, using a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + "> **NOTE**:\n", + ">\n", + "> *1. To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a server (e.g. Binder), the webcam will not work.*\n", + ">\n", + "> *2. Popup mode may not work if you run this notebook on a remote computer (e.g. Binder).*\n", + "\n", + "If you do not have a webcam, you can still run this demo with a video file. Any [format supported by OpenCV](https://docs.opencv.org/4.5.1/dd/d43/tutorial_py_video_display.html) will work." + ] + }, + { + "cell_type": "markdown", + "source": [ + "Using the following method, you can click and move your mouse over the picture on the left to interact." + ] + }, + { + "cell_type": "code", + "source": [ + "USE_WEBCAM = False\n", + "\n", + "cam_id = 0\n", + "video_path = \"https://storage.openvinotoolkit.org/data/test_data/videos/face-demographics-walking.mp4\"\n", + "\n", + "source = cam_id if USE_WEBCAM else video_path\n", + "\n", + "run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 15 }, + { modified: 14, original: 16 }, + { modified: 15, original: 17 }, + { modified: 16, original: 18 }, + { modified: 17, original: 19 }, + { modified: 18, original: 20 }, + { modified: 19, original: 21 }, + { modified: 20, original: 22 }, + { modified: 21, original: 23 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 2, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + { + modifiedLength: 1, + modifiedStart: 6, + originalLength: 1, + originalStart: 6 + }, + { + modifiedLength: 1, + modifiedStart: 7, + originalLength: 1, + originalStart: 7 + }, + { + modifiedLength: 1, + modifiedStart: 8, + originalLength: 1, + originalStart: 8 + }, + { + modifiedLength: 1, + modifiedStart: 12, + originalLength: 1, + originalStart: 12 + }, + { + modifiedLength: 0, + modifiedStart: 13, + originalLength: 2, + originalStart: 13 + }, + { + modifiedLength: 1, + modifiedStart: 14, + originalLength: 1, + originalStart: 16 + } + ]); + }); + test('Detect modification in 2 cells', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "source": [ + "# Live 3D Human Pose Estimation with OpenVINO\n", + "\n", + "This notebook demonstrates live 3D Human Pose Estimation with OpenVINO via a webcam. We utilize the model [human-pose-estimation-3d-0001](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001) from [Open Model Zoo](https://github.com/openvinotoolkit/open_model_zoo/). At the end of this notebook, you will see live inference results from your webcam (if available). Alternatively, you can also upload a video file to test out the algorithms.\n", + "**Make sure you have properly installed the [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) and been using JupyterLab to run the demo as suggested in the `README.md`**\n", + "\n", + "> **NOTE**: _To use a webcam, you must run this Jupyter notebook on a computer with a webcam. If you run on a remote server, the webcam will not work. However, you can still do inference on a video file in the final step. This demo utilizes the Python interface in `Three.js` integrated with WebGL to process data from the model inference. These results are processed and displayed in the notebook._\n", + "\n", + "_To ensure that the results are displayed correctly, run the code in a recommended browser on one of the following operating systems:_\n", + "_Ubuntu, Windows: Chrome_\n", + "_macOS: Safari_\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Imports](#Imports)\n", + "- [The model](#The-model)\n", + " - [Download the model](#Download-the-model)\n", + " - [Convert Model to OpenVINO IR format](#Convert-Model-to-OpenVINO-IR-format)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Load the model](#Load-the-model)\n", + "- [Processing](#Processing)\n", + " - [Model Inference](#Model-Inference)\n", + " - [Draw 2D Pose Overlays](#Draw-2D-Pose-Overlays)\n", + " - [Main Processing Function](#Main-Processing-Function)\n", + "- [Run](#Run)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "**The `pythreejs` extension may not display properly when using the latest Jupyter Notebook release (2.4.1). Therefore, it is recommended to use Jupyter Lab instead.**" + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install pythreejs \"openvino-dev>=2024.0.0\" \"opencv-python\" \"torch\" \"onnx\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import collections\n", + "import sys\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import cv2\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "from IPython.display import clear_output, display\n", + "import openvino as ov\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "import notebook_utils as utils\n", + "\n", + "sys.path.append(\"./engine\")\n", + "import engine.engine3js as engine\n", + "from engine.parse_poses import parse_poses" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## The model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Download the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We use `omz_downloader`, which is a command line tool from the `openvino-dev` package. `omz_downloader` automatically creates a directory structure and downloads the selected model." + ] + }, + { + "cell_type": "code", + "source": [ + "# directory where model will be downloaded\n", + "base_model_dir = \"model\"\n", + "\n", + "# model name as named in Open Model Zoo\n", + "model_name = \"human-pose-estimation-3d-0001\"\n", + "# selected precision (FP32, FP16)\n", + "precision = \"FP32\"\n", + "\n", + "BASE_MODEL_NAME = f\"{base_model_dir}/public/{model_name}/{model_name}\"\n", + "model_path = Path(BASE_MODEL_NAME).with_suffix(\".pth\")\n", + "onnx_path = Path(BASE_MODEL_NAME).with_suffix(\".onnx\")\n", + "\n", + "ir_model_path = f\"model/public/{model_name}/{precision}/{model_name}.xml\"\n", + "model_weights_path = f\"model/public/{model_name}/{precision}/{model_name}.bin\"\n", + "\n", + "if not model_path.exists():\n", + " download_command = f\"omz_downloader \" f\"--name {model_name} \" f\"--output_dir {base_model_dir}\"\n", + " ! $download_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Convert Model to OpenVINO IR format\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The selected model comes from the public directory, which means it must be converted into OpenVINO Intermediate Representation (OpenVINO IR). We use `omz_converter` to convert the ONNX format model to the OpenVINO IR format." + ] + }, + { + "cell_type": "code", + "source": [ + "if not onnx_path.exists():\n", + " convert_command = (\n", + " f\"omz_converter \" f\"--name {model_name} \" f\"--precisions {precision} \" f\"--download_dir {base_model_dir} \" f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $convert_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()\n", + "\n", + "device = widgets.Dropdown(\n", + " options=core.available_devices + [\"AUTO\"],\n", + " value=\"AUTO\",\n", + " description=\"Device:\",\n", + " disabled=False,\n", + ")\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Converted models are located in a fixed structure, which indicates vendor, model name and precision.\n", + "\n", + "First, initialize the inference engine, OpenVINO Runtime. Then, read the network architecture and model weights from the `.bin` and `.xml` files to compile for the desired device. An inference request is then created to infer the compiled model." + ] + }, + { + "cell_type": "code", + "source": [ + "# initialize inference engine\n", + "core = ov.Core()\n", + "# read the network and corresponding weights from file\n", + "model = core.read_model(model=ir_model_path, weights=model_weights_path)\n", + "# load the model on the specified device\n", + "compiled_model = core.compile_model(model=model, device_name=device.value)\n", + "infer_request = compiled_model.create_infer_request()\n", + "input_tensor_name = model.inputs[0].get_any_name()\n", + "\n", + "# get input and output names of nodes\n", + "input_layer = compiled_model.input(0)\n", + "output_layers = list(compiled_model.outputs)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The input for the model is data from the input image and the outputs are heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "input_layer.any_name, [o.any_name for o in output_layers]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Processing\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Frames captured from video files or the live webcam are used as the input for the 3D model. This is how you obtain the output heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "def model_infer(scaled_img, stride):\n", + " \"\"\"\n", + " Run model inference on the input image\n", + "\n", + " Parameters:\n", + " scaled_img: resized image according to the input size of the model\n", + " stride: int, the stride of the window\n", + " \"\"\"\n", + "\n", + " # Remove excess space from the picture\n", + " img = scaled_img[\n", + " 0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride),\n", + " 0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride),\n", + " ]\n", + "\n", + " img = np.transpose(img, (2, 0, 1))[None,]\n", + " infer_request.infer({input_tensor_name: img})\n", + " # A set of three inference results is obtained\n", + " results = {name: infer_request.get_tensor(name).data[:] for name in {\"features\", \"heatmaps\", \"pafs\"}}\n", + " # Get the results\n", + " results = (results[\"features\"][0], results[\"heatmaps\"][0], results[\"pafs\"][0])\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Draw 2D Pose Overlays\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We need to define some connections between the joints in advance, so that we can draw the structure of the human body in the resulting image after obtaining the inference results.\n", + "Joints are drawn as circles and limbs are drawn as lines. The code is based on the [3D Human Pose Estimation Demo](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/human_pose_estimation_3d_demo/python) from Open Model Zoo." + ] + }, + { + "cell_type": "code", + "source": [ + "# 3D edge index array\n", + "body_edges = np.array(\n", + " [\n", + " [0, 1],\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [1, 15],\n", + " [15, 16], # nose - l_eye - l_ear\n", + " [1, 17],\n", + " [17, 18], # nose - r_eye - r_ear\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "body_edges_2d = np.array(\n", + " [\n", + " [0, 1], # neck - nose\n", + " [1, 16],\n", + " [16, 18], # nose - l_eye - l_ear\n", + " [1, 15],\n", + " [15, 17], # nose - r_eye - r_ear\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "def draw_poses(frame, poses_2d, scaled_img, use_popup):\n", + " \"\"\"\n", + " Draw 2D pose overlays on the image to visualize estimated poses.\n", + " Joints are drawn as circles and limbs are drawn as lines.\n", + "\n", + " :param frame: the input image\n", + " :param poses_2d: array of human joint pairs\n", + " \"\"\"\n", + " for pose in poses_2d:\n", + " pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose()\n", + " was_found = pose[2] > 0\n", + "\n", + " pose[0], pose[1] = (\n", + " pose[0] * frame.shape[1] / scaled_img.shape[1],\n", + " pose[1] * frame.shape[0] / scaled_img.shape[0],\n", + " )\n", + "\n", + " # Draw joints.\n", + " for edge in body_edges_2d:\n", + " if was_found[edge[0]] and was_found[edge[1]]:\n", + " cv2.line(\n", + " frame,\n", + " tuple(pose[0:2, edge[0]].astype(np.int32)),\n", + " tuple(pose[0:2, edge[1]].astype(np.int32)),\n", + " (255, 255, 0),\n", + " 4,\n", + " cv2.LINE_AA,\n", + " )\n", + " # Draw limbs.\n", + " for kpt_id in range(pose.shape[1]):\n", + " if pose[2, kpt_id] != -1:\n", + " cv2.circle(\n", + " frame,\n", + " tuple(pose[0:2, kpt_id].astype(np.int32)),\n", + " 3,\n", + " (0, 255, 255),\n", + " -1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Main Processing Function\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run 3D pose estimation on the specified source. It could be either a webcam feed or a video file." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0):\n", + " \"\"\"\n", + " 2D image as input, using OpenVINO as inference backend,\n", + " get joints 3D coordinates, and draw 3D human skeleton in the scene\n", + "\n", + " :param source: The webcam number to feed the video stream with primary webcam set to \"0\", or the video path.\n", + " :param flip: To be used by VideoPlayer function for flipping capture image.\n", + " :param use_popup: False for showing encoded frames over this notebook, True for creating a popup window.\n", + " :param skip_frames: Number of frames to skip at the beginning of the video.\n", + " \"\"\"\n", + "\n", + " focal_length = -1 # default\n", + " stride = 8\n", + " player = None\n", + " skeleton_set = None\n", + "\n", + " try:\n", + " # create video player to play with target fps video_path\n", + " # get the frame from camera\n", + " # You can skip first N frames to fast forward video. change 'skip_first_frames'\n", + " player = utils.VideoPlayer(source, flip=flip, fps=30, skip_first_frames=skip_frames)\n", + " # start capturing\n", + " player.start()\n", + "\n", + " input_image = player.next()\n", + " # set the window size\n", + " resize_scale = 450 / input_image.shape[1]\n", + " windows_width = int(input_image.shape[1] * resize_scale)\n", + " windows_height = int(input_image.shape[0] * resize_scale)\n", + "\n", + " # use visualization library\n", + " engine3D = engine.Engine3js(grid=True, axis=True, view_width=windows_width, view_height=windows_height)\n", + "\n", + " if use_popup:\n", + " # display the 3D human pose in this notebook, and origin frame in popup window\n", + " display(engine3D.renderer)\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE)\n", + " else:\n", + " # set the 2D image box, show both human pose and image in the notebook\n", + " imgbox = widgets.Image(format=\"jpg\", height=windows_height, width=windows_width)\n", + " display(widgets.HBox([engine3D.renderer, imgbox]))\n", + "\n", + " skeleton = engine.Skeleton(body_edges=body_edges)\n", + "\n", + " processing_times = collections.deque()\n", + "\n", + " while True:\n", + " # grab the frame\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + "\n", + " # resize image and change dims to fit neural network input\n", + " # (see https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001)\n", + " scaled_img = cv2.resize(frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2]))\n", + "\n", + " if focal_length < 0: # Focal length is unknown\n", + " focal_length = np.float32(0.8 * scaled_img.shape[1])\n", + "\n", + " # inference start\n", + " start_time = time.time()\n", + " # get results\n", + " inference_result = model_infer(scaled_img, stride)\n", + "\n", + " # inference stop\n", + " stop_time = time.time()\n", + " processing_times.append(stop_time - start_time)\n", + " # Process the point to point coordinates of the data\n", + " poses_3d, poses_2d = parse_poses(inference_result, 1, stride, focal_length, True)\n", + "\n", + " # use processing times from last 200 frames\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + "\n", + " if len(poses_3d) > 0:\n", + " # From here, you can rotate the 3D point positions using the function \"draw_poses\",\n", + " # or you can directly make the correct mapping below to properly display the object image on the screen\n", + " poses_3d_copy = poses_3d.copy()\n", + " x = poses_3d_copy[:, 0::4]\n", + " y = poses_3d_copy[:, 1::4]\n", + " z = poses_3d_copy[:, 2::4]\n", + " poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = (\n", + " -z + np.ones(poses_3d[:, 2::4].shape) * 200,\n", + " -y + np.ones(poses_3d[:, 2::4].shape) * 100,\n", + " -x,\n", + " )\n", + "\n", + " poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3]\n", + " people = skeleton(poses_3d=poses_3d)\n", + "\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " except Exception:\n", + " pass\n", + "\n", + " engine3D.scene_add(people)\n", + " skeleton_set = people\n", + "\n", + " # draw 2D\n", + " frame = draw_poses(frame, poses_2d, scaled_img, use_popup)\n", + "\n", + " else:\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " skeleton_set = None\n", + " except Exception:\n", + " pass\n", + "\n", + " cv2.putText(\n", + " frame,\n", + " f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " (10, 30),\n", + " cv2.FONT_HERSHEY_COMPLEX,\n", + " 0.7,\n", + " (0, 0, 255),\n", + " 1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " if use_popup:\n", + " cv2.imshow(title, frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27, use ESC to exit\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # encode numpy array to jpg\n", + " imgbox.value = cv2.imencode(\n", + " \".jpg\",\n", + " frame,\n", + " params=[cv2.IMWRITE_JPEG_QUALITY, 90],\n", + " )[1].tobytes()\n", + "\n", + " engine3D.renderer.render(engine3D.scene, engine3D.cam)\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " clear_output()\n", + " if player is not None:\n", + " # stop capturing\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()\n", + " if skeleton_set:\n", + " engine3D.scene_remove(skeleton_set)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Run\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run, using a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + "> **NOTE**:\n", + ">\n", + "> *1. To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a server (e.g. Binder), the webcam will not work.*\n", + ">\n", + "> *2. Popup mode may not work if you run this notebook on a remote computer (e.g. Binder).*\n", + "\n", + "If you do not have a webcam, you can still run this demo with a video file. Any [format supported by OpenCV](https://docs.opencv.org/4.5.1/dd/d43/tutorial_py_video_display.html) will work." + ] + }, + { + "cell_type": "markdown", + "source": [ + "Using the following method, you can click and move your mouse over the picture on the left to interact." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "USE_WEBCAM = False\n", + "\n", + "cam_id = 0\n", + "video_path = \"https://github.com/intel-iot-devkit/sample-videos/raw/master/face-demographics-walking.mp4\"\n", + "\n", + "source = cam_id if USE_WEBCAM else video_path\n", + "\n", + "run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)" + ] + } + ].map(fromJupyterCell) + , [ + { + "cell_type": "markdown", + "source": [ + "# Live 3D Human Pose Estimation with OpenVINO\n", + "\n", + "This notebook demonstrates live 3D Human Pose Estimation with OpenVINO via a webcam. We utilize the model [human-pose-estimation-3d-0001](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001) from [Open Model Zoo](https://github.com/openvinotoolkit/open_model_zoo/). At the end of this notebook, you will see live inference results from your webcam (if available). Alternatively, you can also upload a video file to test out the algorithms.\n", + "**Make sure you have properly installed the [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) and been using JupyterLab to run the demo as suggested in the `README.md`**\n", + "\n", + "> **NOTE**: _To use a webcam, you must run this Jupyter notebook on a computer with a webcam. If you run on a remote server, the webcam will not work. However, you can still do inference on a video file in the final step. This demo utilizes the Python interface in `Three.js` integrated with WebGL to process data from the model inference. These results are processed and displayed in the notebook._\n", + "\n", + "_To ensure that the results are displayed correctly, run the code in a recommended browser on one of the following operating systems:_\n", + "_Ubuntu, Windows: Chrome_\n", + "_macOS: Safari_\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Imports](#Imports)\n", + "- [The model](#The-model)\n", + " - [Download the model](#Download-the-model)\n", + " - [Convert Model to OpenVINO IR format](#Convert-Model-to-OpenVINO-IR-format)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Load the model](#Load-the-model)\n", + "- [Processing](#Processing)\n", + " - [Model Inference](#Model-Inference)\n", + " - [Draw 2D Pose Overlays](#Draw-2D-Pose-Overlays)\n", + " - [Main Processing Function](#Main-Processing-Function)\n", + "- [Run](#Run)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "**The `pythreejs` extension may not display properly when using a Jupyter Notebook release. Therefore, it is recommended to use Jupyter Lab instead.**" + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install pythreejs \"openvino-dev>=2024.0.0\" \"opencv-python\" \"torch\" \"onnx\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import collections\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import cv2\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "from IPython.display import clear_output, display\n", + "import openvino as ov\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "with open(\"notebook_utils.py\", \"w\") as f:\n", + " f.write(r.text)\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/engine3js.py\",\n", + ")\n", + "with open(\"engine3js.py\", \"w\") as f:\n", + " f.write(r.text)\n", + "\n", + "import notebook_utils as utils\n", + "import engine3js as engine" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## The model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Download the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We use `omz_downloader`, which is a command line tool from the `openvino-dev` package. `omz_downloader` automatically creates a directory structure and downloads the selected model." + ] + }, + { + "cell_type": "code", + "source": [ + "# directory where model will be downloaded\n", + "base_model_dir = \"model\"\n", + "\n", + "# model name as named in Open Model Zoo\n", + "model_name = \"human-pose-estimation-3d-0001\"\n", + "# selected precision (FP32, FP16)\n", + "precision = \"FP32\"\n", + "\n", + "BASE_MODEL_NAME = f\"{base_model_dir}/public/{model_name}/{model_name}\"\n", + "model_path = Path(BASE_MODEL_NAME).with_suffix(\".pth\")\n", + "onnx_path = Path(BASE_MODEL_NAME).with_suffix(\".onnx\")\n", + "\n", + "ir_model_path = f\"model/public/{model_name}/{precision}/{model_name}.xml\"\n", + "model_weights_path = f\"model/public/{model_name}/{precision}/{model_name}.bin\"\n", + "\n", + "if not model_path.exists():\n", + " download_command = f\"omz_downloader \" f\"--name {model_name} \" f\"--output_dir {base_model_dir}\"\n", + " ! $download_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Convert Model to OpenVINO IR format\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The selected model comes from the public directory, which means it must be converted into OpenVINO Intermediate Representation (OpenVINO IR). We use `omz_converter` to convert the ONNX format model to the OpenVINO IR format." + ] + }, + { + "cell_type": "code", + "source": [ + "if not onnx_path.exists():\n", + " convert_command = (\n", + " f\"omz_converter \" f\"--name {model_name} \" f\"--precisions {precision} \" f\"--download_dir {base_model_dir} \" f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $convert_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()\n", + "\n", + "device = widgets.Dropdown(\n", + " options=core.available_devices + [\"AUTO\"],\n", + " value=\"AUTO\",\n", + " description=\"Device:\",\n", + " disabled=False,\n", + ")\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Converted models are located in a fixed structure, which indicates vendor, model name and precision.\n", + "\n", + "First, initialize the inference engine, OpenVINO Runtime. Then, read the network architecture and model weights from the `.bin` and `.xml` files to compile for the desired device. An inference request is then created to infer the compiled model." + ] + }, + { + "cell_type": "code", + "source": [ + "# initialize inference engine\n", + "core = ov.Core()\n", + "# read the network and corresponding weights from file\n", + "model = core.read_model(model=ir_model_path, weights=model_weights_path)\n", + "# load the model on the specified device\n", + "compiled_model = core.compile_model(model=model, device_name=device.value)\n", + "infer_request = compiled_model.create_infer_request()\n", + "input_tensor_name = model.inputs[0].get_any_name()\n", + "\n", + "# get input and output names of nodes\n", + "input_layer = compiled_model.input(0)\n", + "output_layers = list(compiled_model.outputs)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The input for the model is data from the input image and the outputs are heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "input_layer.any_name, [o.any_name for o in output_layers]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Processing\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Frames captured from video files or the live webcam are used as the input for the 3D model. This is how you obtain the output heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "def model_infer(scaled_img, stride):\n", + " \"\"\"\n", + " Run model inference on the input image\n", + "\n", + " Parameters:\n", + " scaled_img: resized image according to the input size of the model\n", + " stride: int, the stride of the window\n", + " \"\"\"\n", + "\n", + " # Remove excess space from the picture\n", + " img = scaled_img[\n", + " 0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride),\n", + " 0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride),\n", + " ]\n", + "\n", + " img = np.transpose(img, (2, 0, 1))[None,]\n", + " infer_request.infer({input_tensor_name: img})\n", + " # A set of three inference results is obtained\n", + " results = {name: infer_request.get_tensor(name).data[:] for name in {\"features\", \"heatmaps\", \"pafs\"}}\n", + " # Get the results\n", + " results = (results[\"features\"][0], results[\"heatmaps\"][0], results[\"pafs\"][0])\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Draw 2D Pose Overlays\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We need to define some connections between the joints in advance, so that we can draw the structure of the human body in the resulting image after obtaining the inference results.\n", + "Joints are drawn as circles and limbs are drawn as lines. The code is based on the [3D Human Pose Estimation Demo](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/human_pose_estimation_3d_demo/python) from Open Model Zoo." + ] + }, + { + "cell_type": "code", + "source": [ + "# 3D edge index array\n", + "body_edges = np.array(\n", + " [\n", + " [0, 1],\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [1, 15],\n", + " [15, 16], # nose - l_eye - l_ear\n", + " [1, 17],\n", + " [17, 18], # nose - r_eye - r_ear\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "body_edges_2d = np.array(\n", + " [\n", + " [0, 1], # neck - nose\n", + " [1, 16],\n", + " [16, 18], # nose - l_eye - l_ear\n", + " [1, 15],\n", + " [15, 17], # nose - r_eye - r_ear\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "def draw_poses(frame, poses_2d, scaled_img, use_popup):\n", + " \"\"\"\n", + " Draw 2D pose overlays on the image to visualize estimated poses.\n", + " Joints are drawn as circles and limbs are drawn as lines.\n", + "\n", + " :param frame: the input image\n", + " :param poses_2d: array of human joint pairs\n", + " \"\"\"\n", + " for pose in poses_2d:\n", + " pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose()\n", + " was_found = pose[2] > 0\n", + "\n", + " pose[0], pose[1] = (\n", + " pose[0] * frame.shape[1] / scaled_img.shape[1],\n", + " pose[1] * frame.shape[0] / scaled_img.shape[0],\n", + " )\n", + "\n", + " # Draw joints.\n", + " for edge in body_edges_2d:\n", + " if was_found[edge[0]] and was_found[edge[1]]:\n", + " cv2.line(\n", + " frame,\n", + " tuple(pose[0:2, edge[0]].astype(np.int32)),\n", + " tuple(pose[0:2, edge[1]].astype(np.int32)),\n", + " (255, 255, 0),\n", + " 4,\n", + " cv2.LINE_AA,\n", + " )\n", + " # Draw limbs.\n", + " for kpt_id in range(pose.shape[1]):\n", + " if pose[2, kpt_id] != -1:\n", + " cv2.circle(\n", + " frame,\n", + " tuple(pose[0:2, kpt_id].astype(np.int32)),\n", + " 3,\n", + " (0, 255, 255),\n", + " -1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Main Processing Function\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run 3D pose estimation on the specified source. It could be either a webcam feed or a video file." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0):\n", + " \"\"\"\n", + " 2D image as input, using OpenVINO as inference backend,\n", + " get joints 3D coordinates, and draw 3D human skeleton in the scene\n", + "\n", + " :param source: The webcam number to feed the video stream with primary webcam set to \"0\", or the video path.\n", + " :param flip: To be used by VideoPlayer function for flipping capture image.\n", + " :param use_popup: False for showing encoded frames over this notebook, True for creating a popup window.\n", + " :param skip_frames: Number of frames to skip at the beginning of the video.\n", + " \"\"\"\n", + "\n", + " focal_length = -1 # default\n", + " stride = 8\n", + " player = None\n", + " skeleton_set = None\n", + "\n", + " try:\n", + " # create video player to play with target fps video_path\n", + " # get the frame from camera\n", + " # You can skip first N frames to fast forward video. change 'skip_first_frames'\n", + " player = utils.VideoPlayer(source, flip=flip, fps=30, skip_first_frames=skip_frames)\n", + " # start capturing\n", + " player.start()\n", + "\n", + " input_image = player.next()\n", + " # set the window size\n", + " resize_scale = 450 / input_image.shape[1]\n", + " windows_width = int(input_image.shape[1] * resize_scale)\n", + " windows_height = int(input_image.shape[0] * resize_scale)\n", + "\n", + " # use visualization library\n", + " engine3D = engine.Engine3js(grid=True, axis=True, view_width=windows_width, view_height=windows_height)\n", + "\n", + " if use_popup:\n", + " # display the 3D human pose in this notebook, and origin frame in popup window\n", + " display(engine3D.renderer)\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE)\n", + " else:\n", + " # set the 2D image box, show both human pose and image in the notebook\n", + " imgbox = widgets.Image(format=\"jpg\", height=windows_height, width=windows_width)\n", + " display(widgets.HBox([engine3D.renderer, imgbox]))\n", + "\n", + " skeleton = engine.Skeleton(body_edges=body_edges)\n", + "\n", + " processing_times = collections.deque()\n", + "\n", + " while True:\n", + " # grab the frame\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + "\n", + " # resize image and change dims to fit neural network input\n", + " # (see https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001)\n", + " scaled_img = cv2.resize(frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2]))\n", + "\n", + " if focal_length < 0: # Focal length is unknown\n", + " focal_length = np.float32(0.8 * scaled_img.shape[1])\n", + "\n", + " # inference start\n", + " start_time = time.time()\n", + " # get results\n", + " inference_result = model_infer(scaled_img, stride)\n", + "\n", + " # inference stop\n", + " stop_time = time.time()\n", + " processing_times.append(stop_time - start_time)\n", + " # Process the point to point coordinates of the data\n", + " poses_3d, poses_2d = engine.parse_poses(inference_result, 1, stride, focal_length, True)\n", + "\n", + " # use processing times from last 200 frames\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + "\n", + " if len(poses_3d) > 0:\n", + " # From here, you can rotate the 3D point positions using the function \"draw_poses\",\n", + " # or you can directly make the correct mapping below to properly display the object image on the screen\n", + " poses_3d_copy = poses_3d.copy()\n", + " x = poses_3d_copy[:, 0::4]\n", + " y = poses_3d_copy[:, 1::4]\n", + " z = poses_3d_copy[:, 2::4]\n", + " poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = (\n", + " -z + np.ones(poses_3d[:, 2::4].shape) * 200,\n", + " -y + np.ones(poses_3d[:, 2::4].shape) * 100,\n", + " -x,\n", + " )\n", + "\n", + " poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3]\n", + " people = skeleton(poses_3d=poses_3d)\n", + "\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " except Exception:\n", + " pass\n", + "\n", + " engine3D.scene_add(people)\n", + " skeleton_set = people\n", + "\n", + " # draw 2D\n", + " frame = draw_poses(frame, poses_2d, scaled_img, use_popup)\n", + "\n", + " else:\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " skeleton_set = None\n", + " except Exception:\n", + " pass\n", + "\n", + " cv2.putText(\n", + " frame,\n", + " f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " (10, 30),\n", + " cv2.FONT_HERSHEY_COMPLEX,\n", + " 0.7,\n", + " (0, 0, 255),\n", + " 1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " if use_popup:\n", + " cv2.imshow(title, frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27, use ESC to exit\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # encode numpy array to jpg\n", + " imgbox.value = cv2.imencode(\n", + " \".jpg\",\n", + " frame,\n", + " params=[cv2.IMWRITE_JPEG_QUALITY, 90],\n", + " )[1].tobytes()\n", + "\n", + " engine3D.renderer.render(engine3D.scene, engine3D.cam)\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " clear_output()\n", + " if player is not None:\n", + " # stop capturing\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()\n", + " if skeleton_set:\n", + " engine3D.scene_remove(skeleton_set)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Run\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run, using a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + "> **NOTE**:\n", + ">\n", + "> *1. To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a server (e.g. Binder), the webcam will not work.*\n", + ">\n", + "> *2. Popup mode may not work if you run this notebook on a remote computer (e.g. Binder).*\n", + "\n", + "If you do not have a webcam, you can still run this demo with a video file. Any [format supported by OpenCV](https://docs.opencv.org/4.5.1/dd/d43/tutorial_py_video_display.html) will work." + ] + }, + { + "cell_type": "markdown", + "source": [ + "Using the following method, you can click and move your mouse over the picture on the left to interact." + ] + }, + { + "cell_type": "code", + "source": [ + "USE_WEBCAM = False\n", + "\n", + "cam_id = 0\n", + "video_path = \"https://github.com/intel-iot-devkit/sample-videos/raw/master/face-demographics-walking.mp4\"\n", + "\n", + "source = cam_id if USE_WEBCAM else video_path\n", + "\n", + "run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 18 }, + { modified: 19, original: 19 }, + { modified: 20, original: 20 }, + { modified: 21, original: 21 }, + { modified: 22, original: 22 }, + { modified: 23, original: 23 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 1, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 4, originalLength: 1, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 20, originalLength: 1, modifiedStart: 20, modifiedLength: 1 } satisfies IDiffChange, + ]); + + }); + test('Detect modification in multiple cells', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "source": [ + "# Live 3D Human Pose Estimation with OpenVINO\n", + "\n", + "This notebook demonstrates live 3D Human Pose Estimation with OpenVINO via a webcam. We utilize the model [human-pose-estimation-3d-0001](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001) from [Open Model Zoo](https://github.com/openvinotoolkit/open_model_zoo/). At the end of this notebook, you will see live inference results from your webcam (if available). Alternatively, you can also upload a video file to test out the algorithms.\n", + "**Make sure you have properly installed the [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) and been using JupyterLab to run the demo as suggested in the `README.md`**\n", + "\n", + "> **NOTE**: _To use a webcam, you must run this Jupyter notebook on a computer with a webcam. If you run on a remote server, the webcam will not work. However, you can still do inference on a video file in the final step. This demo utilizes the Python interface in `Three.js` integrated with WebGL to process data from the model inference. These results are processed and displayed in the notebook._\n", + "\n", + "_To ensure that the results are displayed correctly, run the code in a recommended browser on one of the following operating systems:_\n", + "_Ubuntu, Windows: Chrome_\n", + "_macOS: Safari_\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Imports](#Imports)\n", + "- [The model](#The-model)\n", + " - [Download the model](#Download-the-model)\n", + " - [Convert Model to OpenVINO IR format](#Convert-Model-to-OpenVINO-IR-format)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Load the model](#Load-the-model)\n", + "- [Processing](#Processing)\n", + " - [Model Inference](#Model-Inference)\n", + " - [Draw 2D Pose Overlays](#Draw-2D-Pose-Overlays)\n", + " - [Main Processing Function](#Main-Processing-Function)\n", + "- [Run](#Run)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "**The `pythreejs` extension may not display properly when using the latest Jupyter Notebook release (2.4.1). Therefore, it is recommended to use Jupyter Lab instead.**" + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install pythreejs \"openvino-dev>=2024.0.0\" \"opencv-python\" \"torch\" \"onnx\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import collections\n", + "import sys\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import cv2\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "from IPython.display import clear_output, display\n", + "import openvino as ov\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "r = requests.get(\n", + " url='https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py',\n", + ")\n", + "open('notebook_utils.py', 'w').write(r.text)\n", + "import notebook_utils as utils\n", + "\n", + "sys.path.append(\"./engine\")\n", + "import engine.engine3js as engine\n", + "from engine.parse_poses import parse_poses" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## The model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Download the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We use `omz_downloader`, which is a command line tool from the `openvino-dev` package. `omz_downloader` automatically creates a directory structure and downloads the selected model." + ] + }, + { + "cell_type": "code", + "source": [ + "# directory where model will be downloaded\n", + "base_model_dir = \"model\"\n", + "\n", + "# model name as named in Open Model Zoo\n", + "model_name = \"human-pose-estimation-3d-0001\"\n", + "# selected precision (FP32, FP16)\n", + "precision = \"FP32\"\n", + "\n", + "BASE_MODEL_NAME = f\"{base_model_dir}/public/{model_name}/{model_name}\"\n", + "model_path = Path(BASE_MODEL_NAME).with_suffix(\".pth\")\n", + "onnx_path = Path(BASE_MODEL_NAME).with_suffix(\".onnx\")\n", + "\n", + "ir_model_path = f\"model/public/{model_name}/{precision}/{model_name}.xml\"\n", + "model_weights_path = f\"model/public/{model_name}/{precision}/{model_name}.bin\"\n", + "\n", + "if not model_path.exists():\n", + " download_command = (\n", + " f\"omz_downloader \" f\"--name {model_name} \" f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $download_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Convert Model to OpenVINO IR format\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The selected model comes from the public directory, which means it must be converted into OpenVINO Intermediate Representation (OpenVINO IR). We use `omz_converter` to convert the ONNX format model to the OpenVINO IR format." + ] + }, + { + "cell_type": "code", + "source": [ + "if not onnx_path.exists():\n", + " convert_command = (\n", + " f\"omz_converter \"\n", + " f\"--name {model_name} \"\n", + " f\"--precisions {precision} \"\n", + " f\"--download_dir {base_model_dir} \"\n", + " f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $convert_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()\n", + "\n", + "device = widgets.Dropdown(\n", + " options=core.available_devices + [\"AUTO\"],\n", + " value='AUTO',\n", + " description='Device:',\n", + " disabled=False,\n", + ")\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Converted models are located in a fixed structure, which indicates vendor, model name and precision.\n", + "\n", + "First, initialize the inference engine, OpenVINO Runtime. Then, read the network architecture and model weights from the `.bin` and `.xml` files to compile for the desired device. An inference request is then created to infer the compiled model." + ] + }, + { + "cell_type": "code", + "source": [ + "# initialize inference engine\n", + "core = ov.Core()\n", + "# read the network and corresponding weights from file\n", + "model = core.read_model(model=ir_model_path, weights=model_weights_path)\n", + "# load the model on the specified device\n", + "compiled_model = core.compile_model(model=model, device_name=device.value)\n", + "infer_request = compiled_model.create_infer_request()\n", + "input_tensor_name = model.inputs[0].get_any_name()\n", + "\n", + "# get input and output names of nodes\n", + "input_layer = compiled_model.input(0)\n", + "output_layers = list(compiled_model.outputs)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The input for the model is data from the input image and the outputs are heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "input_layer.any_name, [o.any_name for o in output_layers]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Processing\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Frames captured from video files or the live webcam are used as the input for the 3D model. This is how you obtain the output heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "def model_infer(scaled_img, stride):\n", + " \"\"\"\n", + " Run model inference on the input image\n", + "\n", + " Parameters:\n", + " scaled_img: resized image according to the input size of the model\n", + " stride: int, the stride of the window\n", + " \"\"\"\n", + "\n", + " # Remove excess space from the picture\n", + " img = scaled_img[\n", + " 0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride),\n", + " 0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride),\n", + " ]\n", + "\n", + " img = np.transpose(img, (2, 0, 1))[\n", + " None,\n", + " ]\n", + " infer_request.infer({input_tensor_name: img})\n", + " # A set of three inference results is obtained\n", + " results = {\n", + " name: infer_request.get_tensor(name).data[:]\n", + " for name in {\"features\", \"heatmaps\", \"pafs\"}\n", + " }\n", + " # Get the results\n", + " results = (results[\"features\"][0], results[\"heatmaps\"][0], results[\"pafs\"][0])\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Draw 2D Pose Overlays\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We need to define some connections between the joints in advance, so that we can draw the structure of the human body in the resulting image after obtaining the inference results.\n", + "Joints are drawn as circles and limbs are drawn as lines. The code is based on the [3D Human Pose Estimation Demo](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/human_pose_estimation_3d_demo/python) from Open Model Zoo." + ] + }, + { + "cell_type": "code", + "source": [ + "# 3D edge index array\n", + "body_edges = np.array(\n", + " [\n", + " [0, 1], \n", + " [0, 9], [9, 10], [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 3], [3, 4], [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [1, 15], [15, 16], # nose - l_eye - l_ear\n", + " [1, 17], [17, 18], # nose - r_eye - r_ear\n", + " [0, 6], [6, 7], [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12], [12, 13], [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "body_edges_2d = np.array(\n", + " [\n", + " [0, 1], # neck - nose\n", + " [1, 16], [16, 18], # nose - l_eye - l_ear\n", + " [1, 15], [15, 17], # nose - r_eye - r_ear\n", + " [0, 3], [3, 4], [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [0, 9], [9, 10], [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 6], [6, 7], [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12], [12, 13], [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ") \n", + "\n", + "\n", + "def draw_poses(frame, poses_2d, scaled_img, use_popup):\n", + " \"\"\"\n", + " Draw 2D pose overlays on the image to visualize estimated poses.\n", + " Joints are drawn as circles and limbs are drawn as lines.\n", + "\n", + " :param frame: the input image\n", + " :param poses_2d: array of human joint pairs\n", + " \"\"\"\n", + " for pose in poses_2d:\n", + " pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose()\n", + " was_found = pose[2] > 0\n", + "\n", + " pose[0], pose[1] = (\n", + " pose[0] * frame.shape[1] / scaled_img.shape[1],\n", + " pose[1] * frame.shape[0] / scaled_img.shape[0],\n", + " )\n", + "\n", + " # Draw joints.\n", + " for edge in body_edges_2d:\n", + " if was_found[edge[0]] and was_found[edge[1]]:\n", + " cv2.line(\n", + " frame,\n", + " tuple(pose[0:2, edge[0]].astype(np.int32)),\n", + " tuple(pose[0:2, edge[1]].astype(np.int32)),\n", + " (255, 255, 0),\n", + " 4,\n", + " cv2.LINE_AA,\n", + " )\n", + " # Draw limbs.\n", + " for kpt_id in range(pose.shape[1]):\n", + " if pose[2, kpt_id] != -1:\n", + " cv2.circle(\n", + " frame,\n", + " tuple(pose[0:2, kpt_id].astype(np.int32)),\n", + " 3,\n", + " (0, 255, 255),\n", + " -1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Main Processing Function\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run 3D pose estimation on the specified source. It could be either a webcam feed or a video file." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0):\n", + " \"\"\"\n", + " 2D image as input, using OpenVINO as inference backend,\n", + " get joints 3D coordinates, and draw 3D human skeleton in the scene\n", + "\n", + " :param source: The webcam number to feed the video stream with primary webcam set to \"0\", or the video path.\n", + " :param flip: To be used by VideoPlayer function for flipping capture image.\n", + " :param use_popup: False for showing encoded frames over this notebook, True for creating a popup window.\n", + " :param skip_frames: Number of frames to skip at the beginning of the video.\n", + " \"\"\"\n", + "\n", + " focal_length = -1 # default\n", + " stride = 8\n", + " player = None\n", + " skeleton_set = None\n", + "\n", + " try:\n", + " # create video player to play with target fps video_path\n", + " # get the frame from camera\n", + " # You can skip first N frames to fast forward video. change 'skip_first_frames'\n", + " player = utils.VideoPlayer(source, flip=flip, fps=30, skip_first_frames=skip_frames)\n", + " # start capturing\n", + " player.start()\n", + "\n", + " input_image = player.next()\n", + " # set the window size\n", + " resize_scale = 450 / input_image.shape[1]\n", + " windows_width = int(input_image.shape[1] * resize_scale)\n", + " windows_height = int(input_image.shape[0] * resize_scale)\n", + "\n", + " # use visualization library\n", + " engine3D = engine.Engine3js(grid=True, axis=True, view_width=windows_width, view_height=windows_height)\n", + "\n", + " if use_popup:\n", + " # display the 3D human pose in this notebook, and origin frame in popup window\n", + " display(engine3D.renderer)\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE)\n", + " else:\n", + " # set the 2D image box, show both human pose and image in the notebook\n", + " imgbox = widgets.Image(\n", + " format=\"jpg\", height=windows_height, width=windows_width\n", + " )\n", + " display(widgets.HBox([engine3D.renderer, imgbox]))\n", + "\n", + " skeleton = engine.Skeleton(body_edges=body_edges)\n", + "\n", + " processing_times = collections.deque()\n", + "\n", + " while True:\n", + " # grab the frame\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + "\n", + " # resize image and change dims to fit neural network input\n", + " # (see https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001)\n", + " scaled_img = cv2.resize(frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2]))\n", + "\n", + " if focal_length < 0: # Focal length is unknown\n", + " focal_length = np.float32(0.8 * scaled_img.shape[1])\n", + "\n", + " # inference start\n", + " start_time = time.time()\n", + " # get results\n", + " inference_result = model_infer(scaled_img, stride)\n", + "\n", + " # inference stop\n", + " stop_time = time.time()\n", + " processing_times.append(stop_time - start_time)\n", + " # Process the point to point coordinates of the data\n", + " poses_3d, poses_2d = parse_poses(inference_result, 1, stride, focal_length, True)\n", + "\n", + " # use processing times from last 200 frames\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + "\n", + " if len(poses_3d) > 0:\n", + " # From here, you can rotate the 3D point positions using the function \"draw_poses\",\n", + " # or you can directly make the correct mapping below to properly display the object image on the screen\n", + " poses_3d_copy = poses_3d.copy()\n", + " x = poses_3d_copy[:, 0::4]\n", + " y = poses_3d_copy[:, 1::4]\n", + " z = poses_3d_copy[:, 2::4]\n", + " poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = (\n", + " -z + np.ones(poses_3d[:, 2::4].shape) * 200,\n", + " -y + np.ones(poses_3d[:, 2::4].shape) * 100,\n", + " -x,\n", + " )\n", + "\n", + " poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3]\n", + " people = skeleton(poses_3d=poses_3d)\n", + "\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " except Exception:\n", + " pass\n", + "\n", + " engine3D.scene_add(people)\n", + " skeleton_set = people\n", + "\n", + " # draw 2D\n", + " frame = draw_poses(frame, poses_2d, scaled_img, use_popup)\n", + "\n", + " else:\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " skeleton_set = None\n", + " except Exception:\n", + " pass\n", + "\n", + " cv2.putText(\n", + " frame,\n", + " f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " (10, 30),\n", + " cv2.FONT_HERSHEY_COMPLEX,\n", + " 0.7,\n", + " (0, 0, 255),\n", + " 1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " if use_popup:\n", + " cv2.imshow(title, frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27, use ESC to exit\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # encode numpy array to jpg\n", + " imgbox.value = cv2.imencode(\n", + " \".jpg\",\n", + " frame,\n", + " params=[cv2.IMWRITE_JPEG_QUALITY, 90],\n", + " )[1].tobytes()\n", + "\n", + " engine3D.renderer.render(engine3D.scene, engine3D.cam)\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " clear_output()\n", + " if player is not None:\n", + " # stop capturing\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()\n", + " if skeleton_set:\n", + " engine3D.scene_remove(skeleton_set)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Run\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run, using a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + "> **NOTE**:\n", + ">\n", + "> *1. To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a server (e.g. Binder), the webcam will not work.*\n", + ">\n", + "> *2. Popup mode may not work if you run this notebook on a remote computer (e.g. Binder).*\n", + "\n", + "If you do not have a webcam, you can still run this demo with a video file. Any [format supported by OpenCV](https://docs.opencv.org/4.5.1/dd/d43/tutorial_py_video_display.html) will work." + ] + }, + { + "cell_type": "markdown", + "source": [ + "Using the following method, you can click and move your mouse over the picture on the left to interact." + ] + }, + { + "cell_type": "code", + "source": [ + "USE_WEBCAM = False\n", + "\n", + "cam_id = 0\n", + "video_path = \"https://github.com/intel-iot-devkit/sample-videos/raw/master/face-demographics-walking.mp4\"\n", + "\n", + "source = cam_id if USE_WEBCAM else video_path\n", + "\n", + "run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)" + ] + } + ].map(fromJupyterCell) + , [ + { + "cell_type": "markdown", + "source": [ + "# Live 3D Human Pose Estimation with OpenVINO\n", + "\n", + "This notebook demonstrates live 3D Human Pose Estimation with OpenVINO via a webcam. We utilize the model [human-pose-estimation-3d-0001](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001) from [Open Model Zoo](https://github.com/openvinotoolkit/open_model_zoo/). At the end of this notebook, you will see live inference results from your webcam (if available). Alternatively, you can also upload a video file to test out the algorithms.\n", + "**Make sure you have properly installed the [Jupyter extension](https://github.com/jupyter-widgets/pythreejs#jupyterlab) and been using JupyterLab to run the demo as suggested in the `README.md`**\n", + "\n", + "> **NOTE**: _To use a webcam, you must run this Jupyter notebook on a computer with a webcam. If you run on a remote server, the webcam will not work. However, you can still do inference on a video file in the final step. This demo utilizes the Python interface in `Three.js` integrated with WebGL to process data from the model inference. These results are processed and displayed in the notebook._\n", + "\n", + "_To ensure that the results are displayed correctly, run the code in a recommended browser on one of the following operating systems:_\n", + "_Ubuntu, Windows: Chrome_\n", + "_macOS: Safari_\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Imports](#Imports)\n", + "- [The model](#The-model)\n", + " - [Download the model](#Download-the-model)\n", + " - [Convert Model to OpenVINO IR format](#Convert-Model-to-OpenVINO-IR-format)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Load the model](#Load-the-model)\n", + "- [Processing](#Processing)\n", + " - [Model Inference](#Model-Inference)\n", + " - [Draw 2D Pose Overlays](#Draw-2D-Pose-Overlays)\n", + " - [Main Processing Function](#Main-Processing-Function)\n", + "- [Run](#Run)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "**The `pythreejs` extension may not display properly when using the latest Jupyter Notebook release (2.4.1). Therefore, it is recommended to use Jupyter Lab instead.**" + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install pythreejs \"openvino-dev>=2024.0.0\" \"opencv-python\" \"torch\" \"onnx\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import collections\n", + "import sys\n", + "import time\n", + "from pathlib import Path\n", + "\n", + "import cv2\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "from IPython.display import clear_output, display\n", + "import openvino as ov\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "import notebook_utils as utils\n", + "\n", + "sys.path.append(\"./engine\")\n", + "import engine.engine3js as engine\n", + "from engine.parse_poses import parse_poses" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## The model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Download the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We use `omz_downloader`, which is a command line tool from the `openvino-dev` package. `omz_downloader` automatically creates a directory structure and downloads the selected model." + ] + }, + { + "cell_type": "code", + "source": [ + "# directory where model will be downloaded\n", + "base_model_dir = \"model\"\n", + "\n", + "# model name as named in Open Model Zoo\n", + "model_name = \"human-pose-estimation-3d-0001\"\n", + "# selected precision (FP32, FP16)\n", + "precision = \"FP32\"\n", + "\n", + "BASE_MODEL_NAME = f\"{base_model_dir}/public/{model_name}/{model_name}\"\n", + "model_path = Path(BASE_MODEL_NAME).with_suffix(\".pth\")\n", + "onnx_path = Path(BASE_MODEL_NAME).with_suffix(\".onnx\")\n", + "\n", + "ir_model_path = f\"model/public/{model_name}/{precision}/{model_name}.xml\"\n", + "model_weights_path = f\"model/public/{model_name}/{precision}/{model_name}.bin\"\n", + "\n", + "if not model_path.exists():\n", + " download_command = (\n", + " f\"omz_downloader \" f\"--name {model_name} \" f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $download_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Convert Model to OpenVINO IR format\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The selected model comes from the public directory, which means it must be converted into OpenVINO Intermediate Representation (OpenVINO IR). We use `omz_converter` to convert the ONNX format model to the OpenVINO IR format." + ] + }, + { + "cell_type": "code", + "source": [ + "if not onnx_path.exists():\n", + " convert_command = (\n", + " f\"omz_converter \"\n", + " f\"--name {model_name} \"\n", + " f\"--precisions {precision} \"\n", + " f\"--download_dir {base_model_dir} \"\n", + " f\"--output_dir {base_model_dir}\"\n", + " )\n", + " ! $convert_command" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()\n", + "\n", + "device = widgets.Dropdown(\n", + " options=core.available_devices + [\"AUTO\"],\n", + " value=\"AUTO\",\n", + " description=\"Device:\",\n", + " disabled=False,\n", + ")\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Converted models are located in a fixed structure, which indicates vendor, model name and precision.\n", + "\n", + "First, initialize the inference engine, OpenVINO Runtime. Then, read the network architecture and model weights from the `.bin` and `.xml` files to compile for the desired device. An inference request is then created to infer the compiled model." + ] + }, + { + "cell_type": "code", + "source": [ + "# initialize inference engine\n", + "core = ov.Core()\n", + "# read the network and corresponding weights from file\n", + "model = core.read_model(model=ir_model_path, weights=model_weights_path)\n", + "# load the model on the specified device\n", + "compiled_model = core.compile_model(model=model, device_name=device.value)\n", + "infer_request = compiled_model.create_infer_request()\n", + "input_tensor_name = model.inputs[0].get_any_name()\n", + "\n", + "# get input and output names of nodes\n", + "input_layer = compiled_model.input(0)\n", + "output_layers = list(compiled_model.outputs)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The input for the model is data from the input image and the outputs are heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "input_layer.any_name, [o.any_name for o in output_layers]" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Processing\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "### Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Frames captured from video files or the live webcam are used as the input for the 3D model. This is how you obtain the output heat maps, PAF (part affinity fields) and features." + ] + }, + { + "cell_type": "code", + "source": [ + "def model_infer(scaled_img, stride):\n", + " \"\"\"\n", + " Run model inference on the input image\n", + "\n", + " Parameters:\n", + " scaled_img: resized image according to the input size of the model\n", + " stride: int, the stride of the window\n", + " \"\"\"\n", + "\n", + " # Remove excess space from the picture\n", + " img = scaled_img[\n", + " 0 : scaled_img.shape[0] - (scaled_img.shape[0] % stride),\n", + " 0 : scaled_img.shape[1] - (scaled_img.shape[1] % stride),\n", + " ]\n", + "\n", + " img = np.transpose(img, (2, 0, 1))[None,]\n", + " infer_request.infer({input_tensor_name: img})\n", + " # A set of three inference results is obtained\n", + " results = {\n", + " name: infer_request.get_tensor(name).data[:]\n", + " for name in {\"features\", \"heatmaps\", \"pafs\"}\n", + " }\n", + " # Get the results\n", + " results = (results[\"features\"][0], results[\"heatmaps\"][0], results[\"pafs\"][0])\n", + "\n", + " return results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Draw 2D Pose Overlays\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We need to define some connections between the joints in advance, so that we can draw the structure of the human body in the resulting image after obtaining the inference results.\n", + "Joints are drawn as circles and limbs are drawn as lines. The code is based on the [3D Human Pose Estimation Demo](https://github.com/openvinotoolkit/open_model_zoo/tree/master/demos/human_pose_estimation_3d_demo/python) from Open Model Zoo." + ] + }, + { + "cell_type": "code", + "source": [ + "# 3D edge index array\n", + "body_edges = np.array(\n", + " [\n", + " [0, 1],\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [1, 15],\n", + " [15, 16], # nose - l_eye - l_ear\n", + " [1, 17],\n", + " [17, 18], # nose - r_eye - r_ear\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "body_edges_2d = np.array(\n", + " [\n", + " [0, 1], # neck - nose\n", + " [1, 16],\n", + " [16, 18], # nose - l_eye - l_ear\n", + " [1, 15],\n", + " [15, 17], # nose - r_eye - r_ear\n", + " [0, 3],\n", + " [3, 4],\n", + " [4, 5], # neck - l_shoulder - l_elbow - l_wrist\n", + " [0, 9],\n", + " [9, 10],\n", + " [10, 11], # neck - r_shoulder - r_elbow - r_wrist\n", + " [0, 6],\n", + " [6, 7],\n", + " [7, 8], # neck - l_hip - l_knee - l_ankle\n", + " [0, 12],\n", + " [12, 13],\n", + " [13, 14], # neck - r_hip - r_knee - r_ankle\n", + " ]\n", + ")\n", + "\n", + "\n", + "def draw_poses(frame, poses_2d, scaled_img, use_popup):\n", + " \"\"\"\n", + " Draw 2D pose overlays on the image to visualize estimated poses.\n", + " Joints are drawn as circles and limbs are drawn as lines.\n", + "\n", + " :param frame: the input image\n", + " :param poses_2d: array of human joint pairs\n", + " \"\"\"\n", + " for pose in poses_2d:\n", + " pose = np.array(pose[0:-1]).reshape((-1, 3)).transpose()\n", + " was_found = pose[2] > 0\n", + "\n", + " pose[0], pose[1] = (\n", + " pose[0] * frame.shape[1] / scaled_img.shape[1],\n", + " pose[1] * frame.shape[0] / scaled_img.shape[0],\n", + " )\n", + "\n", + " # Draw joints.\n", + " for edge in body_edges_2d:\n", + " if was_found[edge[0]] and was_found[edge[1]]:\n", + " cv2.line(\n", + " frame,\n", + " tuple(pose[0:2, edge[0]].astype(np.int32)),\n", + " tuple(pose[0:2, edge[1]].astype(np.int32)),\n", + " (255, 255, 0),\n", + " 4,\n", + " cv2.LINE_AA,\n", + " )\n", + " # Draw limbs.\n", + " for kpt_id in range(pose.shape[1]):\n", + " if pose[2, kpt_id] != -1:\n", + " cv2.circle(\n", + " frame,\n", + " tuple(pose[0:2, kpt_id].astype(np.int32)),\n", + " 3,\n", + " (0, 255, 255),\n", + " -1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " return frame" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Main Processing Function\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run 3D pose estimation on the specified source. It could be either a webcam feed or a video file." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "def run_pose_estimation(source=0, flip=False, use_popup=False, skip_frames=0):\n", + " \"\"\"\n", + " 2D image as input, using OpenVINO as inference backend,\n", + " get joints 3D coordinates, and draw 3D human skeleton in the scene\n", + "\n", + " :param source: The webcam number to feed the video stream with primary webcam set to \"0\", or the video path.\n", + " :param flip: To be used by VideoPlayer function for flipping capture image.\n", + " :param use_popup: False for showing encoded frames over this notebook, True for creating a popup window.\n", + " :param skip_frames: Number of frames to skip at the beginning of the video.\n", + " \"\"\"\n", + "\n", + " focal_length = -1 # default\n", + " stride = 8\n", + " player = None\n", + " skeleton_set = None\n", + "\n", + " try:\n", + " # create video player to play with target fps video_path\n", + " # get the frame from camera\n", + " # You can skip first N frames to fast forward video. change 'skip_first_frames'\n", + " player = utils.VideoPlayer(\n", + " source, flip=flip, fps=30, skip_first_frames=skip_frames\n", + " )\n", + " # start capturing\n", + " player.start()\n", + "\n", + " input_image = player.next()\n", + " # set the window size\n", + " resize_scale = 450 / input_image.shape[1]\n", + " windows_width = int(input_image.shape[1] * resize_scale)\n", + " windows_height = int(input_image.shape[0] * resize_scale)\n", + "\n", + " # use visualization library\n", + " engine3D = engine.Engine3js(\n", + " grid=True, axis=True, view_width=windows_width, view_height=windows_height\n", + " )\n", + "\n", + " if use_popup:\n", + " # display the 3D human pose in this notebook, and origin frame in popup window\n", + " display(engine3D.renderer)\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(title, cv2.WINDOW_KEEPRATIO | cv2.WINDOW_AUTOSIZE)\n", + " else:\n", + " # set the 2D image box, show both human pose and image in the notebook\n", + " imgbox = widgets.Image(\n", + " format=\"jpg\", height=windows_height, width=windows_width\n", + " )\n", + " display(widgets.HBox([engine3D.renderer, imgbox]))\n", + "\n", + " skeleton = engine.Skeleton(body_edges=body_edges)\n", + "\n", + " processing_times = collections.deque()\n", + "\n", + " while True:\n", + " # grab the frame\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + "\n", + " # resize image and change dims to fit neural network input\n", + " # (see https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/human-pose-estimation-3d-0001)\n", + " scaled_img = cv2.resize(\n", + " frame, dsize=(model.inputs[0].shape[3], model.inputs[0].shape[2])\n", + " )\n", + "\n", + " if focal_length < 0: # Focal length is unknown\n", + " focal_length = np.float32(0.8 * scaled_img.shape[1])\n", + "\n", + " # inference start\n", + " start_time = time.time()\n", + " # get results\n", + " inference_result = model_infer(scaled_img, stride)\n", + "\n", + " # inference stop\n", + " stop_time = time.time()\n", + " processing_times.append(stop_time - start_time)\n", + " # Process the point to point coordinates of the data\n", + " poses_3d, poses_2d = parse_poses(\n", + " inference_result, 1, stride, focal_length, True\n", + " )\n", + "\n", + " # use processing times from last 200 frames\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + "\n", + " if len(poses_3d) > 0:\n", + " # From here, you can rotate the 3D point positions using the function \"draw_poses\",\n", + " # or you can directly make the correct mapping below to properly display the object image on the screen\n", + " poses_3d_copy = poses_3d.copy()\n", + " x = poses_3d_copy[:, 0::4]\n", + " y = poses_3d_copy[:, 1::4]\n", + " z = poses_3d_copy[:, 2::4]\n", + " poses_3d[:, 0::4], poses_3d[:, 1::4], poses_3d[:, 2::4] = (\n", + " -z + np.ones(poses_3d[:, 2::4].shape) * 200,\n", + " -y + np.ones(poses_3d[:, 2::4].shape) * 100,\n", + " -x,\n", + " )\n", + "\n", + " poses_3d = poses_3d.reshape(poses_3d.shape[0], 19, -1)[:, :, 0:3]\n", + " people = skeleton(poses_3d=poses_3d)\n", + "\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " except Exception:\n", + " pass\n", + "\n", + " engine3D.scene_add(people)\n", + " skeleton_set = people\n", + "\n", + " # draw 2D\n", + " frame = draw_poses(frame, poses_2d, scaled_img, use_popup)\n", + "\n", + " else:\n", + " try:\n", + " engine3D.scene_remove(skeleton_set)\n", + " skeleton_set = None\n", + " except Exception:\n", + " pass\n", + "\n", + " cv2.putText(\n", + " frame,\n", + " f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " (10, 30),\n", + " cv2.FONT_HERSHEY_COMPLEX,\n", + " 0.7,\n", + " (0, 0, 255),\n", + " 1,\n", + " cv2.LINE_AA,\n", + " )\n", + "\n", + " if use_popup:\n", + " cv2.imshow(title, frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27, use ESC to exit\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # encode numpy array to jpg\n", + " imgbox.value = cv2.imencode(\n", + " \".jpg\",\n", + " frame,\n", + " params=[cv2.IMWRITE_JPEG_QUALITY, 90],\n", + " )[1].tobytes()\n", + "\n", + " engine3D.renderer.render(engine3D.scene, engine3D.cam)\n", + "\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " clear_output()\n", + " if player is not None:\n", + " # stop capturing\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()\n", + " if skeleton_set:\n", + " engine3D.scene_remove(skeleton_set)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Run\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Run, using a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + "> **NOTE**:\n", + ">\n", + "> *1. To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a server (e.g. Binder), the webcam will not work.*\n", + ">\n", + "> *2. Popup mode may not work if you run this notebook on a remote computer (e.g. Binder).*\n", + "\n", + "If you do not have a webcam, you can still run this demo with a video file. Any [format supported by OpenCV](https://docs.opencv.org/4.5.1/dd/d43/tutorial_py_video_display.html) will work." + ] + }, + { + "cell_type": "markdown", + "source": [ + "Using the following method, you can click and move your mouse over the picture on the left to interact." + ] + }, + { + "cell_type": "code", + "metadata": { + "tags": [] + }, + "source": [ + "USE_WEBCAM = False\n", + "\n", + "cam_id = 0\n", + "video_path = \"https://github.com/intel-iot-devkit/sample-videos/raw/master/face-demographics-walking.mp4\"\n", + "\n", + "source = cam_id if USE_WEBCAM else video_path\n", + "\n", + "run_pose_estimation(source=source, flip=isinstance(source, int), use_popup=False)" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 18 }, + { modified: 19, original: 19 }, + { modified: 20, original: 20 }, + { modified: 21, original: 21 }, + { modified: 22, original: 22 }, + { modified: 23, original: 23 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 4, originalLength: 1, modifiedStart: 4, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 10, originalLength: 1, modifiedStart: 10, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 16, originalLength: 1, modifiedStart: 16, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 18, originalLength: 1, modifiedStart: 18, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 20, originalLength: 1, modifiedStart: 20, modifiedLength: 1 } satisfies IDiffChange, + ]); + + }); + test('Modification with Insertion, detected as deletion of a cell', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "source": [ + "# Video generation with ZeroScope and OpenVINO\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Install and import required packages](#Install-and-import-required-packages)\n", + "- [Load the model](#Load-the-model)\n", + "- [Convert the model](#Convert-the-model)\n", + " - [Define the conversion function](#Define-the-conversion-function)\n", + " - [UNet](#UNet)\n", + " - [VAE](#VAE)\n", + " - [Text encoder](#Text-encoder)\n", + "- [Build a pipeline](#Build-a-pipeline)\n", + "- [Inference with OpenVINO](#Inference-with-OpenVINO)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Define a prompt](#Define-a-prompt)\n", + " - [Video generation](#Video-generation)\n", + "- [Interactive demo](#Interactive-demo)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The ZeroScope model is a free and open-source text-to-video model that can generate realistic and engaging videos from text descriptions. It is based on the [Modelscope](https://modelscope.cn/models/damo/text-to-video-synthesis/summary) model, but it has been improved to produce higher-quality videos with a 16:9 aspect ratio and no Shutterstock watermark. The ZeroScope model is available in two versions: ZeroScope_v2 576w, which is optimized for rapid content creation at a resolution of 576x320 pixels, and ZeroScope_v2 XL, which upscales videos to a high-definition resolution of 1024x576.\n", + "\n", + "The ZeroScope model is trained on a dataset of over 9,000 videos and 29,000 tagged frames. It uses a diffusion model to generate videos, which means that it starts with a random noise image and gradually adds detail to it until it matches the text description. The ZeroScope model is still under development, but it has already been used to create some impressive videos. For example, it has been used to create videos of people dancing, playing sports, and even driving cars.\n", + "\n", + "The ZeroScope model is a powerful tool that can be used to create various videos, from simple animations to complex scenes. It is still under development, but it has the potential to revolutionize the way we create and consume video content.\n", + "\n", + "Both versions of the ZeroScope model are available on Hugging Face:\n", + " - [ZeroScope_v2 576w](https://huggingface.co/cerspense/zeroscope_v2_576w)\n", + " - [ZeroScope_v2 XL](https://huggingface.co/cerspense/zeroscope_v2_XL)\n", + "\n", + "We will use the first one." + ] + }, + { + "cell_type": "markdown", + "source": [ + "
    \n", + " This tutorial requires at least 24GB of free memory to generate a video with a frame size of 432x240 and 16 frames. Increasing either of these values will require more memory and take more time.\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Install and import required packages\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "To work with text-to-video synthesis model, we will use Hugging Face's [Diffusers](https://github.com/huggingface/diffusers) library. It provides already pretrained model from `cerspense`." + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install -q --extra-index-url https://download.pytorch.org/whl/cpu \"diffusers>=0.18.0\" \"torch>=2.1\" transformers \"openvino>=2023.1.0\" numpy \"gradio>=4.19\"" + ] + }, + { + "cell_type": "code", + "source": [ + "import gc\n", + "from typing import Optional, Union, List, Callable\n", + "import base64\n", + "import tempfile\n", + "import warnings\n", + "\n", + "import diffusers\n", + "import transformers\n", + "import numpy as np\n", + "import IPython\n", + "import torch\n", + "import PIL\n", + "import gradio as gr\n", + "\n", + "import openvino as ov" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Original 576x320 inference requires a lot of RAM (>100GB), so let's run our example on a smaller frame size, keeping the same aspect ratio. Try reducing values below to reduce the memory consumption." + ] + }, + { + "cell_type": "code", + "source": [ + "WIDTH = 432 # must be divisible by 8\n", + "HEIGHT = 240 # must be divisible by 8\n", + "NUM_FRAMES = 16" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The model is loaded from HuggingFace using `.from_pretrained` method of `diffusers.DiffusionPipeline`." + ] + }, + { + "cell_type": "code", + "source": [ + "pipe = diffusers.DiffusionPipeline.from_pretrained(\"cerspense/zeroscope_v2_576w\")" + ] + }, + { + "cell_type": "code", + "source": [ + "unet = pipe.unet\n", + "unet.eval()\n", + "vae = pipe.vae\n", + "vae.eval()\n", + "text_encoder = pipe.text_encoder\n", + "text_encoder.eval()\n", + "tokenizer = pipe.tokenizer\n", + "scheduler = pipe.scheduler\n", + "vae_scale_factor = pipe.vae_scale_factor\n", + "unet_in_channels = pipe.unet.config.in_channels\n", + "sample_width = WIDTH // vae_scale_factor\n", + "sample_height = HEIGHT // vae_scale_factor\n", + "del pipe\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Convert the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The architecture for generating videos from text comprises three distinct sub-networks: one for extracting text features, another for translating text features into the video latent space using a diffusion model, and a final one for mapping the video latent space to the visual space. The collective parameters of the entire model amount to approximately 1.7 billion. It's capable of processing English input. The diffusion model is built upon the Unet3D model and achieves video generation by iteratively denoising a starting point of pure Gaussian noise video." + ] + }, + { + "cell_type": "markdown", + "source": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Define the conversion function\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Model components are PyTorch modules, that can be converted with `ov.convert_model` function directly. We also use `ov.save_model` function to serialize the result of conversion." + ] + }, + { + "cell_type": "code", + "source": [ + "warnings.filterwarnings(\"ignore\", category=torch.jit.TracerWarning)" + ] + }, + { + "cell_type": "code", + "source": [ + "from pathlib import Path\n", + "\n", + "\n", + "def convert(model: torch.nn.Module, xml_path: str, **convert_kwargs) -> Path:\n", + " xml_path = Path(xml_path)\n", + " if not xml_path.exists():\n", + " xml_path.parent.mkdir(parents=True, exist_ok=True)\n", + " with torch.no_grad():\n", + " converted_model = ov.convert_model(model, **convert_kwargs)\n", + " ov.save_model(converted_model, xml_path)\n", + " del converted_model\n", + " gc.collect()\n", + " torch._C._jit_clear_class_registry()\n", + " torch.jit._recursive.concrete_type_store = torch.jit._recursive.ConcreteTypeStore()\n", + " torch.jit._state._clear_class_state()\n", + " return xml_path" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### UNet\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text-to-video generation pipeline main component is a conditional 3D UNet model that takes a noisy sample, conditional state, and a timestep and returns a sample shaped output." + ] + }, + { + "cell_type": "code", + "source": [ + "unet_xml_path = convert(\n", + " unet,\n", + " \"models/unet.xml\",\n", + " example_input={\n", + " \"sample\": torch.randn(2, 4, 2, int(sample_height // 2), int(sample_width // 2)),\n", + " \"timestep\": torch.tensor(1),\n", + " \"encoder_hidden_states\": torch.randn(2, 77, 1024),\n", + " },\n", + " input=[\n", + " (\"sample\", (2, 4, NUM_FRAMES, sample_height, sample_width)),\n", + " (\"timestep\", ()),\n", + " (\"encoder_hidden_states\", (2, 77, 1024)),\n", + " ],\n", + ")\n", + "del unet\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### VAE\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Variational autoencoder (VAE) uses UNet output to decode latents to visual representations. Our VAE model has KL loss for encoding images into latents and decoding latent representations into images. For inference, we need only decoder part." + ] + }, + { + "cell_type": "code", + "source": [ + "class VaeDecoderWrapper(torch.nn.Module):\n", + " def __init__(self, vae):\n", + " super().__init__()\n", + " self.vae = vae\n", + "\n", + " def forward(self, z: torch.FloatTensor):\n", + " return self.vae.decode(z)" + ] + }, + { + "cell_type": "code", + "source": [ + "vae_decoder_xml_path = convert(\n", + " VaeDecoderWrapper(vae),\n", + " \"models/vae.xml\",\n", + " example_input=torch.randn(2, 4, 32, 32),\n", + " input=((NUM_FRAMES, 4, sample_height, sample_width)),\n", + ")\n", + "del vae\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Text encoder\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text encoder is used to encode the input prompt to tensor. Default tensor length is 77." + ] + }, + { + "cell_type": "code", + "source": [ + "text_encoder_xml = convert(\n", + " text_encoder,\n", + " \"models/text_encoder.xml\",\n", + " example_input=torch.ones(1, 77, dtype=torch.int64),\n", + " input=((1, 77), ov.Type.i64),\n", + ")\n", + "del text_encoder\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Build a pipeline\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def tensor2vid(video: torch.Tensor, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) -> List[np.ndarray]:\n", + " # This code is copied from https://github.com/modelscope/modelscope/blob/1509fdb973e5871f37148a4b5e5964cafd43e64d/modelscope/pipelines/multi_modal/text_to_video_synthesis_pipeline.py#L78\n", + " # reshape to ncfhw\n", + " mean = torch.tensor(mean, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " std = torch.tensor(std, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " # unnormalize back to [0,1]\n", + " video = video.mul_(std).add_(mean)\n", + " video.clamp_(0, 1)\n", + " # prepare the final outputs\n", + " i, c, f, h, w = video.shape\n", + " images = video.permute(2, 3, 0, 4, 1).reshape(f, h, i * w, c) # 1st (frames, h, batch_size, w, c) 2nd (frames, h, batch_size * w, c)\n", + " images = images.unbind(dim=0) # prepare a list of indvidual (consecutive frames)\n", + " images = [(image.cpu().numpy() * 255).astype(\"uint8\") for image in images] # f h w c\n", + " return images" + ] + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " from diffusers.utils import randn_tensor\n", + "except ImportError:\n", + " from diffusers.utils.torch_utils import randn_tensor\n", + "\n", + "\n", + "class OVTextToVideoSDPipeline(diffusers.DiffusionPipeline):\n", + " def __init__(\n", + " self,\n", + " vae_decoder: ov.CompiledModel,\n", + " text_encoder: ov.CompiledModel,\n", + " tokenizer: transformers.CLIPTokenizer,\n", + " unet: ov.CompiledModel,\n", + " scheduler: diffusers.schedulers.DDIMScheduler,\n", + " ):\n", + " super().__init__()\n", + "\n", + " self.vae_decoder = vae_decoder\n", + " self.text_encoder = text_encoder\n", + " self.tokenizer = tokenizer\n", + " self.unet = unet\n", + " self.scheduler = scheduler\n", + " self.vae_scale_factor = vae_scale_factor\n", + " self.unet_in_channels = unet_in_channels\n", + " self.width = WIDTH\n", + " self.height = HEIGHT\n", + " self.num_frames = NUM_FRAMES\n", + "\n", + " def __call__(\n", + " self,\n", + " prompt: Union[str, List[str]] = None,\n", + " num_inference_steps: int = 50,\n", + " guidance_scale: float = 9.0,\n", + " negative_prompt: Optional[Union[str, List[str]]] = None,\n", + " eta: float = 0.0,\n", + " generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,\n", + " latents: Optional[torch.FloatTensor] = None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " output_type: Optional[str] = \"np\",\n", + " return_dict: bool = True,\n", + " callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,\n", + " callback_steps: int = 1,\n", + " ):\n", + " r\"\"\"\n", + " Function invoked when calling the pipeline for generation.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts to guide the video generation. If not defined, one has to pass `prompt_embeds`.\n", + " instead.\n", + " num_inference_steps (`int`, *optional*, defaults to 50):\n", + " The number of denoising steps. More denoising steps usually lead to a higher quality videos at the\n", + " expense of slower inference.\n", + " guidance_scale (`float`, *optional*, defaults to 7.5):\n", + " Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).\n", + " `guidance_scale` is defined as `w` of equation 2. of [Imagen\n", + " Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >\n", + " 1`. Higher guidance scale encourages to generate videos that are closely linked to the text `prompt`,\n", + " usually at the expense of lower video quality.\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the video generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " eta (`float`, *optional*, defaults to 0.0):\n", + " Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to\n", + " [`schedulers.DDIMScheduler`], will be ignored for others.\n", + " generator (`torch.Generator` or `List[torch.Generator]`, *optional*):\n", + " One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)\n", + " to make generation deterministic.\n", + " latents (`torch.FloatTensor`, *optional*):\n", + " Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for video\n", + " generation. Can be used to tweak the same generation with different prompts. If not provided, a latents\n", + " tensor will ge generated by sampling using the supplied random `generator`. Latents should be of shape\n", + " `(batch_size, num_channel, num_frames, height, width)`.\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " output_type (`str`, *optional*, defaults to `\"np\"`):\n", + " The output format of the generate video. Choose between `torch.FloatTensor` or `np.array`.\n", + " return_dict (`bool`, *optional*, defaults to `True`):\n", + " Whether or not to return a [`~pipelines.stable_diffusion.TextToVideoSDPipelineOutput`] instead of a\n", + " plain tuple.\n", + " callback (`Callable`, *optional*):\n", + " A function that will be called every `callback_steps` steps during inference. The function will be\n", + " called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`.\n", + " callback_steps (`int`, *optional*, defaults to 1):\n", + " The frequency at which the `callback` function will be called. If not specified, the callback will be\n", + " called at every step.\n", + "\n", + " Returns:\n", + " `List[np.ndarray]`: generated video frames\n", + " \"\"\"\n", + "\n", + " num_images_per_prompt = 1\n", + "\n", + " # 1. Check inputs. Raise error if not correct\n", + " self.check_inputs(\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt,\n", + " prompt_embeds,\n", + " negative_prompt_embeds,\n", + " )\n", + "\n", + " # 2. Define call parameters\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)\n", + " # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`\n", + " # corresponds to doing no classifier free guidance.\n", + " do_classifier_free_guidance = guidance_scale > 1.0\n", + "\n", + " # 3. Encode input prompt\n", + " prompt_embeds = self._encode_prompt(\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt,\n", + " prompt_embeds=prompt_embeds,\n", + " negative_prompt_embeds=negative_prompt_embeds,\n", + " )\n", + "\n", + " # 4. Prepare timesteps\n", + " self.scheduler.set_timesteps(num_inference_steps)\n", + " timesteps = self.scheduler.timesteps\n", + "\n", + " # 5. Prepare latent variables\n", + " num_channels_latents = self.unet_in_channels\n", + " latents = self.prepare_latents(\n", + " batch_size * num_images_per_prompt,\n", + " num_channels_latents,\n", + " prompt_embeds.dtype,\n", + " generator,\n", + " latents,\n", + " )\n", + "\n", + " # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline\n", + " extra_step_kwargs = {\"generator\": generator, \"eta\": eta}\n", + "\n", + " # 7. Denoising loop\n", + " num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order\n", + " with self.progress_bar(total=num_inference_steps) as progress_bar:\n", + " for i, t in enumerate(timesteps):\n", + " # expand the latents if we are doing classifier free guidance\n", + " latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents\n", + " latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)\n", + "\n", + " # predict the noise residual\n", + " noise_pred = self.unet(\n", + " {\n", + " \"sample\": latent_model_input,\n", + " \"timestep\": t,\n", + " \"encoder_hidden_states\": prompt_embeds,\n", + " }\n", + " )[0]\n", + " noise_pred = torch.tensor(noise_pred)\n", + "\n", + " # perform guidance\n", + " if do_classifier_free_guidance:\n", + " noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)\n", + " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + "\n", + " # reshape latents\n", + " bsz, channel, frames, width, height = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + " noise_pred = noise_pred.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + "\n", + " # compute the previous noisy sample x_t -> x_t-1\n", + " latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample\n", + "\n", + " # reshape latents back\n", + " latents = latents[None, :].reshape(bsz, frames, channel, width, height).permute(0, 2, 1, 3, 4)\n", + "\n", + " # call the callback, if provided\n", + " if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):\n", + " progress_bar.update()\n", + " if callback is not None and i % callback_steps == 0:\n", + " callback(i, t, latents)\n", + "\n", + " video_tensor = self.decode_latents(latents)\n", + "\n", + " if output_type == \"pt\":\n", + " video = video_tensor\n", + " else:\n", + " video = tensor2vid(video_tensor)\n", + "\n", + " if not return_dict:\n", + " return (video,)\n", + "\n", + " return {\"frames\": video}\n", + "\n", + " # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline._encode_prompt\n", + " def _encode_prompt(\n", + " self,\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt=None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " ):\n", + " r\"\"\"\n", + " Encodes the prompt into text encoder hidden states.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " prompt to be encoded\n", + " num_images_per_prompt (`int`):\n", + " number of images that should be generated per prompt\n", + " do_classifier_free_guidance (`bool`):\n", + " whether to use classifier free guidance or not\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the image generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " \"\"\"\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " if prompt_embeds is None:\n", + " text_inputs = self.tokenizer(\n", + " prompt,\n", + " padding=\"max_length\",\n", + " max_length=self.tokenizer.model_max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + " text_input_ids = text_inputs.input_ids\n", + " untruncated_ids = self.tokenizer(prompt, padding=\"longest\", return_tensors=\"pt\").input_ids\n", + "\n", + " if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):\n", + " removed_text = self.tokenizer.batch_decode(untruncated_ids[:, self.tokenizer.model_max_length - 1 : -1])\n", + " print(\n", + " \"The following part of your input was truncated because CLIP can only handle sequences up to\"\n", + " f\" {self.tokenizer.model_max_length} tokens: {removed_text}\"\n", + " )\n", + "\n", + " prompt_embeds = self.text_encoder(text_input_ids)\n", + " prompt_embeds = prompt_embeds[0]\n", + " prompt_embeds = torch.tensor(prompt_embeds)\n", + "\n", + " bs_embed, seq_len, _ = prompt_embeds.shape\n", + " # duplicate text embeddings for each generation per prompt, using mps friendly method\n", + " prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # get unconditional embeddings for classifier free guidance\n", + " if do_classifier_free_guidance and negative_prompt_embeds is None:\n", + " uncond_tokens: List[str]\n", + " if negative_prompt is None:\n", + " uncond_tokens = [\"\"] * batch_size\n", + " elif type(prompt) is not type(negative_prompt):\n", + " raise TypeError(f\"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !=\" f\" {type(prompt)}.\")\n", + " elif isinstance(negative_prompt, str):\n", + " uncond_tokens = [negative_prompt]\n", + " elif batch_size != len(negative_prompt):\n", + " raise ValueError(\n", + " f\"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:\"\n", + " f\" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches\"\n", + " \" the batch size of `prompt`.\"\n", + " )\n", + " else:\n", + " uncond_tokens = negative_prompt\n", + "\n", + " max_length = prompt_embeds.shape[1]\n", + " uncond_input = self.tokenizer(\n", + " uncond_tokens,\n", + " padding=\"max_length\",\n", + " max_length=max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + "\n", + " negative_prompt_embeds = self.text_encoder(uncond_input.input_ids)\n", + " negative_prompt_embeds = negative_prompt_embeds[0]\n", + " negative_prompt_embeds = torch.tensor(negative_prompt_embeds)\n", + "\n", + " if do_classifier_free_guidance:\n", + " # duplicate unconditional embeddings for each generation per prompt, using mps friendly method\n", + " seq_len = negative_prompt_embeds.shape[1]\n", + "\n", + " negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # For classifier free guidance, we need to do two forward passes.\n", + " # Here we concatenate the unconditional and text embeddings into a single batch\n", + " # to avoid doing two forward passes\n", + " prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])\n", + "\n", + " return prompt_embeds\n", + "\n", + " def prepare_latents(\n", + " self,\n", + " batch_size,\n", + " num_channels_latents,\n", + " dtype,\n", + " generator,\n", + " latents=None,\n", + " ):\n", + " shape = (\n", + " batch_size,\n", + " num_channels_latents,\n", + " self.num_frames,\n", + " self.height // self.vae_scale_factor,\n", + " self.width // self.vae_scale_factor,\n", + " )\n", + " if isinstance(generator, list) and len(generator) != batch_size:\n", + " raise ValueError(\n", + " f\"You have passed a list of generators of length {len(generator)}, but requested an effective batch\"\n", + " f\" size of {batch_size}. Make sure the batch size matches the length of the generators.\"\n", + " )\n", + "\n", + " if latents is None:\n", + " latents = randn_tensor(shape, generator=generator, dtype=dtype)\n", + "\n", + " # scale the initial noise by the standard deviation required by the scheduler\n", + " latents = latents * self.scheduler.init_noise_sigma\n", + " return latents\n", + "\n", + " def check_inputs(\n", + " self,\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt=None,\n", + " prompt_embeds=None,\n", + " negative_prompt_embeds=None,\n", + " ):\n", + " if self.height % 8 != 0 or self.width % 8 != 0:\n", + " raise ValueError(f\"`height` and `width` have to be divisible by 8 but are {self.height} and {self.width}.\")\n", + "\n", + " if (callback_steps is None) or (callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)):\n", + " raise ValueError(f\"`callback_steps` has to be a positive integer but is {callback_steps} of type\" f\" {type(callback_steps)}.\")\n", + "\n", + " if prompt is not None and prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to\" \" only forward one of the two.\"\n", + " )\n", + " elif prompt is None and prompt_embeds is None:\n", + " raise ValueError(\"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined.\")\n", + " elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):\n", + " raise ValueError(f\"`prompt` has to be of type `str` or `list` but is {type(prompt)}\")\n", + "\n", + " if negative_prompt is not None and negative_prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:\"\n", + " f\" {negative_prompt_embeds}. Please make sure to only forward one of the two.\"\n", + " )\n", + "\n", + " if prompt_embeds is not None and negative_prompt_embeds is not None:\n", + " if prompt_embeds.shape != negative_prompt_embeds.shape:\n", + " raise ValueError(\n", + " \"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but\"\n", + " f\" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`\"\n", + " f\" {negative_prompt_embeds.shape}.\"\n", + " )\n", + "\n", + " def decode_latents(self, latents):\n", + " scale_factor = 0.18215\n", + " latents = 1 / scale_factor * latents\n", + "\n", + " batch_size, channels, num_frames, height, width = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(batch_size * num_frames, channels, height, width)\n", + " image = self.vae_decoder(latents)[0]\n", + " image = torch.tensor(image)\n", + " video = (\n", + " image[None, :]\n", + " .reshape(\n", + " (\n", + " batch_size,\n", + " num_frames,\n", + " -1,\n", + " )\n", + " + image.shape[2:]\n", + " )\n", + " .permute(0, 2, 1, 3, 4)\n", + " )\n", + " # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16\n", + " video = video.float()\n", + " return video" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Inference with OpenVINO\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "\n", + "from notebook_utils import device_widget\n", + "\n", + "device = device_widget()\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_unet = core.compile_model(unet_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_vae_decoder = core.compile_model(vae_decoder_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_text_encoder = core.compile_model(text_encoder_xml, device_name=device.value)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Here we replace the pipeline parts with versions converted to OpenVINO IR and compiled to specific device. Note that we use original pipeline tokenizer and scheduler." + ] + }, + { + "cell_type": "code", + "source": [ + "ov_pipe = OVTextToVideoSDPipeline(ov_vae_decoder, ov_text_encoder, tokenizer, ov_unet, scheduler)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define a prompt\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "prompt = \"A panda eating bamboo on a rock.\"" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's generate a video for our prompt. For full list of arguments, see `__call__` function definition of `OVTextToVideoSDPipeline` class in [Build a pipeline](#Build-a-pipeline) section." + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Video generation\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "frames = ov_pipe(prompt, num_inference_steps=25)[\"frames\"]" + ] + }, + { + "cell_type": "code", + "source": [ + "images = [PIL.Image.fromarray(frame) for frame in frames]\n", + "images[0].save(\"output.gif\", save_all=True, append_images=images[1:], duration=125, loop=0)\n", + "with open(\"output.gif\", \"rb\") as gif_file:\n", + " b64 = f\"data:image/gif;base64,{base64.b64encode(gif_file.read()).decode()}\"\n", + "IPython.display.HTML(f'')" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Interactive demo\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def generate(prompt, seed, num_inference_steps, _=gr.Progress(track_tqdm=True)):\n", + " generator = torch.Generator().manual_seed(seed)\n", + " frames = ov_pipe(\n", + " prompt,\n", + " num_inference_steps=num_inference_steps,\n", + " generator=generator,\n", + " )[\"frames\"]\n", + " out_file = tempfile.NamedTemporaryFile(suffix=\".gif\", delete=False)\n", + " images = [PIL.Image.fromarray(frame) for frame in frames]\n", + " images[0].save(out_file, save_all=True, append_images=images[1:], duration=125, loop=0)\n", + " return out_file.name\n", + "\n", + "\n", + "demo = gr.Interface(\n", + " generate,\n", + " [\n", + " gr.Textbox(label=\"Prompt\"),\n", + " gr.Slider(0, 1000000, value=42, label=\"Seed\", step=1),\n", + " gr.Slider(10, 50, value=25, label=\"Number of inference steps\", step=1),\n", + " ],\n", + " gr.Image(label=\"Result\"),\n", + " examples=[\n", + " [\"An astronaut riding a horse.\", 0, 25],\n", + " [\"A panda eating bamboo on a rock.\", 0, 25],\n", + " [\"Spiderman is surfing.\", 0, 25],\n", + " ],\n", + " allow_flagging=\"never\",\n", + ")\n", + "\n", + "try:\n", + " demo.queue().launch(debug=True)\n", + "except Exception:\n", + " demo.queue().launch(share=True, debug=True)\n", + "# if you are launching remotely, specify server_name and server_port\n", + "# demo.launch(server_name='your server name', server_port='server port in int')\n", + "# Read more in the docs: https://gradio.app/docs/" + ] + } + ].map(fromJupyterCell) + , + [ + { + "cell_type": "markdown", + "source": [ + "# Video generation with ZeroScope and OpenVINO\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Install and import required packages](#Install-and-import-required-packages)\n", + "- [Load the model](#Load-the-model)\n", + "- [Convert the model](#Convert-the-model)\n", + " - [Define the conversion function](#Define-the-conversion-function)\n", + " - [UNet](#UNet)\n", + " - [VAE](#VAE)\n", + " - [Text encoder](#Text-encoder)\n", + "- [Build a pipeline](#Build-a-pipeline)\n", + "- [Inference with OpenVINO](#Inference-with-OpenVINO)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Define a prompt](#Define-a-prompt)\n", + " - [Video generation](#Video-generation)\n", + "- [Interactive demo](#Interactive-demo)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The ZeroScope model is a free and open-source text-to-video model that can generate realistic and engaging videos from text descriptions. It is based on the [Modelscope](https://modelscope.cn/models/damo/text-to-video-synthesis/summary) model, but it has been improved to produce higher-quality videos with a 16:9 aspect ratio and no Shutterstock watermark. The ZeroScope model is available in two versions: ZeroScope_v2 576w, which is optimized for rapid content creation at a resolution of 576x320 pixels, and ZeroScope_v2 XL, which upscales videos to a high-definition resolution of 1024x576.\n", + "\n", + "The ZeroScope model is trained on a dataset of over 9,000 videos and 29,000 tagged frames. It uses a diffusion model to generate videos, which means that it starts with a random noise image and gradually adds detail to it until it matches the text description. The ZeroScope model is still under development, but it has already been used to create some impressive videos. For example, it has been used to create videos of people dancing, playing sports, and even driving cars.\n", + "\n", + "The ZeroScope model is a powerful tool that can be used to create various videos, from simple animations to complex scenes. It is still under development, but it has the potential to revolutionize the way we create and consume video content.\n", + "\n", + "Both versions of the ZeroScope model are available on Hugging Face:\n", + " - [ZeroScope_v2 576w](https://huggingface.co/cerspense/zeroscope_v2_576w)\n", + " - [ZeroScope_v2 XL](https://huggingface.co/cerspense/zeroscope_v2_XL)\n", + "\n", + "We will use the first one." + ] + }, + { + "cell_type": "markdown", + "source": [ + "
    \n", + " This tutorial requires at least 24GB of free memory to generate a video with a frame size of 432x240 and 16 frames. Increasing either of these values will require more memory and take more time.\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Install and import required packages\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "To work with text-to-video synthesis model, we will use Hugging Face's [Diffusers](https://github.com/huggingface/diffusers) library. It provides already pretrained model from `cerspense`." + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install -q --extra-index-url https://download.pytorch.org/whl/cpu \"diffusers>=0.18.0\" \"torch>=2.1\" transformers \"openvino>=2023.1.0\" numpy \"gradio>=4.19\"" + ] + }, + { + "cell_type": "code", + "source": [ + "import gc\n", + "from typing import Optional, Union, List, Callable\n", + "import base64\n", + "import tempfile\n", + "import warnings\n", + "\n", + "import diffusers\n", + "import transformers\n", + "import numpy as np\n", + "import IPython\n", + "import torch\n", + "import PIL\n", + "import gradio as gr\n", + "\n", + "import openvino as ov" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Original 576x320 inference requires a lot of RAM (>100GB), so let's run our example on a smaller frame size, keeping the same aspect ratio. Try reducing values below to reduce the memory consumption." + ] + }, + { + "cell_type": "code", + "source": [ + "WIDTH = 432 # must be divisible by 8\n", + "HEIGHT = 240 # must be divisible by 8\n", + "NUM_FRAMES = 16" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The model is loaded from HuggingFace using `.from_pretrained` method of `diffusers.DiffusionPipeline`." + ] + }, + { + "cell_type": "code", + "source": [ + "pipe = diffusers.DiffusionPipeline.from_pretrained(\"cerspense/zeroscope_v2_576w\")" + ] + }, + { + "cell_type": "code", + "source": [ + "unet = pipe.unet\n", + "unet.eval()\n", + "vae = pipe.vae\n", + "vae.eval()\n", + "text_encoder = pipe.text_encoder\n", + "text_encoder.eval()\n", + "tokenizer = pipe.tokenizer\n", + "scheduler = pipe.scheduler\n", + "vae_scale_factor = pipe.vae_scale_factor\n", + "unet_in_channels = pipe.unet.config.in_channels\n", + "sample_width = WIDTH // vae_scale_factor\n", + "sample_height = HEIGHT // vae_scale_factor\n", + "del pipe\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Convert the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The architecture for generating videos from text comprises three distinct sub-networks: one for extracting text features, another for translating text features into the video latent space using a diffusion model, and a final one for mapping the video latent space to the visual space. The collective parameters of the entire model amount to approximately 1.7 billion. It's capable of processing English input. The diffusion model is built upon the Unet3D model and achieves video generation by iteratively denoising a starting point of pure Gaussian noise video." + ] + }, + { + "cell_type": "markdown", + "source": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Define the conversion function\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Model components are PyTorch modules, that can be converted with `ov.convert_model` function directly. We also use `ov.save_model` function to serialize the result of conversion." + ] + }, + { + "cell_type": "code", + "source": [ + "warnings.filterwarnings(\"ignore\", category=torch.jit.TracerWarning)" + ] + }, + { + "cell_type": "code", + "source": [ + "from pathlib import Path\n", + "\n", + "\n", + "def convert(model: torch.nn.Module, xml_path: str, **convert_kwargs) -> Path:\n", + " xml_path = Path(xml_path)\n", + " if not xml_path.exists():\n", + " xml_path.parent.mkdir(parents=True, exist_ok=True)\n", + " with torch.no_grad():\n", + " converted_model = ov.convert_model(model, **convert_kwargs)\n", + " ov.save_model(converted_model, xml_path)\n", + " del converted_model\n", + " gc.collect()\n", + " torch._C._jit_clear_class_registry()\n", + " torch.jit._recursive.concrete_type_store = torch.jit._recursive.ConcreteTypeStore()\n", + " torch.jit._state._clear_class_state()\n", + " return xml_path" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### UNet\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text-to-video generation pipeline main component is a conditional 3D UNet model that takes a noisy sample, conditional state, and a timestep and returns a sample shaped output." + ] + }, + { + "cell_type": "code", + "source": [ + "unet_xml_path = convert(\n", + " unet,\n", + " \"models/unet.xml\",\n", + " example_input={\n", + " \"sample\": torch.randn(2, 4, 2, int(sample_height // 2), int(sample_width // 2)),\n", + " \"timestep\": torch.tensor(1),\n", + " \"encoder_hidden_states\": torch.randn(2, 77, 1024),\n", + " },\n", + " input=[\n", + " (\"sample\", (2, 4, NUM_FRAMES, sample_height, sample_width)),\n", + " (\"timestep\", ()),\n", + " (\"encoder_hidden_states\", (2, 77, 1024)),\n", + " ],\n", + ")\n", + "del unet\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### VAE\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Variational autoencoder (VAE) uses UNet output to decode latents to visual representations. Our VAE model has KL loss for encoding images into latents and decoding latent representations into images. For inference, we need only decoder part." + ] + }, + { + "cell_type": "code", + "source": [ + "class VaeDecoderWrapper(torch.nn.Module):\n", + " def __init__(self, vae):\n", + " super().__init__()\n", + " self.vae = vae\n", + "\n", + " def forward(self, z: torch.FloatTensor):\n", + " return self.vae.decode(z)" + ] + }, + { + "cell_type": "code", + "source": [ + "vae_decoder_xml_path = convert(\n", + " VaeDecoderWrapper(vae),\n", + " \"models/vae.xml\",\n", + " example_input=torch.randn(2, 4, 32, 32),\n", + " input=((NUM_FRAMES, 4, sample_height, sample_width)),\n", + ")\n", + "del vae\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Text encoder\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text encoder is used to encode the input prompt to tensor. Default tensor length is 77." + ] + }, + { + "cell_type": "code", + "source": [ + "text_encoder_xml = convert(\n", + " text_encoder,\n", + " \"models/text_encoder.xml\",\n", + " example_input=torch.ones(1, 77, dtype=torch.int64),\n", + " input=((1, 77), ov.Type.i64),\n", + ")\n", + "del text_encoder\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Build a pipeline\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def tensor2vid(video: torch.Tensor, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) -> List[np.ndarray]:\n", + " # This code is copied from https://github.com/modelscope/modelscope/blob/1509fdb973e5871f37148a4b5e5964cafd43e64d/modelscope/pipelines/multi_modal/text_to_video_synthesis_pipeline.py#L78\n", + " # reshape to ncfhw\n", + " mean = torch.tensor(mean, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " std = torch.tensor(std, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " # unnormalize back to [0,1]\n", + " video = video.mul_(std).add_(mean)\n", + " video.clamp_(0, 1)\n", + " # prepare the final outputs\n", + " i, c, f, h, w = video.shape\n", + " images = video.permute(2, 3, 0, 4, 1).reshape(f, h, i * w, c) # 1st (frames, h, batch_size, w, c) 2nd (frames, h, batch_size * w, c)\n", + " images = images.unbind(dim=0) # prepare a list of indvidual (consecutive frames)\n", + " images = [(image.cpu().numpy() * 255).astype(\"uint8\") for image in images] # f h w c\n", + " return images" + ] + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " from diffusers.utils import randn_tensor\n", + "except ImportError:\n", + " from diffusers.utils.torch_utils import randn_tensor\n", + "\n", + "\n", + "class OVTextToVideoSDPipeline(diffusers.DiffusionPipeline):\n", + " def __init__(\n", + " self,\n", + " vae_decoder: ov.CompiledModel,\n", + " text_encoder: ov.CompiledModel,\n", + " tokenizer: transformers.CLIPTokenizer,\n", + " unet: ov.CompiledModel,\n", + " scheduler: diffusers.schedulers.DDIMScheduler,\n", + " ):\n", + " super().__init__()\n", + "\n", + " self.vae_decoder = vae_decoder\n", + " self.text_encoder = text_encoder\n", + " self.tokenizer = tokenizer\n", + " self.unet = unet\n", + " self.scheduler = scheduler\n", + " self.vae_scale_factor = vae_scale_factor\n", + " self.unet_in_channels = unet_in_channels\n", + " self.width = WIDTH\n", + " self.height = HEIGHT\n", + " self.num_frames = NUM_FRAMES\n", + "\n", + " def __call__(\n", + " self,\n", + " prompt: Union[str, List[str]] = None,\n", + " num_inference_steps: int = 50,\n", + " guidance_scale: float = 9.0,\n", + " negative_prompt: Optional[Union[str, List[str]]] = None,\n", + " eta: float = 0.0,\n", + " generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,\n", + " latents: Optional[torch.FloatTensor] = None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " output_type: Optional[str] = \"np\",\n", + " return_dict: bool = True,\n", + " callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,\n", + " callback_steps: int = 1,\n", + " ):\n", + " r\"\"\"\n", + " Function invoked when calling the pipeline for generation.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts to guide the video generation. If not defined, one has to pass `prompt_embeds`.\n", + " instead.\n", + " num_inference_steps (`int`, *optional*, defaults to 50):\n", + " The number of denoising steps. More denoising steps usually lead to a higher quality videos at the\n", + " expense of slower inference.\n", + " guidance_scale (`float`, *optional*, defaults to 7.5):\n", + " Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).\n", + " `guidance_scale` is defined as `w` of equation 2. of [Imagen\n", + " Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >\n", + " 1`. Higher guidance scale encourages to generate videos that are closely linked to the text `prompt`,\n", + " usually at the expense of lower video quality.\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the video generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " eta (`float`, *optional*, defaults to 0.0):\n", + " Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to\n", + " [`schedulers.DDIMScheduler`], will be ignored for others.\n", + " generator (`torch.Generator` or `List[torch.Generator]`, *optional*):\n", + " One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)\n", + " to make generation deterministic.\n", + " latents (`torch.FloatTensor`, *optional*):\n", + " Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for video\n", + " generation. Can be used to tweak the same generation with different prompts. If not provided, a latents\n", + " tensor will ge generated by sampling using the supplied random `generator`. Latents should be of shape\n", + " `(batch_size, num_channel, num_frames, height, width)`.\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " output_type (`str`, *optional*, defaults to `\"np\"`):\n", + " The output format of the generate video. Choose between `torch.FloatTensor` or `np.array`.\n", + " return_dict (`bool`, *optional*, defaults to `True`):\n", + " Whether or not to return a [`~pipelines.stable_diffusion.TextToVideoSDPipelineOutput`] instead of a\n", + " plain tuple.\n", + " callback (`Callable`, *optional*):\n", + " A function that will be called every `callback_steps` steps during inference. The function will be\n", + " called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`.\n", + " callback_steps (`int`, *optional*, defaults to 1):\n", + " The frequency at which the `callback` function will be called. If not specified, the callback will be\n", + " called at every step.\n", + "\n", + " Returns:\n", + " `List[np.ndarray]`: generated video frames\n", + " \"\"\"\n", + "\n", + " num_images_per_prompt = 1\n", + "\n", + " # 1. Check inputs. Raise error if not correct\n", + " self.check_inputs(\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt,\n", + " prompt_embeds,\n", + " negative_prompt_embeds,\n", + " )\n", + "\n", + " # 2. Define call parameters\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)\n", + " # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`\n", + " # corresponds to doing no classifier free guidance.\n", + " do_classifier_free_guidance = guidance_scale > 1.0\n", + "\n", + " # 3. Encode input prompt\n", + " prompt_embeds = self._encode_prompt(\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt,\n", + " prompt_embeds=prompt_embeds,\n", + " negative_prompt_embeds=negative_prompt_embeds,\n", + " )\n", + "\n", + " # 4. Prepare timesteps\n", + " self.scheduler.set_timesteps(num_inference_steps)\n", + " timesteps = self.scheduler.timesteps\n", + "\n", + " # 5. Prepare latent variables\n", + " num_channels_latents = self.unet_in_channels\n", + " latents = self.prepare_latents(\n", + " batch_size * num_images_per_prompt,\n", + " num_channels_latents,\n", + " prompt_embeds.dtype,\n", + " generator,\n", + " latents,\n", + " )\n", + "\n", + " # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline\n", + " extra_step_kwargs = {\"generator\": generator, \"eta\": eta}\n", + "\n", + " # 7. Denoising loop\n", + " num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order\n", + " with self.progress_bar(total=num_inference_steps) as progress_bar:\n", + " for i, t in enumerate(timesteps):\n", + " # expand the latents if we are doing classifier free guidance\n", + " latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents\n", + " latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)\n", + "\n", + " # predict the noise residual\n", + " noise_pred = self.unet(\n", + " {\n", + " \"sample\": latent_model_input,\n", + " \"timestep\": t,\n", + " \"encoder_hidden_states\": prompt_embeds,\n", + " }\n", + " )[0]\n", + " noise_pred = torch.tensor(noise_pred)\n", + "\n", + " # perform guidance\n", + " if do_classifier_free_guidance:\n", + " noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)\n", + " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + "\n", + " # reshape latents\n", + " bsz, channel, frames, width, height = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + " noise_pred = noise_pred.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + "\n", + " # compute the previous noisy sample x_t -> x_t-1\n", + " latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample\n", + "\n", + " # reshape latents back\n", + " latents = latents[None, :].reshape(bsz, frames, channel, width, height).permute(0, 2, 1, 3, 4)\n", + "\n", + " # call the callback, if provided\n", + " if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):\n", + " progress_bar.update()\n", + " if callback is not None and i % callback_steps == 0:\n", + " callback(i, t, latents)\n", + "\n", + " video_tensor = self.decode_latents(latents)\n", + "\n", + " if output_type == \"pt\":\n", + " video = video_tensor\n", + " else:\n", + " video = tensor2vid(video_tensor)\n", + "\n", + " if not return_dict:\n", + " return (video,)\n", + "\n", + " return {\"frames\": video}\n", + "\n", + " # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline._encode_prompt\n", + " def _encode_prompt(\n", + " self,\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt=None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " ):\n", + " r\"\"\"\n", + " Encodes the prompt into text encoder hidden states.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " prompt to be encoded\n", + " num_images_per_prompt (`int`):\n", + " number of images that should be generated per prompt\n", + " do_classifier_free_guidance (`bool`):\n", + " whether to use classifier free guidance or not\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the image generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " \"\"\"\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " if prompt_embeds is None:\n", + " text_inputs = self.tokenizer(\n", + " prompt,\n", + " padding=\"max_length\",\n", + " max_length=self.tokenizer.model_max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + " text_input_ids = text_inputs.input_ids\n", + " untruncated_ids = self.tokenizer(prompt, padding=\"longest\", return_tensors=\"pt\").input_ids\n", + "\n", + " if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):\n", + " removed_text = self.tokenizer.batch_decode(untruncated_ids[:, self.tokenizer.model_max_length - 1 : -1])\n", + " print(\n", + " \"The following part of your input was truncated because CLIP can only handle sequences up to\"\n", + " f\" {self.tokenizer.model_max_length} tokens: {removed_text}\"\n", + " )\n", + "\n", + " prompt_embeds = self.text_encoder(text_input_ids)\n", + " prompt_embeds = prompt_embeds[0]\n", + " prompt_embeds = torch.tensor(prompt_embeds)\n", + "\n", + " bs_embed, seq_len, _ = prompt_embeds.shape\n", + " # duplicate text embeddings for each generation per prompt, using mps friendly method\n", + " prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # get unconditional embeddings for classifier free guidance\n", + " if do_classifier_free_guidance and negative_prompt_embeds is None:\n", + " uncond_tokens: List[str]\n", + " if negative_prompt is None:\n", + " uncond_tokens = [\"\"] * batch_size\n", + " elif type(prompt) is not type(negative_prompt):\n", + " raise TypeError(f\"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !=\" f\" {type(prompt)}.\")\n", + " elif isinstance(negative_prompt, str):\n", + " uncond_tokens = [negative_prompt]\n", + " elif batch_size != len(negative_prompt):\n", + " raise ValueError(\n", + " f\"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:\"\n", + " f\" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches\"\n", + " \" the batch size of `prompt`.\"\n", + " )\n", + " else:\n", + " uncond_tokens = negative_prompt\n", + "\n", + " max_length = prompt_embeds.shape[1]\n", + " uncond_input = self.tokenizer(\n", + " uncond_tokens,\n", + " padding=\"max_length\",\n", + " max_length=max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + "\n", + " negative_prompt_embeds = self.text_encoder(uncond_input.input_ids)\n", + " negative_prompt_embeds = negative_prompt_embeds[0]\n", + " negative_prompt_embeds = torch.tensor(negative_prompt_embeds)\n", + "\n", + " if do_classifier_free_guidance:\n", + " # duplicate unconditional embeddings for each generation per prompt, using mps friendly method\n", + " seq_len = negative_prompt_embeds.shape[1]\n", + "\n", + " negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # For classifier free guidance, we need to do two forward passes.\n", + " # Here we concatenate the unconditional and text embeddings into a single batch\n", + " # to avoid doing two forward passes\n", + " prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])\n", + "\n", + " return prompt_embeds\n", + "\n", + " def prepare_latents(\n", + " self,\n", + " batch_size,\n", + " num_channels_latents,\n", + " dtype,\n", + " generator,\n", + " latents=None,\n", + " ):\n", + " shape = (\n", + " batch_size,\n", + " num_channels_latents,\n", + " self.num_frames,\n", + " self.height // self.vae_scale_factor,\n", + " self.width // self.vae_scale_factor,\n", + " )\n", + " if isinstance(generator, list) and len(generator) != batch_size:\n", + " raise ValueError(\n", + " f\"You have passed a list of generators of length {len(generator)}, but requested an effective batch\"\n", + " f\" size of {batch_size}. Make sure the batch size matches the length of the generators.\"\n", + " )\n", + "\n", + " if latents is None:\n", + " latents = randn_tensor(shape, generator=generator, dtype=dtype)\n", + "\n", + " # scale the initial noise by the standard deviation required by the scheduler\n", + " latents = latents * self.scheduler.init_noise_sigma\n", + " return latents\n", + "\n", + " def check_inputs(\n", + " self,\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt=None,\n", + " prompt_embeds=None,\n", + " negative_prompt_embeds=None,\n", + " ):\n", + " if self.height % 8 != 0 or self.width % 8 != 0:\n", + " raise ValueError(f\"`height` and `width` have to be divisible by 8 but are {self.height} and {self.width}.\")\n", + "\n", + " if (callback_steps is None) or (callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)):\n", + " raise ValueError(f\"`callback_steps` has to be a positive integer but is {callback_steps} of type\" f\" {type(callback_steps)}.\")\n", + "\n", + " if prompt is not None and prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to\" \" only forward one of the two.\"\n", + " )\n", + " elif prompt is None and prompt_embeds is None:\n", + " raise ValueError(\"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined.\")\n", + " elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):\n", + " raise ValueError(f\"`prompt` has to be of type `str` or `list` but is {type(prompt)}\")\n", + "\n", + " if negative_prompt is not None and negative_prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:\"\n", + " f\" {negative_prompt_embeds}. Please make sure to only forward one of the two.\"\n", + " )\n", + "\n", + " if prompt_embeds is not None and negative_prompt_embeds is not None:\n", + " if prompt_embeds.shape != negative_prompt_embeds.shape:\n", + " raise ValueError(\n", + " \"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but\"\n", + " f\" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`\"\n", + " f\" {negative_prompt_embeds.shape}.\"\n", + " )\n", + "\n", + " def decode_latents(self, latents):\n", + " scale_factor = 0.18215\n", + " latents = 1 / scale_factor * latents\n", + "\n", + " batch_size, channels, num_frames, height, width = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(batch_size * num_frames, channels, height, width)\n", + " image = self.vae_decoder(latents)[0]\n", + " image = torch.tensor(image)\n", + " video = (\n", + " image[None, :]\n", + " .reshape(\n", + " (\n", + " batch_size,\n", + " num_frames,\n", + " -1,\n", + " )\n", + " + image.shape[2:]\n", + " )\n", + " .permute(0, 2, 1, 3, 4)\n", + " )\n", + " # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16\n", + " video = video.float()\n", + " return video" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Inference with OpenVINO\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "\n", + "from notebook_utils import device_widget\n", + "\n", + "device = device_widget()\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_unet = core.compile_model(unet_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_vae_decoder = core.compile_model(vae_decoder_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_text_encoder = core.compile_model(text_encoder_xml, device_name=device.value)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Here we replace the pipeline parts with versions converted to OpenVINO IR and compiled to specific device. Note that we use original pipeline tokenizer and scheduler." + ] + }, + { + "cell_type": "code", + "source": [ + "ov_pipe = OVTextToVideoSDPipeline(ov_vae_decoder, ov_text_encoder, tokenizer, ov_unet, scheduler)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define a prompt\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "prompt = \"A panda eating bamboo on a rock.\"" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's generate a video for our prompt. For full list of arguments, see `__call__` function definition of `OVTextToVideoSDPipeline` class in [Build a pipeline](#Build-a-pipeline) section." + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Video generation\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "frames = ov_pipe(prompt, num_inference_steps=25)[\"frames\"]" + ] + }, + { + "cell_type": "code", + "source": [ + "images = [PIL.Image.fromarray(frame) for frame in frames]\n", + "images[0].save(\"output.gif\", save_all=True, append_images=images[1:], duration=125, loop=0)\n", + "with open(\"output.gif\", \"rb\") as gif_file:\n", + " b64 = f\"data:image/gif;base64,{base64.b64encode(gif_file.read()).decode()}\"\n", + "IPython.display.HTML(f'')" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Interactive demo\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "test_replace": { + " demo.queue().launch(debug=True)": " demo.queue.launch()", + " demo.queue().launch(share=True, debug=True)": " demo.queue().launch(share=True)" + } + }, + "source": [ + "def generate(prompt, seed, num_inference_steps, _=gr.Progress(track_tqdm=True)):\n", + " generator = torch.Generator().manual_seed(seed)\n", + " frames = ov_pipe(\n", + " prompt,\n", + " num_inference_steps=num_inference_steps,\n", + " generator=generator,\n", + " )[\"frames\"]\n", + " out_file = tempfile.NamedTemporaryFile(suffix=\".gif\", delete=False)\n", + " images = [PIL.Image.fromarray(frame) for frame in frames]\n", + " images[0].save(out_file, save_all=True, append_images=images[1:], duration=125, loop=0)\n", + " return out_file.name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "if not Path(\"gradio_helper.py\").exists():\n", + " r = requests.get(url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/notebooks/zeroscope-text2video/gradio_helper.py\")\n", + " open(\"gradio_helper.py\", \"w\").write(r.text)\n", + "\n", + "from gradio_helper import make_demo\n", + "\n", + "demo = make_demo(fn=generate)\n", + "\n", + "try:\n", + " demo.queue().launch(debug=True)\n", + "except Exception:\n", + " demo.queue().launch(share=True, debug=True)\n", + "# If you are launching remotely, specify server_name and server_port\n", + "# EXAMPLE: `demo.launch(server_name='your server name', server_port='server port in int')`\n", + "# To learn more please refer to the Gradio docs: https://gradio.app/docs/" + ] + }, + { + "cell_type": "code", + "source": [ + "# please uncomment and run this cell for stopping gradio interface\n", + "# demo.close()" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 18 }, + { modified: 19, original: 19 }, + { modified: 20, original: 20 }, + { modified: 21, original: 21 }, + { modified: 22, original: 22 }, + { modified: 23, original: 23 }, + { modified: 24, original: 24 }, + { modified: 25, original: 25 }, + { modified: 26, original: 26 }, + { modified: 27, original: 27 }, + { modified: 28, original: 28 }, + { modified: 29, original: 29 }, + { modified: 30, original: 30 }, + { modified: 31, original: 31 }, + { modified: 32, original: 32 }, + { modified: 33, original: 33 }, + { modified: 34, original: 34 }, + { modified: 35, original: 35 }, + { modified: 36, original: 36 }, + { modified: 37, original: 37 }, + { modified: 38, original: 38 }, + { modified: 39, original: 39 }, + { modified: 40, original: 40 }, + { modified: 41, original: 41 }, + { modified: 42, original: 42 }, + { modified: 43, original: 43 }, + { modified: 44, original: 44 }, + { modified: 45, original: 45 }, + { modified: 46, original: 46 }, + { modified: 47, original: 47 }, + { modified: 48, original: 48 }, + { modified: 49, original: 49 }, + { modified: 50, original: -1 }, + { modified: 51, original: -1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 49, originalLength: 1, modifiedStart: 49, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 50, originalLength: 0, modifiedStart: 50, modifiedLength: 2 } satisfies IDiffChange, + ]); + }); + test('Modification of three cells', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "source": [ + "# Video generation with ZeroScope and OpenVINO\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Install and import required packages](#Install-and-import-required-packages)\n", + "- [Load the model](#Load-the-model)\n", + "- [Convert the model](#Convert-the-model)\n", + " - [Define the conversion function](#Define-the-conversion-function)\n", + " - [UNet](#UNet)\n", + " - [VAE](#VAE)\n", + " - [Text encoder](#Text-encoder)\n", + "- [Build a pipeline](#Build-a-pipeline)\n", + "- [Inference with OpenVINO](#Inference-with-OpenVINO)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Define a prompt](#Define-a-prompt)\n", + " - [Video generation](#Video-generation)\n", + "- [Interactive demo](#Interactive-demo)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The ZeroScope model is a free and open-source text-to-video model that can generate realistic and engaging videos from text descriptions. It is based on the [Modelscope](https://modelscope.cn/models/damo/text-to-video-synthesis/summary) model, but it has been improved to produce higher-quality videos with a 16:9 aspect ratio and no Shutterstock watermark. The ZeroScope model is available in two versions: ZeroScope_v2 576w, which is optimized for rapid content creation at a resolution of 576x320 pixels, and ZeroScope_v2 XL, which upscales videos to a high-definition resolution of 1024x576.\n", + "\n", + "The ZeroScope model is trained on a dataset of over 9,000 videos and 29,000 tagged frames. It uses a diffusion model to generate videos, which means that it starts with a random noise image and gradually adds detail to it until it matches the text description. The ZeroScope model is still under development, but it has already been used to create some impressive videos. For example, it has been used to create videos of people dancing, playing sports, and even driving cars.\n", + "\n", + "The ZeroScope model is a powerful tool that can be used to create various videos, from simple animations to complex scenes. It is still under development, but it has the potential to revolutionize the way we create and consume video content.\n", + "\n", + "Both versions of the ZeroScope model are available on Hugging Face:\n", + " - [ZeroScope_v2 576w](https://huggingface.co/cerspense/zeroscope_v2_576w)\n", + " - [ZeroScope_v2 XL](https://huggingface.co/cerspense/zeroscope_v2_XL)\n", + "\n", + "We will use the first one." + ] + }, + { + "cell_type": "markdown", + "source": [ + "
    \n", + " This tutorial requires at least 24GB of free memory to generate a video with a frame size of 432x240 and 16 frames. Increasing either of these values will require more memory and take more time.\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Install and import required packages\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "To work with text-to-video synthesis model, we will use Hugging Face's [Diffusers](https://github.com/huggingface/diffusers) library. It provides already pretrained model from `cerspense`." + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install -q --extra-index-url https://download.pytorch.org/whl/cpu \"diffusers>=0.18.0\" \"torch>=2.1\" transformers \"openvino>=2023.1.0\" numpy \"gradio>=4.19\"" + ] + }, + { + "cell_type": "code", + "source": [ + "import gc\n", + "from typing import Optional, Union, List, Callable\n", + "import base64\n", + "import tempfile\n", + "import warnings\n", + "\n", + "import diffusers\n", + "import transformers\n", + "import numpy as np\n", + "import IPython\n", + "import ipywidgets as widgets\n", + "import torch\n", + "import PIL\n", + "import gradio as gr\n", + "\n", + "import openvino as ov" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Original 576x320 inference requires a lot of RAM (>100GB), so let's run our example on a smaller frame size, keeping the same aspect ratio. Try reducing values below to reduce the memory consumption." + ] + }, + { + "cell_type": "code", + "source": [ + "WIDTH = 432 # must be divisible by 8\n", + "HEIGHT = 240 # must be divisible by 8\n", + "NUM_FRAMES = 16" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The model is loaded from HuggingFace using `.from_pretrained` method of `diffusers.DiffusionPipeline`." + ] + }, + { + "cell_type": "code", + "source": [ + "pipe = diffusers.DiffusionPipeline.from_pretrained(\"cerspense/zeroscope_v2_576w\")" + ] + }, + { + "cell_type": "code", + "source": [ + "unet = pipe.unet\n", + "unet.eval()\n", + "vae = pipe.vae\n", + "vae.eval()\n", + "text_encoder = pipe.text_encoder\n", + "text_encoder.eval()\n", + "tokenizer = pipe.tokenizer\n", + "scheduler = pipe.scheduler\n", + "vae_scale_factor = pipe.vae_scale_factor\n", + "unet_in_channels = pipe.unet.config.in_channels\n", + "sample_width = WIDTH // vae_scale_factor\n", + "sample_height = HEIGHT // vae_scale_factor\n", + "del pipe\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Convert the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The architecture for generating videos from text comprises three distinct sub-networks: one for extracting text features, another for translating text features into the video latent space using a diffusion model, and a final one for mapping the video latent space to the visual space. The collective parameters of the entire model amount to approximately 1.7 billion. It's capable of processing English input. The diffusion model is built upon the Unet3D model and achieves video generation by iteratively denoising a starting point of pure Gaussian noise video." + ] + }, + { + "cell_type": "markdown", + "source": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Define the conversion function\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Model components are PyTorch modules, that can be converted with `ov.convert_model` function directly. We also use `ov.save_model` function to serialize the result of conversion." + ] + }, + { + "cell_type": "code", + "source": [ + "warnings.filterwarnings(\"ignore\", category=torch.jit.TracerWarning)" + ] + }, + { + "cell_type": "code", + "source": [ + "from pathlib import Path\n", + "\n", + "\n", + "def convert(model: torch.nn.Module, xml_path: str, **convert_kwargs) -> Path:\n", + " xml_path = Path(xml_path)\n", + " if not xml_path.exists():\n", + " xml_path.parent.mkdir(parents=True, exist_ok=True)\n", + " with torch.no_grad():\n", + " converted_model = ov.convert_model(model, **convert_kwargs)\n", + " ov.save_model(converted_model, xml_path)\n", + " del converted_model\n", + " gc.collect()\n", + " torch._C._jit_clear_class_registry()\n", + " torch.jit._recursive.concrete_type_store = torch.jit._recursive.ConcreteTypeStore()\n", + " torch.jit._state._clear_class_state()\n", + " return xml_path" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### UNet\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text-to-video generation pipeline main component is a conditional 3D UNet model that takes a noisy sample, conditional state, and a timestep and returns a sample shaped output." + ] + }, + { + "cell_type": "code", + "source": [ + "unet_xml_path = convert(\n", + " unet,\n", + " \"models/unet.xml\",\n", + " example_input={\n", + " \"sample\": torch.randn(2, 4, 2, int(sample_height // 2), int(sample_width // 2)),\n", + " \"timestep\": torch.tensor(1),\n", + " \"encoder_hidden_states\": torch.randn(2, 77, 1024),\n", + " },\n", + " input=[\n", + " (\"sample\", (2, 4, NUM_FRAMES, sample_height, sample_width)),\n", + " (\"timestep\", ()),\n", + " (\"encoder_hidden_states\", (2, 77, 1024)),\n", + " ],\n", + ")\n", + "del unet\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### VAE\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Variational autoencoder (VAE) uses UNet output to decode latents to visual representations. Our VAE model has KL loss for encoding images into latents and decoding latent representations into images. For inference, we need only decoder part." + ] + }, + { + "cell_type": "code", + "source": [ + "class VaeDecoderWrapper(torch.nn.Module):\n", + " def __init__(self, vae):\n", + " super().__init__()\n", + " self.vae = vae\n", + "\n", + " def forward(self, z: torch.FloatTensor):\n", + " return self.vae.decode(z)" + ] + }, + { + "cell_type": "code", + "source": [ + "vae_decoder_xml_path = convert(\n", + " VaeDecoderWrapper(vae),\n", + " \"models/vae.xml\",\n", + " example_input=torch.randn(2, 4, 32, 32),\n", + " input=((NUM_FRAMES, 4, sample_height, sample_width)),\n", + ")\n", + "del vae\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Text encoder\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text encoder is used to encode the input prompt to tensor. Default tensor length is 77." + ] + }, + { + "cell_type": "code", + "source": [ + "text_encoder_xml = convert(\n", + " text_encoder,\n", + " \"models/text_encoder.xml\",\n", + " example_input=torch.ones(1, 77, dtype=torch.int64),\n", + " input=((1, 77), ov.Type.i64),\n", + ")\n", + "del text_encoder\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Build a pipeline\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def tensor2vid(video: torch.Tensor, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) -> List[np.ndarray]:\n", + " # This code is copied from https://github.com/modelscope/modelscope/blob/1509fdb973e5871f37148a4b5e5964cafd43e64d/modelscope/pipelines/multi_modal/text_to_video_synthesis_pipeline.py#L78\n", + " # reshape to ncfhw\n", + " mean = torch.tensor(mean, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " std = torch.tensor(std, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " # unnormalize back to [0,1]\n", + " video = video.mul_(std).add_(mean)\n", + " video.clamp_(0, 1)\n", + " # prepare the final outputs\n", + " i, c, f, h, w = video.shape\n", + " images = video.permute(2, 3, 0, 4, 1).reshape(f, h, i * w, c) # 1st (frames, h, batch_size, w, c) 2nd (frames, h, batch_size * w, c)\n", + " images = images.unbind(dim=0) # prepare a list of indvidual (consecutive frames)\n", + " images = [(image.cpu().numpy() * 255).astype(\"uint8\") for image in images] # f h w c\n", + " return images" + ] + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " from diffusers.utils import randn_tensor\n", + "except ImportError:\n", + " from diffusers.utils.torch_utils import randn_tensor\n", + "\n", + "\n", + "class OVTextToVideoSDPipeline(diffusers.DiffusionPipeline):\n", + " def __init__(\n", + " self,\n", + " vae_decoder: ov.CompiledModel,\n", + " text_encoder: ov.CompiledModel,\n", + " tokenizer: transformers.CLIPTokenizer,\n", + " unet: ov.CompiledModel,\n", + " scheduler: diffusers.schedulers.DDIMScheduler,\n", + " ):\n", + " super().__init__()\n", + "\n", + " self.vae_decoder = vae_decoder\n", + " self.text_encoder = text_encoder\n", + " self.tokenizer = tokenizer\n", + " self.unet = unet\n", + " self.scheduler = scheduler\n", + " self.vae_scale_factor = vae_scale_factor\n", + " self.unet_in_channels = unet_in_channels\n", + " self.width = WIDTH\n", + " self.height = HEIGHT\n", + " self.num_frames = NUM_FRAMES\n", + "\n", + " def __call__(\n", + " self,\n", + " prompt: Union[str, List[str]] = None,\n", + " num_inference_steps: int = 50,\n", + " guidance_scale: float = 9.0,\n", + " negative_prompt: Optional[Union[str, List[str]]] = None,\n", + " eta: float = 0.0,\n", + " generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,\n", + " latents: Optional[torch.FloatTensor] = None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " output_type: Optional[str] = \"np\",\n", + " return_dict: bool = True,\n", + " callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,\n", + " callback_steps: int = 1,\n", + " ):\n", + " r\"\"\"\n", + " Function invoked when calling the pipeline for generation.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts to guide the video generation. If not defined, one has to pass `prompt_embeds`.\n", + " instead.\n", + " num_inference_steps (`int`, *optional*, defaults to 50):\n", + " The number of denoising steps. More denoising steps usually lead to a higher quality videos at the\n", + " expense of slower inference.\n", + " guidance_scale (`float`, *optional*, defaults to 7.5):\n", + " Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).\n", + " `guidance_scale` is defined as `w` of equation 2. of [Imagen\n", + " Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >\n", + " 1`. Higher guidance scale encourages to generate videos that are closely linked to the text `prompt`,\n", + " usually at the expense of lower video quality.\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the video generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " eta (`float`, *optional*, defaults to 0.0):\n", + " Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to\n", + " [`schedulers.DDIMScheduler`], will be ignored for others.\n", + " generator (`torch.Generator` or `List[torch.Generator]`, *optional*):\n", + " One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)\n", + " to make generation deterministic.\n", + " latents (`torch.FloatTensor`, *optional*):\n", + " Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for video\n", + " generation. Can be used to tweak the same generation with different prompts. If not provided, a latents\n", + " tensor will ge generated by sampling using the supplied random `generator`. Latents should be of shape\n", + " `(batch_size, num_channel, num_frames, height, width)`.\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " output_type (`str`, *optional*, defaults to `\"np\"`):\n", + " The output format of the generate video. Choose between `torch.FloatTensor` or `np.array`.\n", + " return_dict (`bool`, *optional*, defaults to `True`):\n", + " Whether or not to return a [`~pipelines.stable_diffusion.TextToVideoSDPipelineOutput`] instead of a\n", + " plain tuple.\n", + " callback (`Callable`, *optional*):\n", + " A function that will be called every `callback_steps` steps during inference. The function will be\n", + " called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`.\n", + " callback_steps (`int`, *optional*, defaults to 1):\n", + " The frequency at which the `callback` function will be called. If not specified, the callback will be\n", + " called at every step.\n", + "\n", + " Returns:\n", + " `List[np.ndarray]`: generated video frames\n", + " \"\"\"\n", + "\n", + " num_images_per_prompt = 1\n", + "\n", + " # 1. Check inputs. Raise error if not correct\n", + " self.check_inputs(\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt,\n", + " prompt_embeds,\n", + " negative_prompt_embeds,\n", + " )\n", + "\n", + " # 2. Define call parameters\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)\n", + " # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`\n", + " # corresponds to doing no classifier free guidance.\n", + " do_classifier_free_guidance = guidance_scale > 1.0\n", + "\n", + " # 3. Encode input prompt\n", + " prompt_embeds = self._encode_prompt(\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt,\n", + " prompt_embeds=prompt_embeds,\n", + " negative_prompt_embeds=negative_prompt_embeds,\n", + " )\n", + "\n", + " # 4. Prepare timesteps\n", + " self.scheduler.set_timesteps(num_inference_steps)\n", + " timesteps = self.scheduler.timesteps\n", + "\n", + " # 5. Prepare latent variables\n", + " num_channels_latents = self.unet_in_channels\n", + " latents = self.prepare_latents(\n", + " batch_size * num_images_per_prompt,\n", + " num_channels_latents,\n", + " prompt_embeds.dtype,\n", + " generator,\n", + " latents,\n", + " )\n", + "\n", + " # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline\n", + " extra_step_kwargs = {\"generator\": generator, \"eta\": eta}\n", + "\n", + " # 7. Denoising loop\n", + " num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order\n", + " with self.progress_bar(total=num_inference_steps) as progress_bar:\n", + " for i, t in enumerate(timesteps):\n", + " # expand the latents if we are doing classifier free guidance\n", + " latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents\n", + " latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)\n", + "\n", + " # predict the noise residual\n", + " noise_pred = self.unet(\n", + " {\n", + " \"sample\": latent_model_input,\n", + " \"timestep\": t,\n", + " \"encoder_hidden_states\": prompt_embeds,\n", + " }\n", + " )[0]\n", + " noise_pred = torch.tensor(noise_pred)\n", + "\n", + " # perform guidance\n", + " if do_classifier_free_guidance:\n", + " noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)\n", + " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + "\n", + " # reshape latents\n", + " bsz, channel, frames, width, height = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + " noise_pred = noise_pred.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + "\n", + " # compute the previous noisy sample x_t -> x_t-1\n", + " latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample\n", + "\n", + " # reshape latents back\n", + " latents = latents[None, :].reshape(bsz, frames, channel, width, height).permute(0, 2, 1, 3, 4)\n", + "\n", + " # call the callback, if provided\n", + " if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):\n", + " progress_bar.update()\n", + " if callback is not None and i % callback_steps == 0:\n", + " callback(i, t, latents)\n", + "\n", + " video_tensor = self.decode_latents(latents)\n", + "\n", + " if output_type == \"pt\":\n", + " video = video_tensor\n", + " else:\n", + " video = tensor2vid(video_tensor)\n", + "\n", + " if not return_dict:\n", + " return (video,)\n", + "\n", + " return {\"frames\": video}\n", + "\n", + " # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline._encode_prompt\n", + " def _encode_prompt(\n", + " self,\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt=None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " ):\n", + " r\"\"\"\n", + " Encodes the prompt into text encoder hidden states.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " prompt to be encoded\n", + " num_images_per_prompt (`int`):\n", + " number of images that should be generated per prompt\n", + " do_classifier_free_guidance (`bool`):\n", + " whether to use classifier free guidance or not\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the image generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " \"\"\"\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " if prompt_embeds is None:\n", + " text_inputs = self.tokenizer(\n", + " prompt,\n", + " padding=\"max_length\",\n", + " max_length=self.tokenizer.model_max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + " text_input_ids = text_inputs.input_ids\n", + " untruncated_ids = self.tokenizer(prompt, padding=\"longest\", return_tensors=\"pt\").input_ids\n", + "\n", + " if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):\n", + " removed_text = self.tokenizer.batch_decode(untruncated_ids[:, self.tokenizer.model_max_length - 1 : -1])\n", + " print(\n", + " \"The following part of your input was truncated because CLIP can only handle sequences up to\"\n", + " f\" {self.tokenizer.model_max_length} tokens: {removed_text}\"\n", + " )\n", + "\n", + " prompt_embeds = self.text_encoder(text_input_ids)\n", + " prompt_embeds = prompt_embeds[0]\n", + " prompt_embeds = torch.tensor(prompt_embeds)\n", + "\n", + " bs_embed, seq_len, _ = prompt_embeds.shape\n", + " # duplicate text embeddings for each generation per prompt, using mps friendly method\n", + " prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # get unconditional embeddings for classifier free guidance\n", + " if do_classifier_free_guidance and negative_prompt_embeds is None:\n", + " uncond_tokens: List[str]\n", + " if negative_prompt is None:\n", + " uncond_tokens = [\"\"] * batch_size\n", + " elif type(prompt) is not type(negative_prompt):\n", + " raise TypeError(f\"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !=\" f\" {type(prompt)}.\")\n", + " elif isinstance(negative_prompt, str):\n", + " uncond_tokens = [negative_prompt]\n", + " elif batch_size != len(negative_prompt):\n", + " raise ValueError(\n", + " f\"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:\"\n", + " f\" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches\"\n", + " \" the batch size of `prompt`.\"\n", + " )\n", + " else:\n", + " uncond_tokens = negative_prompt\n", + "\n", + " max_length = prompt_embeds.shape[1]\n", + " uncond_input = self.tokenizer(\n", + " uncond_tokens,\n", + " padding=\"max_length\",\n", + " max_length=max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + "\n", + " negative_prompt_embeds = self.text_encoder(uncond_input.input_ids)\n", + " negative_prompt_embeds = negative_prompt_embeds[0]\n", + " negative_prompt_embeds = torch.tensor(negative_prompt_embeds)\n", + "\n", + " if do_classifier_free_guidance:\n", + " # duplicate unconditional embeddings for each generation per prompt, using mps friendly method\n", + " seq_len = negative_prompt_embeds.shape[1]\n", + "\n", + " negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # For classifier free guidance, we need to do two forward passes.\n", + " # Here we concatenate the unconditional and text embeddings into a single batch\n", + " # to avoid doing two forward passes\n", + " prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])\n", + "\n", + " return prompt_embeds\n", + "\n", + " def prepare_latents(\n", + " self,\n", + " batch_size,\n", + " num_channels_latents,\n", + " dtype,\n", + " generator,\n", + " latents=None,\n", + " ):\n", + " shape = (\n", + " batch_size,\n", + " num_channels_latents,\n", + " self.num_frames,\n", + " self.height // self.vae_scale_factor,\n", + " self.width // self.vae_scale_factor,\n", + " )\n", + " if isinstance(generator, list) and len(generator) != batch_size:\n", + " raise ValueError(\n", + " f\"You have passed a list of generators of length {len(generator)}, but requested an effective batch\"\n", + " f\" size of {batch_size}. Make sure the batch size matches the length of the generators.\"\n", + " )\n", + "\n", + " if latents is None:\n", + " latents = randn_tensor(shape, generator=generator, dtype=dtype)\n", + "\n", + " # scale the initial noise by the standard deviation required by the scheduler\n", + " latents = latents * self.scheduler.init_noise_sigma\n", + " return latents\n", + "\n", + " def check_inputs(\n", + " self,\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt=None,\n", + " prompt_embeds=None,\n", + " negative_prompt_embeds=None,\n", + " ):\n", + " if self.height % 8 != 0 or self.width % 8 != 0:\n", + " raise ValueError(f\"`height` and `width` have to be divisible by 8 but are {self.height} and {self.width}.\")\n", + "\n", + " if (callback_steps is None) or (callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)):\n", + " raise ValueError(f\"`callback_steps` has to be a positive integer but is {callback_steps} of type\" f\" {type(callback_steps)}.\")\n", + "\n", + " if prompt is not None and prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to\" \" only forward one of the two.\"\n", + " )\n", + " elif prompt is None and prompt_embeds is None:\n", + " raise ValueError(\"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined.\")\n", + " elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):\n", + " raise ValueError(f\"`prompt` has to be of type `str` or `list` but is {type(prompt)}\")\n", + "\n", + " if negative_prompt is not None and negative_prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:\"\n", + " f\" {negative_prompt_embeds}. Please make sure to only forward one of the two.\"\n", + " )\n", + "\n", + " if prompt_embeds is not None and negative_prompt_embeds is not None:\n", + " if prompt_embeds.shape != negative_prompt_embeds.shape:\n", + " raise ValueError(\n", + " \"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but\"\n", + " f\" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`\"\n", + " f\" {negative_prompt_embeds.shape}.\"\n", + " )\n", + "\n", + " def decode_latents(self, latents):\n", + " scale_factor = 0.18215\n", + " latents = 1 / scale_factor * latents\n", + "\n", + " batch_size, channels, num_frames, height, width = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(batch_size * num_frames, channels, height, width)\n", + " image = self.vae_decoder(latents)[0]\n", + " image = torch.tensor(image)\n", + " video = (\n", + " image[None, :]\n", + " .reshape(\n", + " (\n", + " batch_size,\n", + " num_frames,\n", + " -1,\n", + " )\n", + " + image.shape[2:]\n", + " )\n", + " .permute(0, 2, 1, 3, 4)\n", + " )\n", + " # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16\n", + " video = video.float()\n", + " return video" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Inference with OpenVINO\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "device = widgets.Dropdown(\n", + " options=core.available_devices + [\"AUTO\"],\n", + " value=\"AUTO\",\n", + " description=\"Device:\",\n", + " disabled=False,\n", + ")\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_unet = core.compile_model(unet_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_vae_decoder = core.compile_model(vae_decoder_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_text_encoder = core.compile_model(text_encoder_xml, device_name=device.value)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Here we replace the pipeline parts with versions converted to OpenVINO IR and compiled to specific device. Note that we use original pipeline tokenizer and scheduler." + ] + }, + { + "cell_type": "code", + "source": [ + "ov_pipe = OVTextToVideoSDPipeline(ov_vae_decoder, ov_text_encoder, tokenizer, ov_unet, scheduler)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define a prompt\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "prompt = \"A panda eating bamboo on a rock.\"" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's generate a video for our prompt. For full list of arguments, see `__call__` function definition of `OVTextToVideoSDPipeline` class in [Build a pipeline](#Build-a-pipeline) section." + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Video generation\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "frames = ov_pipe(prompt, num_inference_steps=25)[\"frames\"]" + ] + }, + { + "cell_type": "code", + "source": [ + "images = [PIL.Image.fromarray(frame) for frame in frames]\n", + "images[0].save(\"output.gif\", save_all=True, append_images=images[1:], duration=125, loop=0)\n", + "with open(\"output.gif\", \"rb\") as gif_file:\n", + " b64 = f\"data:image/gif;base64,{base64.b64encode(gif_file.read()).decode()}\"\n", + "IPython.display.HTML(f'')" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Interactive demo\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def generate(prompt, seed, num_inference_steps, _=gr.Progress(track_tqdm=True)):\n", + " generator = torch.Generator().manual_seed(seed)\n", + " frames = ov_pipe(\n", + " prompt,\n", + " num_inference_steps=num_inference_steps,\n", + " generator=generator,\n", + " )[\"frames\"]\n", + " out_file = tempfile.NamedTemporaryFile(suffix=\".gif\", delete=False)\n", + " images = [PIL.Image.fromarray(frame) for frame in frames]\n", + " images[0].save(out_file, save_all=True, append_images=images[1:], duration=125, loop=0)\n", + " return out_file.name\n", + "\n", + "\n", + "demo = gr.Interface(\n", + " generate,\n", + " [\n", + " gr.Textbox(label=\"Prompt\"),\n", + " gr.Slider(0, 1000000, value=42, label=\"Seed\", step=1),\n", + " gr.Slider(10, 50, value=25, label=\"Number of inference steps\", step=1),\n", + " ],\n", + " gr.Image(label=\"Result\"),\n", + " examples=[\n", + " [\"An astronaut riding a horse.\", 0, 25],\n", + " [\"A panda eating bamboo on a rock.\", 0, 25],\n", + " [\"Spiderman is surfing.\", 0, 25],\n", + " ],\n", + " allow_flagging=\"never\",\n", + ")\n", + "\n", + "try:\n", + " demo.queue().launch(debug=True)\n", + "except Exception:\n", + " demo.queue().launch(share=True, debug=True)\n", + "# if you are launching remotely, specify server_name and server_port\n", + "# demo.launch(server_name='your server name', server_port='server port in int')\n", + "# Read more in the docs: https://gradio.app/docs/" + ] + } + ] + .map(fromJupyterCell) + , + [ + { + "cell_type": "markdown", + "source": [ + "# Video generation with ZeroScope and OpenVINO\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Install and import required packages](#Install-and-import-required-packages)\n", + "- [Load the model](#Load-the-model)\n", + "- [Convert the model](#Convert-the-model)\n", + " - [Define the conversion function](#Define-the-conversion-function)\n", + " - [UNet](#UNet)\n", + " - [VAE](#VAE)\n", + " - [Text encoder](#Text-encoder)\n", + "- [Build a pipeline](#Build-a-pipeline)\n", + "- [Inference with OpenVINO](#Inference-with-OpenVINO)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Define a prompt](#Define-a-prompt)\n", + " - [Video generation](#Video-generation)\n", + "- [Interactive demo](#Interactive-demo)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The ZeroScope model is a free and open-source text-to-video model that can generate realistic and engaging videos from text descriptions. It is based on the [Modelscope](https://modelscope.cn/models/damo/text-to-video-synthesis/summary) model, but it has been improved to produce higher-quality videos with a 16:9 aspect ratio and no Shutterstock watermark. The ZeroScope model is available in two versions: ZeroScope_v2 576w, which is optimized for rapid content creation at a resolution of 576x320 pixels, and ZeroScope_v2 XL, which upscales videos to a high-definition resolution of 1024x576.\n", + "\n", + "The ZeroScope model is trained on a dataset of over 9,000 videos and 29,000 tagged frames. It uses a diffusion model to generate videos, which means that it starts with a random noise image and gradually adds detail to it until it matches the text description. The ZeroScope model is still under development, but it has already been used to create some impressive videos. For example, it has been used to create videos of people dancing, playing sports, and even driving cars.\n", + "\n", + "The ZeroScope model is a powerful tool that can be used to create various videos, from simple animations to complex scenes. It is still under development, but it has the potential to revolutionize the way we create and consume video content.\n", + "\n", + "Both versions of the ZeroScope model are available on Hugging Face:\n", + " - [ZeroScope_v2 576w](https://huggingface.co/cerspense/zeroscope_v2_576w)\n", + " - [ZeroScope_v2 XL](https://huggingface.co/cerspense/zeroscope_v2_XL)\n", + "\n", + "We will use the first one." + ] + }, + { + "cell_type": "markdown", + "source": [ + "
    \n", + " This tutorial requires at least 24GB of free memory to generate a video with a frame size of 432x240 and 16 frames. Increasing either of these values will require more memory and take more time.\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Install and import required packages\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "To work with text-to-video synthesis model, we will use Hugging Face's [Diffusers](https://github.com/huggingface/diffusers) library. It provides already pretrained model from `cerspense`." + ] + }, + { + "cell_type": "code", + "source": [ + "%pip install -q --extra-index-url https://download.pytorch.org/whl/cpu \"diffusers>=0.18.0\" \"torch>=2.1\" transformers \"openvino>=2023.1.0\" numpy \"gradio>=4.19\"" + ] + }, + { + "cell_type": "code", + "source": [ + "import gc\n", + "from typing import Optional, Union, List, Callable\n", + "import base64\n", + "import tempfile\n", + "import warnings\n", + "\n", + "import diffusers\n", + "import transformers\n", + "import numpy as np\n", + "import IPython\n", + "import torch\n", + "import PIL\n", + "import gradio as gr\n", + "\n", + "import openvino as ov" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Original 576x320 inference requires a lot of RAM (>100GB), so let's run our example on a smaller frame size, keeping the same aspect ratio. Try reducing values below to reduce the memory consumption." + ] + }, + { + "cell_type": "code", + "source": [ + "WIDTH = 432 # must be divisible by 8\n", + "HEIGHT = 240 # must be divisible by 8\n", + "NUM_FRAMES = 16" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Load the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The model is loaded from HuggingFace using `.from_pretrained` method of `diffusers.DiffusionPipeline`." + ] + }, + { + "cell_type": "code", + "source": [ + "pipe = diffusers.DiffusionPipeline.from_pretrained(\"cerspense/zeroscope_v2_576w\")" + ] + }, + { + "cell_type": "code", + "source": [ + "unet = pipe.unet\n", + "unet.eval()\n", + "vae = pipe.vae\n", + "vae.eval()\n", + "text_encoder = pipe.text_encoder\n", + "text_encoder.eval()\n", + "tokenizer = pipe.tokenizer\n", + "scheduler = pipe.scheduler\n", + "vae_scale_factor = pipe.vae_scale_factor\n", + "unet_in_channels = pipe.unet.config.in_channels\n", + "sample_width = WIDTH // vae_scale_factor\n", + "sample_height = HEIGHT // vae_scale_factor\n", + "del pipe\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Convert the model\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "The architecture for generating videos from text comprises three distinct sub-networks: one for extracting text features, another for translating text features into the video latent space using a diffusion model, and a final one for mapping the video latent space to the visual space. The collective parameters of the entire model amount to approximately 1.7 billion. It's capable of processing English input. The diffusion model is built upon the Unet3D model and achieves video generation by iteratively denoising a starting point of pure Gaussian noise video." + ] + }, + { + "cell_type": "markdown", + "source": [ + "![](data:image/png;base64,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6Vv1VWglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqtKCVdk7osejTpV6m273NOyd0WPRp0q9Tbd7mqrSglXZO6LHo06Veptu9zTsndFj0adKvU23e5qq0oJV2Tuix6NOlXqbbvc07J3RY9GnSr1Nt3uaqt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)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define the conversion function\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Model components are PyTorch modules, that can be converted with `ov.convert_model` function directly. We also use `ov.save_model` function to serialize the result of conversion." + ] + }, + { + "cell_type": "code", + "source": [ + "warnings.filterwarnings(\"ignore\", category=torch.jit.TracerWarning)" + ] + }, + { + "cell_type": "code", + "source": [ + "from pathlib import Path\n", + "\n", + "\n", + "def convert(model: torch.nn.Module, xml_path: str, **convert_kwargs) -> Path:\n", + " xml_path = Path(xml_path)\n", + " if not xml_path.exists():\n", + " xml_path.parent.mkdir(parents=True, exist_ok=True)\n", + " with torch.no_grad():\n", + " converted_model = ov.convert_model(model, **convert_kwargs)\n", + " ov.save_model(converted_model, xml_path)\n", + " del converted_model\n", + " gc.collect()\n", + " torch._C._jit_clear_class_registry()\n", + " torch.jit._recursive.concrete_type_store = torch.jit._recursive.ConcreteTypeStore()\n", + " torch.jit._state._clear_class_state()\n", + " return xml_path" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### UNet\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text-to-video generation pipeline main component is a conditional 3D UNet model that takes a noisy sample, conditional state, and a timestep and returns a sample shaped output." + ] + }, + { + "cell_type": "code", + "source": [ + "unet_xml_path = convert(\n", + " unet,\n", + " \"models/unet.xml\",\n", + " example_input={\n", + " \"sample\": torch.randn(2, 4, 2, int(sample_height // 2), int(sample_width // 2)),\n", + " \"timestep\": torch.tensor(1),\n", + " \"encoder_hidden_states\": torch.randn(2, 77, 1024),\n", + " },\n", + " input=[\n", + " (\"sample\", (2, 4, NUM_FRAMES, sample_height, sample_width)),\n", + " (\"timestep\", ()),\n", + " (\"encoder_hidden_states\", (2, 77, 1024)),\n", + " ],\n", + ")\n", + "del unet\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### VAE\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Variational autoencoder (VAE) uses UNet output to decode latents to visual representations. Our VAE model has KL loss for encoding images into latents and decoding latent representations into images. For inference, we need only decoder part." + ] + }, + { + "cell_type": "code", + "source": [ + "class VaeDecoderWrapper(torch.nn.Module):\n", + " def __init__(self, vae):\n", + " super().__init__()\n", + " self.vae = vae\n", + "\n", + " def forward(self, z: torch.FloatTensor):\n", + " return self.vae.decode(z)" + ] + }, + { + "cell_type": "code", + "source": [ + "vae_decoder_xml_path = convert(\n", + " VaeDecoderWrapper(vae),\n", + " \"models/vae.xml\",\n", + " example_input=torch.randn(2, 4, 32, 32),\n", + " input=((NUM_FRAMES, 4, sample_height, sample_width)),\n", + ")\n", + "del vae\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Text encoder\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Text encoder is used to encode the input prompt to tensor. Default tensor length is 77." + ] + }, + { + "cell_type": "code", + "source": [ + "text_encoder_xml = convert(\n", + " text_encoder,\n", + " \"models/text_encoder.xml\",\n", + " example_input=torch.ones(1, 77, dtype=torch.int64),\n", + " input=((1, 77), ov.Type.i64),\n", + ")\n", + "del text_encoder\n", + "gc.collect();" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Build a pipeline\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def tensor2vid(video: torch.Tensor, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) -> List[np.ndarray]:\n", + " # This code is copied from https://github.com/modelscope/modelscope/blob/1509fdb973e5871f37148a4b5e5964cafd43e64d/modelscope/pipelines/multi_modal/text_to_video_synthesis_pipeline.py#L78\n", + " # reshape to ncfhw\n", + " mean = torch.tensor(mean, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " std = torch.tensor(std, device=video.device).reshape(1, -1, 1, 1, 1)\n", + " # unnormalize back to [0,1]\n", + " video = video.mul_(std).add_(mean)\n", + " video.clamp_(0, 1)\n", + " # prepare the final outputs\n", + " i, c, f, h, w = video.shape\n", + " images = video.permute(2, 3, 0, 4, 1).reshape(f, h, i * w, c) # 1st (frames, h, batch_size, w, c) 2nd (frames, h, batch_size * w, c)\n", + " images = images.unbind(dim=0) # prepare a list of indvidual (consecutive frames)\n", + " images = [(image.cpu().numpy() * 255).astype(\"uint8\") for image in images] # f h w c\n", + " return images" + ] + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " from diffusers.utils import randn_tensor\n", + "except ImportError:\n", + " from diffusers.utils.torch_utils import randn_tensor\n", + "\n", + "\n", + "class OVTextToVideoSDPipeline(diffusers.DiffusionPipeline):\n", + " def __init__(\n", + " self,\n", + " vae_decoder: ov.CompiledModel,\n", + " text_encoder: ov.CompiledModel,\n", + " tokenizer: transformers.CLIPTokenizer,\n", + " unet: ov.CompiledModel,\n", + " scheduler: diffusers.schedulers.DDIMScheduler,\n", + " ):\n", + " super().__init__()\n", + "\n", + " self.vae_decoder = vae_decoder\n", + " self.text_encoder = text_encoder\n", + " self.tokenizer = tokenizer\n", + " self.unet = unet\n", + " self.scheduler = scheduler\n", + " self.vae_scale_factor = vae_scale_factor\n", + " self.unet_in_channels = unet_in_channels\n", + " self.width = WIDTH\n", + " self.height = HEIGHT\n", + " self.num_frames = NUM_FRAMES\n", + "\n", + " def __call__(\n", + " self,\n", + " prompt: Union[str, List[str]] = None,\n", + " num_inference_steps: int = 50,\n", + " guidance_scale: float = 9.0,\n", + " negative_prompt: Optional[Union[str, List[str]]] = None,\n", + " eta: float = 0.0,\n", + " generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,\n", + " latents: Optional[torch.FloatTensor] = None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " output_type: Optional[str] = \"np\",\n", + " return_dict: bool = True,\n", + " callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None,\n", + " callback_steps: int = 1,\n", + " ):\n", + " r\"\"\"\n", + " Function invoked when calling the pipeline for generation.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts to guide the video generation. If not defined, one has to pass `prompt_embeds`.\n", + " instead.\n", + " num_inference_steps (`int`, *optional*, defaults to 50):\n", + " The number of denoising steps. More denoising steps usually lead to a higher quality videos at the\n", + " expense of slower inference.\n", + " guidance_scale (`float`, *optional*, defaults to 7.5):\n", + " Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598).\n", + " `guidance_scale` is defined as `w` of equation 2. of [Imagen\n", + " Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale >\n", + " 1`. Higher guidance scale encourages to generate videos that are closely linked to the text `prompt`,\n", + " usually at the expense of lower video quality.\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the video generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " eta (`float`, *optional*, defaults to 0.0):\n", + " Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to\n", + " [`schedulers.DDIMScheduler`], will be ignored for others.\n", + " generator (`torch.Generator` or `List[torch.Generator]`, *optional*):\n", + " One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)\n", + " to make generation deterministic.\n", + " latents (`torch.FloatTensor`, *optional*):\n", + " Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for video\n", + " generation. Can be used to tweak the same generation with different prompts. If not provided, a latents\n", + " tensor will ge generated by sampling using the supplied random `generator`. Latents should be of shape\n", + " `(batch_size, num_channel, num_frames, height, width)`.\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " output_type (`str`, *optional*, defaults to `\"np\"`):\n", + " The output format of the generate video. Choose between `torch.FloatTensor` or `np.array`.\n", + " return_dict (`bool`, *optional*, defaults to `True`):\n", + " Whether or not to return a [`~pipelines.stable_diffusion.TextToVideoSDPipelineOutput`] instead of a\n", + " plain tuple.\n", + " callback (`Callable`, *optional*):\n", + " A function that will be called every `callback_steps` steps during inference. The function will be\n", + " called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`.\n", + " callback_steps (`int`, *optional*, defaults to 1):\n", + " The frequency at which the `callback` function will be called. If not specified, the callback will be\n", + " called at every step.\n", + "\n", + " Returns:\n", + " `List[np.ndarray]`: generated video frames\n", + " \"\"\"\n", + "\n", + " num_images_per_prompt = 1\n", + "\n", + " # 1. Check inputs. Raise error if not correct\n", + " self.check_inputs(\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt,\n", + " prompt_embeds,\n", + " negative_prompt_embeds,\n", + " )\n", + "\n", + " # 2. Define call parameters\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)\n", + " # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`\n", + " # corresponds to doing no classifier free guidance.\n", + " do_classifier_free_guidance = guidance_scale > 1.0\n", + "\n", + " # 3. Encode input prompt\n", + " prompt_embeds = self._encode_prompt(\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt,\n", + " prompt_embeds=prompt_embeds,\n", + " negative_prompt_embeds=negative_prompt_embeds,\n", + " )\n", + "\n", + " # 4. Prepare timesteps\n", + " self.scheduler.set_timesteps(num_inference_steps)\n", + " timesteps = self.scheduler.timesteps\n", + "\n", + " # 5. Prepare latent variables\n", + " num_channels_latents = self.unet_in_channels\n", + " latents = self.prepare_latents(\n", + " batch_size * num_images_per_prompt,\n", + " num_channels_latents,\n", + " prompt_embeds.dtype,\n", + " generator,\n", + " latents,\n", + " )\n", + "\n", + " # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline\n", + " extra_step_kwargs = {\"generator\": generator, \"eta\": eta}\n", + "\n", + " # 7. Denoising loop\n", + " num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order\n", + " with self.progress_bar(total=num_inference_steps) as progress_bar:\n", + " for i, t in enumerate(timesteps):\n", + " # expand the latents if we are doing classifier free guidance\n", + " latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents\n", + " latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)\n", + "\n", + " # predict the noise residual\n", + " noise_pred = self.unet(\n", + " {\n", + " \"sample\": latent_model_input,\n", + " \"timestep\": t,\n", + " \"encoder_hidden_states\": prompt_embeds,\n", + " }\n", + " )[0]\n", + " noise_pred = torch.tensor(noise_pred)\n", + "\n", + " # perform guidance\n", + " if do_classifier_free_guidance:\n", + " noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)\n", + " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + "\n", + " # reshape latents\n", + " bsz, channel, frames, width, height = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + " noise_pred = noise_pred.permute(0, 2, 1, 3, 4).reshape(bsz * frames, channel, width, height)\n", + "\n", + " # compute the previous noisy sample x_t -> x_t-1\n", + " latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample\n", + "\n", + " # reshape latents back\n", + " latents = latents[None, :].reshape(bsz, frames, channel, width, height).permute(0, 2, 1, 3, 4)\n", + "\n", + " # call the callback, if provided\n", + " if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):\n", + " progress_bar.update()\n", + " if callback is not None and i % callback_steps == 0:\n", + " callback(i, t, latents)\n", + "\n", + " video_tensor = self.decode_latents(latents)\n", + "\n", + " if output_type == \"pt\":\n", + " video = video_tensor\n", + " else:\n", + " video = tensor2vid(video_tensor)\n", + "\n", + " if not return_dict:\n", + " return (video,)\n", + "\n", + " return {\"frames\": video}\n", + "\n", + " # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline._encode_prompt\n", + " def _encode_prompt(\n", + " self,\n", + " prompt,\n", + " num_images_per_prompt,\n", + " do_classifier_free_guidance,\n", + " negative_prompt=None,\n", + " prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " negative_prompt_embeds: Optional[torch.FloatTensor] = None,\n", + " ):\n", + " r\"\"\"\n", + " Encodes the prompt into text encoder hidden states.\n", + "\n", + " Args:\n", + " prompt (`str` or `List[str]`, *optional*):\n", + " prompt to be encoded\n", + " num_images_per_prompt (`int`):\n", + " number of images that should be generated per prompt\n", + " do_classifier_free_guidance (`bool`):\n", + " whether to use classifier free guidance or not\n", + " negative_prompt (`str` or `List[str]`, *optional*):\n", + " The prompt or prompts not to guide the image generation. If not defined, one has to pass\n", + " `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is\n", + " less than `1`).\n", + " prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not\n", + " provided, text embeddings will be generated from `prompt` input argument.\n", + " negative_prompt_embeds (`torch.FloatTensor`, *optional*):\n", + " Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt\n", + " weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input\n", + " argument.\n", + " \"\"\"\n", + " if prompt is not None and isinstance(prompt, str):\n", + " batch_size = 1\n", + " elif prompt is not None and isinstance(prompt, list):\n", + " batch_size = len(prompt)\n", + " else:\n", + " batch_size = prompt_embeds.shape[0]\n", + "\n", + " if prompt_embeds is None:\n", + " text_inputs = self.tokenizer(\n", + " prompt,\n", + " padding=\"max_length\",\n", + " max_length=self.tokenizer.model_max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + " text_input_ids = text_inputs.input_ids\n", + " untruncated_ids = self.tokenizer(prompt, padding=\"longest\", return_tensors=\"pt\").input_ids\n", + "\n", + " if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):\n", + " removed_text = self.tokenizer.batch_decode(untruncated_ids[:, self.tokenizer.model_max_length - 1 : -1])\n", + " print(\n", + " \"The following part of your input was truncated because CLIP can only handle sequences up to\"\n", + " f\" {self.tokenizer.model_max_length} tokens: {removed_text}\"\n", + " )\n", + "\n", + " prompt_embeds = self.text_encoder(text_input_ids)\n", + " prompt_embeds = prompt_embeds[0]\n", + " prompt_embeds = torch.tensor(prompt_embeds)\n", + "\n", + " bs_embed, seq_len, _ = prompt_embeds.shape\n", + " # duplicate text embeddings for each generation per prompt, using mps friendly method\n", + " prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # get unconditional embeddings for classifier free guidance\n", + " if do_classifier_free_guidance and negative_prompt_embeds is None:\n", + " uncond_tokens: List[str]\n", + " if negative_prompt is None:\n", + " uncond_tokens = [\"\"] * batch_size\n", + " elif type(prompt) is not type(negative_prompt):\n", + " raise TypeError(f\"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !=\" f\" {type(prompt)}.\")\n", + " elif isinstance(negative_prompt, str):\n", + " uncond_tokens = [negative_prompt]\n", + " elif batch_size != len(negative_prompt):\n", + " raise ValueError(\n", + " f\"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:\"\n", + " f\" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches\"\n", + " \" the batch size of `prompt`.\"\n", + " )\n", + " else:\n", + " uncond_tokens = negative_prompt\n", + "\n", + " max_length = prompt_embeds.shape[1]\n", + " uncond_input = self.tokenizer(\n", + " uncond_tokens,\n", + " padding=\"max_length\",\n", + " max_length=max_length,\n", + " truncation=True,\n", + " return_tensors=\"pt\",\n", + " )\n", + "\n", + " negative_prompt_embeds = self.text_encoder(uncond_input.input_ids)\n", + " negative_prompt_embeds = negative_prompt_embeds[0]\n", + " negative_prompt_embeds = torch.tensor(negative_prompt_embeds)\n", + "\n", + " if do_classifier_free_guidance:\n", + " # duplicate unconditional embeddings for each generation per prompt, using mps friendly method\n", + " seq_len = negative_prompt_embeds.shape[1]\n", + "\n", + " negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)\n", + " negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)\n", + "\n", + " # For classifier free guidance, we need to do two forward passes.\n", + " # Here we concatenate the unconditional and text embeddings into a single batch\n", + " # to avoid doing two forward passes\n", + " prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])\n", + "\n", + " return prompt_embeds\n", + "\n", + " def prepare_latents(\n", + " self,\n", + " batch_size,\n", + " num_channels_latents,\n", + " dtype,\n", + " generator,\n", + " latents=None,\n", + " ):\n", + " shape = (\n", + " batch_size,\n", + " num_channels_latents,\n", + " self.num_frames,\n", + " self.height // self.vae_scale_factor,\n", + " self.width // self.vae_scale_factor,\n", + " )\n", + " if isinstance(generator, list) and len(generator) != batch_size:\n", + " raise ValueError(\n", + " f\"You have passed a list of generators of length {len(generator)}, but requested an effective batch\"\n", + " f\" size of {batch_size}. Make sure the batch size matches the length of the generators.\"\n", + " )\n", + "\n", + " if latents is None:\n", + " latents = randn_tensor(shape, generator=generator, dtype=dtype)\n", + "\n", + " # scale the initial noise by the standard deviation required by the scheduler\n", + " latents = latents * self.scheduler.init_noise_sigma\n", + " return latents\n", + "\n", + " def check_inputs(\n", + " self,\n", + " prompt,\n", + " callback_steps,\n", + " negative_prompt=None,\n", + " prompt_embeds=None,\n", + " negative_prompt_embeds=None,\n", + " ):\n", + " if self.height % 8 != 0 or self.width % 8 != 0:\n", + " raise ValueError(f\"`height` and `width` have to be divisible by 8 but are {self.height} and {self.width}.\")\n", + "\n", + " if (callback_steps is None) or (callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0)):\n", + " raise ValueError(f\"`callback_steps` has to be a positive integer but is {callback_steps} of type\" f\" {type(callback_steps)}.\")\n", + "\n", + " if prompt is not None and prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to\" \" only forward one of the two.\"\n", + " )\n", + " elif prompt is None and prompt_embeds is None:\n", + " raise ValueError(\"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined.\")\n", + " elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):\n", + " raise ValueError(f\"`prompt` has to be of type `str` or `list` but is {type(prompt)}\")\n", + "\n", + " if negative_prompt is not None and negative_prompt_embeds is not None:\n", + " raise ValueError(\n", + " f\"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:\"\n", + " f\" {negative_prompt_embeds}. Please make sure to only forward one of the two.\"\n", + " )\n", + "\n", + " if prompt_embeds is not None and negative_prompt_embeds is not None:\n", + " if prompt_embeds.shape != negative_prompt_embeds.shape:\n", + " raise ValueError(\n", + " \"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but\"\n", + " f\" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`\"\n", + " f\" {negative_prompt_embeds.shape}.\"\n", + " )\n", + "\n", + " def decode_latents(self, latents):\n", + " scale_factor = 0.18215\n", + " latents = 1 / scale_factor * latents\n", + "\n", + " batch_size, channels, num_frames, height, width = latents.shape\n", + " latents = latents.permute(0, 2, 1, 3, 4).reshape(batch_size * num_frames, channels, height, width)\n", + " image = self.vae_decoder(latents)[0]\n", + " image = torch.tensor(image)\n", + " video = (\n", + " image[None, :]\n", + " .reshape(\n", + " (\n", + " batch_size,\n", + " num_frames,\n", + " -1,\n", + " )\n", + " + image.shape[2:]\n", + " )\n", + " .permute(0, 2, 1, 3, 4)\n", + " )\n", + " # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16\n", + " video = video.float()\n", + " return video" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Inference with OpenVINO\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "core = ov.Core()" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "\n", + "from notebook_utils import device_widget\n", + "\n", + "device = device_widget()\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_unet = core.compile_model(unet_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_vae_decoder = core.compile_model(vae_decoder_xml_path, device_name=device.value)" + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ov_text_encoder = core.compile_model(text_encoder_xml, device_name=device.value)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Here we replace the pipeline parts with versions converted to OpenVINO IR and compiled to specific device. Note that we use original pipeline tokenizer and scheduler." + ] + }, + { + "cell_type": "code", + "source": [ + "ov_pipe = OVTextToVideoSDPipeline(ov_vae_decoder, ov_text_encoder, tokenizer, ov_unet, scheduler)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define a prompt\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "prompt = \"A panda eating bamboo on a rock.\"" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Let's generate a video for our prompt. For full list of arguments, see `__call__` function definition of `OVTextToVideoSDPipeline` class in [Build a pipeline](#Build-a-pipeline) section." + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Video generation\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "frames = ov_pipe(prompt, num_inference_steps=25)[\"frames\"]" + ] + }, + { + "cell_type": "code", + "source": [ + "images = [PIL.Image.fromarray(frame) for frame in frames]\n", + "images[0].save(\"output.gif\", save_all=True, append_images=images[1:], duration=125, loop=0)\n", + "with open(\"output.gif\", \"rb\") as gif_file:\n", + " b64 = f\"data:image/gif;base64,{base64.b64encode(gif_file.read()).decode()}\"\n", + "IPython.display.HTML(f'')" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Interactive demo\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "code", + "source": [ + "def generate(prompt, seed, num_inference_steps, _=gr.Progress(track_tqdm=True)):\n", + " generator = torch.Generator().manual_seed(seed)\n", + " frames = ov_pipe(\n", + " prompt,\n", + " num_inference_steps=num_inference_steps,\n", + " generator=generator,\n", + " )[\"frames\"]\n", + " out_file = tempfile.NamedTemporaryFile(suffix=\".gif\", delete=False)\n", + " images = [PIL.Image.fromarray(frame) for frame in frames]\n", + " images[0].save(out_file, save_all=True, append_images=images[1:], duration=125, loop=0)\n", + " return out_file.name\n", + "\n", + "\n", + "demo = gr.Interface(\n", + " generate,\n", + " [\n", + " gr.Textbox(label=\"Prompt\"),\n", + " gr.Slider(0, 1000000, value=42, label=\"Seed\", step=1),\n", + " gr.Slider(10, 50, value=25, label=\"Number of inference steps\", step=1),\n", + " ],\n", + " gr.Image(label=\"Result\"),\n", + " examples=[\n", + " [\"An astronaut riding a horse.\", 0, 25],\n", + " [\"A panda eating bamboo on a rock.\", 0, 25],\n", + " [\"Spiderman is surfing.\", 0, 25],\n", + " ],\n", + " allow_flagging=\"never\",\n", + ")\n", + "\n", + "try:\n", + " demo.queue().launch(debug=True)\n", + "except Exception:\n", + " demo.queue().launch(share=True, debug=True)\n", + "# if you are launching remotely, specify server_name and server_port\n", + "# demo.launch(server_name='your server name', server_port='server port in int')\n", + "# Read more in the docs: https://gradio.app/docs/" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 18 }, + { modified: 19, original: 19 }, + { modified: 20, original: 20 }, + { modified: 21, original: 21 }, + { modified: 22, original: 22 }, + { modified: 23, original: 23 }, + { modified: 24, original: 24 }, + { modified: 25, original: 25 }, + { modified: 26, original: 26 }, + { modified: 27, original: 27 }, + { modified: 28, original: 28 }, + { modified: 29, original: 29 }, + { modified: 30, original: 30 }, + { modified: 31, original: 31 }, + { modified: 32, original: 32 }, + { modified: 33, original: 33 }, + { modified: 34, original: 34 }, + { modified: 35, original: 35 }, + { modified: 36, original: 36 }, + { modified: 37, original: 37 }, + { modified: 38, original: 38 }, + { modified: 39, original: 39 }, + { modified: 40, original: 40 }, + { modified: 41, original: 41 }, + { modified: 42, original: 42 }, + { modified: 43, original: 43 }, + { modified: 44, original: 44 }, + { modified: 45, original: 45 }, + { modified: 46, original: 46 }, + { modified: 47, original: 47 }, + { modified: 48, original: 48 }, + { modified: 49, original: 49 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 6, originalLength: 1, modifiedStart: 6, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 15, originalLength: 1, modifiedStart: 15, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 36, originalLength: 1, modifiedStart: 36, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 50, originalLength: 0, modifiedStart: 50, modifiedLength: 2 } satisfies IDiffChange, + ]); + + }); + test('Modification of multiple cells', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "id": "a2e7d62b-5779-4211-822c-457c77321f8b", + "source": [ + "# Convert and Optimize YOLOv11 instance segmentation model with OpenVINO™\n", + "\n", + "Instance segmentation goes a step further than object detection and involves identifying individual objects in an image and segmenting them from the rest of the image. Instance segmentation as an object detection are often used as key components in computer vision systems. \n", + "Applications that use real-time instance segmentation models include video analytics, robotics, autonomous vehicles, multi-object tracking and object counting, medical image analysis, and many others.\n", + "\n", + "\n", + "This tutorial demonstrates step-by-step instructions on how to run and optimize PyTorch YOLOv11 with OpenVINO. We consider the steps required for instance segmentation scenario. You can find more details about model on [model page](https://docs.ultralytics.com/models/yolo11/) in Ultralytics documentation.\n", + "\n", + "The tutorial consists of the following steps:\n", + "- Prepare the PyTorch model.\n", + "- Download and prepare a dataset.\n", + "- Validate the original model.\n", + "- Convert the PyTorch model to OpenVINO IR.\n", + "- Validate the converted model.\n", + "- Prepare and run optimization pipeline.\n", + "- Compare performance of the FP32 and quantized models.\n", + "- Compare accuracy of the FP32 and quantized models.\n", + "- Live demo\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Get PyTorch model](#Get-PyTorch-model)\n", + " - [Prerequisites](#Prerequisites)\n", + "- [Instantiate model](#Instantiate-model)\n", + " - [Convert model to OpenVINO IR](#Convert-model-to-OpenVINO-IR)\n", + " - [Verify model inference](#Verify-model-inference)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Test on single image](#Test-on-single-image)\n", + "- [Optimize model using NNCF Post-training Quantization API](#Optimize-model-using-NNCF-Post-training-Quantization-API)\n", + " - [Validate Quantized model inference](#Validate-Quantized-model-inference)\n", + "- [Compare the Original and Quantized Models](#Compare-the-Original-and-Quantized-Models)\n", + " - [Compare performance of the Original and Quantized Models](#Compare-performance-of-the-Original-and-Quantized-Models)\n", + "- [Other ways to optimize model](#Other-ways-to-optimize-model)\n", + "- [Live demo](#Live-demo)\n", + " - [Run Live Object Detection and Segmentation](#Run-Live-Object-Detection-and-Segmentation)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "d7a12678-b12f-48d1-9735-398855733e46", + "source": [ + "## Get PyTorch model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Generally, PyTorch models represent an instance of the [`torch.nn.Module`](https://pytorch.org/docs/stable/generated/torch.nn.Module.html) class, initialized by a state dictionary with model weights.\n", + "We will use the YOLOv11 nano model (also known as `yolo11n-seg`) pre-trained on a COCO dataset, which is available in this [repo](https://github.com/ultralytics/ultralytics). Similar steps are also applicable to other YOLOv11 models.\n", + "Typical steps to obtain a pre-trained model:\n", + "1. Create an instance of a model class.\n", + "2. Load a checkpoint state dict, which contains the pre-trained model weights.\n", + "3. Turn the model to evaluation for switching some operations to inference mode.\n", + "\n", + "In this case, the creators of the model provide an API that enables converting the YOLOv11 model to OpenVINO IR. Therefore, we do not need to do these steps manually." + ] + }, + { + "cell_type": "markdown", + "id": "e2267760-cbfe-41c6-958d-cad9f845d5bb", + "source": [ + "#### Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Install necessary packages." + ] + }, + { + "cell_type": "code", + "id": "30d04872-6916-454c-9211-6c644b50dc04", + "source": [ + "%pip install -q \"openvino>=2024.0.0\" \"nncf>=2.9.0\"\n", + "%pip install -q \"torch>=2.1\" \"torchvision>=0.16\" \"ultralytics==8.3.0\" opencv-python tqdm --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "markdown", + "id": "1bbe319c", + "source": [ + "Import required utility functions.\n", + "The lower cell will download the `notebook_utils` Python module from GitHub." + ] + }, + { + "cell_type": "code", + "id": "a2f6cd89", + "source": [ + "from pathlib import Path\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "from notebook_utils import download_file, VideoPlayer, device_widget" + ] + }, + { + "cell_type": "code", + "id": "373658bd-7e64-4479-914e-f2742d330afd", + "source": [ + "# Download a test sample\n", + "IMAGE_PATH = Path(\"./data/coco_bike.jpg\")\n", + "download_file(\n", + " url=\"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_bike.jpg\",\n", + " filename=IMAGE_PATH.name,\n", + " directory=IMAGE_PATH.parent,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ee32fd08-650c-4751-bb41-d8afccb2495e", + "source": [ + "## Instantiate model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "For loading the model, required to specify a path to the model checkpoint. It can be some local path or name available on models hub (in this case model checkpoint will be downloaded automatically). \n", + "You can select model using widget bellow:" + ] + }, + { + "cell_type": "code", + "id": "7c3963a5-b50b-4ffe-b710-ccd1f4a65380", + "source": [ + "import ipywidgets as widgets\n", + "\n", + "model_id = [\n", + " \"yolo11n-seg\",\n", + " \"yolo11s-seg\",\n", + " \"yolo11m-seg\",\n", + " \"yolo11l-seg\",\n", + " \"yolo11x-seg\",\n", + " \"yolov8n-seg\",\n", + " \"yolov8s-seg\",\n", + " \"yolov8m-seg\",\n", + " \"yolov8l-seg\",\n", + " \"yolov8x-seg\",\n", + "]\n", + "\n", + "model_name = widgets.Dropdown(options=model_id, value=model_id[0], description=\"Model\")\n", + "\n", + "model_name" + ] + }, + { + "cell_type": "markdown", + "id": "60105d86-d7e0-47a9-ad88-4fff014ba3d8", + "source": [ + "Making prediction, the model accepts a path to input image and returns list with Results class object. Results contains boxes for object detection model and boxes and masks for segmentation model. Also it contains utilities for processing results, for example, `plot()` method for drawing.\n", + "\n", + "Let us consider the examples:" + ] + }, + { + "cell_type": "code", + "id": "d4994e1e-a3ee-4620-bc8b-237a55c47742", + "source": [ + "from PIL import Image\n", + "from ultralytics import YOLO\n", + "\n", + "SEG_MODEL_NAME = model_name.value\n", + "\n", + "seg_model = YOLO(f\"{SEG_MODEL_NAME}.pt\")\n", + "label_map = seg_model.model.names\n", + "\n", + "res = seg_model(IMAGE_PATH)\n", + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "e345ffcc-c4b8-44ba-8b03-f37e63a060da", + "source": [ + "### Convert model to OpenVINO IR\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Ultralytics provides API for convenient model exporting to different formats including OpenVINO IR. `model.export` is responsible for model conversion. We need to specify the format, and additionally, we can preserve dynamic shapes in the model." + ] + }, + { + "cell_type": "code", + "id": "5c3ef363-f6a1-4fe5-b7ff-eb5a9c0789f1", + "source": [ + "# instance segmentation model\n", + "seg_model_path = Path(f\"{SEG_MODEL_NAME}_openvino_model/{SEG_MODEL_NAME}.xml\")\n", + "if not seg_model_path.exists():\n", + " seg_model.export(format=\"openvino\", dynamic=True, half=True)" + ] + }, + { + "cell_type": "markdown", + "id": "713cd45f-2b19-4a1e-bc9d-5e69bd95d896", + "source": [ + "### Verify model inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We can reuse the base model pipeline for pre- and postprocessing just replacing the inference method where we will use the IR model for inference." + ] + }, + { + "cell_type": "markdown", + "id": "f9b9c472", + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "id": "4e0652b2", + "source": [ + "device = device_widget()\n", + "\n", + "device" + ] + }, + { + "cell_type": "markdown", + "id": "cd163eab-e803-4ae8-96da-22c3bf303630", + "source": [ + "### Test on single image\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "34a65afa-4201-428b-af11-b35e0d206e93", + "source": [ + "import openvino as ov\n", + "\n", + "core = ov.Core()\n", + "seg_ov_model = core.read_model(seg_model_path)\n", + "\n", + "ov_config = {}\n", + "if device.value != \"CPU\":\n", + " seg_ov_model.reshape({0: [1, 3, 640, 640]})\n", + "if \"GPU\" in device.value or (\"AUTO\" in device.value and \"GPU\" in core.available_devices):\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "seg_compiled_model = core.compile_model(seg_ov_model, device.value, ov_config)" + ] + }, + { + "cell_type": "code", + "id": "389a4698-b7fc-4aa2-9833-9681f575d88b", + "source": [ + "seg_model = YOLO(seg_model_path.parent, task=\"segment\")\n", + "\n", + "if seg_model.predictor is None:\n", + " custom = {\"conf\": 0.25, \"batch\": 1, \"save\": False, \"mode\": \"predict\"} # method defaults\n", + " args = {**seg_model.overrides, **custom}\n", + " seg_model.predictor = seg_model._smart_load(\"predictor\")(overrides=args, _callbacks=seg_model.callbacks)\n", + " seg_model.predictor.setup_model(model=seg_model.model)\n", + "\n", + "seg_model.predictor.model.ov_compiled_model = seg_compiled_model" + ] + }, + { + "cell_type": "code", + "id": "12d2522f-e939-4512-b74a-3e20baf1edc8", + "source": [ + "res = seg_model(IMAGE_PATH)\n", + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "7e918cf6-cdce-4e90-a6d7-c435a3c08d93", + "source": [ + "Great! The result is the same, as produced by original models." + ] + }, + { + "cell_type": "markdown", + "id": "13b69e12-2e22-44c1-bfed-fdc88f6c424d", + "source": [ + "## Optimize model using NNCF Post-training Quantization API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "[NNCF](https://github.com/openvinotoolkit/nncf) provides a suite of advanced algorithms for Neural Networks inference optimization in OpenVINO with minimal accuracy drop.\n", + "We will use 8-bit quantization in post-training mode (without the fine-tuning pipeline) to optimize YOLOv11.\n", + "\n", + "The optimization process contains the following steps:\n", + "\n", + "1. Create a Dataset for quantization.\n", + "2. Run `nncf.quantize` for getting an optimized model.\n", + "3. Serialize OpenVINO IR model, using the `openvino.runtime.serialize` function." + ] + }, + { + "cell_type": "markdown", + "id": "7d7eeae8-f3f4-4e95-b1a6-a23085f03ebc", + "source": [ + "Please select below whether you would like to run quantization to improve model inference speed." + ] + }, + { + "cell_type": "code", + "id": "4690b9fc-c49f-4882-b8a5-c82f842a9126", + "source": [ + "import ipywidgets as widgets\n", + "\n", + "int8_model_seg_path = Path(f\"{SEG_MODEL_NAME}_openvino_int8_model/{SEG_MODEL_NAME}.xml\")\n", + "quantized_seg_model = None\n", + "\n", + "to_quantize = widgets.Checkbox(\n", + " value=True,\n", + " description=\"Quantization\",\n", + " disabled=False,\n", + ")\n", + "\n", + "to_quantize" + ] + }, + { + "cell_type": "markdown", + "id": "73cfdbd4-3b65-49e0-b8ab-ceb1d0362481", + "source": [ + "Let's load `skip magic` extension to skip quantization if `to_quantize` is not selected" + ] + }, + { + "cell_type": "code", + "id": "bc6121aa-3a07-4066-a123-fe0ed0e72478", + "source": [ + "# Fetch skip_kernel_extension module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/skip_kernel_extension.py\",\n", + ")\n", + "open(\"skip_kernel_extension.py\", \"w\").write(r.text)\n", + "\n", + "%load_ext skip_kernel_extension" + ] + }, + { + "cell_type": "markdown", + "id": "94fb2515-3645-4798-bc7c-082129ac86c0", + "source": [ + "Reuse validation dataloader in accuracy testing for quantization. \n", + "For that, it should be wrapped into the `nncf.Dataset` object and define a transformation function for getting only input tensors." + ] + }, + { + "cell_type": "code", + "id": "fd994958-6988-4a1d-ac7f-3efbd97135cc", + "source": [ + "# %%skip not $to_quantize.value\n", + "\n", + "\n", + "import nncf\n", + "from typing import Dict\n", + "\n", + "from zipfile import ZipFile\n", + "\n", + "from ultralytics.data.utils import DATASETS_DIR\n", + "from ultralytics.utils import DEFAULT_CFG\n", + "from ultralytics.cfg import get_cfg\n", + "from ultralytics.data.converter import coco80_to_coco91_class\n", + "from ultralytics.data.utils import check_det_dataset\n", + "from ultralytics.utils import ops\n", + "\n", + "\n", + "if not int8_model_seg_path.exists():\n", + " DATA_URL = \"http://images.cocodataset.org/zips/val2017.zip\"\n", + " LABELS_URL = \"https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels-segments.zip\"\n", + " CFG_URL = \"https://raw.githubusercontent.com/ultralytics/ultralytics/v8.1.0/ultralytics/cfg/datasets/coco.yaml\"\n", + "\n", + " OUT_DIR = DATASETS_DIR\n", + "\n", + " DATA_PATH = OUT_DIR / \"val2017.zip\"\n", + " LABELS_PATH = OUT_DIR / \"coco2017labels-segments.zip\"\n", + " CFG_PATH = OUT_DIR / \"coco.yaml\"\n", + "\n", + " download_file(DATA_URL, DATA_PATH.name, DATA_PATH.parent)\n", + " download_file(LABELS_URL, LABELS_PATH.name, LABELS_PATH.parent)\n", + " download_file(CFG_URL, CFG_PATH.name, CFG_PATH.parent)\n", + "\n", + " if not (OUT_DIR / \"coco/labels\").exists():\n", + " with ZipFile(LABELS_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR)\n", + " with ZipFile(DATA_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR / \"coco/images\")\n", + "\n", + " args = get_cfg(cfg=DEFAULT_CFG)\n", + " args.data = str(CFG_PATH)\n", + " seg_validator = seg_model.task_map[seg_model.task][\"validator\"](args=args)\n", + " seg_validator.data = check_det_dataset(args.data)\n", + " seg_validator.stride = 32\n", + " seg_data_loader = seg_validator.get_dataloader(OUT_DIR / \"coco/\", 1)\n", + "\n", + " seg_validator.is_coco = True\n", + " seg_validator.class_map = coco80_to_coco91_class()\n", + " seg_validator.names = label_map\n", + " seg_validator.metrics.names = seg_validator.names\n", + " seg_validator.nc = 80\n", + " seg_validator.nm = 32\n", + " seg_validator.process = ops.process_mask\n", + " seg_validator.plot_masks = []\n", + "\n", + " def transform_fn(data_item: Dict):\n", + " \"\"\"\n", + " Quantization transform function. Extracts and preprocess input data from dataloader item for quantization.\n", + " Parameters:\n", + " data_item: Dict with data item produced by DataLoader during iteration\n", + " Returns:\n", + " input_tensor: Input data for quantization\n", + " \"\"\"\n", + " input_tensor = seg_validator.preprocess(data_item)[\"img\"].numpy()\n", + " return input_tensor\n", + "\n", + " quantization_dataset = nncf.Dataset(seg_data_loader, transform_fn)" + ] + }, + { + "cell_type": "markdown", + "id": "91629284-e261-494d-9464-146ed7190084", + "source": [ + "The `nncf.quantize` function provides an interface for model quantization. It requires an instance of the OpenVINO Model and quantization dataset. \n", + "Optionally, some additional parameters for the configuration quantization process (number of samples for quantization, preset, ignored scope, etc.) can be provided. Ultralytics models contain non-ReLU activation functions, which require asymmetric quantization of activations. To achieve a better result, we will use a `mixed` quantization preset. It provides symmetric quantization of weights and asymmetric quantization of activations. For more accurate results, we should keep the operation in the postprocessing subgraph in floating point precision, using the `ignored_scope` parameter.\n", + "\n", + ">**Note**: Model post-training quantization is time-consuming process. Be patient, it can take several minutes depending on your hardware." + ] + }, + { + "cell_type": "code", + "id": "b2e8cec4-d0b3-4da0-b54c-7964e7bcbfe2", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "if not int8_model_seg_path.exists():\n", + " ignored_scope = nncf.IgnoredScope( # post-processing\n", + " subgraphs=[\n", + " nncf.Subgraph(inputs=[f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat\",\n", + " f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_1\",\n", + " f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_2\",\n", + " f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_7\"],\n", + " outputs=[f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_8\"])\n", + " ]\n", + " )\n", + "\n", + " # Segmentation model\n", + " quantized_seg_model = nncf.quantize(\n", + " seg_ov_model,\n", + " quantization_dataset,\n", + " preset=nncf.QuantizationPreset.MIXED,\n", + " ignored_scope=ignored_scope\n", + " )\n", + "\n", + " print(f\"Quantized segmentation model will be saved to {int8_model_seg_path}\")\n", + " ov.save_model(quantized_seg_model, str(int8_model_seg_path))" + ] + }, + { + "cell_type": "markdown", + "id": "c8c92a68-3eb8-4ea5-9e35-d496f45b3cc3", + "source": [ + "### Validate Quantized model inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "`nncf.quantize` returns the OpenVINO Model class instance, which is suitable for loading on a device for making predictions. `INT8` model input data and output result formats have no difference from the floating point model representation. Therefore, we can reuse the same `detect` function defined above for getting the `INT8` model result on the image." + ] + }, + { + "cell_type": "code", + "id": "4003a3ae", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "id": "711200ec-2f26-47cc-b62d-3802961d5711", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "if quantized_seg_model is None:\n", + " quantized_seg_model = core.read_model(int8_model_seg_path)\n", + "\n", + "ov_config = {}\n", + "if device.value != \"CPU\":\n", + " quantized_seg_model.reshape({0: [1, 3, 640, 640]})\n", + "if \"GPU\" in device.value or (\"AUTO\" in device.value and \"GPU\" in core.available_devices):\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "\n", + "quantized_seg_compiled_model = core.compile_model(quantized_seg_model, device.value, ov_config)" + ] + }, + { + "cell_type": "code", + "id": "b2e3ea3b-5935-4474-9f08-f51fc688b9c0", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "\n", + "if seg_model.predictor is None:\n", + " custom = {\"conf\": 0.25, \"batch\": 1, \"save\": False, \"mode\": \"predict\"} # method defaults\n", + " args = {**seg_model.overrides, **custom}\n", + " seg_model.predictor = seg_model._smart_load(\"predictor\")(overrides=args, _callbacks=seg_model.callbacks)\n", + " seg_model.predictor.setup_model(model=seg_model.model)\n", + "\n", + "seg_model.predictor.model.ov_compiled_model = quantized_seg_compiled_model" + ] + }, + { + "cell_type": "code", + "id": "bef2ed5d-5762-41a6-af2e-ee4f9fd56557", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "res = seg_model(IMAGE_PATH)\n", + "display(Image.fromarray(res[0].plot()[:, :, ::-1]))" + ] + }, + { + "cell_type": "markdown", + "id": "b289f057", + "source": [ + "## Compare the Original and Quantized Models\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "cell_type": "markdown", + "id": "59081cae-9c92-46f0-8117-ed8c6fa6ff19", + "source": [ + "### Compare performance of the Original and Quantized Models\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "Finally, use the OpenVINO [Benchmark Tool](https://docs.openvino.ai/2024/learn-openvino/openvino-samples/benchmark-tool.html) to measure the inference performance of the `FP32` and `INT8` models.\n", + "\n", + "> **Note**: For more accurate performance, it is recommended to run `benchmark_app` in a terminal/command prompt after closing other applications. Run `benchmark_app -m -d CPU -shape \"\"` to benchmark async inference on CPU on specific input data shape for one minute. Change `CPU` to `GPU` to benchmark on GPU. Run `benchmark_app --help` to see an overview of all command-line options." + ] + }, + { + "cell_type": "code", + "id": "b8677df4", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "id": "546828d2", + "source": [ + "if int8_model_seg_path.exists():\n", + " !benchmark_app -m $seg_model_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "code", + "id": "1d43554b", + "source": [ + "if int8_model_seg_path.exists():\n", + " !benchmark_app -m $int8_model_seg_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "markdown", + "id": "184bcebd-588d-4b4d-a60a-0fa753a07ef9", + "source": [ + "## Other ways to optimize model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The performance could be also improved by another OpenVINO method such as async inference pipeline or preprocessing API.\n", + "\n", + "Async Inference pipeline help to utilize the device more optimal. The key advantage of the Async API is that when a device is busy with inference, the application can perform other tasks in parallel (for example, populating inputs or scheduling other requests) rather than wait for the current inference to complete first. To understand how to perform async inference using openvino, refer to [Async API tutorial](../async-api/async-api.ipynb)\n", + "\n", + "Preprocessing API enables making preprocessing a part of the model reducing application code and dependency on additional image processing libraries. \n", + "The main advantage of Preprocessing API is that preprocessing steps will be integrated into the execution graph and will be performed on a selected device (CPU/GPU etc.) rather than always being executed on CPU as part of an application. This will also improve selected device utilization. For more information, refer to the overview of [Preprocessing API tutorial](../optimize-preprocessing/optimize-preprocessing.ipynb). To see, how it could be used with YOLOV8 object detection model, please, see [Convert and Optimize YOLOv8 real-time object detection with OpenVINO tutorial](./yolov8-object-detection.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "2e3b4862-c182-4ce4-a473-9f38d98deab8", + "source": [ + "## Live demo\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The following code runs model inference on a video:" + ] + }, + { + "cell_type": "code", + "id": "4c7bb92e-e301-45a9-b5ff-f7953fad298f", + "source": [ + "import collections\n", + "import time\n", + "import cv2\n", + "from IPython import display\n", + "import numpy as np\n", + "\n", + "\n", + "def run_instance_segmentation(\n", + " source=0,\n", + " flip=False,\n", + " use_popup=False,\n", + " skip_first_frames=0,\n", + " model=seg_model,\n", + " device=device.value,\n", + "):\n", + " player = None\n", + "\n", + " ov_config = {}\n", + " if device != \"CPU\":\n", + " model.reshape({0: [1, 3, 640, 640]})\n", + " if \"GPU\" in device or (\"AUTO\" in device and \"GPU\" in core.available_devices):\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + " compiled_model = core.compile_model(model, device, ov_config)\n", + "\n", + " if seg_model.predictor is None:\n", + " custom = {\"conf\": 0.25, \"batch\": 1, \"save\": False, \"mode\": \"predict\"} # method defaults\n", + " args = {**seg_model.overrides, **custom}\n", + " seg_model.predictor = seg_model._smart_load(\"predictor\")(overrides=args, _callbacks=seg_model.callbacks)\n", + " seg_model.predictor.setup_model(model=seg_model.model)\n", + "\n", + " seg_model.predictor.model.ov_compiled_model = compiled_model\n", + "\n", + " try:\n", + " # Create a video player to play with target fps.\n", + " player = VideoPlayer(source=source, flip=flip, fps=30, skip_first_frames=skip_first_frames)\n", + " # Start capturing.\n", + " player.start()\n", + " if use_popup:\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(winname=title, flags=cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n", + "\n", + " processing_times = collections.deque()\n", + " while True:\n", + " # Grab the frame.\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + " # If the frame is larger than full HD, reduce size to improve the performance.\n", + " scale = 1280 / max(frame.shape)\n", + " if scale < 1:\n", + " frame = cv2.resize(\n", + " src=frame,\n", + " dsize=None,\n", + " fx=scale,\n", + " fy=scale,\n", + " interpolation=cv2.INTER_AREA,\n", + " )\n", + " # Get the results.\n", + " input_image = np.array(frame)\n", + "\n", + " start_time = time.time()\n", + " detections = seg_model(input_image)\n", + " stop_time = time.time()\n", + " frame = detections[0].plot()\n", + "\n", + " processing_times.append(stop_time - start_time)\n", + " # Use processing times from last 200 frames.\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " _, f_width = frame.shape[:2]\n", + " # Mean processing time [ms].\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + " cv2.putText(\n", + " img=frame,\n", + " text=f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " org=(20, 40),\n", + " fontFace=cv2.FONT_HERSHEY_COMPLEX,\n", + " fontScale=f_width / 1000,\n", + " color=(0, 0, 255),\n", + " thickness=1,\n", + " lineType=cv2.LINE_AA,\n", + " )\n", + " # Use this workaround if there is flickering.\n", + " if use_popup:\n", + " cv2.imshow(winname=title, mat=frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # Encode numpy array to jpg.\n", + " _, encoded_img = cv2.imencode(ext=\".jpg\", img=frame, params=[cv2.IMWRITE_JPEG_QUALITY, 100])\n", + " # Create an IPython image.\n", + " i = display.Image(data=encoded_img)\n", + " # Display the image in this notebook.\n", + " display.clear_output(wait=True)\n", + " display.display(i)\n", + " # ctrl-c\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " # any different error\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " if player is not None:\n", + " # Stop capturing.\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()" + ] + }, + { + "cell_type": "markdown", + "id": "cc5b4ba6-f478-4417-b09d-93fee5adca41", + "source": [ + "### Run Live Object Detection and Segmentation\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Use a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + ">**NOTE**: To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a remote server (for example, in Binder or Google Colab service), the webcam will not work. By default, the lower cell will run model inference on a video file. If you want to try live inference on your webcam set `WEBCAM_INFERENCE = True`" + ] + }, + { + "cell_type": "code", + "id": "90708017", + "source": [ + "WEBCAM_INFERENCE = False\n", + "\n", + "if WEBCAM_INFERENCE:\n", + " VIDEO_SOURCE = 0 # Webcam\n", + "else:\n", + " VIDEO_SOURCE = \"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/people.mp4\"" + ] + }, + { + "cell_type": "code", + "id": "e8f1239c", + "source": [ + "device" + ] + }, + { + "cell_type": "code", + "id": "440f2d0e", + "source": [ + "run_instance_segmentation(\n", + " source=VIDEO_SOURCE,\n", + " flip=True,\n", + " use_popup=False,\n", + " model=seg_ov_model,\n", + " device=device.value,\n", + ")" + ] + } + ] + .map(fromJupyterCell) + , + [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "a2e7d62b-5779-4211-822c-457c77321f8b", + "metadata": {}, + "source": [ + "# Convert and Optimize YOLOv11 instance segmentation model with OpenVINO™\n", + "\n", + "Instance segmentation goes a step further than object detection and involves identifying individual objects in an image and segmenting them from the rest of the image. Instance segmentation as an object detection are often used as key components in computer vision systems. \n", + "Applications that use real-time instance segmentation models include video analytics, robotics, autonomous vehicles, multi-object tracking and object counting, medical image analysis, and many others.\n", + "\n", + "\n", + "This tutorial demonstrates step-by-step instructions on how to run and optimize PyTorch YOLOv11 with OpenVINO. We consider the steps required for instance segmentation scenario. You can find more details about model on [model page](https://docs.ultralytics.com/models/yolo11/) in Ultralytics documentation.\n", + "\n", + "The tutorial consists of the following steps:\n", + "- Prepare the PyTorch model.\n", + "- Download and prepare a dataset.\n", + "- Validate the original model.\n", + "- Convert the PyTorch model to OpenVINO IR.\n", + "- Validate the converted model.\n", + "- Prepare and run optimization pipeline.\n", + "- Compare performance of the FP32 and quantized models.\n", + "- Compare accuracy of the FP32 and quantized models.\n", + "- Live demo\n", + "\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Get PyTorch model](#Get-PyTorch-model)\n", + " - [Prerequisites](#Prerequisites)\n", + "- [Instantiate model](#Instantiate-model)\n", + " - [Convert model to OpenVINO IR](#Convert-model-to-OpenVINO-IR)\n", + " - [Verify model inference](#Verify-model-inference)\n", + " - [Select inference device](#Select-inference-device)\n", + " - [Test on single image](#Test-on-single-image)\n", + "- [Optimize model using NNCF Post-training Quantization API](#Optimize-model-using-NNCF-Post-training-Quantization-API)\n", + " - [Validate Quantized model inference](#Validate-Quantized-model-inference)\n", + "- [Compare the Original and Quantized Models](#Compare-the-Original-and-Quantized-Models)\n", + " - [Compare performance of the Original and Quantized Models](#Compare-performance-of-the-Original-and-Quantized-Models)\n", + "- [Other ways to optimize model](#Other-ways-to-optimize-model)\n", + "- [Live demo](#Live-demo)\n", + " - [Run Live Object Detection and Segmentation](#Run-Live-Object-Detection-and-Segmentation)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d7a12678-b12f-48d1-9735-398855733e46", + "metadata": {}, + "source": [ + "## Get PyTorch model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Generally, PyTorch models represent an instance of the [`torch.nn.Module`](https://pytorch.org/docs/stable/generated/torch.nn.Module.html) class, initialized by a state dictionary with model weights.\n", + "We will use the YOLOv11 nano model (also known as `yolo11n-seg`) pre-trained on a COCO dataset, which is available in this [repo](https://github.com/ultralytics/ultralytics). Similar steps are also applicable to other YOLOv11 models.\n", + "Typical steps to obtain a pre-trained model:\n", + "1. Create an instance of a model class.\n", + "2. Load a checkpoint state dict, which contains the pre-trained model weights.\n", + "3. Turn the model to evaluation for switching some operations to inference mode.\n", + "\n", + "In this case, the creators of the model provide an API that enables converting the YOLOv11 model to OpenVINO IR. Therefore, we do not need to do these steps manually." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e2267760-cbfe-41c6-958d-cad9f845d5bb", + "metadata": {}, + "source": [ + "#### Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Install necessary packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30d04872-6916-454c-9211-6c644b50dc04", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -q \"openvino>=2024.0.0\" \"nncf>=2.9.0\"\n", + "%pip install -q \"torch>=2.1\" \"torchvision>=0.16\" \"ultralytics==8.3.0\" opencv-python tqdm --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "1bbe319c", + "metadata": {}, + "source": [ + "Import required utility functions.\n", + "The lower cell will download the `notebook_utils` Python module from GitHub." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2f6cd89", + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "import requests\n", + "\n", + "if not Path(\"notebook_utils.py\").exists():\n", + " r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + " )\n", + "\n", + " open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "\n", + "\n", + "from notebook_utils import download_file, VideoPlayer, device_widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "373658bd-7e64-4479-914e-f2742d330afd", + "metadata": {}, + "outputs": [], + "source": [ + "# Download a test sample\n", + "IMAGE_PATH = Path(\"./data/coco_bike.jpg\")\n", + "\n", + "if not IMAGE_PATH.exists():\n", + " download_file(\n", + " url=\"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_bike.jpg\",\n", + " filename=IMAGE_PATH.name,\n", + " directory=IMAGE_PATH.parent,\n", + " )" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "ee32fd08-650c-4751-bb41-d8afccb2495e", + "metadata": {}, + "source": [ + "## Instantiate model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "For loading the model, required to specify a path to the model checkpoint. It can be some local path or name available on models hub (in this case model checkpoint will be downloaded automatically). \n", + "You can select model using widget bellow:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c3963a5-b50b-4ffe-b710-ccd1f4a65380", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "model_id = [\n", + " \"yolo11n-seg\",\n", + " \"yolo11s-seg\",\n", + " \"yolo11m-seg\",\n", + " \"yolo11l-seg\",\n", + " \"yolo11x-seg\",\n", + " \"yolov8n-seg\",\n", + " \"yolov8s-seg\",\n", + " \"yolov8m-seg\",\n", + " \"yolov8l-seg\",\n", + " \"yolov8x-seg\",\n", + "]\n", + "\n", + "model_name = widgets.Dropdown(options=model_id, value=model_id[0], description=\"Model\")\n", + "\n", + "model_name" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "60105d86-d7e0-47a9-ad88-4fff014ba3d8", + "metadata": {}, + "source": [ + "Making prediction, the model accepts a path to input image and returns list with Results class object. Results contains boxes for object detection model and boxes and masks for segmentation model. Also it contains utilities for processing results, for example, `plot()` method for drawing.\n", + "\n", + "Let us consider the examples:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4994e1e-a3ee-4620-bc8b-237a55c47742", + "metadata": {}, + "outputs": [], + "source": [ + "from PIL import Image\n", + "from ultralytics import YOLO\n", + "\n", + "SEG_MODEL_NAME = model_name.value\n", + "\n", + "seg_model = YOLO(f\"{SEG_MODEL_NAME}.pt\")\n", + "label_map = seg_model.model.names\n", + "\n", + "res = seg_model(IMAGE_PATH)\n", + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e345ffcc-c4b8-44ba-8b03-f37e63a060da", + "metadata": {}, + "source": [ + "### Convert model to OpenVINO IR\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Ultralytics provides API for convenient model exporting to different formats including OpenVINO IR. `model.export` is responsible for model conversion. We need to specify the format, and additionally, we can preserve dynamic shapes in the model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c3ef363-f6a1-4fe5-b7ff-eb5a9c0789f1", + "metadata": {}, + "outputs": [], + "source": [ + "# instance segmentation model\n", + "seg_model_path = Path(f\"{SEG_MODEL_NAME}_openvino_model/{SEG_MODEL_NAME}.xml\")\n", + "if not seg_model_path.exists():\n", + " seg_model.export(format=\"openvino\", dynamic=True, half=True)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "713cd45f-2b19-4a1e-bc9d-5e69bd95d896", + "metadata": {}, + "source": [ + "### Verify model inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "We can reuse the base model pipeline for pre- and postprocessing just replacing the inference method where we will use the IR model for inference." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f9b9c472", + "metadata": {}, + "source": [ + "### Select inference device\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Select device from dropdown list for running inference using OpenVINO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e0652b2", + "metadata": {}, + "outputs": [], + "source": [ + "device = device_widget()\n", + "\n", + "device" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "cd163eab-e803-4ae8-96da-22c3bf303630", + "metadata": {}, + "source": [ + "### Test on single image\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34a65afa-4201-428b-af11-b35e0d206e93", + "metadata": {}, + "outputs": [], + "source": [ + "import openvino as ov\n", + "\n", + "core = ov.Core()\n", + "seg_ov_model = core.read_model(seg_model_path)\n", + "\n", + "ov_config = {}\n", + "if device.value != \"CPU\":\n", + " seg_ov_model.reshape({0: [1, 3, 640, 640]})\n", + "if \"GPU\" in device.value or (\"AUTO\" in device.value and \"GPU\" in core.available_devices):\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "seg_compiled_model = core.compile_model(seg_ov_model, device.value, ov_config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "389a4698-b7fc-4aa2-9833-9681f575d88b", + "metadata": {}, + "outputs": [], + "source": [ + "seg_model = YOLO(seg_model_path.parent, task=\"segment\")\n", + "\n", + "if seg_model.predictor is None:\n", + " custom = {\"conf\": 0.25, \"batch\": 1, \"save\": False, \"mode\": \"predict\"} # method defaults\n", + " args = {**seg_model.overrides, **custom}\n", + " seg_model.predictor = seg_model._smart_load(\"predictor\")(overrides=args, _callbacks=seg_model.callbacks)\n", + " seg_model.predictor.setup_model(model=seg_model.model)\n", + "\n", + "seg_model.predictor.model.ov_compiled_model = seg_compiled_model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12d2522f-e939-4512-b74a-3e20baf1edc8", + "metadata": {}, + "outputs": [], + "source": [ + "res = seg_model(IMAGE_PATH)\n", + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "7e918cf6-cdce-4e90-a6d7-c435a3c08d93", + "metadata": {}, + "source": [ + "Great! The result is the same, as produced by original models." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "13b69e12-2e22-44c1-bfed-fdc88f6c424d", + "metadata": {}, + "source": [ + "## Optimize model using NNCF Post-training Quantization API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "[NNCF](https://github.com/openvinotoolkit/nncf) provides a suite of advanced algorithms for Neural Networks inference optimization in OpenVINO with minimal accuracy drop.\n", + "We will use 8-bit quantization in post-training mode (without the fine-tuning pipeline) to optimize YOLOv11.\n", + "\n", + "The optimization process contains the following steps:\n", + "\n", + "1. Create a Dataset for quantization.\n", + "2. Run `nncf.quantize` for getting an optimized model.\n", + "3. Serialize OpenVINO IR model, using the `openvino.runtime.serialize` function." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "7d7eeae8-f3f4-4e95-b1a6-a23085f03ebc", + "metadata": {}, + "source": [ + "Please select below whether you would like to run quantization to improve model inference speed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4690b9fc-c49f-4882-b8a5-c82f842a9126", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "int8_model_seg_path = Path(f\"{SEG_MODEL_NAME}_openvino_int8_model/{SEG_MODEL_NAME}.xml\")\n", + "quantized_seg_model = None\n", + "\n", + "to_quantize = widgets.Checkbox(\n", + " value=True,\n", + " description=\"Quantization\",\n", + " disabled=False,\n", + ")\n", + "\n", + "to_quantize" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "73cfdbd4-3b65-49e0-b8ab-ceb1d0362481", + "metadata": {}, + "source": [ + "Let's load `skip magic` extension to skip quantization if `to_quantize` is not selected" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc6121aa-3a07-4066-a123-fe0ed0e72478", + "metadata": {}, + "outputs": [], + "source": [ + "# Fetch skip_kernel_extension module\n", + "import requests\n", + "\n", + "\n", + "if not Path(\"skip_kernel_extension.py\").exists():\n", + " r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/skip_kernel_extension.py\",\n", + " )\n", + " open(\"skip_kernel_extension.py\", \"w\").write(r.text)\n", + "\n", + "%load_ext skip_kernel_extension" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "94fb2515-3645-4798-bc7c-082129ac86c0", + "metadata": {}, + "source": [ + "Reuse validation dataloader in accuracy testing for quantization. \n", + "For that, it should be wrapped into the `nncf.Dataset` object and define a transformation function for getting only input tensors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd994958-6988-4a1d-ac7f-3efbd97135cc", + "metadata": {}, + "outputs": [], + "source": [ + "# %%skip not $to_quantize.value\n", + "\n", + "\n", + "import nncf\n", + "from typing import Dict\n", + "\n", + "from zipfile import ZipFile\n", + "\n", + "from ultralytics.data.utils import DATASETS_DIR\n", + "from ultralytics.utils import DEFAULT_CFG\n", + "from ultralytics.cfg import get_cfg\n", + "from ultralytics.data.converter import coco80_to_coco91_class\n", + "from ultralytics.data.utils import check_det_dataset\n", + "from ultralytics.utils import ops\n", + "\n", + "\n", + "if not int8_model_seg_path.exists():\n", + " DATA_URL = \"http://images.cocodataset.org/zips/val2017.zip\"\n", + " LABELS_URL = \"https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels-segments.zip\"\n", + " CFG_URL = \"https://raw.githubusercontent.com/ultralytics/ultralytics/v8.1.0/ultralytics/cfg/datasets/coco.yaml\"\n", + "\n", + " OUT_DIR = DATASETS_DIR\n", + "\n", + " DATA_PATH = OUT_DIR / \"val2017.zip\"\n", + " LABELS_PATH = OUT_DIR / \"coco2017labels-segments.zip\"\n", + " CFG_PATH = OUT_DIR / \"coco.yaml\"\n", + "\n", + " download_file(DATA_URL, DATA_PATH.name, DATA_PATH.parent)\n", + " download_file(LABELS_URL, LABELS_PATH.name, LABELS_PATH.parent)\n", + " download_file(CFG_URL, CFG_PATH.name, CFG_PATH.parent)\n", + "\n", + " if not (OUT_DIR / \"coco/labels\").exists():\n", + " with ZipFile(LABELS_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR)\n", + " with ZipFile(DATA_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR / \"coco/images\")\n", + "\n", + " args = get_cfg(cfg=DEFAULT_CFG)\n", + " args.data = str(CFG_PATH)\n", + " seg_validator = seg_model.task_map[seg_model.task][\"validator\"](args=args)\n", + " seg_validator.data = check_det_dataset(args.data)\n", + " seg_validator.stride = 32\n", + " seg_data_loader = seg_validator.get_dataloader(OUT_DIR / \"coco/\", 1)\n", + "\n", + " seg_validator.is_coco = True\n", + " seg_validator.class_map = coco80_to_coco91_class()\n", + " seg_validator.names = label_map\n", + " seg_validator.metrics.names = seg_validator.names\n", + " seg_validator.nc = 80\n", + " seg_validator.nm = 32\n", + " seg_validator.process = ops.process_mask\n", + " seg_validator.plot_masks = []\n", + "\n", + " def transform_fn(data_item: Dict):\n", + " \"\"\"\n", + " Quantization transform function. Extracts and preprocess input data from dataloader item for quantization.\n", + " Parameters:\n", + " data_item: Dict with data item produced by DataLoader during iteration\n", + " Returns:\n", + " input_tensor: Input data for quantization\n", + " \"\"\"\n", + " input_tensor = seg_validator.preprocess(data_item)[\"img\"].numpy()\n", + " return input_tensor\n", + "\n", + " quantization_dataset = nncf.Dataset(seg_data_loader, transform_fn)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "91629284-e261-494d-9464-146ed7190084", + "metadata": {}, + "source": [ + "The `nncf.quantize` function provides an interface for model quantization. It requires an instance of the OpenVINO Model and quantization dataset. \n", + "Optionally, some additional parameters for the configuration quantization process (number of samples for quantization, preset, ignored scope, etc.) can be provided. Ultralytics models contain non-ReLU activation functions, which require asymmetric quantization of activations. To achieve a better result, we will use a `mixed` quantization preset. It provides symmetric quantization of weights and asymmetric quantization of activations. For more accurate results, we should keep the operation in the postprocessing subgraph in floating point precision, using the `ignored_scope` parameter.\n", + "\n", + ">**Note**: Model post-training quantization is time-consuming process. Be patient, it can take several minutes depending on your hardware." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e8cec4-d0b3-4da0-b54c-7964e7bcbfe2", + "metadata": { + "test_replace": { + "ignored_scope=ignored_scope\n": "ignored_scope=ignored_scope, subset_size=10\n" + } + }, + "outputs": [], + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "if not int8_model_seg_path.exists():\n", + " ignored_scope = nncf.IgnoredScope( # post-processing\n", + " subgraphs=[\n", + " nncf.Subgraph(inputs=[f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat\",\n", + " f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_1\",\n", + " f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_2\",\n", + " f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_7\"],\n", + " outputs=[f\"__module.model.{22 if 'v8' in SEG_MODEL_NAME else 23}/aten::cat/Concat_8\"])\n", + " ]\n", + " )\n", + "\n", + " # Segmentation model\n", + " quantized_seg_model = nncf.quantize(\n", + " seg_ov_model,\n", + " quantization_dataset,\n", + " preset=nncf.QuantizationPreset.MIXED,\n", + " ignored_scope=ignored_scope\n", + " )\n", + "\n", + " print(f\"Quantized segmentation model will be saved to {int8_model_seg_path}\")\n", + " ov.save_model(quantized_seg_model, str(int8_model_seg_path))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c8c92a68-3eb8-4ea5-9e35-d496f45b3cc3", + "metadata": {}, + "source": [ + "### Validate Quantized model inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "`nncf.quantize` returns the OpenVINO Model class instance, which is suitable for loading on a device for making predictions. `INT8` model input data and output result formats have no difference from the floating point model representation. Therefore, we can reuse the same `detect` function defined above for getting the `INT8` model result on the image." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4003a3ae", + "metadata": {}, + "outputs": [], + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "711200ec-2f26-47cc-b62d-3802961d5711", + "metadata": {}, + "outputs": [], + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "if quantized_seg_model is None:\n", + " quantized_seg_model = core.read_model(int8_model_seg_path)\n", + "\n", + "ov_config = {}\n", + "if device.value != \"CPU\":\n", + " quantized_seg_model.reshape({0: [1, 3, 640, 640]})\n", + "if \"GPU\" in device.value or (\"AUTO\" in device.value and \"GPU\" in core.available_devices):\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "\n", + "quantized_seg_compiled_model = core.compile_model(quantized_seg_model, device.value, ov_config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e3ea3b-5935-4474-9f08-f51fc688b9c0", + "metadata": {}, + "outputs": [], + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "\n", + "if seg_model.predictor is None:\n", + " custom = {\"conf\": 0.25, \"batch\": 1, \"save\": False, \"mode\": \"predict\"} # method defaults\n", + " args = {**seg_model.overrides, **custom}\n", + " seg_model.predictor = seg_model._smart_load(\"predictor\")(overrides=args, _callbacks=seg_model.callbacks)\n", + " seg_model.predictor.setup_model(model=seg_model.model)\n", + "\n", + "seg_model.predictor.model.ov_compiled_model = quantized_seg_compiled_model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bef2ed5d-5762-41a6-af2e-ee4f9fd56557", + "metadata": {}, + "outputs": [], + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "res = seg_model(IMAGE_PATH)\n", + "display(Image.fromarray(res[0].plot()[:, :, ::-1]))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "b289f057", + "metadata": {}, + "source": [ + "## Compare the Original and Quantized Models\n", + "[back to top ⬆️](#Table-of-contents:)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "59081cae-9c92-46f0-8117-ed8c6fa6ff19", + "metadata": {}, + "source": [ + "### Compare performance of the Original and Quantized Models\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "Finally, use the OpenVINO [Benchmark Tool](https://docs.openvino.ai/2024/learn-openvino/openvino-samples/benchmark-tool.html) to measure the inference performance of the `FP32` and `INT8` models.\n", + "\n", + "> **Note**: For more accurate performance, it is recommended to run `benchmark_app` in a terminal/command prompt after closing other applications. Run `benchmark_app -m -d CPU -shape \"\"` to benchmark async inference on CPU on specific input data shape for one minute. Change `CPU` to `GPU` to benchmark on GPU. Run `benchmark_app --help` to see an overview of all command-line options." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8677df4", + "metadata": {}, + "outputs": [], + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "546828d2", + "metadata": {}, + "outputs": [], + "source": [ + "if int8_model_seg_path.exists():\n", + " !benchmark_app -m $seg_model_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d43554b", + "metadata": {}, + "outputs": [], + "source": [ + "if int8_model_seg_path.exists():\n", + " !benchmark_app -m $int8_model_seg_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "184bcebd-588d-4b4d-a60a-0fa753a07ef9", + "metadata": {}, + "source": [ + "## Other ways to optimize model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The performance could be also improved by another OpenVINO method such as async inference pipeline or preprocessing API.\n", + "\n", + "Async Inference pipeline help to utilize the device more optimal. The key advantage of the Async API is that when a device is busy with inference, the application can perform other tasks in parallel (for example, populating inputs or scheduling other requests) rather than wait for the current inference to complete first. To understand how to perform async inference using openvino, refer to [Async API tutorial](../async-api/async-api.ipynb)\n", + "\n", + "Preprocessing API enables making preprocessing a part of the model reducing application code and dependency on additional image processing libraries. \n", + "The main advantage of Preprocessing API is that preprocessing steps will be integrated into the execution graph and will be performed on a selected device (CPU/GPU etc.) rather than always being executed on CPU as part of an application. This will also improve selected device utilization. For more information, refer to the overview of [Preprocessing API tutorial](../optimize-preprocessing/optimize-preprocessing.ipynb). To see, how it could be used with YOLOV8 object detection model, please, see [Convert and Optimize YOLOv8 real-time object detection with OpenVINO tutorial](./yolov8-object-detection.ipynb)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2e3b4862-c182-4ce4-a473-9f38d98deab8", + "metadata": { + "tags": [] + }, + "source": [ + "## Live demo\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "The following code runs model inference on a video:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c7bb92e-e301-45a9-b5ff-f7953fad298f", + "metadata": {}, + "outputs": [], + "source": [ + "import collections\n", + "import time\n", + "import cv2\n", + "from IPython import display\n", + "import numpy as np\n", + "\n", + "\n", + "def run_instance_segmentation(\n", + " source=0,\n", + " flip=False,\n", + " use_popup=False,\n", + " skip_first_frames=0,\n", + " model=seg_model,\n", + " device=device.value,\n", + " video_width=1280,\n", + "):\n", + " player = None\n", + "\n", + " ov_config = {}\n", + " if device != \"CPU\":\n", + " model.reshape({0: [1, 3, 640, 640]})\n", + " if \"GPU\" in device or (\"AUTO\" in device and \"GPU\" in core.available_devices):\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + " compiled_model = core.compile_model(model, device, ov_config)\n", + "\n", + " if seg_model.predictor is None:\n", + " custom = {\"conf\": 0.25, \"batch\": 1, \"save\": False, \"mode\": \"predict\"} # method defaults\n", + " args = {**seg_model.overrides, **custom}\n", + " seg_model.predictor = seg_model._smart_load(\"predictor\")(overrides=args, _callbacks=seg_model.callbacks)\n", + " seg_model.predictor.setup_model(model=seg_model.model)\n", + "\n", + " seg_model.predictor.model.ov_compiled_model = compiled_model\n", + "\n", + " try:\n", + " # Create a video player to play with target fps.\n", + " player = VideoPlayer(source=source, flip=flip, fps=30, skip_first_frames=skip_first_frames, width=video_width)\n", + " # Start capturing.\n", + " player.start()\n", + " if use_popup:\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(winname=title, flags=cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n", + "\n", + " processing_times = collections.deque()\n", + " while True:\n", + " # Grab the frame.\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + " # If the frame is larger than video_width, reduce size to improve the performance.\n", + " # If more, increase size for better demo expirience.\n", + " scale = video_width / max(frame.shape)\n", + " frame = cv2.resize(\n", + " src=frame,\n", + " dsize=None,\n", + " fx=scale,\n", + " fy=scale,\n", + " interpolation=cv2.INTER_AREA,\n", + " )\n", + " # Get the results.\n", + " input_image = np.array(frame)\n", + "\n", + " start_time = time.time()\n", + " detections = seg_model(input_image)\n", + " stop_time = time.time()\n", + " frame = detections[0].plot()\n", + "\n", + " processing_times.append(stop_time - start_time)\n", + " # Use processing times from last 200 frames.\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " _, f_width = frame.shape[:2]\n", + " # Mean processing time [ms].\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + " cv2.putText(\n", + " img=frame,\n", + " text=f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " org=(20, 40),\n", + " fontFace=cv2.FONT_HERSHEY_COMPLEX,\n", + " fontScale=f_width / 1000,\n", + " color=(0, 0, 255),\n", + " thickness=1,\n", + " lineType=cv2.LINE_AA,\n", + " )\n", + " # Use this workaround if there is flickering.\n", + " if use_popup:\n", + " cv2.imshow(winname=title, mat=frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # Encode numpy array to jpg.\n", + " _, encoded_img = cv2.imencode(ext=\".jpg\", img=frame, params=[cv2.IMWRITE_JPEG_QUALITY, 100])\n", + " # Create an IPython image.\n", + " i = display.Image(data=encoded_img)\n", + " # Display the image in this notebook.\n", + " display.clear_output(wait=True)\n", + " display.display(i)\n", + " # ctrl-c\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " # any different error\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " if player is not None:\n", + " # Stop capturing.\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "cc5b4ba6-f478-4417-b09d-93fee5adca41", + "metadata": {}, + "source": [ + "### Run Live Object Detection and Segmentation\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "Use a webcam as the video input. By default, the primary webcam is set with `source=0`. If you have multiple webcams, each one will be assigned a consecutive number starting at 0. Set `flip=True` when using a front-facing camera. Some web browsers, especially Mozilla Firefox, may cause flickering. If you experience flickering, set `use_popup=True`.\n", + "\n", + ">**NOTE**: To use this notebook with a webcam, you need to run the notebook on a computer with a webcam. If you run the notebook on a remote server (for example, in Binder or Google Colab service), the webcam will not work. By default, the lower cell will run model inference on a video file. If you want to try live inference on your webcam set `WEBCAM_INFERENCE = True`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90708017", + "metadata": {}, + "outputs": [], + "source": [ + "WEBCAM_INFERENCE = False\n", + "\n", + "if WEBCAM_INFERENCE:\n", + " VIDEO_SOURCE = 0 # Webcam\n", + "else:\n", + " if not Path(\"people.mp4\").exists():\n", + " download_file(\n", + " \"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/people.mp4\",\n", + " \"people.mp4\",\n", + " )\n", + " VIDEO_SOURCE = \"people.mp4\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8f1239c", + "metadata": {}, + "outputs": [], + "source": [ + "device" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "440f2d0e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "run_instance_segmentation(\n", + " source=VIDEO_SOURCE,\n", + " flip=True,\n", + " use_popup=False,\n", + " model=seg_ov_model,\n", + " device=device.value,\n", + " video_width=1280,\n", + ")" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 18 }, + { modified: 19, original: 19 }, + { modified: 20, original: 20 }, + { modified: 21, original: 21 }, + { modified: 22, original: 22 }, + { modified: 23, original: 23 }, + { modified: 24, original: 24 }, + { modified: 25, original: 25 }, + { modified: 26, original: 26 }, + { modified: 27, original: 27 }, + { modified: 28, original: 28 }, + { modified: 29, original: 29 }, + { modified: 30, original: 30 }, + { modified: 31, original: 31 }, + { modified: 32, original: 32 }, + { modified: 33, original: 33 }, + { modified: 34, original: 34 }, + { modified: 35, original: 35 }, + { modified: 36, original: 36 }, + { modified: 37, original: 37 }, + { modified: 38, original: 38 }, + { modified: 39, original: 39 }, + { modified: 40, original: 40 }, + { modified: 41, original: 41 }, + { modified: 42, original: 42 }, + { modified: 43, original: 43 }, + { modified: 44, original: 44 }, + { modified: 45, original: 45 }, + { modified: 46, original: 46 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 5, originalLength: 1, modifiedStart: 5, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 6, originalLength: 1, modifiedStart: 6, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 25, originalLength: 1, modifiedStart: 25, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 42, originalLength: 1, modifiedStart: 42, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 44, originalLength: 1, modifiedStart: 44, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 46, originalLength: 1, modifiedStart: 46, modifiedLength: 1 } satisfies IDiffChange, + ]); + + }); + test('Modification of a large cell and insert a new cell', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "id": "e9d010ff-85ba-4e69-b439-ba89e4a3a715", + "source": [ + "# Convert and Optimize YOLOv10 with OpenVINO\n", + "\n", + "Real-time object detection aims to accurately predict object categories and positions in images with low latency. The YOLO series has been at the forefront of this research due to its balance between performance and efficiency. However, reliance on NMS and architectural inefficiencies have hindered optimal performance. YOLOv10 addresses these issues by introducing consistent dual assignments for NMS-free training and a holistic efficiency-accuracy driven model design strategy.\n", + "\n", + "YOLOv10, built on the [Ultralytics Python package](https://pypi.org/project/ultralytics/) by researchers at [Tsinghua University](https://www.tsinghua.edu.cn/en/), introduces a new approach to real-time object detection, addressing both the post-processing and model architecture deficiencies found in previous YOLO versions. By eliminating non-maximum suppression (NMS) and optimizing various model components, YOLOv10 achieves state-of-the-art performance with significantly reduced computational overhead. Extensive experiments demonstrate its superior accuracy-latency trade-offs across multiple model scales.\n", + "\n", + "![yolov10-approach.png](https://github.com/ultralytics/ultralytics/assets/26833433/f9b1bec0-928e-41ce-a205-e12db3c4929a)\n", + "\n", + "More details about model architecture you can find in original [repo](https://github.com/THU-MIG/yolov10), [paper](https://arxiv.org/abs/2405.14458) and [Ultralytics documentation](https://docs.ultralytics.com/models/yolov10/).\n", + "\n", + "This tutorial demonstrates step-by-step instructions on how to run and optimize PyTorch YOLO V10 with OpenVINO.\n", + "\n", + "The tutorial consists of the following steps:\n", + "\n", + "- Prepare PyTorch model\n", + "- Convert PyTorch model to OpenVINO IR\n", + "- Run model inference with OpenVINO\n", + "- Prepare and run optimization pipeline using NNCF\n", + "- Compare performance of the FP16 and quantized models.\n", + "- Run optimized model inference on video\n", + "- Launch interactive Gradio demo\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Download PyTorch model](#Download-PyTorch-model)\n", + "- [Export PyTorch model to OpenVINO IR Format](#Export-PyTorch-model-to-OpenVINO-IR-Format)\n", + "- [Run OpenVINO Inference on AUTO device using Ultralytics API](#Run-OpenVINO-Inference-on-AUTO-device-using-Ultralytics-API)\n", + "- [Run OpenVINO Inference on selected device using Ultralytics API](#Run-OpenVINO-Inference-on-selected-device-using-Ultralytics-API)\n", + "- [Optimize model using NNCF Post-training Quantization API](#Optimize-model-using-NNCF-Post-training-Quantization-API)\n", + " - [Prepare Quantization Dataset](#Prepare-Quantization-Dataset)\n", + " - [Quantize and Save INT8 model](#Quantize-and-Save-INT8-model)\n", + "- [Run Optimized Model Inference](#Run-Optimized-Model-Inference)\n", + " - [Run Optimized Model on AUTO device](#Run-Optimized-Model-on-AUTO-device)\n", + " - [Run Optimized Model Inference on selected device](#Run-Optimized-Model-Inference-on-selected-device)\n", + "- [Compare the Original and Quantized Models](#Compare-the-Original-and-Quantized-Models)\n", + " - [Model size](#Model-size)\n", + " - [Performance](#Performance)\n", + " - [FP16 model performance](#FP16-model-performance)\n", + " - [Int8 model performance](#Int8-model-performance)\n", + "- [Live demo](#Live-demo)\n", + " - [Gradio Interactive Demo](#Gradio-Interactive-Demo)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "144e86a1-c220-4e3b-a8b2-8eda443faf2d", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "bf76580b-860a-4dcd-8b01-f494cdffe37e", + "source": [ + "import os\n", + "\n", + "os.environ[\"GIT_CLONE_PROTECTION_ACTIVE\"] = \"false\"\n", + "\n", + "%pip install -q \"nncf>=2.11.0\"\n", + "%pip install -Uq \"openvino>=2024.3.0\"\n", + "%pip install -q \"git+https://github.com/THU-MIG/yolov10.git\" --extra-index-url https://download.pytorch.org/whl/cpu\n", + "%pip install -q \"torch>=2.1\" \"torchvision>=0.16\" tqdm opencv-python \"gradio>=4.19\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "code", + "id": "75772eac-565b-4234-82bf-f305e0c1ceda", + "source": [ + "from pathlib import Path\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "\n", + "from notebook_utils import download_file, VideoPlayer, device_widget" + ] + }, + { + "cell_type": "markdown", + "id": "2888d8b4-44f6-4418-bae4-f433ff28010b", + "source": [ + "## Download PyTorch model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "There are several version of [YOLO V10](https://github.com/THU-MIG/yolov10/tree/main?tab=readme-ov-file#performance) models provided by model authors. Each of them has different characteristics depends on number of training parameters, performance and accuracy. For demonstration purposes we will use `yolov10n`, but the same steps are also applicable to other models in YOLO V10 series." + ] + }, + { + "cell_type": "code", + "id": "aea6315b-6a29-4f2a-96a9-eb3c1646a3a4", + "source": [ + "models_dir = Path(\"./models\")\n", + "models_dir.mkdir(exist_ok=True)" + ] + }, + { + "cell_type": "code", + "id": "d828401e-b6bd-4796-a7b8-681957a159d4", + "source": [ + "model_weights_url = \"https://github.com/jameslahm/yolov10/releases/download/v1.0/yolov10n.pt\"\n", + "file_name = model_weights_url.split(\"/\")[-1]\n", + "model_name = file_name.replace(\".pt\", \"\")\n", + "\n", + "download_file(model_weights_url, directory=models_dir)" + ] + }, + { + "cell_type": "markdown", + "id": "dc21487d-dd79-4b56-b8e2-5b28c8d92ac8", + "source": [ + "## Export PyTorch model to OpenVINO IR Format\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "markdown", + "id": "8cfec0d6-fb55-4611-861b-326e892fcf09", + "source": [ + "As it was discussed before, YOLO V10 code is designed on top of [Ultralytics](https://docs.ultralytics.com/) library and has similar interface with YOLO V8 (You can check [YOLO V8 notebooks](https://github.com/openvinotoolkit/openvino_notebooks/tree/latest/notebooks/yolov8-optimization) for more detailed instruction how to work with Ultralytics API). Ultralytics support OpenVINO model export using [export](https://docs.ultralytics.com/modes/export/) method of model class. Additionally, we can specify parameters responsible for target input size, static or dynamic input shapes and model precision (FP32/FP16/INT8). INT8 quantization can be additionally performed on export stage, but for making approach more flexible, we consider how to perform quantization using [NNCF](https://github.com/openvinotoolkit/nncf)." + ] + }, + { + "cell_type": "code", + "id": "d488f564-c1d5-4c6a-9b4a-353a3ab749e6", + "source": [ + "import types\n", + "from ultralytics.utils import ops, yaml_load, yaml_save\n", + "from ultralytics import YOLOv10\n", + "import torch\n", + "\n", + "detection_labels = {\n", + " 0: \"person\",\n", + " 1: \"bicycle\",\n", + " 2: \"car\",\n", + " 3: \"motorcycle\",\n", + " 4: \"airplane\",\n", + " 5: \"bus\",\n", + " 6: \"train\",\n", + " 7: \"truck\",\n", + " 8: \"boat\",\n", + " 9: \"traffic light\",\n", + " 10: \"fire hydrant\",\n", + " 11: \"stop sign\",\n", + " 12: \"parking meter\",\n", + " 13: \"bench\",\n", + " 14: \"bird\",\n", + " 15: \"cat\",\n", + " 16: \"dog\",\n", + " 17: \"horse\",\n", + " 18: \"sheep\",\n", + " 19: \"cow\",\n", + " 20: \"elephant\",\n", + " 21: \"bear\",\n", + " 22: \"zebra\",\n", + " 23: \"giraffe\",\n", + " 24: \"backpack\",\n", + " 25: \"umbrella\",\n", + " 26: \"handbag\",\n", + " 27: \"tie\",\n", + " 28: \"suitcase\",\n", + " 29: \"frisbee\",\n", + " 30: \"skis\",\n", + " 31: \"snowboard\",\n", + " 32: \"sports ball\",\n", + " 33: \"kite\",\n", + " 34: \"baseball bat\",\n", + " 35: \"baseball glove\",\n", + " 36: \"skateboard\",\n", + " 37: \"surfboard\",\n", + " 38: \"tennis racket\",\n", + " 39: \"bottle\",\n", + " 40: \"wine glass\",\n", + " 41: \"cup\",\n", + " 42: \"fork\",\n", + " 43: \"knife\",\n", + " 44: \"spoon\",\n", + " 45: \"bowl\",\n", + " 46: \"banana\",\n", + " 47: \"apple\",\n", + " 48: \"sandwich\",\n", + " 49: \"orange\",\n", + " 50: \"broccoli\",\n", + " 51: \"carrot\",\n", + " 52: \"hot dog\",\n", + " 53: \"pizza\",\n", + " 54: \"donut\",\n", + " 55: \"cake\",\n", + " 56: \"chair\",\n", + " 57: \"couch\",\n", + " 58: \"potted plant\",\n", + " 59: \"bed\",\n", + " 60: \"dining table\",\n", + " 61: \"toilet\",\n", + " 62: \"tv\",\n", + " 63: \"laptop\",\n", + " 64: \"mouse\",\n", + " 65: \"remote\",\n", + " 66: \"keyboard\",\n", + " 67: \"cell phone\",\n", + " 68: \"microwave\",\n", + " 69: \"oven\",\n", + " 70: \"toaster\",\n", + " 71: \"sink\",\n", + " 72: \"refrigerator\",\n", + " 73: \"book\",\n", + " 74: \"clock\",\n", + " 75: \"vase\",\n", + " 76: \"scissors\",\n", + " 77: \"teddy bear\",\n", + " 78: \"hair drier\",\n", + " 79: \"toothbrush\",\n", + "}\n", + "\n", + "\n", + "def v10_det_head_forward(self, x):\n", + " one2one = self.forward_feat([xi.detach() for xi in x], self.one2one_cv2, self.one2one_cv3)\n", + " if not self.export:\n", + " one2many = super().forward(x)\n", + "\n", + " if not self.training:\n", + " one2one = self.inference(one2one)\n", + " if not self.export:\n", + " return {\"one2many\": one2many, \"one2one\": one2one}\n", + " else:\n", + " assert self.max_det != -1\n", + " boxes, scores, labels = ops.v10postprocess(one2one.permute(0, 2, 1), self.max_det, self.nc)\n", + " return torch.cat(\n", + " [boxes, scores.unsqueeze(-1), labels.unsqueeze(-1).to(boxes.dtype)],\n", + " dim=-1,\n", + " )\n", + " else:\n", + " return {\"one2many\": one2many, \"one2one\": one2one}\n", + "\n", + "\n", + "ov_model_path = models_dir / f\"{model_name}_openvino_model/{model_name}.xml\"\n", + "if not ov_model_path.exists():\n", + " model = YOLOv10(models_dir / file_name)\n", + " model.model.model[-1].forward = types.MethodType(v10_det_head_forward, model.model.model[-1])\n", + " model.export(format=\"openvino\", dynamic=True, half=True)\n", + " config = yaml_load(ov_model_path.parent / \"metadata.yaml\")\n", + " config[\"names\"] = detection_labels\n", + " yaml_save(ov_model_path.parent / \"metadata.yaml\", config)" + ] + }, + { + "cell_type": "markdown", + "id": "8a23f621-f6cd-4bfd-8b98-24b8c0392761", + "source": [ + "## Run OpenVINO Inference on AUTO device using Ultralytics API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "Now, when we exported model to OpenVINO, we can load it directly into YOLOv10 class, where automatic inference backend will provide easy-to-use user experience to run OpenVINO YOLOv10 model on the similar level like for original PyTorch model. The code bellow demonstrates how to run inference OpenVINO exported model with Ultralytics API on single image. [AUTO device](https://github.com/openvinotoolkit/openvino_notebooks/tree/latest/notebooks/auto-device) will be used for launching model." + ] + }, + { + "cell_type": "code", + "id": "7e8ed361-bf09-4398-ba15-e6c5dda73cd3", + "source": [ + "ov_yolo_model = YOLOv10(ov_model_path.parent, task=\"detect\")" + ] + }, + { + "cell_type": "code", + "id": "8a473286-59b8-498f-8541-df84f041e119", + "source": [ + "from PIL import Image\n", + "\n", + "IMAGE_PATH = Path(\"./data/coco_bike.jpg\")\n", + "download_file(\n", + " url=\"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_bike.jpg\",\n", + " filename=IMAGE_PATH.name,\n", + " directory=IMAGE_PATH.parent,\n", + ")" + ] + }, + { + "cell_type": "code", + "id": "7e116e81-0ad4-4b9e-9f5c-58680fd8f0c6", + "source": [ + "res = ov_yolo_model(IMAGE_PATH, iou=0.45, conf=0.2)\n", + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "943b9b04-4b97-4395-99d1-695465de1140", + "source": [ + "## Run OpenVINO Inference on selected device using Ultralytics API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "In this part of notebook you can select inference device for running model inference to compare results with AUTO." + ] + }, + { + "cell_type": "code", + "id": "41655bec-b006-4376-abbb-d6c8c09e89fb", + "source": [ + "device = device_widget(\"CPU\")\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "id": "f284d5fd-12ba-4c08-afce-d8131ef7ad4d", + "source": [ + "import openvino as ov\n", + "\n", + "core = ov.Core()\n", + "\n", + "ov_model = core.read_model(ov_model_path)\n", + "\n", + "# load model on selected device\n", + "if \"GPU\" in device.value or \"NPU\" in device.value:\n", + " ov_model.reshape({0: [1, 3, 640, 640]})\n", + "ov_config = {}\n", + "if \"GPU\" in device.value:\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "det_compiled_model = core.compile_model(ov_model, device.value, ov_config)" + ] + }, + { + "cell_type": "code", + "id": "12164d21-948a-4ef7-9e1c-b0d41c53cf91", + "source": [ + "ov_yolo_model.predictor.model.ov_compiled_model = det_compiled_model" + ] + }, + { + "cell_type": "code", + "id": "ecb9d396-ab1e-4398-a110-18c08b00ab14", + "source": [ + "res = ov_yolo_model(IMAGE_PATH, iou=0.45, conf=0.2)" + ] + }, + { + "cell_type": "code", + "id": "a4abf758-3a72-4ee0-afde-38076602f1c7", + "source": [ + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "992b6ac3-4248-4848-8fd6-f9c1b9fc2051", + "source": [ + "## Optimize model using NNCF Post-training Quantization API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "[NNCF](https://github.com/openvinotoolkit/nncf) provides a suite of advanced algorithms for Neural Networks inference optimization in OpenVINO with minimal accuracy drop.\n", + "We will use 8-bit quantization in post-training mode (without the fine-tuning pipeline) to optimize YOLOv10.\n", + "\n", + "The optimization process contains the following steps:\n", + "\n", + "1. Create a Dataset for quantization.\n", + "2. Run `nncf.quantize` for getting an optimized model.\n", + "3. Serialize OpenVINO IR model, using the `openvino.save_model` function.\n", + "\n", + "Quantization is time and memory consuming process, you can skip this step using checkbox bellow:" + ] + }, + { + "cell_type": "code", + "id": "ed9d9ce5-8df9-4803-ae85-bf0744de914c", + "source": [ + "import ipywidgets as widgets\n", + "\n", + "int8_model_det_path = models_dir / \"int8\" / f\"{model_name}_openvino_model/{model_name}.xml\"\n", + "ov_yolo_int8_model = None\n", + "\n", + "to_quantize = widgets.Checkbox(\n", + " value=True,\n", + " description=\"Quantization\",\n", + " disabled=False,\n", + ")\n", + "\n", + "to_quantize" + ] + }, + { + "cell_type": "code", + "id": "52287a23-b5ef-4c34-873a-32b398d26d1a", + "source": [ + "# Fetch skip_kernel_extension module\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/skip_kernel_extension.py\",\n", + ")\n", + "open(\"skip_kernel_extension.py\", \"w\").write(r.text)\n", + "\n", + "%load_ext skip_kernel_extension" + ] + }, + { + "cell_type": "markdown", + "id": "89ba64af-aa73-4ed9-a2f9-527bd9ae8221", + "source": [ + "### Prepare Quantization Dataset\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "For starting quantization, we need to prepare dataset. We will use validation subset from [MS COCO dataset](https://cocodataset.org/) for model quantization and Ultralytics validation data loader for preparing input data." + ] + }, + { + "cell_type": "code", + "id": "5e10435e-9cd6-4f29-a865-721d6209314d", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "from zipfile import ZipFile\n", + "\n", + "from ultralytics.data.utils import DATASETS_DIR\n", + "\n", + "if not int8_model_det_path.exists():\n", + "\n", + " DATA_URL = \"http://images.cocodataset.org/zips/val2017.zip\"\n", + " LABELS_URL = \"https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels-segments.zip\"\n", + " CFG_URL = \"https://raw.githubusercontent.com/ultralytics/ultralytics/v8.1.0/ultralytics/cfg/datasets/coco.yaml\"\n", + "\n", + " OUT_DIR = DATASETS_DIR\n", + "\n", + " DATA_PATH = OUT_DIR / \"val2017.zip\"\n", + " LABELS_PATH = OUT_DIR / \"coco2017labels-segments.zip\"\n", + " CFG_PATH = OUT_DIR / \"coco.yaml\"\n", + "\n", + " download_file(DATA_URL, DATA_PATH.name, DATA_PATH.parent)\n", + " download_file(LABELS_URL, LABELS_PATH.name, LABELS_PATH.parent)\n", + " download_file(CFG_URL, CFG_PATH.name, CFG_PATH.parent)\n", + "\n", + " if not (OUT_DIR / \"coco/labels\").exists():\n", + " with ZipFile(LABELS_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR)\n", + " with ZipFile(DATA_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR / \"coco/images\")" + ] + }, + { + "cell_type": "code", + "id": "b0a14818-21fd-4c71-8e58-2297dfafaadd", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "from ultralytics.utils import DEFAULT_CFG\n", + "from ultralytics.cfg import get_cfg\n", + "from ultralytics.data.converter import coco80_to_coco91_class\n", + "from ultralytics.data.utils import check_det_dataset\n", + "\n", + "if not int8_model_det_path.exists():\n", + " args = get_cfg(cfg=DEFAULT_CFG)\n", + " args.data = str(CFG_PATH)\n", + " det_validator = ov_yolo_model.task_map[ov_yolo_model.task][\"validator\"](args=args)\n", + "\n", + " det_validator.data = check_det_dataset(args.data)\n", + " det_validator.stride = 32\n", + " det_data_loader = det_validator.get_dataloader(OUT_DIR / \"coco\", 1)" + ] + }, + { + "cell_type": "markdown", + "id": "83fa49f4-e40c-48c7-82a6-8ef56e20b343", + "source": [ + "NNCF provides `nncf.Dataset` wrapper for using native framework dataloaders in quantization pipeline. Additionally, we specify transform function that will be responsible for preparing input data in model expected format." + ] + }, + { + "cell_type": "code", + "id": "253f0e0f-131e-43b9-b1be-639d4b3d1fe5", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "import nncf\n", + "from typing import Dict\n", + "\n", + "\n", + "def transform_fn(data_item:Dict):\n", + " \"\"\"\n", + " Quantization transform function. Extracts and preprocess input data from dataloader item for quantization.\n", + " Parameters:\n", + " data_item: Dict with data item produced by DataLoader during iteration\n", + " Returns:\n", + " input_tensor: Input data for quantization\n", + " \"\"\"\n", + " input_tensor = det_validator.preprocess(data_item)['img'].numpy()\n", + " return input_tensor\n", + "\n", + "if not int8_model_det_path.exists():\n", + " quantization_dataset = nncf.Dataset(det_data_loader, transform_fn)" + ] + }, + { + "cell_type": "markdown", + "id": "ff8a6372-2097-4308-a4e5-e4651242a8df", + "source": [ + "### Quantize and Save INT8 model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "The `nncf.quantize` function provides an interface for model quantization. It requires an instance of the OpenVINO Model and quantization dataset. \n", + "Optionally, some additional parameters for the configuration quantization process (number of samples for quantization, preset, ignored scope, etc.) can be provided. YOLOv10 model contains non-ReLU activation functions, which require asymmetric quantization of activations. To achieve a better result, we will use a `mixed` quantization preset. It provides symmetric quantization of weights and asymmetric quantization of activations.\n", + "\n", + ">**Note**: Model post-training quantization is time-consuming process. Be patient, it can take several minutes depending on your hardware." + ] + }, + { + "cell_type": "code", + "id": "4c12ae38-239e-4fe1-8296-47567f98538c", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "import shutil\n", + "\n", + "if not int8_model_det_path.exists():\n", + " quantized_det_model = nncf.quantize(\n", + " ov_model,\n", + " quantization_dataset,\n", + " preset=nncf.QuantizationPreset.MIXED,\n", + " )\n", + "\n", + " ov.save_model(quantized_det_model, int8_model_det_path)\n", + " shutil.copy(ov_model_path.parent / \"metadata.yaml\", int8_model_det_path.parent / \"metadata.yaml\")" + ] + }, + { + "cell_type": "markdown", + "id": "41b8a3eb-adb8-40cf-8602-311ff8d75a76", + "source": [ + "## Run Optimized Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "The way of usage INT8 quantized model is the same like for model before quantization. Let's check inference result of quantized model on single image" + ] + }, + { + "cell_type": "markdown", + "id": "f438df41-d614-4cca-a746-a173d92dddf5", + "source": [ + "### Run Optimized Model on AUTO device\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "af61303b-16bd-4206-b926-2c28b3a859a4", + "source": [ + "%%skip not $to_quantize.value\n", + "ov_yolo_int8_model = YOLOv10(int8_model_det_path.parent, task=\"detect\")" + ] + }, + { + "cell_type": "code", + "id": "db13b8dd-49a1-4046-8a1a-491eee095a1b", + "source": [ + "%%skip not $to_quantize.value\n", + "res = ov_yolo_int8_model(IMAGE_PATH, iou=0.45, conf=0.2)" + ] + }, + { + "cell_type": "code", + "id": "0423ba31-f0ee-4bfc-a18a-f7b870a9683d", + "source": [ + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "4b4f7118-e046-4f87-b403-87053f2b0ac3", + "source": [ + "### Run Optimized Model Inference on selected device\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "142f22ae-6a8d-4578-b97f-4d9c77123418", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "id": "2f43767a-1994-4202-a411-4738c92223da", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "ov_config = {}\n", + "if \"GPU\" in device.value or \"NPU\" in device.value:\n", + " ov_model.reshape({0: [1, 3, 640, 640]})\n", + "ov_config = {}\n", + "if \"GPU\" in device.value:\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "\n", + "quantized_det_model = core.read_model(int8_model_det_path)\n", + "quantized_det_compiled_model = core.compile_model(quantized_det_model, device.value, ov_config)\n", + "\n", + "ov_yolo_int8_model.predictor.model.ov_compiled_model = quantized_det_compiled_model\n", + "\n", + "res = ov_yolo_int8_model(IMAGE_PATH, iou=0.45, conf=0.2)" + ] + }, + { + "cell_type": "code", + "id": "51d64c7b-7595-41ca-87dd-4c85308f84d6", + "source": [ + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "c7b70f75-a706-4f24-a487-88a3ad645dd5", + "source": [ + "## Compare the Original and Quantized Models\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "markdown", + "id": "d6561246-a9e0-4cdf-8207-7e2f919d8582", + "source": [ + "### Model size\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "43d84d15-75be-4430-a58b-61cfd8e08dd0", + "source": [ + "ov_model_weights = ov_model_path.with_suffix(\".bin\")\n", + "print(f\"Size of FP16 model is {ov_model_weights.stat().st_size / 1024 / 1024:.2f} MB\")\n", + "if int8_model_det_path.exists():\n", + " ov_int8_weights = int8_model_det_path.with_suffix(\".bin\")\n", + " print(f\"Size of model with INT8 compressed weights is {ov_int8_weights.stat().st_size / 1024 / 1024:.2f} MB\")\n", + " print(f\"Compression rate for INT8 model: {ov_model_weights.stat().st_size / ov_int8_weights.stat().st_size:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "f72b3d84-91f3-493c-8f4e-46598ffbd6d1", + "source": [ + "### Performance\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "### FP16 model performance\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "909ea508-e007-4313-ab0f-bfa0e3906176", + "source": [ + "!benchmark_app -m $ov_model_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "markdown", + "id": "9852a8ef-4c45-43dd-a9ef-53ca6f13c99e", + "source": [ + "### Int8 model performance\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "8254626d-6c4f-4ca3-936f-a7c47e402e03", + "source": [ + "if int8_model_det_path.exists():\n", + " !benchmark_app -m $int8_model_det_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "markdown", + "id": "12d35bc1-8101-45ea-8bd7-53720b98f5e5", + "source": [ + "## Live demo\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "The following code runs model inference on a video:" + ] + }, + { + "cell_type": "code", + "id": "246bd379-b1d5-4eb2-928f-97c7c2455a92", + "source": [ + "import collections\n", + "import time\n", + "from IPython import display\n", + "import cv2\n", + "import numpy as np\n", + "\n", + "\n", + "# Main processing function to run object detection.\n", + "def run_object_detection(\n", + " source=0,\n", + " flip=False,\n", + " use_popup=False,\n", + " skip_first_frames=0,\n", + " det_model=ov_yolo_int8_model,\n", + " device=device.value,\n", + "):\n", + " player = None\n", + " try:\n", + " # Create a video player to play with target fps.\n", + " player = VideoPlayer(source=source, flip=flip, fps=30, skip_first_frames=skip_first_frames)\n", + " # Start capturing.\n", + " player.start()\n", + " if use_popup:\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(winname=title, flags=cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n", + "\n", + " processing_times = collections.deque()\n", + " while True:\n", + " # Grab the frame.\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + " # If the frame is larger than full HD, reduce size to improve the performance.\n", + " scale = 1280 / max(frame.shape)\n", + " if scale < 1:\n", + " frame = cv2.resize(\n", + " src=frame,\n", + " dsize=None,\n", + " fx=scale,\n", + " fy=scale,\n", + " interpolation=cv2.INTER_AREA,\n", + " )\n", + " # Get the results.\n", + " input_image = np.array(frame)\n", + "\n", + " start_time = time.time()\n", + " detections = det_model(input_image, iou=0.45, conf=0.2, verbose=False)\n", + " stop_time = time.time()\n", + " frame = detections[0].plot()\n", + "\n", + " processing_times.append(stop_time - start_time)\n", + " # Use processing times from last 200 frames.\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " _, f_width = frame.shape[:2]\n", + " # Mean processing time [ms].\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + " cv2.putText(\n", + " img=frame,\n", + " text=f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " org=(20, 40),\n", + " fontFace=cv2.FONT_HERSHEY_COMPLEX,\n", + " fontScale=f_width / 1000,\n", + " color=(0, 0, 255),\n", + " thickness=1,\n", + " lineType=cv2.LINE_AA,\n", + " )\n", + " # Use this workaround if there is flickering.\n", + " if use_popup:\n", + " cv2.imshow(winname=title, mat=frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # Encode numpy array to jpg.\n", + " _, encoded_img = cv2.imencode(ext=\".jpg\", img=frame, params=[cv2.IMWRITE_JPEG_QUALITY, 100])\n", + " # Create an IPython image.\n", + " i = display.Image(data=encoded_img)\n", + " # Display the image in this notebook.\n", + " display.clear_output(wait=True)\n", + " display.display(i)\n", + " # ctrl-c\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " # any different error\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " if player is not None:\n", + " # Stop capturing.\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()" + ] + }, + { + "cell_type": "code", + "id": "8db930f4-fc41-4635-9248-deab2b6cfcb8", + "source": [ + "use_int8 = widgets.Checkbox(\n", + " value=ov_yolo_int8_model is not None,\n", + " description=\"Use int8 model\",\n", + " disabled=ov_yolo_int8_model is None,\n", + ")\n", + "\n", + "use_int8" + ] + }, + { + "cell_type": "code", + "id": "59e37135-1bdc-40ff-8789-b5b4491cab84", + "source": [ + "WEBCAM_INFERENCE = False\n", + "\n", + "if WEBCAM_INFERENCE:\n", + " VIDEO_SOURCE = 0 # Webcam\n", + "else:\n", + " download_file(\n", + " \"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/people.mp4\",\n", + " directory=\"data\",\n", + " )\n", + " VIDEO_SOURCE = \"data/people.mp4\"" + ] + }, + { + "cell_type": "code", + "id": "88c6f47c-1c47-451d-89ba-0a29760f5ec4", + "source": [ + "run_object_detection(\n", + " det_model=ov_yolo_model if not use_int8.value else ov_yolo_int8_model,\n", + " source=VIDEO_SOURCE,\n", + " flip=True,\n", + " use_popup=False,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "9f612d80-68e2-45f8-aa62-9f3a05ca9de8", + "source": [ + "### Gradio Interactive Demo\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "ffdbe248-7086-4804-b4b9-c3dd454bf80c", + "source": [ + "import gradio as gr\n", + "\n", + "\n", + "def yolov10_inference(image, int8, conf_threshold, iou_threshold):\n", + " model = ov_yolo_model if not int8 else ov_yolo_int8_model\n", + " results = model(source=image, iou=iou_threshold, conf=conf_threshold, verbose=False)[0]\n", + " annotated_image = Image.fromarray(results.plot())\n", + "\n", + " return annotated_image\n", + "\n", + "\n", + "with gr.Blocks() as demo:\n", + " gr.HTML(\n", + " \"\"\"\n", + "

    \n", + " YOLOv10: Real-Time End-to-End Object Detection using OpenVINO\n", + "

    \n", + " \"\"\"\n", + " )\n", + " with gr.Row():\n", + " with gr.Column():\n", + " image = gr.Image(type=\"numpy\", label=\"Image\")\n", + " conf_threshold = gr.Slider(\n", + " label=\"Confidence Threshold\",\n", + " minimum=0.1,\n", + " maximum=1.0,\n", + " step=0.1,\n", + " value=0.2,\n", + " )\n", + " iou_threshold = gr.Slider(\n", + " label=\"IoU Threshold\",\n", + " minimum=0.1,\n", + " maximum=1.0,\n", + " step=0.1,\n", + " value=0.45,\n", + " )\n", + " use_int8 = gr.Checkbox(\n", + " value=ov_yolo_int8_model is not None,\n", + " visible=ov_yolo_int8_model is not None,\n", + " label=\"Use INT8 model\",\n", + " )\n", + " yolov10_infer = gr.Button(value=\"Detect Objects\")\n", + "\n", + " with gr.Column():\n", + " output_image = gr.Image(type=\"pil\", label=\"Annotated Image\")\n", + "\n", + " yolov10_infer.click(\n", + " fn=yolov10_inference,\n", + " inputs=[\n", + " image,\n", + " use_int8,\n", + " conf_threshold,\n", + " iou_threshold,\n", + " ],\n", + " outputs=[output_image],\n", + " )\n", + " examples = gr.Examples(\n", + " [\n", + " \"data/coco_bike.jpg\",\n", + " ],\n", + " inputs=[\n", + " image,\n", + " ],\n", + " )\n", + "\n", + "\n", + "try:\n", + " demo.launch(debug=True)\n", + "except Exception:\n", + " demo.launch(debug=True, share=True)" + ] + } + ] + .map(fromJupyterCell) + , + [ + { + "cell_type": "markdown", + "id": "e9d010ff-85ba-4e69-b439-ba89e4a3a715", + "source": [ + "# Convert and Optimize YOLOv10 with OpenVINO\n", + "\n", + "Real-time object detection aims to accurately predict object categories and positions in images with low latency. The YOLO series has been at the forefront of this research due to its balance between performance and efficiency. However, reliance on NMS and architectural inefficiencies have hindered optimal performance. YOLOv10 addresses these issues by introducing consistent dual assignments for NMS-free training and a holistic efficiency-accuracy driven model design strategy.\n", + "\n", + "YOLOv10, built on the [Ultralytics Python package](https://pypi.org/project/ultralytics/) by researchers at [Tsinghua University](https://www.tsinghua.edu.cn/en/), introduces a new approach to real-time object detection, addressing both the post-processing and model architecture deficiencies found in previous YOLO versions. By eliminating non-maximum suppression (NMS) and optimizing various model components, YOLOv10 achieves state-of-the-art performance with significantly reduced computational overhead. Extensive experiments demonstrate its superior accuracy-latency trade-offs across multiple model scales.\n", + "\n", + "![yolov10-approach.png](https://github.com/ultralytics/ultralytics/assets/26833433/f9b1bec0-928e-41ce-a205-e12db3c4929a)\n", + "\n", + "More details about model architecture you can find in original [repo](https://github.com/THU-MIG/yolov10), [paper](https://arxiv.org/abs/2405.14458) and [Ultralytics documentation](https://docs.ultralytics.com/models/yolov10/).\n", + "\n", + "This tutorial demonstrates step-by-step instructions on how to run and optimize PyTorch YOLO V10 with OpenVINO.\n", + "\n", + "The tutorial consists of the following steps:\n", + "\n", + "- Prepare PyTorch model\n", + "- Convert PyTorch model to OpenVINO IR\n", + "- Run model inference with OpenVINO\n", + "- Prepare and run optimization pipeline using NNCF\n", + "- Compare performance of the FP16 and quantized models.\n", + "- Run optimized model inference on video\n", + "- Launch interactive Gradio demo\n", + "\n", + "#### Table of contents:\n", + "\n", + "- [Prerequisites](#Prerequisites)\n", + "- [Download PyTorch model](#Download-PyTorch-model)\n", + "- [Export PyTorch model to OpenVINO IR Format](#Export-PyTorch-model-to-OpenVINO-IR-Format)\n", + "- [Run OpenVINO Inference on AUTO device using Ultralytics API](#Run-OpenVINO-Inference-on-AUTO-device-using-Ultralytics-API)\n", + "- [Run OpenVINO Inference on selected device using Ultralytics API](#Run-OpenVINO-Inference-on-selected-device-using-Ultralytics-API)\n", + "- [Optimize model using NNCF Post-training Quantization API](#Optimize-model-using-NNCF-Post-training-Quantization-API)\n", + " - [Prepare Quantization Dataset](#Prepare-Quantization-Dataset)\n", + " - [Quantize and Save INT8 model](#Quantize-and-Save-INT8-model)\n", + "- [Run Optimized Model Inference](#Run-Optimized-Model-Inference)\n", + " - [Run Optimized Model on AUTO device](#Run-Optimized-Model-on-AUTO-device)\n", + " - [Run Optimized Model Inference on selected device](#Run-Optimized-Model-Inference-on-selected-device)\n", + "- [Compare the Original and Quantized Models](#Compare-the-Original-and-Quantized-Models)\n", + " - [Model size](#Model-size)\n", + " - [Performance](#Performance)\n", + " - [FP16 model performance](#FP16-model-performance)\n", + " - [Int8 model performance](#Int8-model-performance)\n", + "- [Live demo](#Live-demo)\n", + " - [Gradio Interactive Demo](#Gradio-Interactive-Demo)\n", + "\n", + "\n", + "### Installation Instructions\n", + "\n", + "This is a self-contained example that relies solely on its own code.\n", + "\n", + "We recommend running the notebook in a virtual environment. You only need a Jupyter server to start.\n", + "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "144e86a1-c220-4e3b-a8b2-8eda443faf2d", + "source": [ + "## Prerequisites\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "bf76580b-860a-4dcd-8b01-f494cdffe37e", + "source": [ + "import os\n", + "\n", + "os.environ[\"GIT_CLONE_PROTECTION_ACTIVE\"] = \"false\"\n", + "\n", + "%pip install -q \"nncf>=2.11.0\"\n", + "%pip install -Uq \"openvino>=2024.3.0\"\n", + "%pip install -q \"git+https://github.com/THU-MIG/yolov10.git\" --extra-index-url https://download.pytorch.org/whl/cpu\n", + "%pip install -q \"torch>=2.1\" \"torchvision>=0.16\" tqdm opencv-python \"gradio>=4.19\" --extra-index-url https://download.pytorch.org/whl/cpu" + ] + }, + { + "cell_type": "code", + "id": "75772eac-565b-4234-82bf-f305e0c1ceda", + "source": [ + "from pathlib import Path\n", + "\n", + "# Fetch `notebook_utils` module\n", + "import requests\n", + "\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", + ")\n", + "\n", + "open(\"notebook_utils.py\", \"w\").write(r.text)\n", + "\n", + "from notebook_utils import download_file, VideoPlayer, device_widget" + ] + }, + { + "cell_type": "markdown", + "id": "2888d8b4-44f6-4418-bae4-f433ff28010b", + "source": [ + "## Download PyTorch model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "There are several version of [YOLO V10](https://github.com/THU-MIG/yolov10/tree/main?tab=readme-ov-file#performance) models provided by model authors. Each of them has different characteristics depends on number of training parameters, performance and accuracy. For demonstration purposes we will use `yolov10n`, but the same steps are also applicable to other models in YOLO V10 series." + ] + }, + { + "cell_type": "code", + "id": "aea6315b-6a29-4f2a-96a9-eb3c1646a3a4", + "source": [ + "models_dir = Path(\"./models\")\n", + "models_dir.mkdir(exist_ok=True)" + ] + }, + { + "cell_type": "code", + "id": "d828401e-b6bd-4796-a7b8-681957a159d4", + "source": [ + "model_weights_url = \"https://github.com/jameslahm/yolov10/releases/download/v1.0/yolov10n.pt\"\n", + "file_name = model_weights_url.split(\"/\")[-1]\n", + "model_name = file_name.replace(\".pt\", \"\")\n", + "\n", + "download_file(model_weights_url, directory=models_dir)" + ] + }, + { + "cell_type": "markdown", + "id": "dc21487d-dd79-4b56-b8e2-5b28c8d92ac8", + "source": [ + "## Export PyTorch model to OpenVINO IR Format\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "markdown", + "id": "8cfec0d6-fb55-4611-861b-326e892fcf09", + "source": [ + "As it was discussed before, YOLO V10 code is designed on top of [Ultralytics](https://docs.ultralytics.com/) library and has similar interface with YOLO V8 (You can check [YOLO V8 notebooks](https://github.com/openvinotoolkit/openvino_notebooks/tree/latest/notebooks/yolov8-optimization) for more detailed instruction how to work with Ultralytics API). Ultralytics support OpenVINO model export using [export](https://docs.ultralytics.com/modes/export/) method of model class. Additionally, we can specify parameters responsible for target input size, static or dynamic input shapes and model precision (FP32/FP16/INT8). INT8 quantization can be additionally performed on export stage, but for making approach more flexible, we consider how to perform quantization using [NNCF](https://github.com/openvinotoolkit/nncf)." + ] + }, + { + "cell_type": "code", + "id": "d488f564-c1d5-4c6a-9b4a-353a3ab749e6", + "source": [ + "import types\n", + "from ultralytics.utils import ops, yaml_load, yaml_save\n", + "from ultralytics import YOLOv10\n", + "import torch\n", + "\n", + "detection_labels = {\n", + " 0: \"person\",\n", + " 1: \"bicycle\",\n", + " 2: \"car\",\n", + " 3: \"motorcycle\",\n", + " 4: \"airplane\",\n", + " 5: \"bus\",\n", + " 6: \"train\",\n", + " 7: \"truck\",\n", + " 8: \"boat\",\n", + " 9: \"traffic light\",\n", + " 10: \"fire hydrant\",\n", + " 11: \"stop sign\",\n", + " 12: \"parking meter\",\n", + " 13: \"bench\",\n", + " 14: \"bird\",\n", + " 15: \"cat\",\n", + " 16: \"dog\",\n", + " 17: \"horse\",\n", + " 18: \"sheep\",\n", + " 19: \"cow\",\n", + " 20: \"elephant\",\n", + " 21: \"bear\",\n", + " 22: \"zebra\",\n", + " 23: \"giraffe\",\n", + " 24: \"backpack\",\n", + " 25: \"umbrella\",\n", + " 26: \"handbag\",\n", + " 27: \"tie\",\n", + " 28: \"suitcase\",\n", + " 29: \"frisbee\",\n", + " 30: \"skis\",\n", + " 31: \"snowboard\",\n", + " 32: \"sports ball\",\n", + " 33: \"kite\",\n", + " 34: \"baseball bat\",\n", + " 35: \"baseball glove\",\n", + " 36: \"skateboard\",\n", + " 37: \"surfboard\",\n", + " 38: \"tennis racket\",\n", + " 39: \"bottle\",\n", + " 40: \"wine glass\",\n", + " 41: \"cup\",\n", + " 42: \"fork\",\n", + " 43: \"knife\",\n", + " 44: \"spoon\",\n", + " 45: \"bowl\",\n", + " 46: \"banana\",\n", + " 47: \"apple\",\n", + " 48: \"sandwich\",\n", + " 49: \"orange\",\n", + " 50: \"broccoli\",\n", + " 51: \"carrot\",\n", + " 52: \"hot dog\",\n", + " 53: \"pizza\",\n", + " 54: \"donut\",\n", + " 55: \"cake\",\n", + " 56: \"chair\",\n", + " 57: \"couch\",\n", + " 58: \"potted plant\",\n", + " 59: \"bed\",\n", + " 60: \"dining table\",\n", + " 61: \"toilet\",\n", + " 62: \"tv\",\n", + " 63: \"laptop\",\n", + " 64: \"mouse\",\n", + " 65: \"remote\",\n", + " 66: \"keyboard\",\n", + " 67: \"cell phone\",\n", + " 68: \"microwave\",\n", + " 69: \"oven\",\n", + " 70: \"toaster\",\n", + " 71: \"sink\",\n", + " 72: \"refrigerator\",\n", + " 73: \"book\",\n", + " 74: \"clock\",\n", + " 75: \"vase\",\n", + " 76: \"scissors\",\n", + " 77: \"teddy bear\",\n", + " 78: \"hair drier\",\n", + " 79: \"toothbrush\",\n", + "}\n", + "\n", + "\n", + "def v10_det_head_forward(self, x):\n", + " one2one = self.forward_feat([xi.detach() for xi in x], self.one2one_cv2, self.one2one_cv3)\n", + " if not self.export:\n", + " one2many = super().forward(x)\n", + "\n", + " if not self.training:\n", + " one2one = self.inference(one2one)\n", + " if not self.export:\n", + " return {\"one2many\": one2many, \"one2one\": one2one}\n", + " else:\n", + " assert self.max_det != -1\n", + " boxes, scores, labels = ops.v10postprocess(one2one.permute(0, 2, 1), self.max_det, self.nc)\n", + " return torch.cat(\n", + " [boxes, scores.unsqueeze(-1), labels.unsqueeze(-1).to(boxes.dtype)],\n", + " dim=-1,\n", + " )\n", + " else:\n", + " return {\"one2many\": one2many, \"one2one\": one2one}\n", + "\n", + "\n", + "ov_model_path = models_dir / f\"{model_name}_openvino_model/{model_name}.xml\"\n", + "if not ov_model_path.exists():\n", + " model = YOLOv10(models_dir / file_name)\n", + " model.model.model[-1].forward = types.MethodType(v10_det_head_forward, model.model.model[-1])\n", + " model.export(format=\"openvino\", dynamic=True, half=True)\n", + " config = yaml_load(ov_model_path.parent / \"metadata.yaml\")\n", + " config[\"names\"] = detection_labels\n", + " yaml_save(ov_model_path.parent / \"metadata.yaml\", config)" + ] + }, + { + "cell_type": "markdown", + "id": "8a23f621-f6cd-4bfd-8b98-24b8c0392761", + "source": [ + "## Run OpenVINO Inference on AUTO device using Ultralytics API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "Now, when we exported model to OpenVINO, we can load it directly into YOLOv10 class, where automatic inference backend will provide easy-to-use user experience to run OpenVINO YOLOv10 model on the similar level like for original PyTorch model. The code bellow demonstrates how to run inference OpenVINO exported model with Ultralytics API on single image. [AUTO device](https://github.com/openvinotoolkit/openvino_notebooks/tree/latest/notebooks/auto-device) will be used for launching model." + ] + }, + { + "cell_type": "code", + "id": "7e8ed361-bf09-4398-ba15-e6c5dda73cd3", + "source": [ + "ov_yolo_model = YOLOv10(ov_model_path.parent, task=\"detect\")" + ] + }, + { + "cell_type": "code", + "id": "8a473286-59b8-498f-8541-df84f041e119", + "source": [ + "from PIL import Image\n", + "\n", + "IMAGE_PATH = Path(\"./data/coco_bike.jpg\")\n", + "download_file(\n", + " url=\"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/image/coco_bike.jpg\",\n", + " filename=IMAGE_PATH.name,\n", + " directory=IMAGE_PATH.parent,\n", + ")" + ] + }, + { + "cell_type": "code", + "id": "7e116e81-0ad4-4b9e-9f5c-58680fd8f0c6", + "source": [ + "res = ov_yolo_model(IMAGE_PATH, iou=0.45, conf=0.2)\n", + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "943b9b04-4b97-4395-99d1-695465de1140", + "source": [ + "## Run OpenVINO Inference on selected device using Ultralytics API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "In this part of notebook you can select inference device for running model inference to compare results with AUTO." + ] + }, + { + "cell_type": "code", + "id": "41655bec-b006-4376-abbb-d6c8c09e89fb", + "source": [ + "device = device_widget(\"CPU\")\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "id": "f284d5fd-12ba-4c08-afce-d8131ef7ad4d", + "source": [ + "import openvino as ov\n", + "\n", + "core = ov.Core()\n", + "\n", + "ov_model = core.read_model(ov_model_path)\n", + "\n", + "# load model on selected device\n", + "if \"GPU\" in device.value or \"NPU\" in device.value:\n", + " ov_model.reshape({0: [1, 3, 640, 640]})\n", + "ov_config = {}\n", + "if \"GPU\" in device.value:\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "det_compiled_model = core.compile_model(ov_model, device.value, ov_config)" + ] + }, + { + "cell_type": "code", + "id": "12164d21-948a-4ef7-9e1c-b0d41c53cf91", + "source": [ + "ov_yolo_model.predictor.model.ov_compiled_model = det_compiled_model" + ] + }, + { + "cell_type": "code", + "id": "ecb9d396-ab1e-4398-a110-18c08b00ab14", + "source": [ + "res = ov_yolo_model(IMAGE_PATH, iou=0.45, conf=0.2)" + ] + }, + { + "cell_type": "code", + "id": "a4abf758-3a72-4ee0-afde-38076602f1c7", + "source": [ + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "992b6ac3-4248-4848-8fd6-f9c1b9fc2051", + "source": [ + "## Optimize model using NNCF Post-training Quantization API\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "[NNCF](https://github.com/openvinotoolkit/nncf) provides a suite of advanced algorithms for Neural Networks inference optimization in OpenVINO with minimal accuracy drop.\n", + "We will use 8-bit quantization in post-training mode (without the fine-tuning pipeline) to optimize YOLOv10.\n", + "\n", + "The optimization process contains the following steps:\n", + "\n", + "1. Create a Dataset for quantization.\n", + "2. Run `nncf.quantize` for getting an optimized model.\n", + "3. Serialize OpenVINO IR model, using the `openvino.save_model` function.\n", + "\n", + "Quantization is time and memory consuming process, you can skip this step using checkbox bellow:" + ] + }, + { + "cell_type": "code", + "id": "ed9d9ce5-8df9-4803-ae85-bf0744de914c", + "source": [ + "import ipywidgets as widgets\n", + "\n", + "int8_model_det_path = models_dir / \"int8\" / f\"{model_name}_openvino_model/{model_name}.xml\"\n", + "ov_yolo_int8_model = None\n", + "\n", + "to_quantize = widgets.Checkbox(\n", + " value=True,\n", + " description=\"Quantization\",\n", + " disabled=False,\n", + ")\n", + "\n", + "to_quantize" + ] + }, + { + "cell_type": "code", + "id": "52287a23-b5ef-4c34-873a-32b398d26d1a", + "source": [ + "# Fetch skip_kernel_extension module\n", + "r = requests.get(\n", + " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/skip_kernel_extension.py\",\n", + ")\n", + "open(\"skip_kernel_extension.py\", \"w\").write(r.text)\n", + "\n", + "%load_ext skip_kernel_extension" + ] + }, + { + "cell_type": "markdown", + "id": "89ba64af-aa73-4ed9-a2f9-527bd9ae8221", + "source": [ + "### Prepare Quantization Dataset\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "For starting quantization, we need to prepare dataset. We will use validation subset from [MS COCO dataset](https://cocodataset.org/) for model quantization and Ultralytics validation data loader for preparing input data." + ] + }, + { + "cell_type": "code", + "id": "5e10435e-9cd6-4f29-a865-721d6209314d", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "from zipfile import ZipFile\n", + "\n", + "from ultralytics.data.utils import DATASETS_DIR\n", + "\n", + "if not int8_model_det_path.exists():\n", + "\n", + " DATA_URL = \"http://images.cocodataset.org/zips/val2017.zip\"\n", + " LABELS_URL = \"https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels-segments.zip\"\n", + " CFG_URL = \"https://raw.githubusercontent.com/ultralytics/ultralytics/v8.1.0/ultralytics/cfg/datasets/coco.yaml\"\n", + "\n", + " OUT_DIR = DATASETS_DIR\n", + "\n", + " DATA_PATH = OUT_DIR / \"val2017.zip\"\n", + " LABELS_PATH = OUT_DIR / \"coco2017labels-segments.zip\"\n", + " CFG_PATH = OUT_DIR / \"coco.yaml\"\n", + "\n", + " download_file(DATA_URL, DATA_PATH.name, DATA_PATH.parent)\n", + " download_file(LABELS_URL, LABELS_PATH.name, LABELS_PATH.parent)\n", + " download_file(CFG_URL, CFG_PATH.name, CFG_PATH.parent)\n", + "\n", + " if not (OUT_DIR / \"coco/labels\").exists():\n", + " with ZipFile(LABELS_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR)\n", + " with ZipFile(DATA_PATH, \"r\") as zip_ref:\n", + " zip_ref.extractall(OUT_DIR / \"coco/images\")" + ] + }, + { + "cell_type": "code", + "id": "b0a14818-21fd-4c71-8e58-2297dfafaadd", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "from ultralytics.utils import DEFAULT_CFG\n", + "from ultralytics.cfg import get_cfg\n", + "from ultralytics.data.converter import coco80_to_coco91_class\n", + "from ultralytics.data.utils import check_det_dataset\n", + "\n", + "if not int8_model_det_path.exists():\n", + " args = get_cfg(cfg=DEFAULT_CFG)\n", + " args.data = str(CFG_PATH)\n", + " det_validator = ov_yolo_model.task_map[ov_yolo_model.task][\"validator\"](args=args)\n", + "\n", + " det_validator.data = check_det_dataset(args.data)\n", + " det_validator.stride = 32\n", + " det_data_loader = det_validator.get_dataloader(OUT_DIR / \"coco\", 1)" + ] + }, + { + "cell_type": "markdown", + "id": "83fa49f4-e40c-48c7-82a6-8ef56e20b343", + "source": [ + "NNCF provides `nncf.Dataset` wrapper for using native framework dataloaders in quantization pipeline. Additionally, we specify transform function that will be responsible for preparing input data in model expected format." + ] + }, + { + "cell_type": "code", + "id": "253f0e0f-131e-43b9-b1be-639d4b3d1fe5", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "import nncf\n", + "from typing import Dict\n", + "\n", + "\n", + "def transform_fn(data_item:Dict):\n", + " \"\"\"\n", + " Quantization transform function. Extracts and preprocess input data from dataloader item for quantization.\n", + " Parameters:\n", + " data_item: Dict with data item produced by DataLoader during iteration\n", + " Returns:\n", + " input_tensor: Input data for quantization\n", + " \"\"\"\n", + " input_tensor = det_validator.preprocess(data_item)['img'].numpy()\n", + " return input_tensor\n", + "\n", + "if not int8_model_det_path.exists():\n", + " quantization_dataset = nncf.Dataset(det_data_loader, transform_fn)" + ] + }, + { + "cell_type": "markdown", + "id": "ff8a6372-2097-4308-a4e5-e4651242a8df", + "source": [ + "### Quantize and Save INT8 model\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "The `nncf.quantize` function provides an interface for model quantization. It requires an instance of the OpenVINO Model and quantization dataset. \n", + "Optionally, some additional parameters for the configuration quantization process (number of samples for quantization, preset, ignored scope, etc.) can be provided. YOLOv10 model contains non-ReLU activation functions, which require asymmetric quantization of activations. To achieve a better result, we will use a `mixed` quantization preset. It provides symmetric quantization of weights and asymmetric quantization of activations.\n", + "\n", + ">**Note**: Model post-training quantization is time-consuming process. Be patient, it can take several minutes depending on your hardware." + ] + }, + { + "cell_type": "code", + "id": "4c12ae38-239e-4fe1-8296-47567f98538c", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "import shutil\n", + "\n", + "if not int8_model_det_path.exists():\n", + " quantized_det_model = nncf.quantize(\n", + " ov_model,\n", + " quantization_dataset,\n", + " preset=nncf.QuantizationPreset.MIXED,\n", + " )\n", + "\n", + " ov.save_model(quantized_det_model, int8_model_det_path)\n", + " shutil.copy(ov_model_path.parent / \"metadata.yaml\", int8_model_det_path.parent / \"metadata.yaml\")" + ] + }, + { + "cell_type": "markdown", + "id": "41b8a3eb-adb8-40cf-8602-311ff8d75a76", + "source": [ + "## Run Optimized Model Inference\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "The way of usage INT8 quantized model is the same like for model before quantization. Let's check inference result of quantized model on single image" + ] + }, + { + "cell_type": "markdown", + "id": "f438df41-d614-4cca-a746-a173d92dddf5", + "source": [ + "### Run Optimized Model on AUTO device\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "af61303b-16bd-4206-b926-2c28b3a859a4", + "source": [ + "%%skip not $to_quantize.value\n", + "ov_yolo_int8_model = YOLOv10(int8_model_det_path.parent, task=\"detect\")" + ] + }, + { + "cell_type": "code", + "id": "db13b8dd-49a1-4046-8a1a-491eee095a1b", + "source": [ + "%%skip not $to_quantize.value\n", + "res = ov_yolo_int8_model(IMAGE_PATH, iou=0.45, conf=0.2)" + ] + }, + { + "cell_type": "code", + "id": "0423ba31-f0ee-4bfc-a18a-f7b870a9683d", + "source": [ + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "4b4f7118-e046-4f87-b403-87053f2b0ac3", + "source": [ + "### Run Optimized Model Inference on selected device\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "142f22ae-6a8d-4578-b97f-4d9c77123418", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "device" + ] + }, + { + "cell_type": "code", + "id": "2f43767a-1994-4202-a411-4738c92223da", + "source": [ + "%%skip not $to_quantize.value\n", + "\n", + "ov_config = {}\n", + "if \"GPU\" in device.value or \"NPU\" in device.value:\n", + " ov_model.reshape({0: [1, 3, 640, 640]})\n", + "ov_config = {}\n", + "if \"GPU\" in device.value:\n", + " ov_config = {\"GPU_DISABLE_WINOGRAD_CONVOLUTION\": \"YES\"}\n", + "\n", + "quantized_det_model = core.read_model(int8_model_det_path)\n", + "quantized_det_compiled_model = core.compile_model(quantized_det_model, device.value, ov_config)\n", + "\n", + "ov_yolo_int8_model.predictor.model.ov_compiled_model = quantized_det_compiled_model\n", + "\n", + "res = ov_yolo_int8_model(IMAGE_PATH, iou=0.45, conf=0.2)" + ] + }, + { + "cell_type": "code", + "id": "51d64c7b-7595-41ca-87dd-4c85308f84d6", + "source": [ + "Image.fromarray(res[0].plot()[:, :, ::-1])" + ] + }, + { + "cell_type": "markdown", + "id": "c7b70f75-a706-4f24-a487-88a3ad645dd5", + "source": [ + "## Compare the Original and Quantized Models\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "markdown", + "id": "d6561246-a9e0-4cdf-8207-7e2f919d8582", + "source": [ + "### Model size\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "43d84d15-75be-4430-a58b-61cfd8e08dd0", + "source": [ + "ov_model_weights = ov_model_path.with_suffix(\".bin\")\n", + "print(f\"Size of FP16 model is {ov_model_weights.stat().st_size / 1024 / 1024:.2f} MB\")\n", + "if int8_model_det_path.exists():\n", + " ov_int8_weights = int8_model_det_path.with_suffix(\".bin\")\n", + " print(f\"Size of model with INT8 compressed weights is {ov_int8_weights.stat().st_size / 1024 / 1024:.2f} MB\")\n", + " print(f\"Compression rate for INT8 model: {ov_model_weights.stat().st_size / ov_int8_weights.stat().st_size:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "f72b3d84-91f3-493c-8f4e-46598ffbd6d1", + "source": [ + "### Performance\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "### FP16 model performance\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "909ea508-e007-4313-ab0f-bfa0e3906176", + "source": [ + "!benchmark_app -m $ov_model_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "markdown", + "id": "9852a8ef-4c45-43dd-a9ef-53ca6f13c99e", + "source": [ + "### Int8 model performance\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "8254626d-6c4f-4ca3-936f-a7c47e402e03", + "source": [ + "if int8_model_det_path.exists():\n", + " !benchmark_app -m $int8_model_det_path -d $device.value -api async -shape \"[1,3,640,640]\" -t 15" + ] + }, + { + "cell_type": "markdown", + "id": "12d35bc1-8101-45ea-8bd7-53720b98f5e5", + "source": [ + "## Live demo\n", + "[back to top ⬆️](#Table-of-contents:)\n", + "\n", + "\n", + "The following code runs model inference on a video:" + ] + }, + { + "cell_type": "code", + "id": "246bd379-b1d5-4eb2-928f-97c7c2455a92", + "source": [ + "import collections\n", + "import time\n", + "from IPython import display\n", + "import cv2\n", + "import numpy as np\n", + "\n", + "\n", + "# Main processing function to run object detection.\n", + "def run_object_detection(\n", + " source=0,\n", + " flip=False,\n", + " use_popup=False,\n", + " skip_first_frames=0,\n", + " det_model=ov_yolo_int8_model,\n", + " device=device.value,\n", + "):\n", + " player = None\n", + " try:\n", + " # Create a video player to play with target fps.\n", + " player = VideoPlayer(source=source, flip=flip, fps=30, skip_first_frames=skip_first_frames)\n", + " # Start capturing.\n", + " player.start()\n", + " if use_popup:\n", + " title = \"Press ESC to Exit\"\n", + " cv2.namedWindow(winname=title, flags=cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n", + "\n", + " processing_times = collections.deque()\n", + " while True:\n", + " # Grab the frame.\n", + " frame = player.next()\n", + " if frame is None:\n", + " print(\"Source ended\")\n", + " break\n", + " # If the frame is larger than full HD, reduce size to improve the performance.\n", + " scale = 1280 / max(frame.shape)\n", + " if scale < 1:\n", + " frame = cv2.resize(\n", + " src=frame,\n", + " dsize=None,\n", + " fx=scale,\n", + " fy=scale,\n", + " interpolation=cv2.INTER_AREA,\n", + " )\n", + " # Get the results.\n", + " input_image = np.array(frame)\n", + "\n", + " start_time = time.time()\n", + " detections = det_model(input_image, iou=0.45, conf=0.2, verbose=False)\n", + " stop_time = time.time()\n", + " frame = detections[0].plot()\n", + "\n", + " processing_times.append(stop_time - start_time)\n", + " # Use processing times from last 200 frames.\n", + " if len(processing_times) > 200:\n", + " processing_times.popleft()\n", + "\n", + " _, f_width = frame.shape[:2]\n", + " # Mean processing time [ms].\n", + " processing_time = np.mean(processing_times) * 1000\n", + " fps = 1000 / processing_time\n", + " cv2.putText(\n", + " img=frame,\n", + " text=f\"Inference time: {processing_time:.1f}ms ({fps:.1f} FPS)\",\n", + " org=(20, 40),\n", + " fontFace=cv2.FONT_HERSHEY_COMPLEX,\n", + " fontScale=f_width / 1000,\n", + " color=(0, 0, 255),\n", + " thickness=1,\n", + " lineType=cv2.LINE_AA,\n", + " )\n", + " # Use this workaround if there is flickering.\n", + " if use_popup:\n", + " cv2.imshow(winname=title, mat=frame)\n", + " key = cv2.waitKey(1)\n", + " # escape = 27\n", + " if key == 27:\n", + " break\n", + " else:\n", + " # Encode numpy array to jpg.\n", + " _, encoded_img = cv2.imencode(ext=\".jpg\", img=frame, params=[cv2.IMWRITE_JPEG_QUALITY, 100])\n", + " # Create an IPython image.\n", + " i = display.Image(data=encoded_img)\n", + " # Display the image in this notebook.\n", + " display.clear_output(wait=True)\n", + " display.display(i)\n", + " # ctrl-c\n", + " except KeyboardInterrupt:\n", + " print(\"Interrupted\")\n", + " # any different error\n", + " except RuntimeError as e:\n", + " print(e)\n", + " finally:\n", + " if player is not None:\n", + " # Stop capturing.\n", + " player.stop()\n", + " if use_popup:\n", + " cv2.destroyAllWindows()" + ] + }, + { + "cell_type": "code", + "id": "8db930f4-fc41-4635-9248-deab2b6cfcb8", + "source": [ + "use_int8 = widgets.Checkbox(\n", + " value=ov_yolo_int8_model is not None,\n", + " description=\"Use int8 model\",\n", + " disabled=ov_yolo_int8_model is None,\n", + ")\n", + "\n", + "use_int8" + ] + }, + { + "cell_type": "code", + "id": "59e37135-1bdc-40ff-8789-b5b4491cab84", + "source": [ + "WEBCAM_INFERENCE = False\n", + "\n", + "if WEBCAM_INFERENCE:\n", + " VIDEO_SOURCE = 0 # Webcam\n", + "else:\n", + " download_file(\n", + " \"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/people.mp4\",\n", + " directory=\"data\",\n", + " )\n", + " VIDEO_SOURCE = \"data/people.mp4\"" + ] + }, + { + "cell_type": "code", + "id": "88c6f47c-1c47-451d-89ba-0a29760f5ec4", + "source": [ + "run_object_detection(\n", + " det_model=ov_yolo_model if not use_int8.value else ov_yolo_int8_model,\n", + " source=VIDEO_SOURCE,\n", + " flip=True,\n", + " use_popup=False,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "9f612d80-68e2-45f8-aa62-9f3a05ca9de8", + "source": [ + "### Gradio Interactive Demo\n", + "[back to top ⬆️](#Table-of-contents:)" + ] + }, + { + "cell_type": "code", + "id": "2e5a6249", + "source": [ + "def yolov10_inference(image, int8, conf_threshold, iou_threshold):\n", + " model = ov_yolo_model if not int8 else ov_yolo_int8_model\n", + " results = model(source=image, iou=iou_threshold, conf=conf_threshold, verbose=False)[0]\n", + " annotated_image = Image.fromarray(results.plot())\n", + "\n", + " return annotated_image" + ] + }, + { + "cell_type": "code", + "id": "ffdbe248-7086-4804-b4b9-c3dd454bf80c", + "source": [ + "if not Path(\"gradio_helper.py\").exists():\n", + " r = requests.get(url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/notebooks/yolov10-optimization/gradio_helper.py\")\n", + " open(\"gradio_helper.py\", \"w\").write(r.text)\n", + "\n", + "from gradio_helper import make_demo\n", + "\n", + "demo = make_demo(fn=yolov10_inference, quantized=ov_yolo_int8_model is not None)\n", + "\n", + "try:\n", + " demo.launch(debug=True)\n", + "except Exception:\n", + " demo.launch(debug=True, share=True)\n", + "# If you are launching remotely, specify server_name and server_port\n", + "# EXAMPLE: `demo.launch(server_name='your server name', server_port='server port in int')`\n", + "# To learn more please refer to the Gradio docs: https://gradio.app/docs/" + ] + }, + { + "cell_type": "code", + "id": "4b123c5e", + "source": [ + "# please uncomment and run this cell for stopping gradio interface\n", + "# demo.close()" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 18 }, + { modified: 19, original: 19 }, + { modified: 20, original: 20 }, + { modified: 21, original: 21 }, + { modified: 22, original: 22 }, + { modified: 23, original: 23 }, + { modified: 24, original: 24 }, + { modified: 25, original: 25 }, + { modified: 26, original: 26 }, + { modified: 27, original: 27 }, + { modified: 28, original: 28 }, + { modified: 29, original: 29 }, + { modified: 30, original: 30 }, + { modified: 31, original: 31 }, + { modified: 32, original: 32 }, + { modified: 33, original: 33 }, + { modified: 34, original: 34 }, + { modified: 35, original: 35 }, + { modified: 36, original: 36 }, + { modified: 37, original: 37 }, + { modified: 38, original: 38 }, + { modified: 39, original: 39 }, + { modified: 40, original: 40 }, + { modified: 41, original: 41 }, + { modified: 42, original: 42 }, + { modified: 43, original: 43 }, + { modified: 44, original: 44 }, + { modified: 45, original: 45 }, + { modified: 46, original: 46 }, + { modified: 47, original: 47 }, + { modified: 48, original: 48 }, + { modified: 49, original: 49 }, + { modified: 50, original: 50 }, + { modified: 51, original: 51 }, + { modified: 52, original: 52 }, + { modified: 53, original: -1 }, + { modified: 54, original: -1 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 52, originalLength: 1, modifiedStart: 52, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 53, originalLength: 0, modifiedStart: 53, modifiedLength: 2 } satisfies IDiffChange, + ]); + + }); + test('Misalignment due to 2 deleted cells', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "markdown", + "metadata": { + "id": "F0Wut2G5kz2Z" + }, + "source": [ + "[![Roboflow Notebooks](https://ik.imagekit.io/roboflow/notebooks/template/bannertest2-2.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672932710194)](https://github.com/roboflow/notebooks)\n", + "\n", + "# Fast Segment Anything Model (FastSAM)\n", + "\n", + "---\n", + "\n", + "[![GitHub](https://badges.aleen42.com/src/github.svg)](https://github.com/CASIA-IVA-Lab/FastSAM)\n", + "[![arXiv](https://img.shields.io/badge/arXiv-2306.12156-b31b1b.svg)](https://arxiv.org/pdf/2306.12156.pdf)\n", + "[![YouTube](https://badges.aleen42.com/src/youtube.svg)](https://youtu.be/yHNPyqazYYU)\n", + "[![Roboflow](https://raw.githubusercontent.com/roboflow-ai/notebooks/main/assets/badges/roboflow-blogpost.svg)](https://blog.roboflow.com/how-to-use-fastsam)\n", + "\n", + "The Fast Segment Anything Model (FastSAM) is a CNN Segment Anything Model trained by only 2% of the SA-1B dataset published by SAM authors.\n", + "\n", + "![fast-segment-anything-model-figure-1](https://media.roboflow.com/notebooks/examples/fast-sam-figure-1.png)\n", + "\n", + "The FastSAM authors claim it achieves comparable performance to the SAM method at 50 times the speed.\n", + "\n", + "![fast-segment-anything-model-figure-2](https://media.roboflow.com/notebooks/examples/fast-sam-figure-2.png)\n", + "\n", + "## Complementary Materials Covering SAM\n", + "\n", + "---\n", + "\n", + "[![GitHub](https://badges.aleen42.com/src/github.svg)](https://github.com/facebookresearch/segment-anything)\n", + "[![arXiv](https://img.shields.io/badge/arXiv-2304.02643-b31b1b.svg)](https://arxiv.org/abs/2304.02643)\n", + "[![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-segment-anything-with-sam.ipynb)\n", + "[![YouTube](https://badges.aleen42.com/src/youtube.svg)](https://youtu.be/D-D6ZmadzPE)\n", + "[![Roboflow](https://raw.githubusercontent.com/roboflow-ai/notebooks/main/assets/badges/roboflow-blogpost.svg)](https://blog.roboflow.com/how-to-use-segment-anything-model-sam)\n", + "\n", + "## Steps in this Tutorial\n", + "\n", + "In this tutorial, we are going to cover:\n", + "\n", + "- Before you start\n", + "- Install FastSAM, SAM, and other dependencies\n", + "- Download FastSAM and SAM weights\n", + "- Download example data\n", + "- Imports\n", + "- Load FastSAM\n", + "- FastSAM inference\n", + "- FastSAM box prompt inference\n", + "- FastSAM point prompt inference\n", + "- FastSAM text prompt inference\n", + "- SAM vs FastSAM\n", + "- Roboflow benchmark dataset\n", + "\n", + "## 🔥 Let's begin!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W4u6KjiVdNZM" + }, + "source": [ + "## Before you start\n", + "\n", + "Let's make sure that we have access to GPU. We can use `nvidia-smi` command to do that. In case of any problems navigate to `Edit` -> `Notebook settings` -> `Hardware accelerator`, set it to `GPU`, and then click `Save`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Frcrk09FhJeV", + "outputId": "683a378e-4a8f-4239-901b-037279e92e2b" + }, + "outputs": [], + "source": [ + "!nvidia-smi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PUACTM2zdmJI" + }, + "source": [ + "**NOTE:** To make it easier for us to manage datasets, images and models we create a `HOME` constant." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PRtHQpTNdnm7", + "outputId": "2824c70d-dcf9-4006-b32d-adc6797fd708" + }, + "outputs": [], + "source": [ + "import os\n", + "HOME = os.getcwd()\n", + "print(\"HOME:\", HOME)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YN3DPGZSn57p" + }, + "source": [ + "## Install FastSAM, SAM, and other dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vr3kcq1KfFir", + "outputId": "234e5336-ba28-4114-d9e7-efd7f2752ca8" + }, + "outputs": [], + "source": [ + "%cd {HOME}\n", + "\n", + "# install FastSAM\n", + "!git clone https://github.com/CASIA-IVA-Lab/FastSAM.git\n", + "!pip -q install -r FastSAM/requirements.txt\n", + "# install CLIP\n", + "!pip -q install git+https://github.com/openai/CLIP.git\n", + "# install SAM\n", + "!pip -q install git+https://github.com/facebookresearch/segment-anything.git\n", + "# install other dependencies\n", + "!pip -q install roboflow supervision jupyter_bbox_widget" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BMmgSfDWfFis" + }, + "source": [ + "## Download FastSAM and SAM weights" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ADpOBElSfFis", + "outputId": "b3d89d4c-e2ad-4f5e-a910-b52695263151" + }, + "outputs": [], + "source": [ + "!mkdir -p {HOME}/weights\n", + "!wget -P {HOME}/weights -q https://huggingface.co/spaces/An-619/FastSAM/resolve/main/weights/FastSAM.pt\n", + "!wget -P {HOME}/weights -q https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth\n", + "!ls -lh {HOME}/weights" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "W6w1ck4z72sn" + }, + "outputs": [], + "source": [ + "FAST_SAM_CHECKPOINT_PATH = f\"{HOME}/weights/FastSAM.pt\"\n", + "SAM_SAM_CHECKPOINT_PATH = f\"{HOME}/weights/sam_vit_h_4b8939.pth\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t1ef2h1R9WMb" + }, + "source": [ + "## Download example data\n", + "\n", + "**NONE:** Let's download few example images. Feel free to use your images or videos." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CL4gVrnl9dmR", + "outputId": "db8f78d2-eaf4-41de-9d1f-e5fd6b588a87" + }, + "outputs": [], + "source": [ + "!mkdir -p {HOME}/data\n", + "!wget -P {HOME}/data -q https://media.roboflow.com/notebooks/examples/dog.jpeg\n", + "!wget -P {HOME}/data -q https://media.roboflow.com/notebooks/examples/robot.jpeg\n", + "!ls -lh {HOME}/data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J_a_icikYiVN" + }, + "source": [ + "## Imports\n", + "\n", + "**NOTE:** `FastSAM` code is not distributed via `pip` not it is packaged. Make sure to run code below from `{HOME}/FastSAM` directory. ⚠️" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u_NVTXOwYppS", + "outputId": "b86184de-5827-4b9c-eb5c-959539abe699" + }, + "outputs": [], + "source": [ + "%cd {HOME}/FastSAM\n", + "\n", + "import os\n", + "import cv2\n", + "import torch\n", + "import roboflow\n", + "import base64\n", + "\n", + "import supervision as sv\n", + "import numpy as np\n", + "\n", + "from roboflow import Roboflow\n", + "from fastsam import FastSAM, FastSAMPrompt\n", + "from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a_Iizsy07pb7" + }, + "source": [ + "## Load FastSAM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ao8tVDVq7ebG" + }, + "outputs": [], + "source": [ + "DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n", + "print(f\"DEVICE = {DEVICE}\")\n", + "fast_sam = FastSAM(FAST_SAM_CHECKPOINT_PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SvsRFtw_--Nn" + }, + "source": [ + "## FastSAM inference\n", + "\n", + "* `retina_masks=True` determines whether the model uses retina masks for generating segmentation masks.\n", + "* `imgsz=1024` sets the input image size to 1024x1024 pixels for processing by the model.\n", + "* `conf=0.4` sets the minimum confidence threshold for object detection.\n", + "* `iou=0.9` sets the minimum intersection over union threshold for non-maximum suppression to filter out duplicate detections." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "L49P5e-0Iaea" + }, + "outputs": [], + "source": [ + "IMAGE_PATH = f\"{HOME}/data/dog.jpeg\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "os.makedirs(f\"{HOME}/output\", exist_ok=True)\n", + "output_image_path = f\"{HOME}/output/output-{os.path.basename(IMAGE_PATH)}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KV4XzCg1_fAS", + "outputId": "1df75d90-cf73-4d78-feed-01911a81f6e4" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.4,\n", + " iou=0.9)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.everything_prompt()\n", + "prompt_process.plot(annotations=masks, output=f\"{HOME}/output\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uPrxqbaBOBMw" + }, + "source": [ + "**NOTE:** `prompt_process.everything_prompt` returns `torch.Tensor` when DEVICE = 'cuda:0'. `prompt_process.everything_prompt` returns `numpy.ndarray` when DEVICE = 'cpu'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Convert masks to boolean (True/False)\n", + "def masks_to_bool(masks):\n", + " if type(masks) == np.ndarray:\n", + " masks = masks.astype(bool)\n", + " else:\n", + " masks = masks.cpu().numpy().astype(bool)\n", + " return masks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PUJkojNiKBQa" + }, + "outputs": [], + "source": [ + "def annotate_image(image_path: str, masks: np.ndarray) -> np.ndarray:\n", + " image = cv2.imread(image_path)\n", + "\n", + " xyxy = sv.mask_to_xyxy(masks=masks)\n", + " detections = sv.Detections(xyxy=xyxy, mask=masks)\n", + "\n", + " mask_annotator = sv.MaskAnnotator(color_lookup = sv.ColorLookup.INDEX)\n", + " return mask_annotator.annotate(scene=image.copy(), detections=detections)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "wwtmiH6pLA8N", + "outputId": "17948850-f051-4a1f-b2b8-d01c2a1f4f20" + }, + "outputs": [], + "source": [ + "masks = masks_to_bool(masks)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jYkdczKoRUVB" + }, + "source": [ + "**NOTE:** The quality of the generated masks is quite poor. Let's see if we can improve it by manipulating the parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dDFGi6EhO2ul", + "outputId": "f5a03357-eb37-446e-dce7-0659e4c2a368" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.everything_prompt()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "OiBPjQW-O59c", + "outputId": "afb011fc-933e-4d69-b4dd-e5e2713f407b" + }, + "outputs": [], + "source": [ + "masks = masks_to_bool(masks)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GoymBQ16KJbv" + }, + "source": [ + "## FastSAM box prompt inference" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w1_T5SXpMNku" + }, + "source": [ + "### Draw Box" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "04-1G1WrMJUm" + }, + "outputs": [], + "source": [ + "# helper function that loads an image before adding it to the widget\n", + "\n", + "def encode_image(filepath):\n", + " with open(filepath, 'rb') as f:\n", + " image_bytes = f.read()\n", + " encoded = str(base64.b64encode(image_bytes), 'utf-8')\n", + " return \"data:image/jpg;base64,\"+encoded" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xrxd-Ug9MSWG" + }, + "source": [ + "**NOTE:** Execute cell below and use your mouse to draw bounding box on the image 👇" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000, + "referenced_widgets": [ + "a747e095e21448f29d8697fb6b1a7a74", + "6401ca8c2f574ba5ab62f0f06b9e0d06" + ] + }, + "id": "nLA7xmDIMTfx", + "outputId": "06b51195-d54f-44ed-9236-258e94d624c5" + }, + "outputs": [], + "source": [ + "IS_COLAB = True\n", + "\n", + "if IS_COLAB:\n", + " from google.colab import output\n", + " output.enable_custom_widget_manager()\n", + "\n", + "from jupyter_bbox_widget import BBoxWidget\n", + "\n", + "widget = BBoxWidget()\n", + "widget.image = encode_image(IMAGE_PATH)\n", + "widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "td6tYT4INOyD", + "outputId": "73a4e886-dcb8-447c-d3f7-1b3a6496e44d" + }, + "outputs": [], + "source": [ + "widget.bboxes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XfUvm9bwNUAe" + }, + "source": [ + "### Generate mask with FastSAM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bI6kPDSROBI-" + }, + "outputs": [], + "source": [ + "# default_box is going to be used if you will not draw any box on image above\n", + "default_box = {'x': 68, 'y': 247, 'width': 555, 'height': 678, 'label': ''}\n", + "\n", + "box = widget.bboxes[0] if widget.bboxes else default_box\n", + "box = [\n", + " box['x'],\n", + " box['y'],\n", + " box['x'] + box['width'],\n", + " box['y'] + box['height']\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LF879hDqO5iB", + "outputId": "4e7498c4-4007-4614-d46c-0e35608a04a2" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.box_prompt(bbox=box)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yt6i2sNJPEdk" + }, + "source": [ + "**NOTE:** `prompt_process.box_prompt` returns `numy.ndarray`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "EJThTct_PA68", + "outputId": "f6eddd24-f603-4702-8325-6cff7925fe0f" + }, + "outputs": [], + "source": [ + "masks = masks_to_bool(masks)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vR-THJthhWH5" + }, + "source": [ + "## FastSAM point prompt inference" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xyfpOPPwhpMy" + }, + "source": [ + "### Select point" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OC7d3pZ3hop9" + }, + "outputs": [], + "source": [ + "point = [405, 505]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rE5tV4e3h6nG" + }, + "source": [ + "### Generate mask with FastSAM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "31FIby6Zh-Wa", + "outputId": "a12c4d1c-837b-4b1b-9240-664797442413" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.point_prompt(points=[point], pointlabel=[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d7tpVo8CiM3s" + }, + "source": [ + "**NOTE:** `prompt_process.point_prompt` returns `numy.ndarray`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "lMVpGxShiPky", + "outputId": "224d84ee-e664-400b-c0df-49507d229f62" + }, + "outputs": [], + "source": [ + "masks = masks_to_bool(masks)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HOC7v6ATHEr6" + }, + "source": [ + "## FastSAM text prompt inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4CzLLtXOHeGk", + "outputId": "cecc50d8-e2be-4b33-850a-a2442e3e30b6" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.text_prompt(text='cap')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KYW79zD0OKsv" + }, + "source": [ + "**NOTE:** `prompt_process.text_prompt` returns `numy.ndarray`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "zODY5xU1LMCb", + "outputId": "a01540a7-deb3-4555-916c-2ac59928e0f3" + }, + "outputs": [], + "source": [ + "masks = masks_to_bool(masks)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XFOUWUMZEk7m" + }, + "source": [ + "## SAM vs FastSAM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7PKBZ7Hhahhw" + }, + "outputs": [], + "source": [ + "IMAGE_PATH = f\"{HOME}/data/robot.jpeg\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c48hRY7dVJeL" + }, + "source": [ + "### Load SAM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PdKpFCCmaETG" + }, + "outputs": [], + "source": [ + "MODEL_TYPE = \"vit_h\"\n", + "\n", + "sam = sam_model_registry[MODEL_TYPE](checkpoint=SAM_SAM_CHECKPOINT_PATH).to(device=DEVICE)\n", + "mask_generator = SamAutomaticMaskGenerator(sam)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V5bPGvtBaewO" + }, + "source": [ + "### SAM inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0HgWsk1yalow" + }, + "outputs": [], + "source": [ + "image_bgr = cv2.imread(IMAGE_PATH)\n", + "image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)\n", + "\n", + "sam_result = mask_generator.generate(image_rgb)\n", + "sam_detections = sv.Detections.from_sam(sam_result=sam_result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t03RrSzMasN6" + }, + "source": [ + "### FastSAM inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xuFGLw8MavQI", + "outputId": "b0118973-1e29-419d-b7c0-2933b97d7ce5" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.everything_prompt()\n", + "masks = masks_to_bool(masks)\n", + "xyxy = sv.mask_to_xyxy(masks=masks)\n", + "fast_sam_detections = sv.Detections(xyxy=xyxy, mask=masks)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IYn8xKySbBnc" + }, + "source": [ + "### FastSAM vs. SAM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 321 + }, + "id": "c2Qj7sLjbDvo", + "outputId": "e85e3ed3-8b90-409d-cf95-877a6a9b2034" + }, + "outputs": [], + "source": [ + "mask_annotator = sv.MaskAnnotator(color_lookup = sv.ColorLookup.INDEX)\n", + "\n", + "sam_result = mask_annotator.annotate(scene=image_bgr.copy(), detections=sam_detections)\n", + "fast_sam_result = mask_annotator.annotate(scene=image_bgr.copy(), detections=fast_sam_detections)\n", + "\n", + "sv.plot_images_grid(\n", + " images=[sam_result, fast_sam_result],\n", + " grid_size=(1, 2),\n", + " titles=['SAM', 'FastSAM']\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MfU4NQiGWQMC" + }, + "source": [ + "**NOTE:** There is a lot going on in our test image. Let's plot our masks against a blank background." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 321 + }, + "id": "PIrywlF5QRPz", + "outputId": "8e7c84ae-7869-4523-847a-0678fd1aea53" + }, + "outputs": [], + "source": [ + "mask_annotator = sv.MaskAnnotator(color_lookup = sv.ColorLookup.INDEX)\n", + "\n", + "sam_result = mask_annotator.annotate(scene=np.zeros_like(image_bgr), detections=sam_detections)\n", + "fast_sam_result = mask_annotator.annotate(scene=np.zeros_like(image_bgr), detections=fast_sam_detections)\n", + "\n", + "sv.plot_images_grid(\n", + " images=[sam_result, fast_sam_result],\n", + " grid_size=(1, 2),\n", + " titles=['SAM', 'FastSAM']\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "51Fs3sDGQmIt" + }, + "source": [ + "### Mask diff\n", + "\n", + "**NOTE:** Let's try to understand which masks generated by SAM were not generated by FastSAM." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "O5bIUAhYQrqZ" + }, + "outputs": [], + "source": [ + "def compute_iou(mask1, mask2):\n", + " intersection = np.logical_and(mask1, mask2)\n", + " union = np.logical_or(mask1, mask2)\n", + " return np.sum(intersection) / np.sum(union)\n", + "\n", + "def filter_masks(mask_array1, mask_array2, threshold):\n", + " return np.array([mask1 for mask1 in mask_array1 if max(compute_iou(mask1, mask2) for mask2 in mask_array2) < threshold])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 420 + }, + "id": "1GM4bL9sRbOm", + "outputId": "421fb4f5-7a42-461b-ba1e-7cd46d2d7bd7" + }, + "outputs": [], + "source": [ + "diff_masks = filter_masks(sam_detections.mask, fast_sam_detections.mask, 0.5)\n", + "diff_xyxy = sv.mask_to_xyxy(masks=diff_masks)\n", + "diff_detections = sv.Detections(xyxy=diff_xyxy, mask=diff_masks)\n", + "\n", + "mask_annotator = sv.MaskAnnotator(color_lookup = sv.ColorLookup.INDEX)\n", + "diff_result = mask_annotator.annotate(scene=np.zeros_like(image_bgr), detections=diff_detections)\n", + "sv.plot_image(image=diff_result, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0lPZAZZulhF8" + }, + "source": [ + "## Roboflow Benchmark Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LWN6XwPuSZBY", + "outputId": "86b18c0e-f6d6-486b-eee6-d004f13f531e" + }, + "outputs": [], + "source": [ + "%cd {HOME}\n", + "\n", + "roboflow.login()\n", + "rf = Roboflow()\n", + "\n", + "project = rf.workspace(\"roboticfish\").project(\"underwater_object_detection\")\n", + "dataset = project.version(10).download(\"yolov8\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "T6-PaxWXTZ69" + }, + "outputs": [], + "source": [ + "IMAGE_PATH = os.path.join(dataset.location, 'train', 'images', 'IMG_2311_jpeg_jpg.rf.09ae6820eaff21dc838b1f9b6b20342b.jpg')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "p-gY5jf1Tife", + "outputId": "630d35d8-26f4-4342-8d82-c7b61be26fe8" + }, + "outputs": [], + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.text_prompt(text='Penguin')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "3tBIH0yubHsW", + "outputId": "fc4d0a6a-53c1-4a2b-91f0-75c8f78e10fd" + }, + "outputs": [], + "source": [ + "masks = masks_to_bool(masks)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gP9gEw9Tp8GJ" + }, + "source": [ + "## 🏆 Congratulations\n", + "\n", + "### Learning Resources\n", + "\n", + "Roboflow has produced many resources that you may find interesting as you advance your knowledge of computer vision:\n", + "\n", + "- [Roboflow Notebooks](https://github.com/roboflow/notebooks): A repository of over 20 notebooks that walk through how to train custom models with a range of model types, from YOLOv7 to SegFormer.\n", + "- [Roboflow YouTube](https://www.youtube.com/c/Roboflow): Our library of videos featuring deep dives into the latest in computer vision, detailed tutorials that accompany our notebooks, and more.\n", + "- [Roboflow Discuss](https://discuss.roboflow.com/): Have a question about how to do something on Roboflow? Ask your question on our discussion forum.\n", + "- [Roboflow Models](https://roboflow.com): Learn about state-of-the-art models and their performance. Find links and tutorials to guide your learning.\n", + "\n", + "### Convert data formats\n", + "\n", + "Roboflow provides free utilities to convert data between dozens of popular computer vision formats. Check out [Roboflow Formats](https://roboflow.com/formats) to find tutorials on how to convert data between formats in a few clicks.\n", + "\n", + "### Connect computer vision to your project logic\n", + "\n", + "[Roboflow Templates](https://roboflow.com/templates) is a public gallery of code snippets that you can use to connect computer vision to your project logic. Code snippets range from sending emails after inference to measuring object distance between detections." + ] + } + ] + .map(fromJupyterCell) + , + [ + { + "cell_type": "markdown", + "metadata": { + "id": "F0Wut2G5kz2Z" + }, + "source": [ + "[![Roboflow Notebooks](https://ik.imagekit.io/roboflow/notebooks/template/bannertest2-2.png?ik-sdk-version=javascript-1.4.3&updatedAt=1672932710194)](https://github.com/roboflow/notebooks)\n", + "\n", + "# Fast Segment Anything Model (FastSAM)\n", + "\n", + "---\n", + "\n", + "[![GitHub](https://badges.aleen42.com/src/github.svg)](https://github.com/CASIA-IVA-Lab/FastSAM)\n", + "[![arXiv](https://img.shields.io/badge/arXiv-2306.12156-b31b1b.svg)](https://arxiv.org/pdf/2306.12156.pdf)\n", + "[![YouTube](https://badges.aleen42.com/src/youtube.svg)](https://youtu.be/yHNPyqazYYU)\n", + "[![Roboflow](https://raw.githubusercontent.com/roboflow-ai/notebooks/main/assets/badges/roboflow-blogpost.svg)](https://blog.roboflow.com/how-to-use-fastsam)\n", + "\n", + "The Fast Segment Anything Model (FastSAM) is a CNN Segment Anything Model trained by only 2% of the SA-1B dataset published by SAM authors.\n", + "\n", + "![fast-segment-anything-model-figure-1](https://media.roboflow.com/notebooks/examples/fast-sam-figure-1.png)\n", + "\n", + "The FastSAM authors claim it achieves comparable performance to the SAM method at 50 times the speed.\n", + "\n", + "![fast-segment-anything-model-figure-2](https://media.roboflow.com/notebooks/examples/fast-sam-figure-2.png)\n", + "\n", + "## Complementary Materials Covering SAM\n", + "\n", + "---\n", + "\n", + "[![GitHub](https://badges.aleen42.com/src/github.svg)](https://github.com/facebookresearch/segment-anything)\n", + "[![arXiv](https://img.shields.io/badge/arXiv-2304.02643-b31b1b.svg)](https://arxiv.org/abs/2304.02643)\n", + "[![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/how-to-segment-anything-with-sam.ipynb)\n", + "[![YouTube](https://badges.aleen42.com/src/youtube.svg)](https://youtu.be/D-D6ZmadzPE)\n", + "[![Roboflow](https://raw.githubusercontent.com/roboflow-ai/notebooks/main/assets/badges/roboflow-blogpost.svg)](https://blog.roboflow.com/how-to-use-segment-anything-model-sam)\n", + "\n", + "## Steps in this Tutorial\n", + "\n", + "In this tutorial, we are going to cover:\n", + "\n", + "- Before you start\n", + "- Install FastSAM, SAM, and other dependencies\n", + "- Download FastSAM and SAM weights\n", + "- Download example data\n", + "- Imports\n", + "- Load FastSAM\n", + "- FastSAM inference\n", + "- FastSAM box prompt inference\n", + "- FastSAM point prompt inference\n", + "- FastSAM text prompt inference\n", + "- SAM vs FastSAM\n", + "- Roboflow benchmark dataset\n", + "\n", + "## 🔥 Let's begin!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W4u6KjiVdNZM" + }, + "source": [ + "## Before you start\n", + "\n", + "Let's make sure that we have access to GPU. We can use `nvidia-smi` command to do that. In case of any problems navigate to `Edit` -> `Notebook settings` -> `Hardware accelerator`, set it to `GPU`, and then click `Save`." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Frcrk09FhJeV", + "outputId": "683a378e-4a8f-4239-901b-037279e92e2b" + }, + "source": [ + "!nvidia-smi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PUACTM2zdmJI" + }, + "source": [ + "**NOTE:** To make it easier for us to manage datasets, images and models we create a `HOME` constant." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PRtHQpTNdnm7", + "outputId": "2824c70d-dcf9-4006-b32d-adc6797fd708" + }, + "source": [ + "import os\n", + "HOME = os.getcwd()\n", + "print(\"HOME:\", HOME)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YN3DPGZSn57p" + }, + "source": [ + "## Install FastSAM, SAM, and other dependencies" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vr3kcq1KfFir", + "outputId": "234e5336-ba28-4114-d9e7-efd7f2752ca8" + }, + "source": [ + "%cd {HOME}\n", + "\n", + "# install FastSAM\n", + "!git clone https://github.com/CASIA-IVA-Lab/FastSAM.git\n", + "!pip -q install -r FastSAM/requirements.txt\n", + "# install CLIP\n", + "!pip -q install git+https://github.com/openai/CLIP.git\n", + "# install SAM\n", + "!pip -q install git+https://github.com/facebookresearch/segment-anything.git\n", + "# install other dependencies\n", + "!pip -q install roboflow supervision jupyter_bbox_widget" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BMmgSfDWfFis" + }, + "source": [ + "## Download FastSAM and SAM weights" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ADpOBElSfFis", + "outputId": "b3d89d4c-e2ad-4f5e-a910-b52695263151" + }, + "source": [ + "!mkdir -p {HOME}/weights\n", + "!wget -P {HOME}/weights -q https://huggingface.co/spaces/An-619/FastSAM/resolve/main/weights/FastSAM.pt\n", + "!wget -P {HOME}/weights -q https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth\n", + "!ls -lh {HOME}/weights" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "W6w1ck4z72sn" + }, + "source": [ + "FAST_SAM_CHECKPOINT_PATH = f\"{HOME}/weights/FastSAM.pt\"\n", + "SAM_SAM_CHECKPOINT_PATH = f\"{HOME}/weights/sam_vit_h_4b8939.pth\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t1ef2h1R9WMb" + }, + "source": [ + "## Download example data\n", + "\n", + "**NONE:** Let's download few example images. Feel free to use your images or videos." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CL4gVrnl9dmR", + "outputId": "db8f78d2-eaf4-41de-9d1f-e5fd6b588a87" + }, + "source": [ + "!mkdir -p {HOME}/data\n", + "!wget -P {HOME}/data -q https://media.roboflow.com/notebooks/examples/dog.jpeg\n", + "!wget -P {HOME}/data -q https://media.roboflow.com/notebooks/examples/robot.jpeg\n", + "!ls -lh {HOME}/data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J_a_icikYiVN" + }, + "source": [ + "## Imports\n", + "\n", + "**NOTE:** `FastSAM` code is not distributed via `pip` not it is packaged. Make sure to run code balow from `{HOME}/FastSAM` directory. ⚠️" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u_NVTXOwYppS", + "outputId": "b86184de-5827-4b9c-eb5c-959539abe699" + }, + "source": [ + "%cd {HOME}/FastSAM\n", + "\n", + "import os\n", + "import cv2\n", + "import torch\n", + "import roboflow\n", + "import base64\n", + "\n", + "import supervision as sv\n", + "import numpy as np\n", + "\n", + "from roboflow import Roboflow\n", + "from fastsam import FastSAM, FastSAMPrompt\n", + "from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a_Iizsy07pb7" + }, + "source": [ + "## Load FastSAM" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "ao8tVDVq7ebG" + }, + "source": [ + "DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n", + "\n", + "fast_sam = FastSAM(FAST_SAM_CHECKPOINT_PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SvsRFtw_--Nn" + }, + "source": [ + "## FastSAM inference\n", + "\n", + "* `retina_masks=True` determines whether the model uses retina masks for generating segmentation masks.\n", + "* `imgsz=1024` sets the input image size to 1024x1024 pixels for processing by the model.\n", + "* `conf=0.4` sets the minimum confidence threshold for object detection.\n", + "* `iou=0.9` sets the minimum intersection over union threshold for non-maximum suppression to filter out duplicate detections." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "L49P5e-0Iaea" + }, + "source": [ + "IMAGE_PATH = f\"{HOME}/data/dog.jpeg\"" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KV4XzCg1_fAS", + "outputId": "1df75d90-cf73-4d78-feed-01911a81f6e4" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.4,\n", + " iou=0.9)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.everything_prompt()\n", + "prompt_process.plot(annotations=masks, output=f\"{HOME}/output\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uPrxqbaBOBMw" + }, + "source": [ + "**NOTE:** `prompt_process.everything_prompt` returns `torch.Tensor`" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "PUJkojNiKBQa" + }, + "source": [ + "def annotate_image(image_path: str, masks: np.ndarray) -> np.ndarray:\n", + " image = cv2.imread(image_path)\n", + "\n", + " xyxy = sv.mask_to_xyxy(masks=masks)\n", + " detections = sv.Detections(xyxy=xyxy, mask=masks)\n", + "\n", + " mask_annotator = sv.MaskAnnotator()\n", + " return mask_annotator.annotate(scene=image.copy(), detections=detections)" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "wwtmiH6pLA8N", + "outputId": "17948850-f051-4a1f-b2b8-d01c2a1f4f20" + }, + "source": [ + "masks = masks.cpu().numpy().astype(bool)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jYkdczKoRUVB" + }, + "source": [ + "**NOTE:** The quality of the generated masks is quite poor. Let's see if we can improve it by manipulating the parameters." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dDFGi6EhO2ul", + "outputId": "f5a03357-eb37-446e-dce7-0659e4c2a368" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.everything_prompt()" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "OiBPjQW-O59c", + "outputId": "afb011fc-933e-4d69-b4dd-e5e2713f407b" + }, + "source": [ + "masks = masks.cpu().numpy().astype(bool)\n", + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GoymBQ16KJbv" + }, + "source": [ + "## FastSAM box prompt inference" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w1_T5SXpMNku" + }, + "source": [ + "### Draw Box" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "04-1G1WrMJUm" + }, + "source": [ + "# helper function that loads an image before adding it to the widget\n", + "\n", + "def encode_image(filepath):\n", + " with open(filepath, 'rb') as f:\n", + " image_bytes = f.read()\n", + " encoded = str(base64.b64encode(image_bytes), 'utf-8')\n", + " return \"data:image/jpg;base64,\"+encoded" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xrxd-Ug9MSWG" + }, + "source": [ + "**NOTE:** Execute cell below and use your mouse to draw bounding box on the image 👇" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000, + "referenced_widgets": [ + "a747e095e21448f29d8697fb6b1a7a74", + "6401ca8c2f574ba5ab62f0f06b9e0d06" + ] + }, + "id": "nLA7xmDIMTfx", + "outputId": "06b51195-d54f-44ed-9236-258e94d624c5" + }, + "source": [ + "IS_COLAB = True\n", + "\n", + "if IS_COLAB:\n", + " from google.colab import output\n", + " output.enable_custom_widget_manager()\n", + "\n", + "from jupyter_bbox_widget import BBoxWidget\n", + "\n", + "widget = BBoxWidget()\n", + "widget.image = encode_image(IMAGE_PATH)\n", + "widget" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "td6tYT4INOyD", + "outputId": "73a4e886-dcb8-447c-d3f7-1b3a6496e44d" + }, + "source": [ + "widget.bboxes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XfUvm9bwNUAe" + }, + "source": [ + "### Generate mask with FastSAM" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "bI6kPDSROBI-" + }, + "source": [ + "# default_box is going to be used if you will not draw any box on image above\n", + "default_box = {'x': 68, 'y': 247, 'width': 555, 'height': 678, 'label': ''}\n", + "\n", + "box = widget.bboxes[0] if widget.bboxes else default_box\n", + "box = [\n", + " box['x'],\n", + " box['y'],\n", + " box['x'] + box['width'],\n", + " box['y'] + box['height']\n", + "]" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LF879hDqO5iB", + "outputId": "4e7498c4-4007-4614-d46c-0e35608a04a2" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.box_prompt(bbox=box)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yt6i2sNJPEdk" + }, + "source": [ + "**NOTE:** `prompt_process.box_prompt` returns `numy.ndarray`" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "EJThTct_PA68", + "outputId": "f6eddd24-f603-4702-8325-6cff7925fe0f" + }, + "source": [ + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vR-THJthhWH5" + }, + "source": [ + "## FastSAM point prompt inference" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xyfpOPPwhpMy" + }, + "source": [ + "### Select point" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "OC7d3pZ3hop9" + }, + "source": [ + "point = [405, 505]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rE5tV4e3h6nG" + }, + "source": [ + "### Generate mask with FastSAM" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "31FIby6Zh-Wa", + "outputId": "a12c4d1c-837b-4b1b-9240-664797442413" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.point_prompt(points=[point], pointlabel=[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d7tpVo8CiM3s" + }, + "source": [ + "**NOTE:** `prompt_process.point_prompt` returns `numy.ndarray`" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "lMVpGxShiPky", + "outputId": "224d84ee-e664-400b-c0df-49507d229f62" + }, + "source": [ + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HOC7v6ATHEr6" + }, + "source": [ + "## FastSAM text prompt inference" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4CzLLtXOHeGk", + "outputId": "cecc50d8-e2be-4b33-850a-a2442e3e30b6" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.text_prompt(text='cap')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KYW79zD0OKsv" + }, + "source": [ + "**NOTE:** `prompt_process.text_prompt` returns `numy.ndarray`" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "zODY5xU1LMCb", + "outputId": "a01540a7-deb3-4555-916c-2ac59928e0f3" + }, + "source": [ + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XFOUWUMZEk7m" + }, + "source": [ + "## SAM vs FastSAM" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "7PKBZ7Hhahhw" + }, + "source": [ + "IMAGE_PATH = f\"{HOME}/data/robot.jpeg\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c48hRY7dVJeL" + }, + "source": [ + "### Load SAM" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "PdKpFCCmaETG" + }, + "source": [ + "MODEL_TYPE = \"vit_h\"\n", + "\n", + "sam = sam_model_registry[MODEL_TYPE](checkpoint=SAM_SAM_CHECKPOINT_PATH).to(device=DEVICE)\n", + "mask_generator = SamAutomaticMaskGenerator(sam)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V5bPGvtBaewO" + }, + "source": [ + "### SAM inference" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "0HgWsk1yalow" + }, + "source": [ + "image_bgr = cv2.imread(IMAGE_PATH)\n", + "image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)\n", + "\n", + "sam_result = mask_generator.generate(image_rgb)\n", + "sam_detections = sv.Detections.from_sam(sam_result=sam_result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t03RrSzMasN6" + }, + "source": [ + "### FastSAM inference" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xuFGLw8MavQI", + "outputId": "b0118973-1e29-419d-b7c0-2933b97d7ce5" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.everything_prompt()\n", + "masks = masks.cpu().numpy().astype(bool)\n", + "xyxy = sv.mask_to_xyxy(masks=masks)\n", + "fast_sam_detections = sv.Detections(xyxy=xyxy, mask=masks)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IYn8xKySbBnc" + }, + "source": [ + "### FastSAM vs. SAM" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 321 + }, + "id": "c2Qj7sLjbDvo", + "outputId": "e85e3ed3-8b90-409d-cf95-877a6a9b2034" + }, + "source": [ + "mask_annotator = sv.MaskAnnotator()\n", + "\n", + "sam_result = mask_annotator.annotate(scene=image_bgr.copy(), detections=sam_detections)\n", + "fast_sam_result = mask_annotator.annotate(scene=image_bgr.copy(), detections=fast_sam_detections)\n", + "\n", + "sv.plot_images_grid(\n", + " images=[sam_result, fast_sam_result],\n", + " grid_size=(1, 2),\n", + " titles=['SAM', 'FastSAM']\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MfU4NQiGWQMC" + }, + "source": [ + "**NOTE:** There is a lot going on in our test image. Let's plot our masks against a blank background." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 321 + }, + "id": "PIrywlF5QRPz", + "outputId": "8e7c84ae-7869-4523-847a-0678fd1aea53" + }, + "source": [ + "mask_annotator = sv.MaskAnnotator()\n", + "\n", + "sam_result = mask_annotator.annotate(scene=np.zeros_like(image_bgr), detections=sam_detections)\n", + "fast_sam_result = mask_annotator.annotate(scene=np.zeros_like(image_bgr), detections=fast_sam_detections)\n", + "\n", + "sv.plot_images_grid(\n", + " images=[sam_result, fast_sam_result],\n", + " grid_size=(1, 2),\n", + " titles=['SAM', 'FastSAM']\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "51Fs3sDGQmIt" + }, + "source": [ + "### Mask diff\n", + "\n", + "**NOTE:** Let's try to understand which masks generated by SAM were not generated by FastSAM." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "O5bIUAhYQrqZ" + }, + "source": [ + "def compute_iou(mask1, mask2):\n", + " intersection = np.logical_and(mask1, mask2)\n", + " union = np.logical_or(mask1, mask2)\n", + " return np.sum(intersection) / np.sum(union)\n", + "\n", + "def filter_masks(mask_array1, mask_array2, threshold):\n", + " return np.array([mask1 for mask1 in mask_array1 if max(compute_iou(mask1, mask2) for mask2 in mask_array2) < threshold])" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 420 + }, + "id": "1GM4bL9sRbOm", + "outputId": "421fb4f5-7a42-461b-ba1e-7cd46d2d7bd7" + }, + "source": [ + "diff_masks = filter_masks(sam_detections.mask, fast_sam_detections.mask, 0.5)\n", + "diff_xyxy = sv.mask_to_xyxy(masks=diff_masks)\n", + "diff_detections = sv.Detections(xyxy=diff_xyxy, mask=diff_masks)\n", + "\n", + "mask_annotator = sv.MaskAnnotator()\n", + "diff_result = mask_annotator.annotate(scene=np.zeros_like(image_bgr), detections=diff_detections)\n", + "sv.plot_image(image=diff_result, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0lPZAZZulhF8" + }, + "source": [ + "## Roboflow Benchmark Dataset" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LWN6XwPuSZBY", + "outputId": "86b18c0e-f6d6-486b-eee6-d004f13f531e" + }, + "source": [ + "%cd {HOME}\n", + "\n", + "roboflow.login()\n", + "rf = Roboflow()\n", + "\n", + "project = rf.workspace(\"roboticfish\").project(\"underwater_object_detection\")\n", + "dataset = project.version(10).download(\"yolov8\")" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "T6-PaxWXTZ69" + }, + "source": [ + "IMAGE_PATH = os.path.join(dataset.location, 'train', 'images', 'IMG_2311_jpeg_jpg.rf.09ae6820eaff21dc838b1f9b6b20342b.jpg')" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "p-gY5jf1Tife", + "outputId": "630d35d8-26f4-4342-8d82-c7b61be26fe8" + }, + "source": [ + "results = fast_sam(\n", + " source=IMAGE_PATH,\n", + " device=DEVICE,\n", + " retina_masks=True,\n", + " imgsz=1024,\n", + " conf=0.5,\n", + " iou=0.6)\n", + "prompt_process = FastSAMPrompt(IMAGE_PATH, results, device=DEVICE)\n", + "masks = prompt_process.text_prompt(text='Penguin')" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 653 + }, + "id": "3tBIH0yubHsW", + "outputId": "fc4d0a6a-53c1-4a2b-91f0-75c8f78e10fd" + }, + "source": [ + "annotated_image=annotate_image(image_path=IMAGE_PATH, masks=masks)\n", + "sv.plot_image(image=annotated_image, size=(8, 8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gP9gEw9Tp8GJ" + }, + "source": [ + "## 🏆 Congratulations\n", + "\n", + "### Learning Resources\n", + "\n", + "Roboflow has produced many resources that you may find interesting as you advance your knowledge of computer vision:\n", + "\n", + "- [Roboflow Notebooks](https://github.com/roboflow/notebooks): A repository of over 20 notebooks that walk through how to train custom models with a range of model types, from YOLOv7 to SegFormer.\n", + "- [Roboflow YouTube](https://www.youtube.com/c/Roboflow): Our library of videos featuring deep dives into the latest in computer vision, detailed tutorials that accompany our notebooks, and more.\n", + "- [Roboflow Discuss](https://discuss.roboflow.com/): Have a question about how to do something on Roboflow? Ask your question on our discussion forum.\n", + "- [Roboflow Models](https://roboflow.com): Learn about state-of-the-art models and their performance. Find links and tutorials to guide your learning.\n", + "\n", + "### Convert data formats\n", + "\n", + "Roboflow provides free utilities to convert data between dozens of popular computer vision formats. Check out [Roboflow Formats](https://roboflow.com/formats) to find tutorials on how to convert data between formats in a few clicks.\n", + "\n", + "### Connect computer vision to your project logic\n", + "\n", + "[Roboflow Templates](https://roboflow.com/templates) is a public gallery of code snippets that you can use to connect computer vision to your project logic. Code snippets range from sending emails after inference to measuring object distance between detections." + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: 0 }, + { modified: 1, original: 1 }, + { modified: 2, original: 2 }, + { modified: 3, original: 3 }, + { modified: 4, original: 4 }, + { modified: 5, original: 5 }, + { modified: 6, original: 6 }, + { modified: 7, original: 7 }, + { modified: 8, original: 8 }, + { modified: 9, original: 9 }, + { modified: 10, original: 10 }, + { modified: 11, original: 11 }, + { modified: 12, original: 12 }, + { modified: 13, original: 13 }, + { modified: 14, original: 14 }, + { modified: 15, original: 15 }, + { modified: 16, original: 16 }, + { modified: 17, original: 17 }, + { modified: 18, original: 19 }, + { modified: 19, original: 20 }, + { modified: 20, original: 22 }, + { modified: 21, original: 23 }, + { modified: 22, original: 24 }, + { modified: 23, original: 25 }, + { modified: 24, original: 26 }, + { modified: 25, original: 27 }, + { modified: 26, original: 28 }, + { modified: 27, original: 29 }, + { modified: 28, original: 30 }, + { modified: 29, original: 31 }, + { modified: 30, original: 32 }, + { modified: 31, original: 33 }, + { modified: 32, original: 34 }, + { modified: 33, original: 35 }, + { modified: 34, original: 36 }, + { modified: 35, original: 37 }, + { modified: 36, original: 38 }, + { modified: 37, original: 39 }, + { modified: 38, original: 40 }, + { modified: 39, original: 41 }, + { modified: 40, original: 42 }, + { modified: 41, original: 43 }, + { modified: 42, original: 44 }, + { modified: 43, original: 45 }, + { modified: 44, original: 46 }, + { modified: 45, original: 47 }, + { modified: 46, original: 48 }, + { modified: 47, original: 49 }, + { modified: 48, original: 50 }, + { modified: 49, original: 51 }, + { modified: 50, original: 52 }, + { modified: 51, original: 53 }, + { modified: 52, original: 54 }, + { modified: 53, original: 55 }, + { modified: 54, original: 56 }, + { modified: 55, original: 57 }, + { modified: 56, original: 58 }, + { modified: 57, original: 59 }, + { modified: 58, original: 60 }, + { modified: 59, original: 61 }, + { modified: 60, original: 62 }, + { modified: 61, original: 63 }, + { modified: 62, original: 64 }, + { modified: 63, original: 65 }, + { modified: 64, original: 66 }, + { modified: 65, original: 67 }, + { modified: 66, original: 68 }, + { modified: 67, original: 69 }, + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 12, originalLength: 1, modifiedStart: 12, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 15, originalLength: 1, modifiedStart: 15, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 18, originalLength: 1, modifiedStart: 18, modifiedLength: 0 } satisfies IDiffChange, + { originalStart: 20, originalLength: 1, modifiedStart: 19, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 21, originalLength: 1, modifiedStart: 20, modifiedLength: 0 } satisfies IDiffChange, + { originalStart: 22, originalLength: 1, modifiedStart: 20, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 23, originalLength: 1, modifiedStart: 21, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 26, originalLength: 1, modifiedStart: 24, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 37, originalLength: 1, modifiedStart: 35, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 44, originalLength: 1, modifiedStart: 42, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 48, originalLength: 1, modifiedStart: 46, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 56, originalLength: 1, modifiedStart: 54, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 58, originalLength: 1, modifiedStart: 56, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 60, originalLength: 1, modifiedStart: 58, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 63, originalLength: 1, modifiedStart: 61, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 68, originalLength: 1, modifiedStart: 66, modifiedLength: 1 } satisfies IDiffChange, + // { originalStart: 53, originalLength: 0, modifiedStart: 53, modifiedLength: 2 } satisfies IDiffChange, + ]); + + }); + test('Insert a few cells (one on top), modify a few and completely misaligned', async () => { + const { mapping, diff } = await mapCells( + [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "from cachetools import cached, TTLCache\n", + "from dotenv import load_dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "GITHUB_API_URL = \"https://api.github.com\"\n", + "REPO_OWNER = \"microsoft\"\n", + "ISSUE = 123 # Replace with your milestone number\n", + "load_dotenv()\n", + "TOKEN = os.getenv(\"GH\")\n", + "\n", + "headers = {\n", + " \"Authorization\": f\"Bearer {TOKEN}\",\n", + " \"Accept\": \"application/vnd.github+json\"\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import csv\n", + "from datetime import datetime\n", + "import os\n", + "\n", + "def append_information(repo, open_issues_count, ):\n", + " file_exists = os.path.isfile(filename)\n", + " timestamp = datetime.utcnow().isoformat()\n", + "\n", + " # Open the file in append mode\n", + " with open(filename, 'a', newline='', encoding='utf-8') as csvfile:\n", + " writer = csv.writer(csvfile)\n", + "\n", + " # If the file didn't exist before, write the header row\n", + " if not file_exists:\n", + " writer.writerow([\n", + " \"timestamp\",\n", + " \"repo\",\n", + " \"open_issues_count\",\n", + " \"open_prs_count\",\n", + " \"bug_issues_count\",\n", + " \"feature_request_issues_count\",\n", + " \"invalid_issues_count\"\n", + " ])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_repo_issue_stats(owner, repo, token):\n", + " # Common GitHub GraphQL endpoint\n", + " url = \"https://api.github.com/graphql\"\n", + " headers = {\n", + " \"Authorization\": f\"Bearer {token}\",\n", + " \"Accept\": \"application/vnd.github.v4+json\"\n", + " }\n", + "\n", + " # First query: Get various issue/PR counts from the repository object\n", + " query_repo = \"\"\"\n", + " query ($owner: String!, $name: String!) {\n", + " repository(owner: $owner, name: $name) {\n", + " issues(states: OPEN) {\n", + " totalCount\n", + " }\n", + " pullRequests(states: OPEN) {\n", + " totalCount\n", + " }\n", + " bugIssues: issues(states: OPEN, labels: [\"bug\"]) {\n", + " totalCount\n", + " }\n", + " featureRequestIssues: issues(states: OPEN, labels: [\"feature-request\"]) {\n", + " totalCount\n", + " }\n", + " }\n", + " }\n", + " \"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for REPO in REPOS:\n", + " stats = get_repo_issue_stats(OWNER, REPO, TOKEN)\n", + " print(stats)\n", + "\n", + " append_metrics_to_csv(\n", + " filename=\"repo_metrics.csv\",\n", + " repo=f\"{OWNER}/{REPO}\",\n", + " open_issues_count=stats['open_issues_count'],\n", + " open_prs_count=stats['open_prs_count'],\n", + " bug_issues_count=stats['bug_issues_count'],\n", + " feature_request_issues_count=stats['feature_request_issues_count'],\n", + " invalid_issues_count=stats['invalid_issues_count']\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.graph_objs as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "def plot_repo_metrics(repo, csv_filename=\"repo_metrics.csv\"):\n", + " # Load the CSV data\n", + " df = pd.read_csv(csv_filename)\n", + "\n", + " fig.add_trace(go.Scatter(\n", + " x=df_repo['timestamp'],\n", + " y=df_repo['open_issues_count'],\n", + " mode='lines+markers',\n", + " name='Open Issues',\n", + " hovertemplate='Open Issues: %{y}
    Time: %{x}'\n", + " ))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for REPO in REPOS:\n", + " plot_repo_metrics(f\"{OWNER}/{REPO}\")" + ] + } + ] + .map(fromJupyterCell) + , + [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Fetch Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import sys\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "from cachetools import cached, TTLCache\n", + "from dotenv import load_dotenv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "GITHUB_API_URL = \"https://api.github.com\"\n", + "REPO_OWNER = \"microsoft\"\n", + "ISSUE = 123\n", + "load_dotenv()\n", + "TOKEN = os.getenv(\"GH\")\n", + "\n", + "headers = {\n", + " \"Authorization\": f\"Bearer {TOKEN}\",\n", + " \"Accept\": \"application/vnd.github+json\"\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import csv\n", + "from datetime import datetime\n", + "import os\n", + "\n", + "def append_information(repo, open_issues_count, ):\n", + " file_exists = os.path.isfile(filename)\n", + " timestamp = datetime.utcnow().isoformat()\n", + "\n", + " # Open the file in append mode\n", + " with open(filename, 'a', newline='', encoding='utf-8') as csvfile:\n", + " writer = csv.writer(csvfile)\n", + "\n", + " # If the file didn't exist before, write the header row\n", + " if not file_exists:\n", + " writer.writerow([\n", + " \"timestamp\",\n", + " \"repo\",\n", + " \"open_issues_count\",\n", + " \"open_prs_count\",\n", + " \"bug_issues_count\",\n", + " \"feature_request_issues_count\",\n", + " \"invalid_issues_count\"\n", + " ])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_repo_issue_stats(owner, repo, token):\n", + " # Common GitHub GraphQL endpoint\n", + " url = \"https://api.github.com/graphql\"\n", + " headers = {\n", + " \"Authorization\": f\"Bearer {token}\",\n", + " \"Accept\": \"application/vnd.github.v4+json\"\n", + " }\n", + "\n", + " # First query: Get various issue/PR counts from the repository object\n", + " query_repo = \"\"\"\n", + " query ($owner: String!, $name: String!) {\n", + " repository(owner: $owner, name: $name) {\n", + " issues(states: OPEN) {\n", + " totalCount\n", + " }\n", + " pullRequests(states: OPEN) {\n", + " totalCount\n", + " }\n", + " bugIssues: issues(states: OPEN, labels: [\"bug\"]) {\n", + " totalCount\n", + " }\n", + " featureRequestIssues: issues(states: OPEN, labels: [\"feature-request\"]) {\n", + " totalCount\n", + " }\n", + " }\n", + " }\n", + " \"\"\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for REPO in REPOS:\n", + " stats = get_repo_issue_stats(OWNER, REPO, TOKEN)\n", + " print(stats)\n", + "\n", + " append_metrics_to_csv(\n", + " filename=\"repo_metrics.csv\",\n", + " repo=f\"{OWNER}/{REPO}\",\n", + " open_issues_count=stats['open_issues_count'],\n", + " open_prs_count=stats['open_prs_count'],\n", + " bug_issues_count=stats['bug_issues_count'],\n", + " feature_request_issues_count=stats['feature_request_issues_count'],\n", + " invalid_issues_count=stats['invalid_issues_count']\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.graph_objs as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "def plot_repo_metrics(repo, csv_filename=\"repo_metrics.csv\"):\n", + " # Load the CSV data\n", + " df = pd.read_csv(csv_filename)\n", + "\n", + " fig.add_trace(go.Scatter(\n", + " x=df_repo['timestamp'],\n", + " y=df_repo['open_issues_count'],\n", + " mode='lines+markers',\n", + " name='Open Issues',\n", + " hovertemplate='Open Issues: %{y}
    Time: %{x}'\n", + " ))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Header" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for REPO in REPOS:\n", + " plot_repo_metrics(f\"{OWNER}/{REPO}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot_repo_metrics(f\"{OWNER}/vscode\")" + ] + } + ].map(fromJupyterCell) + ); + + assert.deepStrictEqual(mapping, [ + { modified: 0, original: -1 }, + { modified: 1, original: 0 }, + { modified: 2, original: 1 }, + { modified: 3, original: 2 }, + { modified: 4, original: 3 }, + { modified: 5, original: 4 }, + { modified: 6, original: 5 }, + { modified: 7, original: 6 }, + { modified: 8, original: -1 }, + { modified: 9, original: 7 }, + { modified: 10, original: -1 } + ]); + assert.deepStrictEqual(diff.cellsDiff.changes, [ + { originalStart: 0, originalLength: 0, modifiedStart: 0, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 0, originalLength: 1, modifiedStart: 1, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 1, originalLength: 1, modifiedStart: 2, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 7, originalLength: 0, modifiedStart: 8, modifiedLength: 1 } satisfies IDiffChange, + { originalStart: 8, originalLength: 0, modifiedStart: 10, modifiedLength: 1 } satisfies IDiffChange, + ]); + + + }); + + let uriCounter = 0; + async function mapCells(original: MockNotebookCell[], modified: MockNotebookCell[]) { + const mapping = matchCellBasedOnSimilarties(modified.map(cellToDto), original.map(cellToDto)).map(mapping => ({ modified: mapping.modified, original: mapping.original })); + const originalUri = URI.parse(`file://original${uriCounter++}.ipynb`); + const modifiedUri = URI.parse(`file://modified${uriCounter++}.ipynb`); + worker.$acceptNewModel(originalUri.toString(), {}, {}, original.map((cell, index) => cellToMainCellTdo(originalUri, cell, index))); + worker.$acceptNewModel(modifiedUri.toString(), {}, {}, modified.map((cell, index) => cellToMainCellTdo(modifiedUri, cell, index))); + const diff = await worker.$computeDiff(originalUri.toString(), modifiedUri.toString()); + diff.cellsDiff.changes = diff.cellsDiff.changes.map(change => { + return { + originalStart: change.originalStart, + originalLength: change.originalLength, + modifiedStart: change.modifiedStart, + modifiedLength: change.modifiedLength, + }; + }); + return { mapping, diff }; + } + + + function cellToDto(cell: MockNotebookCell): { getValue(): string; getLinesContent(): string[]; cellKind: CellKind } { + return { + cellKind: cell[2], + getValue: () => cell[0].join('\n'), + getLinesContent: () => cell[0] + }; + } + + function cellToMainCellTdo(notebookUri: URI, cell: MockNotebookCell, index: number): IMainCellDto { + return { + source: cell[0], + language: cell[1], + cellKind: cell[2], + outputs: cell[3] || [], + metadata: cell[4], + eol: '\n', + handle: index, + url: URI.from({ scheme: 'file', path: notebookUri.path, fragment: `cell/${index}` }).toString(), + versionId: 0, + internalMetadata: undefined + } satisfies IMainCellDto; + + } + type MockNotebookCell = [ + lines: string[], + lang: string, + kind: CellKind, + output?: IOutputDto[], + metadata?: NotebookCellMetadata, + ]; + + function fromJupyterCell(cell: { cell_type: string; source: string[] }): MockNotebookCell { + return [ + cell.source, + cell.cell_type === 'code' ? 'python' : 'markdown', + cell.cell_type === 'code' ? CellKind.Code : CellKind.Markup, + ]; + } +}); + + diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts index bbffb0d2..613ee43d 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts @@ -874,6 +874,76 @@ suite('NotebookTextModel', () => { }); }); + test('metadata changes on newly added cells should combine their undo operations', async function () { + await withTestNotebook([ + ['var a = 1;', 'javascript', CellKind.Code, [], {}] + ], async (editor, viewModel, ds) => { + const textModel = editor.textModel; + editor.textModel.applyEdits([ + { + editType: CellEditType.Replace, index: 1, count: 0, cells: [ + ds.add(new TestCell(textModel.viewType, 1, 'var e = 5;', 'javascript', CellKind.Code, [], languageService)), + ds.add(new TestCell(textModel.viewType, 2, 'var f = 6;', 'javascript', CellKind.Code, [], languageService)) + ] + }, + ], true, undefined, () => undefined, undefined, true); + + assert.strictEqual(textModel.cells.length, 3); + + editor.textModel.applyEdits([ + { editType: CellEditType.Metadata, index: 1, metadata: { id: '123' } }, + ], true, undefined, () => undefined, undefined, true); + + assert.strictEqual(textModel.cells[1].metadata.id, '123'); + + await viewModel.undo(); + + assert.strictEqual(textModel.cells.length, 1); + + await viewModel.redo(); + + assert.strictEqual(textModel.cells.length, 3); + }); + }); + + test('changes with non-metadata edit should not combine their undo operations', async function () { + await withTestNotebook([ + ['var a = 1;', 'javascript', CellKind.Code, [], {}] + ], async (editor, viewModel, ds) => { + const textModel = editor.textModel; + editor.textModel.applyEdits([ + { + editType: CellEditType.Replace, index: 1, count: 0, cells: [ + ds.add(new TestCell(textModel.viewType, 1, 'var e = 5;', 'javascript', CellKind.Code, [], languageService)), + ds.add(new TestCell(textModel.viewType, 2, 'var f = 6;', 'javascript', CellKind.Code, [], languageService)) + ] + }, + ], true, undefined, () => undefined, undefined, true); + + assert.strictEqual(textModel.cells.length, 3); + + editor.textModel.applyEdits([ + { editType: CellEditType.Metadata, index: 1, metadata: { id: '123' } }, + { + editType: CellEditType.Output, handle: 0, append: true, outputs: [{ + outputId: 'newOutput', + outputs: [{ mime: Mimes.text, data: valueBytesFromString('cba') }, { mime: 'application/foo', data: valueBytesFromString('cba') }] + }] + } + ], true, undefined, () => undefined, undefined, true); + + assert.strictEqual(textModel.cells[1].metadata.id, '123'); + + await viewModel.undo(); + + assert.strictEqual(textModel.cells.length, 3); + + await viewModel.undo(); + + assert.strictEqual(textModel.cells.length, 1); + }); + }); + test('Destructive sorting in _doApplyEdits #121994', async function () { await withTestNotebook([ ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', data: valueBytesFromString('test') }] }], {}] diff --git a/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts b/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts index e1b956a6..4fa628f9 100644 --- a/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts +++ b/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts @@ -12,9 +12,10 @@ import { OUTPUT_MODE_ID, LOG_MODE_ID } from '../../../services/output/common/out import { OutputLinkComputer } from '../common/outputLinkComputer.js'; import { IDisposable, dispose, Disposable } from '../../../../base/common/lifecycle.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { createWebWorker } from '../../../../base/browser/defaultWorkerFactory.js'; -import { IWorkerClient } from '../../../../base/common/worker/simpleWorker.js'; +import { createWebWorker } from '../../../../base/browser/webWorkerFactory.js'; +import { IWebWorkerClient } from '../../../../base/common/worker/webWorker.js'; import { WorkerTextModelSyncClient } from '../../../../editor/common/services/textModelSync/textModelSync.impl.js'; +import { FileAccess } from '../../../../base/common/network.js'; export class OutputLinkProvider extends Disposable { @@ -88,7 +89,7 @@ export class OutputLinkProvider extends Disposable { } class OutputLinkWorkerClient extends Disposable { - private readonly _workerClient: IWorkerClient; + private readonly _workerClient: IWebWorkerClient; private readonly _workerTextModelSyncClient: WorkerTextModelSyncClient; private readonly _initializeBarrier: Promise; @@ -98,7 +99,7 @@ class OutputLinkWorkerClient extends Disposable { ) { super(); this._workerClient = this._register(createWebWorker( - 'vs/workbench/contrib/output/common/outputLinkComputer', + FileAccess.asBrowserUri('vs/workbench/contrib/output/common/outputLinkComputerMain.js'), 'OutputLinkDetectionWorker' )); this._workerTextModelSyncClient = WorkerTextModelSyncClient.create(this._workerClient, modelService); diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts index bed7368c..95073543 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts @@ -11,20 +11,20 @@ import * as strings from '../../../../base/common/strings.js'; import { Range } from '../../../../editor/common/core/range.js'; import { isWindows } from '../../../../base/common/platform.js'; import { Schemas } from '../../../../base/common/network.js'; -import { IRequestHandler, IWorkerServer } from '../../../../base/common/worker/simpleWorker.js'; +import { IWebWorkerServerRequestHandler, IWebWorkerServer } from '../../../../base/common/worker/webWorker.js'; import { WorkerTextModelSyncServer, ICommonModel } from '../../../../editor/common/services/textModelSync/textModelSync.impl.js'; export interface IResourceCreator { toResource: (folderRelativePath: string) => URI | null; } -export class OutputLinkComputer implements IRequestHandler { +export class OutputLinkComputer implements IWebWorkerServerRequestHandler { _requestHandlerBrand: any; private readonly workerTextModelSyncServer = new WorkerTextModelSyncServer(); private patterns = new Map(); - constructor(workerServer: IWorkerServer) { + constructor(workerServer: IWebWorkerServer) { this.workerTextModelSyncServer.bindToServer(workerServer); } @@ -181,10 +181,6 @@ export class OutputLinkComputer implements IRequestHandler { } } -/** - * Defines the worker entry point. Must be exported and named `create`. - * @skipMangle - */ -export function create(workerServer: IWorkerServer): OutputLinkComputer { +export function create(workerServer: IWebWorkerServer): OutputLinkComputer { return new OutputLinkComputer(workerServer); } diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputerMain.ts b/src/vs/workbench/contrib/output/common/outputLinkComputerMain.ts index 3f296c6d..53283d5b 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputerMain.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputerMain.ts @@ -4,6 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { create } from './outputLinkComputer.js'; -import { bootstrapSimpleWorker } from '../../../../base/common/worker/simpleWorkerBootstrap.js'; +import { bootstrapWebWorker } from '../../../../base/common/worker/webWorkerBootstrap.js'; -bootstrapSimpleWorker(create); +bootstrapWebWorker(create); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index d896d865..af019346 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -26,6 +26,7 @@ import { assertIsDefined } from '../../../../base/common/types.js'; import { isEqual } from '../../../../base/common/resources.js'; import { IUserDataProfileService } from '../../../services/userDataProfile/common/userDataProfile.js'; import { DEFINE_KEYBINDING_EDITOR_CONTRIB_ID, IDefineKeybindingEditorContribution } from '../../../services/preferences/common/preferences.js'; +import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); @@ -88,13 +89,14 @@ class DefineKeybindingEditorContribution extends Disposable implements IDefineKe export class KeybindingEditorDecorationsRenderer extends Disposable { private _updateDecorations: RunOnceScheduler; - private readonly _dec = this._editor.createDecorationsCollection(); + private readonly _dec: IEditorDecorationsCollection; constructor( private _editor: ICodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { super(); + this._dec = this._editor.createDecorationsCollection(); this._updateDecorations = this._register(new RunOnceScheduler(() => this._updateDecorationsNow(), 500)); diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 9bdbebe0..d69acc0a 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -24,7 +24,7 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from '../../../../platform/workspace/common/workspace.js'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from '../../../browser/actions/workspaceCommands.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; -import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; +import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; import { EditorExtensions, IEditorFactoryRegistry, IEditorSerializer } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { ResourceContextKey, RemoteNameContext, WorkbenchStateContext } from '../../../common/contextkeys.js'; @@ -39,7 +39,6 @@ import { PreferencesContribution } from '../common/preferencesContribution.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { KeybindingsEditorInput } from '../../../services/preferences/browser/keybindingsEditorInput.js'; import { DEFINE_KEYBINDING_EDITOR_CONTRIB_ID, IDefineKeybindingEditorContribution, IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { SettingsEditor2Input } from '../../../services/preferences/common/preferencesEditorInput.js'; @@ -1239,6 +1238,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } class SettingsEditorTitleContribution extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'workbench.contrib.settingsEditorTitleBarActions'; + constructor( @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @@ -1249,13 +1251,13 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo private registerSettingsEditorTitleActions() { const registerOpenUserSettingsEditorFromJsonActionDisposables = this._register(new MutableDisposable()); - const openUserSettingsEditorWhen = ContextKeyExpr.and( - ContextKeyExpr.or( - ResourceContextKey.Resource.isEqualTo(this.userDataProfileService.currentProfile.settingsResource.toString()), - ResourceContextKey.Resource.isEqualTo(this.userDataProfilesService.defaultProfile.settingsResource.toString())), - ContextKeyExpr.not('isInDiffEditor')); const registerOpenUserSettingsEditorFromJsonAction = () => { - registerOpenUserSettingsEditorFromJsonActionDisposables.value = undefined; + const openUserSettingsEditorWhen = ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR.toNegated(), + ContextKeyExpr.or( + ResourceContextKey.Resource.isEqualTo(this.userDataProfileService.currentProfile.settingsResource.toString()), + ResourceContextKey.Resource.isEqualTo(this.userDataProfilesService.defaultProfile.settingsResource.toString())), + ContextKeyExpr.not('isInDiffEditor')); registerOpenUserSettingsEditorFromJsonActionDisposables.value = registerAction2(class extends Action2 { constructor() { super({ @@ -1271,9 +1273,9 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo }); } run(accessor: ServicesAccessor, ...args: unknown[]) { - const sanatizedArgs = sanitizeOpenSettingsArgs(args[0]); + const sanitizedArgs = sanitizeOpenSettingsArgs(args[0]); const groupId = getEditorGroupFromArguments(accessor, args)?.id; - return accessor.get(IPreferencesService).openUserSettings({ jsonEditor: false, ...sanatizedArgs, groupId }); + return accessor.get(IPreferencesService).openUserSettings({ jsonEditor: false, ...sanitizedArgs, groupId }); } }); }; @@ -1284,7 +1286,7 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo registerOpenUserSettingsEditorFromJsonAction(); })); - const openSettingsJsonWhen = ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()); + const openSettingsJsonWhen = ContextKeyExpr.and(CONTEXT_SETTINGS_JSON_EDITOR.toNegated(), CONTEXT_SETTINGS_EDITOR); this._register(registerAction2(class extends Action2 { constructor() { super({ @@ -1316,10 +1318,9 @@ function getEditorGroupFromArguments(accessor: ServicesAccessor, args: unknown[] return context.groupedEditors[0]?.group; } -const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); registerWorkbenchContribution2(PreferencesActionsContribution.ID, PreferencesActionsContribution, WorkbenchPhase.BlockStartup); registerWorkbenchContribution2(PreferencesContribution.ID, PreferencesContribution, WorkbenchPhase.BlockStartup); -workbenchContributionsRegistry.registerWorkbenchContribution(SettingsEditorTitleContribution, LifecyclePhase.Restored); +registerWorkbenchContribution2(SettingsEditorTitleContribution.ID, SettingsEditorTitleContribution, WorkbenchPhase.AfterRestored); registerEditorContribution(SettingsEditorContribution.ID, SettingsEditorContribution, EditorContributionInstantiation.AfterFirstRender); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index c8f0b595..1d6c0b83 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -787,7 +787,7 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc class WorkspaceConfigurationRenderer extends Disposable { private static readonly supportedKeys = ['folders', 'tasks', 'launch', 'extensions', 'settings', 'remoteAuthority', 'transient']; - private readonly decorations = this.editor.createDecorationsCollection(); + private readonly decorations: editorCommon.IEditorDecorationsCollection; private renderingDelayer: Delayer = new Delayer(200); constructor(private editor: ICodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, @@ -795,6 +795,7 @@ class WorkspaceConfigurationRenderer extends Disposable { @IMarkerService private readonly markerService: IMarkerService ) { super(); + this.decorations = this.editor.createDecorationsCollection(); this._register(this.editor.getModel()!.onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render()))); } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 57f59b72..762a4622 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISettingsEditorModel, ISetting, ISettingsGroup, ISearchResult, IGroupFilter, SettingMatchType, ISettingMatch, SettingKeyMatchTypes } from '../../../services/preferences/common/preferences.js'; +import { ISettingsEditorModel, ISetting, ISettingsGroup, ISearchResult, IGroupFilter, SettingMatchType, ISettingMatch, SettingKeyMatchTypes, ISettingMatcher } from '../../../services/preferences/common/preferences.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { distinct } from '../../../../base/common/arrays.js'; import * as strings from '../../../../base/common/strings.js'; @@ -82,9 +82,6 @@ function cleanFilter(filter: string): string { } export class LocalSearchProvider implements ISearchProvider { - static readonly EXACT_MATCH_SCORE = 10000; - static readonly START_SCORE = 1000; - constructor( private _filter: string, @IConfigurationService private readonly configurationService: IConfigurationService @@ -92,60 +89,43 @@ export class LocalSearchProvider implements ISearchProvider { this._filter = cleanFilter(this._filter); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { + searchModel(preferencesModel: ISettingsEditorModel, token: CancellationToken): Promise { if (!this._filter) { return Promise.resolve(null); } - let orderedScore = LocalSearchProvider.START_SCORE; // Sort is not stable - const useNewKeyMatchAlgorithm = this.configurationService.getValue('workbench.settings.useWeightedKeySearch') === true; - const settingMatcher = (setting: ISetting) => { - const { matches, matchType, keyMatchScore } = new SettingMatches( + const settingMatcher: ISettingMatcher = (setting: ISetting) => { + let { matches, matchType, keyMatchScore } = new SettingMatches( this._filter, setting, true, - (filter, setting) => preferencesModel.findValueMatches(filter, setting), - useNewKeyMatchAlgorithm, this.configurationService ); if (matchType === SettingMatchType.None || matches.length === 0) { return null; } - - const score = strings.equalsIgnoreCase(this._filter, setting.key) ? - LocalSearchProvider.EXACT_MATCH_SCORE : - orderedScore--; + if (strings.equalsIgnoreCase(this._filter, setting.key)) { + matchType = SettingMatchType.ExactMatch; + } return { matches, matchType, keyMatchScore, - score + score: 0 // only used for RemoteSearchProvider matches. }; }; const filterMatches = preferencesModel.filterSettings(this._filter, this.getGroupFilter(this._filter), settingMatcher); - const exactMatch = filterMatches.find(m => m.score === LocalSearchProvider.EXACT_MATCH_SCORE); - if (exactMatch) { - return Promise.resolve({ - filterMatches: [exactMatch], - exactMatch: true - }); - } else if (useNewKeyMatchAlgorithm) { - // Check the top key match type. - const topKeyMatchType = Math.max(...filterMatches.map(m => (m.matchType & SettingKeyMatchTypes))); - // Always allow description matches as part of https://github.com/microsoft/vscode/issues/239936. - const alwaysAllowedMatchTypes = SettingMatchType.DescriptionOrValueMatch | SettingMatchType.LanguageTagSettingMatch; - const filteredMatches = filterMatches.filter(m => (m.matchType & topKeyMatchType) || (m.matchType & alwaysAllowedMatchTypes)); - return Promise.resolve({ - filterMatches: filteredMatches, - exactMatch: false - }); - } else { - return Promise.resolve({ - filterMatches: filterMatches, - exactMatch: false - }); - } + + // Check the top key match type. + const topKeyMatchType = Math.max(...filterMatches.map(m => (m.matchType & SettingKeyMatchTypes))); + // Always allow description matches as part of https://github.com/microsoft/vscode/issues/239936. + const alwaysAllowedMatchTypes = SettingMatchType.DescriptionOrValueMatch | SettingMatchType.LanguageTagSettingMatch; + const filteredMatches = filterMatches.filter(m => (m.matchType & topKeyMatchType) || (m.matchType & alwaysAllowedMatchTypes) || m.matchType === SettingMatchType.ExactMatch); + return Promise.resolve({ + filterMatches: filteredMatches, + exactMatch: filteredMatches.some(m => m.matchType === SettingMatchType.ExactMatch) + }); } private getGroupFilter(filter: string): IGroupFilter { @@ -169,8 +149,6 @@ export class SettingMatches { searchString: string, setting: ISetting, private searchDescription: boolean, - valuesMatcher: (filter: string, setting: ISetting) => IRange[], - private useNewKeyMatchAlgorithm: boolean, private readonly configurationService: IConfigurationService ) { this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`); @@ -205,68 +183,52 @@ export class SettingMatches { const settingKeyAsWords: string = this._keyToLabel(setting.key); const queryWords = new Set(searchString.split(' ')); for (const word of queryWords) { - // Check if the key contains the word. - // Force contiguous matching iff we're using the new algorithm. - const keyMatches = matchesWords(word, settingKeyAsWords, this.useNewKeyMatchAlgorithm); + // Check if the key contains the word. Use contiguous search. + const keyMatches = matchesWords(word, settingKeyAsWords, true); if (keyMatches?.length) { keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); } } - if (this.useNewKeyMatchAlgorithm) { - // New key match algorithm - if (keyMatchingWords.size === queryWords.size) { - // All words in the query matched with something in the setting key. - // Matches "edit format on paste" to "editor.formatOnPaste". - this.matchType |= SettingMatchType.AllWordsInSettingsLabel; - } else if (keyMatchingWords.size >= 2) { - // Matches "edit paste" to "editor.formatOnPaste". - // The if statement reduces noise by preventing "editor formatonpast" from matching all editor settings. - this.matchType |= SettingMatchType.ContiguousWordsInSettingsLabel; - this.keyMatchScore = keyMatchingWords.size; - } - const searchStringAlphaNumeric = this._toAlphaNumeric(searchString); - const keyAlphaNumeric = this._toAlphaNumeric(setting.key); - const keyIdMatches = matchesContiguousSubString(searchStringAlphaNumeric, keyAlphaNumeric); - if (keyIdMatches?.length) { - // Matches "editorformatonp" to "editor.formatonpaste". - keyMatchingWords.set(setting.key, keyIdMatches.map(match => this.toKeyRange(setting, match))); - this.matchType |= SettingMatchType.ContiguousQueryInSettingId; - } + if (keyMatchingWords.size === queryWords.size) { + // All words in the query matched with something in the setting key. + // Matches "edit format on paste" to "editor.formatOnPaste". + this.matchType |= SettingMatchType.AllWordsInSettingsLabel; + } else if (keyMatchingWords.size >= 2) { + // Matches "edit paste" to "editor.formatOnPaste". + // The if statement reduces noise by preventing "editor formatonpast" from matching all editor settings. + this.matchType |= SettingMatchType.ContiguousWordsInSettingsLabel; + this.keyMatchScore = keyMatchingWords.size; + } + const searchStringAlphaNumeric = this._toAlphaNumeric(searchString); + const keyAlphaNumeric = this._toAlphaNumeric(setting.key); + const keyIdMatches = matchesContiguousSubString(searchStringAlphaNumeric, keyAlphaNumeric); + if (keyIdMatches?.length) { + // Matches "editorformatonp" to "editor.formatonpaste". + keyMatchingWords.set(setting.key, keyIdMatches.map(match => this.toKeyRange(setting, match))); + this.matchType |= SettingMatchType.ContiguousQueryInSettingId; + } - // Fall back to non-contiguous searches if nothing matched yet. - if (this.matchType === SettingMatchType.None) { - keyMatchingWords.clear(); - for (const word of queryWords) { - const keyMatches = matchesWords(word, settingKeyAsWords, false); - if (keyMatches?.length) { - keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); - } - } - if (keyMatchingWords.size >= 2 || (keyMatchingWords.size === 1 && queryWords.size === 1)) { - // Matches "edforonpas" to "editor.formatOnPaste". - // The if statement reduces noise by preventing "editor fomonpast" from matching all editor settings. - this.matchType |= SettingMatchType.NonContiguousWordsInSettingsLabel; - this.keyMatchScore = keyMatchingWords.size; - } else { - const keyIdMatches = matchesSubString(searchStringAlphaNumeric, keyAlphaNumeric); - if (keyIdMatches?.length) { - // Matches "edfmonpas" to "editor.formatOnPaste". - keyMatchingWords.set(setting.key, keyIdMatches.map(match => this.toKeyRange(setting, match))); - this.matchType |= SettingMatchType.NonContiguousQueryInSettingId; - } + // Fall back to non-contiguous key (ID) searches if nothing matched yet. + if (this.matchType === SettingMatchType.None) { + keyMatchingWords.clear(); + for (const word of queryWords) { + const keyMatches = matchesWords(word, settingKeyAsWords, false); + if (keyMatches?.length) { + keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match))); } } - } else { - // Old key match algorithm - if (keyMatchingWords.size) { + if (keyMatchingWords.size >= 2 || (keyMatchingWords.size === 1 && queryWords.size === 1)) { + // Matches "edforonpas" to "editor.formatOnPaste". + // The if statement reduces noise by preventing "editor fomonpast" from matching all editor settings. this.matchType |= SettingMatchType.NonContiguousWordsInSettingsLabel; this.keyMatchScore = keyMatchingWords.size; - } - const keyIdMatches = matchesContiguousSubString(searchString, setting.key); - if (keyIdMatches?.length) { - // Handles cases such as "editor.formatonpaste" where the user tries searching for the ID. - keyMatchingWords.set(setting.key, keyIdMatches.map(match => this.toKeyRange(setting, match))); - this.matchType |= SettingMatchType.ContiguousQueryInSettingId; + } else { + const keyIdMatches = matchesSubString(searchStringAlphaNumeric, keyAlphaNumeric); + if (keyIdMatches?.length) { + // Matches "edfmonpas" to "editor.formatOnPaste". + keyMatchingWords.set(setting.key, keyIdMatches.map(match => this.toKeyRange(setting, match))); + this.matchType |= SettingMatchType.NonContiguousQueryInSettingId; + } } } @@ -280,11 +242,9 @@ export class SettingMatches { } // Description search - // Old algorithm: search the description if we haven't matched anything yet. - // New algorithm: search the description if we found non-contiguous key matches at best. + // Search the description if we found non-contiguous key matches at best. const hasContiguousKeyMatchTypes = this.matchType >= SettingMatchType.ContiguousWordsInSettingsLabel; - const checkDescription = (!this.useNewKeyMatchAlgorithm && this.matchType === SettingMatchType.None) || (this.useNewKeyMatchAlgorithm && !hasContiguousKeyMatchTypes); - if (this.searchDescription && checkDescription) { + if (this.searchDescription && !hasContiguousKeyMatchTypes) { for (const word of queryWords) { // Search the description lines. for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) { @@ -304,10 +264,8 @@ export class SettingMatches { // Value search // Check if the value contains all the words. - // Old algorithm: always search the values. - // New algorithm: search the values if we found non-contiguous key matches at best. - const checkValue = !this.useNewKeyMatchAlgorithm || !hasContiguousKeyMatchTypes; - if (checkValue) { + // Search the values if we found non-contiguous key matches at best. + if (!hasContiguousKeyMatchTypes) { if (setting.enum?.length) { // Search all string values of enums. for (const option of setting.enum) { @@ -459,7 +417,7 @@ class AiRelatedInformationSearchProvider implements IRemoteSearchProvider { this._filter = cleanFilter(filter); } - async searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken | undefined): Promise { + async searchModel(preferencesModel: ISettingsEditorModel, token: CancellationToken): Promise { if ( !this._filter || !this.aiRelatedInformationService.isEnabled() @@ -470,18 +428,19 @@ class AiRelatedInformationSearchProvider implements IRemoteSearchProvider { this._keysProvider.updateModel(preferencesModel); return { - filterMatches: await this.getAiRelatedInformationItems(token) + filterMatches: await this.getAiRelatedInformationItems(token), + exactMatch: false }; } - private async getAiRelatedInformationItems(token?: CancellationToken | undefined) { + private async getAiRelatedInformationItems(token: CancellationToken) { const settingsRecord = this._keysProvider.getSettingsRecord(); const filterMatches: ISettingMatch[] = []; const relatedInformation = await this.aiRelatedInformationService.getRelatedInformation( this._filter, [RelatedInformationType.SettingInformation], - token ?? CancellationToken.None + token ) as SettingInformationResult[]; relatedInformation.sort((a, b) => b.weight - a.weight); @@ -537,7 +496,7 @@ class TfIdfSearchProvider implements IRemoteSearchProvider { return result; } - async searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken | undefined): Promise { + async searchModel(preferencesModel: ISettingsEditorModel, token: CancellationToken): Promise { if (!this._filter) { return null; } @@ -564,15 +523,16 @@ class TfIdfSearchProvider implements IRemoteSearchProvider { } return { - filterMatches: await this.getTfIdfItems(token) + filterMatches: await this.getTfIdfItems(token), + exactMatch: false }; } - private async getTfIdfItems(token?: CancellationToken | undefined): Promise { + private async getTfIdfItems(token: CancellationToken): Promise { const filterMatches: ISettingMatch[] = []; const tfIdfCalculator = new TfIdfCalculator(); tfIdfCalculator.updateDocuments(this._documents); - const tfIdfRankings = tfIdfCalculator.calculateScores(this._filter, token ?? CancellationToken.None); + const tfIdfRankings = tfIdfCalculator.calculateScores(this._filter, token); tfIdfRankings.sort((a, b) => b.score - a.score); const maxScore = tfIdfRankings[0].score; @@ -625,9 +585,9 @@ class RemoteSearchProvider implements IRemoteSearchProvider { this.tfIdfSearchProvider!.setFilter(filter); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { + async searchModel(preferencesModel: ISettingsEditorModel, token: CancellationToken): Promise { if (!this.filter) { - return Promise.resolve(null); + return null; } if (!this.adaSearchProvider) { @@ -635,9 +595,17 @@ class RemoteSearchProvider implements IRemoteSearchProvider { } // Use TF-IDF search as a fallback, ref https://github.com/microsoft/vscode/issues/224946 - return this.adaSearchProvider.searchModel(preferencesModel, token).then((results) => { - return results?.filterMatches.length ? results : this.tfIdfSearchProvider!.searchModel(preferencesModel, token); - }); + let results = await this.adaSearchProvider.searchModel(preferencesModel, token); + if (results?.filterMatches.length) { + return results; + } + if (!token.isCancellationRequested) { + results = await this.tfIdfSearchProvider!.searchModel(preferencesModel, token); + if (results?.filterMatches.length) { + return results; + } + } + return null; } } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 84321a35..9bc4c943 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -37,6 +37,7 @@ import { ILanguageService } from '../../../../editor/common/languages/language.j import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import type { IManagedHover } from '../../../../base/browser/ui/hover/hover.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; export class FolderSettingsActionViewItem extends BaseActionViewItem { @@ -500,13 +501,14 @@ export class EditPreferenceWidget extends Disposable { private _line: number = -1; private _preferences: T[] = []; - private readonly _editPreferenceDecoration = this.editor.createDecorationsCollection(); + private readonly _editPreferenceDecoration: IEditorDecorationsCollection; private readonly _onClick = this._register(new Emitter()); readonly onClick: Event = this._onClick.event; constructor(private editor: ICodeEditor) { super(); + this._editPreferenceDecoration = this.editor.createDecorationsCollection(); this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => { if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.detail.isAfterLines || !this.isVisible()) { return; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index ca7251ac..e5de67fb 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -413,6 +413,7 @@ export class SettingsEditor2 extends EditorPane { // Don't block setInput on render (which can trigger an async search) this.onConfigUpdate(undefined, true).then(() => { + // This event runs when the editor closes. this.inputChangeListener.value = input.onWillDispose(() => { this.searchWidget.setValue(''); }); @@ -1540,7 +1541,9 @@ export class SettingsEditor2 extends EditorPane { private refreshSingleElement(element: SettingsTreeSettingElement): void { if (this.isVisible()) { - this.settingsTree.rerender(element); + if (!element.setting.deprecationMessage || element.isConfigured) { + this.settingsTree.rerender(element); + } } } @@ -1634,8 +1637,7 @@ export class SettingsEditor2 extends EditorPane { this.searchDelayer.cancel(); if (this.searchInProgress) { - this.searchInProgress.cancel(); - this.searchInProgress.dispose(); + this.searchInProgress.dispose(true); this.searchInProgress = null; } @@ -1670,7 +1672,8 @@ export class SettingsEditor2 extends EditorPane { const filterModel = this.instantiationService.createInstance(SearchResultModel, this.viewState, this.settingsOrderByTocIndex, this.workspaceTrustManagementService.isWorkspaceTrusted()); const fullResult: ISearchResult = { - filterMatches: [] + filterMatches: [], + exactMatch: false, }; for (const g of this.defaultSettingsEditorModel.settingsGroups.slice(1)) { for (const sect of g.sections) { @@ -1686,23 +1689,24 @@ export class SettingsEditor2 extends EditorPane { private async triggerFilterPreferences(query: string): Promise { if (this.searchInProgress) { - this.searchInProgress.cancel(); + this.searchInProgress.dispose(true); this.searchInProgress = null; } // Trigger the local search. If it didn't find an exact match, trigger the remote search. const searchInProgress = this.searchInProgress = new CancellationTokenSource(); return this.searchDelayer.trigger(async () => { - if (!searchInProgress.token.isCancellationRequested) { - const localResults = await this.localFilterPreferences(query, searchInProgress.token); - if (localResults && !localResults.exactMatch && !searchInProgress.token.isCancellationRequested) { - await this.remoteSearchPreferences(query, searchInProgress.token); - } - - // Update UI only after all the search results are in - // ref https://github.com/microsoft/vscode/issues/224946 - this.onDidFinishSearch(); + if (searchInProgress.token.isCancellationRequested) { + return; } + const localResults = await this.localFilterPreferences(query, searchInProgress.token); + if (localResults && !localResults.exactMatch && !searchInProgress.token.isCancellationRequested) { + await this.remoteSearchPreferences(query, searchInProgress.token); + } + + // Update UI only after all the search results are in + // ref https://github.com/microsoft/vscode/issues/224946 + this.onDidFinishSearch(); }); } @@ -1717,19 +1721,22 @@ export class SettingsEditor2 extends EditorPane { this.renderTree(undefined, true); } - private localFilterPreferences(query: string, token?: CancellationToken): Promise { + private localFilterPreferences(query: string, token: CancellationToken): Promise { const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query); - return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider, token); + return this.searchWithProvider(SearchResultIdx.Local, localSearchProvider, token); } - private remoteSearchPreferences(query: string, token?: CancellationToken): Promise { + private remoteSearchPreferences(query: string, token: CancellationToken): Promise { const remoteSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query); - return this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider, token); + if (!remoteSearchProvider) { + return Promise.resolve(null); + } + return this.searchWithProvider(SearchResultIdx.Remote, remoteSearchProvider, token); } - private async filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise { - const result = await this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token); - if (token?.isCancellationRequested) { + private async searchWithProvider(type: SearchResultIdx, searchProvider: ISearchProvider, token: CancellationToken): Promise { + const result = await this._searchPreferencesModel(this.defaultSettingsEditorModel, searchProvider, token); + if (token.isCancellationRequested) { // Handle cancellation like this because cancellation is lost inside the search provider due to async/await return null; } @@ -1782,31 +1789,16 @@ export class SettingsEditor2 extends EditorPane { } } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider?: ISearchProvider, token?: CancellationToken): Promise { - const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); - return searchP - .then(undefined, err => { - if (isCancellationError(err)) { - return Promise.reject(err); - } else { - // type SettingsSearchErrorEvent = { - // 'message': string; - // }; - // type SettingsSearchErrorClassification = { - // owner: 'rzhao271'; - // comment: 'Helps understand when settings search errors out'; - // 'message': { 'classification': 'CallstackOrException'; 'purpose': 'FeatureInsight'; 'owner': 'rzhao271'; 'comment': 'The error message of the search error.' }; - // }; - - // const message = getErrorMessage(err).trim(); - // if (message && message !== 'Error') { - // // "Error" = any generic network error - // this.telemetryService.publicLogError2('settingsEditor.searchError', { message }); - // this.logService.info('Setting search error: ' + message); - // } - return null; - } - }); + private async _searchPreferencesModel(model: ISettingsEditorModel, provider: ISearchProvider, token: CancellationToken): Promise { + try { + return await provider.searchModel(model, token); + } catch (err) { + if (isCancellationError(err)) { + return Promise.reject(err); + } else { + return null; + } + } } private layoutSplitView(dimension: DOM.Dimension): void { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index 678042ec..c669643a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -145,7 +145,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { const content = localize('trustLabel', "The setting value can only be applied in a trusted workspace."); const showHover = (focus: boolean) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...this.defaultHoverOptions, content, target: workspaceTrustElement, @@ -186,7 +186,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { const syncIgnoredHoverContent = localize('syncIgnoredTitle', "This setting is ignored during sync"); const showHover = (focus: boolean) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...this.defaultHoverOptions, content: syncIgnoredHoverContent, target: syncIgnoredElement @@ -329,7 +329,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { const content = isPreviewSetting ? PREVIEW_INDICATOR_DESCRIPTION : EXPERIMENTAL_INDICATOR_DESCRIPTION; const showHover = (focus: boolean) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...this.defaultHoverOptions, content, target: this.previewIndicator.element @@ -374,7 +374,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { this.scopeOverridesIndicator.label.text = '$(briefcase) ' + localize('policyLabelText', "Managed by organization"); const content = localize('policyDescription', "This setting is managed by your organization and its actual value cannot be changed."); const showHover = (focus: boolean) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...this.defaultHoverOptions, content, actions: [{ @@ -396,7 +396,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { const content = localize('applicationSettingDescription', "The setting is not specific to the current profile, and will retain its value when switching profiles."); const showHover = (focus: boolean) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...this.defaultHoverOptions, content, target: this.scopeOverridesIndicator.element @@ -512,7 +512,7 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { } const showHover = (focus: boolean) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ content: new MarkdownString().appendMarkdown(defaultOverrideHoverContent), target: this.defaultOverrideIndicator.element, position: { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index 8408d9a1..43df1e38 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { isWeb, isWindows } from '../../../../base/common/platform.js'; import { localize } from '../../../../nls.js'; import { ExtensionToggleData } from '../common/preferences.js'; @@ -12,6 +13,7 @@ export interface ITOCEntry { order?: number; children?: ITOCEntry[]; settings?: Array; + hide?: boolean; } const defaultCommonlyUsedSettings: string[] = [ @@ -234,12 +236,13 @@ export const tocData: ITOCEntry = { { id: 'features/chat', label: localize('chat', 'Chat'), - settings: ['chat.*', 'inlineChat.*'] + settings: ['chat.*', 'inlineChat.*', 'mcp'] }, { id: 'features/issueReporter', label: localize('issueReporter', 'Issue Reporter'), - settings: ['issueReporter.*'] + settings: ['issueReporter.*'], + hide: !isWeb } ] }, @@ -280,7 +283,8 @@ export const tocData: ITOCEntry = { { id: 'application/other', label: localize('other', "Other"), - settings: ['application.*'] + settings: ['application.*'], + hide: isWindows } ] }, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index be9840d7..2f49546c 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -33,8 +33,9 @@ import { Disposable, DisposableStore, isDisposable, toDisposable } from '../../. import { isIOS } from '../../../../base/common/platform.js'; import { escapeRegExpCharacters } from '../../../../base/common/strings.js'; import { isDefined, isUndefinedOrNull } from '../../../../base/common/types.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { URI } from '../../../../base/common/uri.js'; import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; +import { ILanguageService } from '../../../../editor/common/languages/language.js'; import { localize } from '../../../../nls.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; @@ -42,6 +43,7 @@ import { ConfigurationTarget, IConfigurationService, getLanguageTagSettingPlainK import { ConfigurationScope } from '../../../../platform/configuration/common/configurationRegistry.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IListService, WorkbenchObjectTree } from '../../../../platform/list/browser/listService.js'; @@ -55,23 +57,20 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { getIgnoredSettings } from '../../../../platform/userDataSync/common/settingsMerge.js'; import { IUserDataSyncEnablementService, getDefaultIgnoredSettings } from '../../../../platform/userDataSync/common/userDataSync.js'; +import { APPLICATION_SCOPES, APPLY_ALL_PROFILES_SETTING, IWorkbenchConfigurationService } from '../../../services/configuration/common/configuration.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { ISetting, ISettingsGroup, SETTINGS_AUTHORITY, SettingValueType } from '../../../services/preferences/common/preferences.js'; +import { getInvalidTypeError } from '../../../services/preferences/common/preferencesValidation.js'; import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; +import { LANGUAGE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, compareTwoNullableNumbers } from '../common/preferences.js'; +import { settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from '../common/settingsEditorColorRegistry.js'; import { settingsMoreActionIcon } from './preferencesIcons.js'; import { SettingsTarget } from './preferencesWidgets.js'; import { ISettingOverrideClickEvent, SettingsTreeIndicatorsLabel, getIndicatorsLabelAriaLabel } from './settingsEditorSettingIndicators.js'; import { ITOCEntry } from './settingsLayout.js'; import { ISettingsEditorViewState, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement, inspectSetting, objectSettingSupportsRemoveDefaultValue, settingKeyToDisplayFormat } from './settingsTreeModels.js'; import { ExcludeSettingWidget, IBoolObjectDataItem, IIncludeExcludeDataItem, IListDataItem, IObjectDataItem, IObjectEnumOption, IObjectKeySuggester, IObjectValueSuggester, IncludeSettingWidget, ListSettingWidget, ObjectSettingCheckboxWidget, ObjectSettingDropdownWidget, ObjectValue, SettingListEvent } from './settingsWidgets.js'; -import { LANGUAGE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, compareTwoNullableNumbers } from '../common/preferences.js'; -import { settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from '../common/settingsEditorColorRegistry.js'; -import { APPLICATION_SCOPES, APPLY_ALL_PROFILES_SETTING, IWorkbenchConfigurationService } from '../../../services/configuration/common/configuration.js'; -import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; -import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { ISetting, ISettingsGroup, SETTINGS_AUTHORITY, SettingValueType } from '../../../services/preferences/common/preferences.js'; -import { getInvalidTypeError } from '../../../services/preferences/common/preferencesValidation.js'; -import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { URI } from '../../../../base/common/uri.js'; const $ = DOM.$; @@ -568,6 +567,7 @@ function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set[] | undefined; if (tocData.children) { children = tocData.children + .filter(child => child.hide !== true) .map(child => _resolveSettingsTree(child, allSettings, logService)) .filter(child => child.children?.length || child.settings?.length); } @@ -874,7 +874,9 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const descriptionElement = DOM.append(container, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); - toDispose.add(this._hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), modifiedIndicatorElement, () => localize('modified', "The setting has been configured in the current scope."))); + toDispose.add(this._hoverService.setupDelayedHover(modifiedIndicatorElement, { + content: localize('modified', "The setting has been configured in the current scope.") + })); const valueElement = DOM.append(container, $('.setting-item-value')); const controlElement = DOM.append(valueElement, $('div.setting-item-control')); @@ -961,7 +963,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const titleTooltip = setting.key + (element.isConfigured ? ' - Modified' : ''); template.categoryElement.textContent = element.displayCategory ? (element.displayCategory + ': ') : ''; - template.elementDisposables.add(this._hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), template.categoryElement, titleTooltip)); + template.elementDisposables.add(this._hoverService.setupDelayedHover(template.categoryElement, { content: titleTooltip })); template.labelElement.text = element.displayLabel; template.labelElement.title = titleTooltip; @@ -1993,7 +1995,9 @@ class SettingBoolRenderer extends AbstractSettingRenderer implements ITreeRender const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); - toDispose.add(this._hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), modifiedIndicatorElement, localize('modified', "The setting has been configured in the current scope."))); + toDispose.add(this._hoverService.setupDelayedHover(modifiedIndicatorElement, { + content: localize('modified', "The setting has been configured in the current scope.") + })); const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index d48eb562..629e04c3 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -342,7 +342,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { // so we reset the default value source to the non-language-specific default value source for now. this.defaultValueSource = this.setting.nonLanguageSpecificDefaultValueSource; - if (inspected.policyValue) { + if (inspected.policyValue !== undefined) { this.hasPolicyValue = true; isConfigured = false; // The user did not manually configure the setting themselves. displayValue = inspected.policyValue; @@ -596,10 +596,15 @@ export class SettingsTreeModel implements IDisposable { if (tocEntry.settings) { const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)); for (const child of settingChildren) { - if (!child.setting.deprecationMessage || child.isConfigured) { + if (!child.setting.deprecationMessage) { children.push(child); } else { - child.dispose(); + child.inspectSelf(); + if (child.isConfigured) { + children.push(child); + } else { + child.dispose(); + } } } } @@ -634,7 +639,7 @@ export class SettingsTreeModel implements IDisposable { this._userDataProfileService, this._configurationService); - const nameElements = this._treeElementsBySettingName.get(setting.key) || []; + const nameElements = this._treeElementsBySettingName.get(setting.key) ?? []; nameElements.push(element); this._treeElementsBySettingName.set(setting.key, nameElements); return element; @@ -1021,30 +1026,26 @@ export class SearchResultModel extends SettingsTreeModel { this.cachedUniqueSearchResults = { filterMatches: combinedFilterMatches, - exactMatch: localResult?.exactMatch || remoteResult?.exactMatch + exactMatch: localResult.exactMatch // remote results should never have an exact match }; return this.cachedUniqueSearchResults; } getRawResults(): ISearchResult[] { - return this.rawSearchResults || []; + return this.rawSearchResults ?? []; } setResult(order: SearchResultIdx, result: ISearchResult | null): void { this.cachedUniqueSearchResults = null; this.newExtensionSearchResults = null; - this.rawSearchResults = this.rawSearchResults || []; + this.rawSearchResults ??= []; if (!result) { delete this.rawSearchResults[order]; return; } - if (result.exactMatch) { - this.rawSearchResults = []; - } - this.rawSearchResults[order] = result; this.updateChildren(); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 258984d7..c6f874d2 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -8,30 +8,29 @@ import * as DOM from '../../../../base/browser/dom.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; -import { Toggle, unthemedToggleStyles } from '../../../../base/browser/ui/toggle/toggle.js'; +import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js'; import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js'; import { SelectBox } from '../../../../base/browser/ui/selectBox/selectBox.js'; +import { Toggle, unthemedToggleStyles } from '../../../../base/browser/ui/toggle/toggle.js'; import { IAction } from '../../../../base/common/actions.js'; import { disposableTimeout } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { isIOS } from '../../../../base/common/platform.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; import { isDefined, isUndefinedOrNull } from '../../../../base/common/types.js'; -import './media/settingsWidgets.css'; import { localize } from '../../../../nls.js'; import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; -import { settingsDiscardIcon, settingsEditIcon, settingsRemoveIcon } from './preferencesIcons.js'; -import { settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from '../common/settingsEditorColorRegistry.js'; -import { defaultButtonStyles, getInputBoxStyle, getSelectBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { MarkdownString } from '../../../../base/common/htmlContent.js'; -import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; +import { defaultButtonStyles, getInputBoxStyle, getSelectBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { SettingValueType } from '../../../services/preferences/common/preferences.js'; +import { settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from '../common/settingsEditorColorRegistry.js'; +import './media/settingsWidgets.css'; +import { settingsDiscardIcon, settingsEditIcon, settingsRemoveIcon } from './preferencesIcons.js'; const $ = DOM.$; @@ -508,12 +507,6 @@ export class ListSettingWidget extends Abst private dragDetails: ListSettingWidgetDragDetails | undefined; - private getDragImage(item: TListDataItem): HTMLElement { - const dragImage = $('.monaco-drag-image'); - dragImage.textContent = item.value.data; - return dragImage; - } - protected renderItem(item: TListDataItem, idx: number): RowElementGroup { const rowElement = $('.setting-list-row'); const valueElement = DOM.append(rowElement, $('.setting-list-value')); @@ -541,13 +534,8 @@ export class ListSettingWidget extends Abst item, itemIndex: idx }; - if (ev.dataTransfer) { - ev.dataTransfer.dropEffect = 'move'; - const dragImage = this.getDragImage(item); - rowElement.ownerDocument.body.appendChild(dragImage); - ev.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => dragImage.remove(), 0); - } + + applyDragImage(ev, rowElement, item.value.data); })); this.listDisposables.add(DOM.addDisposableListener(rowElement, DOM.EventType.DRAG_OVER, (ev) => { if (!this.dragDetails) { @@ -736,7 +724,7 @@ export class ListSettingWidget extends Abst : localize('listSiblingHintLabel', "List item `{0}` with sibling `${1}`", value.data, sibling); const { rowElement } = rowElementGroup; - this.listDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), rowElement, title)); + this.listDisposables.add(this.hoverService.setupDelayedHover(rowElement, { content: title })); rowElement.setAttribute('aria-label', title); } @@ -802,7 +790,7 @@ export class ExcludeSettingWidget extends ListSettingWidget; + searchModel(preferencesModel: ISettingsEditorModel, token: CancellationToken): Promise; } export interface IRemoteSearchProvider extends ISearchProvider { diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index b57d8931..e5cec6f2 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -125,13 +125,6 @@ registry.registerConfiguration({ 'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the Settings editor Table of Contents while searching. If this setting is being changed in the Settings editor, the setting will take effect after the search query is modified."), 'default': 'filter', 'scope': ConfigurationScope.WINDOW - }, - 'workbench.settings.useWeightedKeySearch': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('useWeightedKeySearch', "Controls whether to use an experimental ranking algorithm for search results in the Settings editor. The newer algorithm is still in development and aims to show fewer and more relevant results."), - 'scope': ConfigurationScope.WINDOW, - 'tags': ['experimental'] } } }); diff --git a/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts b/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts index b57328ba..171b2458 100644 --- a/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts +++ b/src/vs/workbench/contrib/preferences/common/settingsFilesystemProvider.ts @@ -14,6 +14,7 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import * as JSONContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { ILogService, LogLevel } from '../../../../platform/log/common/log.js'; +import { isEqual } from '../../../../base/common/resources.js'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -25,6 +26,8 @@ export class SettingsFileSystemProvider extends Disposable implements IFileSyste protected readonly _onDidChangeFile = this._register(new Emitter()); readonly onDidChangeFile = this._onDidChangeFile.event; + private static SCHEMA_ASSOCIATIONS = URI.parse(`${Schemas.vscode}://schemas-associations/schemas-associations.json`); + constructor( @IPreferencesService private readonly preferencesService: IPreferencesService, @ILogService private readonly logService: ILogService @@ -33,6 +36,9 @@ export class SettingsFileSystemProvider extends Disposable implements IFileSyste this._register(schemaRegistry.onDidChangeSchema(schemaUri => { this._onDidChangeFile.fire([{ resource: URI.parse(schemaUri), type: FileChangeType.UPDATED }]); })); + this._register(schemaRegistry.onDidChangeSchemaAssociations(() => { + this._onDidChangeFile.fire([{ resource: SettingsFileSystemProvider.SCHEMA_ASSOCIATIONS, type: FileChangeType.UPDATED }]); + })); this._register(preferencesService.onDidDefaultSettingsContentChanged(uri => { this._onDidChangeFile.fire([{ resource: uri, type: FileChangeType.UPDATED }]); })); @@ -47,6 +53,8 @@ export class SettingsFileSystemProvider extends Disposable implements IFileSyste let content: string | undefined; if (uri.authority === 'schemas') { content = this.getSchemaContent(uri); + } else if (uri.authority === SettingsFileSystemProvider.SCHEMA_ASSOCIATIONS.authority) { + content = JSON.stringify(schemaRegistry.getSchemaAssociations()); } else if (uri.authority === 'defaultsettings') { content = this.preferencesService.getDefaultSettingsContent(uri); } @@ -67,6 +75,16 @@ export class SettingsFileSystemProvider extends Disposable implements IFileSyste size: 0 }; } + if (isEqual(uri, SettingsFileSystemProvider.SCHEMA_ASSOCIATIONS)) { + const currentTime = Date.now(); + return { + type: FileType.File, + permissions: FilePermission.Readonly, + mtime: currentTime, + ctime: currentTime, + size: 0 + }; + } throw FileSystemProviderErrorCode.FileNotFound; } diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index 3ab1684b..3ae6694f 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -32,7 +32,8 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { IWorkbenchQuickAccessConfiguration } from '../../../browser/quickaccess.js'; import { CHAT_OPEN_ACTION_ID } from '../../chat/browser/actions/chatActions.js'; import { ASK_QUICK_QUESTION_ACTION_ID } from '../../chat/browser/actions/chatQuickInputActions.js'; -import { ChatAgentLocation, IChatAgentService } from '../../chat/common/chatAgents.js'; +import { IChatAgentService } from '../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../chat/common/constants.js'; import { CommandInformationResult, IAiRelatedInformationService, RelatedInformationType } from '../../../services/aiRelatedInformation/common/aiRelatedInformation.js'; import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; diff --git a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts index b01a519c..e64aa7ad 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts @@ -151,7 +151,7 @@ export class ViewQuickAccessProvider extends PickerQuickAccessProvider { const paneComposites = this.paneCompositeService.getPaneComposites(location); diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 9f53c894..3e8b69d1 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -22,6 +22,7 @@ import { IWorkbenchEnvironmentService } from '../../../services/environment/comm import { IProductService } from '../../../../platform/product/common/productService.js'; import { IUserDataSyncEnablementService, IUserDataSyncService, SyncStatus } from '../../../../platform/userDataSync/common/userDataSync.js'; import { IUserDataSyncWorkbenchService } from '../../../services/userDataSync/common/userDataSync.js'; +import { ChatConfiguration } from '../../chat/common/constants.js'; interface IConfiguration extends IWindowsConfiguration { update?: { mode?: string }; @@ -30,8 +31,10 @@ interface IConfiguration extends IWindowsConfiguration { security?: { workspace?: { trust?: { enabled?: boolean } }; restrictUNCAccess?: boolean }; window: IWindowSettings; workbench?: { enableExperiments?: boolean }; + telemetry?: { feedback?: { enabled?: boolean } }; _extensionsGallery?: { enablePPE?: boolean }; accessibility?: { verbosity?: { debug?: boolean } }; + chat?: { unifiedChatView?: boolean; useFileStorage?: boolean }; } export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { @@ -41,19 +44,24 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo 'window.nativeTabs', 'window.nativeFullScreen', 'window.clickThroughInactive', + 'window.controlsStyle', 'update.mode', 'editor.accessibilitySupport', 'security.workspace.trust.enabled', 'workbench.enableExperiments', '_extensionsGallery.enablePPE', 'security.restrictUNCAccess', - 'accessibility.verbosity.debug' + 'accessibility.verbosity.debug', + ChatConfiguration.UnifiedChatView, + ChatConfiguration.UseFileStorage, + 'telemetry.feedback.enabled' ]; private readonly titleBarStyle = new ChangeObserver('string'); private readonly nativeTabs = new ChangeObserver('boolean'); private readonly nativeFullScreen = new ChangeObserver('boolean'); private readonly clickThroughInactive = new ChangeObserver('boolean'); + private readonly controlsStyle = new ChangeObserver('string'); private readonly updateMode = new ChangeObserver('string'); private accessibilitySupport: 'on' | 'off' | 'auto' | undefined; private readonly workspaceTrustEnabled = new ChangeObserver('boolean'); @@ -61,6 +69,9 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private readonly enablePPEExtensionsGallery = new ChangeObserver('boolean'); private readonly restrictUNCAccess = new ChangeObserver('boolean'); private readonly accessibilityVerbosityDebug = new ChangeObserver('boolean'); + private readonly unifiedChatView = new ChangeObserver('boolean'); + private readonly useFileStorage = new ChangeObserver('boolean'); + private readonly telemetryFeedbackEnabled = new ChangeObserver('boolean'); constructor( @IHostService private readonly hostService: IHostService, @@ -117,6 +128,9 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo // macOS: Click through (accept first mouse) processChanged(isMacintosh && this.clickThroughInactive.handleChange(config.window?.clickThroughInactive)); + // Windows/Linux: Window controls style + processChanged(!isMacintosh && this.controlsStyle.handleChange(config.window?.controlsStyle)); + // Update mode processChanged(this.updateMode.handleChange(config.update?.mode)); @@ -136,6 +150,9 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo // Debug accessibility verbosity processChanged(this.accessibilityVerbosityDebug.handleChange(config?.accessibility?.verbosity?.debug)); + + processChanged(this.unifiedChatView.handleChange(config.chat?.unifiedChatView)); + processChanged(this.useFileStorage.handleChange(config.chat?.useFileStorage)); } // Experiments @@ -144,6 +161,9 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo // Profiles processChanged(this.productService.quality !== 'stable' && this.enablePPEExtensionsGallery.handleChange(config._extensionsGallery?.enablePPE)); + // Enable Feedback + processChanged(this.telemetryFeedbackEnabled.handleChange(config.telemetry?.feedback?.enabled)); + if (askToRelaunch && changed && this.hostService.hasFocus) { this.doConfirm( isNative ? diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index d8f7af34..2784bfce 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -8,11 +8,11 @@ import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from '../../../s import { RunOnceScheduler, retry } from '../../../../base/common/async.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { MenuId, IMenuService, MenuItemAction, MenuRegistry, registerAction2, Action2, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; +import { MenuId, IMenuService, MenuItemAction, MenuRegistry, registerAction2, Action2, SubmenuItemAction, IMenu } from '../../../../platform/actions/common/actions.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from '../../../services/statusbar/browser/statusbar.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; -import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { Schemas } from '../../../../base/common/network.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; @@ -41,7 +41,6 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { DomEmitter } from '../../../../base/browser/event.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; -import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { infoIcon } from '../../extensions/browser/extensionsIcons.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; @@ -51,6 +50,11 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js'; import { workbenchConfigurationNodeBase } from '../../../common/configuration.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import Severity from '../../../../base/common/severity.js'; +import { isCancellationError } from '../../../../base/common/errors.js'; +import { toErrorMessage } from '../../../../base/common/errorMessage.js'; +import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js'; type ActionGroup = [string, Array]; @@ -82,18 +86,16 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr private remoteStatusEntry: IStatusbarEntryAccessor | undefined; - private readonly legacyIndicatorMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); // to be removed once migration completed - private readonly remoteIndicatorMenu = this._register(this.menuService.createMenu(MenuId.StatusBarRemoteIndicatorMenu, this.contextKeyService)); + private readonly legacyIndicatorMenu: IMenu; // to be removed once migration completed + private readonly remoteIndicatorMenu: IMenu; private remoteMenuActionsGroups: ActionGroup[] | undefined; - private readonly remoteAuthority = this.environmentService.remoteAuthority; - private virtualWorkspaceLocation: { scheme: string; authority: string } | undefined = undefined; private connectionState: 'initializing' | 'connected' | 'reconnecting' | 'disconnected' | undefined = undefined; private connectionToken: string | undefined = undefined; - private readonly connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); + private readonly connectionStateContextKey: IContextKey<'' | 'initializing' | 'disconnected' | 'connected'>; private networkState: 'online' | 'offline' | 'high-latency' | undefined = undefined; private measureNetworkConnectionLatencyScheduler: RunOnceScheduler | undefined = undefined; @@ -125,6 +127,10 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr return this._remoteExtensionMetadata; } + private get remoteAuthority(): string | undefined { + return this.environmentService.remoteAuthority; + } + private remoteMetadataInitialized: boolean = false; private readonly _onDidChangeEntries = this._register(new Emitter()); private readonly onDidChangeEntries: Event = this._onDidChangeEntries.event; @@ -147,11 +153,19 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr @ITelemetryService private readonly telemetryService: ITelemetryService, @IProductService private readonly productService: IProductService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IDialogService private readonly dialogService: IDialogService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, @IOpenerService private readonly openerService: IOpenerService, @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); + this.legacyIndicatorMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); // to be removed once migration completed + this.remoteIndicatorMenu = this._register(this.menuService.createMenu(MenuId.StatusBarRemoteIndicatorMenu, this.contextKeyService)); + + this.connectionStateContextKey = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', '').bindTo(this.contextKeyService); + // Set initial connection state if (this.remoteAuthority) { this.connectionState = 'initializing'; @@ -618,14 +632,27 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr return markdownTooltip; } - private async installExtension(extensionId: string) { - const galleryExtension = (await this.extensionGalleryService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - - await this.extensionManagementService.installFromGallery(galleryExtension, { - isMachineScoped: false, - donotIncludePackAndDependencies: false, - context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true } - }); + private async installExtension(extensionId: string, remoteLabel: string): Promise { + try { + await this.extensionsWorkbenchService.install(extensionId, { + isMachineScoped: false, + donotIncludePackAndDependencies: false, + context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true } + }); + } catch (error) { + if (!this.lifecycleService.willShutdown) { + const { confirmed } = await this.dialogService.confirm({ + type: Severity.Error, + message: nls.localize('unknownSetupError', "An error occurred while setting up {0}. Would you like to try again?", remoteLabel), + detail: error && !isCancellationError(error) ? toErrorMessage(error) : undefined, + primaryButton: nls.localize('retry', "Retry") + }); + if (confirmed) { + return this.installExtension(extensionId, remoteLabel); + } + } + throw error; + } } private async runRemoteStartCommand(extensionId: string, startCommand: string) { @@ -787,8 +814,13 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr quickPick.busy = true; quickPick.placeholder = nls.localize('remote.startActions.installingExtension', 'Installing extension... '); - await this.installExtension(remoteExtension.id); - quickPick.hide(); + try { + await this.installExtension(remoteExtension.id, selectedItems[0].label); + } catch (error) { + return; + } finally { + quickPick.hide(); + } await this.runRemoteStartCommand(remoteExtension.id, remoteExtension.startCommand); } else { diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index f7c3020d..fb3edaaa 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -26,6 +26,11 @@ import { PersistentConnection } from '../../../../platform/remote/common/remoteA import { IDownloadService } from '../../../../platform/download/common/download.js'; import { DownloadServiceChannel } from '../../../../platform/download/common/downloadIpc.js'; import { RemoteLoggerChannelClient } from '../../../../platform/log/common/logIpc.js'; +import { REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS } from '../../../../platform/remote/common/remote.js'; +import product from '../../../../platform/product/common/product.js'; + + +const EXTENSION_IDENTIFIER_PATTERN = '([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$'; export class LabelContribution implements IWorkbenchContribution { @@ -205,7 +210,7 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'object', markdownDescription: localize('remote.extensionKind', "Override the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote. By overriding an extension's default kind using this setting, you specify if that extension should be installed and enabled locally or remotely."), patternProperties: { - '([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$': { + [EXTENSION_IDENTIFIER_PATTERN]: { oneOf: [{ type: 'array', items: extensionKindSchema }, extensionKindSchema], default: ['ui'], }, @@ -355,6 +360,16 @@ Registry.as(ConfigurationExtensions.Configuration) enum: ['localhost', 'allInterfaces'], default: 'localhost', description: localize('remote.localPortHost', "Specifies the local host name that will be used for port forwarding.") + }, + [REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS]: { + type: 'array', + markdownDescription: localize('remote.defaultExtensionsIfInstalledLocally.markdownDescription', 'List of extensions to install upon connection to a remote when already installed locally.'), + default: product?.remoteDefaultExtensionsIfInstalledLocally || [], + items: { + type: 'string', + pattern: EXTENSION_IDENTIFIER_PATTERN, + patternErrorMessage: localize('remote.defaultExtensionsIfInstalledLocally.invalidFormat', 'Extension identifier must be in format "publisher.name".') + }, } } }); diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index adda8fa2..e2abe63d 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -7,7 +7,7 @@ import { localize } from '../../../../nls.js'; import { basename } from '../../../../base/common/resources.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { VIEW_PANE_ID, ISCMService, ISCMRepository, ISCMViewService } from '../common/scm.js'; +import { VIEW_PANE_ID, ISCMService, ISCMRepository, ISCMViewService, ISCMProvider } from '../common/scm.js'; import { IActivityService, NumberBadge } from '../../../services/activity/common/activity.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; @@ -30,49 +30,11 @@ const ActiveRepositoryContextKeys = { }; export class SCMActiveRepositoryController extends Disposable implements IWorkbenchContribution { - private readonly _countBadgeConfig = observableConfigValue<'all' | 'focused' | 'off'>('scm.countBadge', 'all', this.configurationService); - - private readonly _repositories = observableFromEvent(this, - Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository), - () => this.scmService.repositories); - - private readonly _activeRepositoryHistoryItemRefName = derived(reader => { - const repository = this.scmViewService.activeRepository.read(reader); - const historyProvider = repository?.provider.historyProvider.read(reader); - const historyItemRef = historyProvider?.historyItemRef.read(reader); - - return historyItemRef?.name; - }); - - private readonly _countBadgeRepositories = derived(this, reader => { - switch (this._countBadgeConfig.read(reader)) { - case 'all': { - const repositories = this._repositories.read(reader); - return [...Iterable.map(repositories, r => ({ provider: r.provider, resourceCount: this._getRepositoryResourceCount(r) }))]; - } - case 'focused': { - const repository = this.scmViewService.activeRepository.read(reader); - return repository ? [{ provider: repository.provider, resourceCount: this._getRepositoryResourceCount(repository) }] : []; - } - case 'off': - return []; - default: - throw new Error('Invalid countBadge setting'); - } - }); - - private readonly _countBadge = derived(this, reader => { - let total = 0; - - for (const repository of this._countBadgeRepositories.read(reader)) { - const count = repository.provider.count?.read(reader); - const resourceCount = repository.resourceCount.read(reader); - - total = total + (count ?? resourceCount); - } - - return total; - }); + private readonly _repositories: IObservable>; + private readonly _activeRepositoryHistoryItemRefName: IObservable; + private readonly _countBadgeConfig: IObservable<'all' | 'focused' | 'off'>; + private readonly _countBadgeRepositories: IObservable }[]>; + private readonly _countBadge: IObservable; private _activeRepositoryNameContextKey: IContextKey; private _activeRepositoryBranchNameContextKey: IContextKey; @@ -96,11 +58,57 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe { name: 'activeRepositoryBranchName', contextKey: ActiveRepositoryContextKeys.ActiveRepositoryBranchName.key, } ]); + this._countBadgeConfig = observableConfigValue<'all' | 'focused' | 'off'>('scm.countBadge', 'all', this.configurationService); + + this._repositories = observableFromEvent(this, + Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository), + () => this.scmService.repositories); + + this._activeRepositoryHistoryItemRefName = derived(reader => { + const repository = this.scmViewService.activeRepository.read(reader); + const historyProvider = repository?.provider.historyProvider.read(reader); + const historyItemRef = historyProvider?.historyItemRef.read(reader); + + return historyItemRef?.name; + }); + + this._countBadgeRepositories = derived(this, reader => { + switch (this._countBadgeConfig.read(reader)) { + case 'all': { + const repositories = this._repositories.read(reader); + return [...Iterable.map(repositories, r => ({ provider: r.provider, resourceCount: this._getRepositoryResourceCount(r) }))]; + } + case 'focused': { + const repository = this.scmViewService.activeRepository.read(reader); + return repository ? [{ provider: repository.provider, resourceCount: this._getRepositoryResourceCount(repository) }] : []; + } + case 'off': + return []; + default: + throw new Error('Invalid countBadge setting'); + } + }); + + this._countBadge = derived(this, reader => { + let total = 0; + + for (const repository of this._countBadgeRepositories.read(reader)) { + const count = repository.provider.count?.read(reader); + const resourceCount = repository.resourceCount.read(reader); + + total = total + (count ?? resourceCount); + } + + return total; + }); + this._register(autorunWithStore((reader, store) => { - this._updateActivityCountBadge(this._countBadge.read(reader), store); + const countBadge = this._countBadge.read(reader); + this._updateActivityCountBadge(countBadge, store); })); this._register(autorunWithStore((reader, store) => { + this._repositories.read(reader); const repository = this.scmViewService.activeRepository.read(reader); const commands = repository?.provider.statusBarCommands.read(reader); @@ -168,6 +176,19 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe this.statusbarService.addEntry(statusbarEntry, `status.scm.${index}`, MainThreadStatusBarAlignment.LEFT, { location: { id: `status.scm.${index - 1}`, priority: 10000 }, alignment: MainThreadStatusBarAlignment.RIGHT, compact: true }) ); } + + // Ssource control provider status bar entry + if (this.scmService.repositoryCount > 1) { + const repositoryStatusbarEntry: IStatusbarEntry = { + name: localize('status.scm.provider', "Source Control Provider"), + text: `$(repo) ${repository.provider.name}`, + ariaLabel: label, + tooltip: label, + command: 'scm.setActiveProvider' + }; + + store.add(this.statusbarService.addEntry(repositoryStatusbarEntry, 'status.scm.provider', MainThreadStatusBarAlignment.LEFT, { location: { id: `status.scm.0`, priority: 10000 }, alignment: MainThreadStatusBarAlignment.LEFT, compact: true })); + } } private _updateActiveRepositoryContextKeys(repositoryName: string | undefined, branchName: string | undefined): void { @@ -177,9 +198,7 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe } export class SCMActiveResourceContextKeyController extends Disposable implements IWorkbenchContribution { - private readonly _repositories = observableFromEvent(this, - Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository), - () => this.scmService.repositories); + private readonly _repositories: IObservable>; private readonly _onDidRepositoryChange = new Emitter(); @@ -193,6 +212,10 @@ export class SCMActiveResourceContextKeyController extends Disposable implements const activeResourceHasChangesContextKey = new RawContextKey('scmActiveResourceHasChanges', false, localize('scmActiveResourceHasChanges', "Whether the active resource has changes")); const activeResourceRepositoryContextKey = new RawContextKey('scmActiveResourceRepository', undefined, localize('scmActiveResourceRepository', "The active resource's repository")); + this._repositories = observableFromEvent(this, + Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository), + () => this.scmService.repositories); + this._store.add(autorunWithStore((reader, store) => { for (const repository of this._repositories.read(reader)) { store.add(Event.runAndSubscribe(repository.provider.onDidChangeResources, () => { diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index a38b3742..6faa64d0 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -650,3 +650,7 @@ border-radius: 2px; opacity: 0.5; } + +.monaco-workbench .part.statusbar > .items-container > .statusbar-item .codicon.codicon-repo { + padding-top: 1px; +} diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts index 01c9eb21..ac52c163 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts @@ -22,7 +22,8 @@ import { IWorkbenchContribution } from '../../../common/contributions.js'; import { ResourceMap } from '../../../../base/common/map.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { autorun, autorunWithStore, observableFromEvent } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStore, IObservable, observableFromEvent } from '../../../../base/common/observable.js'; +import { EditorInput } from '../../../common/editor/editorInput.js'; export const quickDiffDecorationCount = new RawContextKey('quickDiffDecorationCount', 0); @@ -188,8 +189,7 @@ export class QuickDiffWorkbenchController extends Disposable implements IWorkben private enabled = false; private readonly quickDiffDecorationCount: IContextKey; - private readonly activeEditor = observableFromEvent(this, - this.editorService.onDidActiveEditorChange, () => this.editorService.activeEditor); + private readonly activeEditor: IObservable; // Resource URI -> Code Editor Id -> Decoration (Disposable) private readonly decorators = new ResourceMap>(); @@ -209,6 +209,9 @@ export class QuickDiffWorkbenchController extends Disposable implements IWorkben this.quickDiffDecorationCount = quickDiffDecorationCount.bindTo(contextKeyService); + this.activeEditor = observableFromEvent(this, + this.editorService.onDidActiveEditorChange, () => this.editorService.activeEditor); + const onDidChangeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorations')); this._register(onDidChangeConfiguration(this.onDidChangeConfiguration, this)); this.onDidChangeConfiguration(); diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index de01355f..f22648c7 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -172,7 +172,7 @@ class QuickDiffWidget extends PeekViewWidget { ) { super(editor, { isResizeable: true, frameWidth: 1, keepEditorSelection: true, className: 'dirty-diff' }, instantiationService); - this._disposables.add(themeService.onDidColorThemeChange(e => this._applyTheme(e.theme))); + this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); this._applyTheme(themeService.getColorTheme()); if (!Iterable.isEmpty(this.model.originalTextModels)) { diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 2995f5cc..a3ce54d9 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -25,9 +25,9 @@ import { ModesRegistry } from '../../../../editor/common/languages/modesRegistry import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { ContextKeys, SCMViewPane } from './scmViewPane.js'; -import { SCMViewService } from './scmViewService.js'; +import { RepositoryPicker, SCMViewService } from './scmViewService.js'; import { SCMRepositoriesViewPane } from './scmRepositoriesViewPane.js'; -import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { Context as SuggestContext } from '../../../../editor/contrib/suggest/browser/suggest.js'; import { MANAGE_TRUST_COMMAND_ID, WorkspaceTrustContext } from '../../workspace/common/workspace.js'; import { IQuickDiffService } from '../common/quickDiff.js'; @@ -534,6 +534,21 @@ CommandsRegistry.registerCommand('scm.openInTerminal', async (accessor, provider await commandService.executeCommand('openInTerminal', provider.rootUri); }); +CommandsRegistry.registerCommand('scm.setActiveProvider', async (accessor) => { + const instantiationService = accessor.get(IInstantiationService); + const scmViewService = accessor.get(ISCMViewService); + + const placeHolder = localize('scmActiveRepositoryPlaceHolder', "Select the active repository, type to filter all repositories"); + const autoQuickItemDescription = localize('scmActiveRepositoryAutoDescription', "The active repository is updated based on focused repository/active editor"); + const repositoryPicker = instantiationService.createInstance(RepositoryPicker, placeHolder, autoQuickItemDescription); + + const result = await repositoryPicker.pickRepository(); + if (result?.repository) { + const repository = result.repository !== 'auto' ? result.repository : undefined; + scmViewService.pinActiveRepository(repository); + } +}); + MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { group: '100_end', command: { diff --git a/src/vs/workbench/contrib/scm/browser/scmAccessibilityHelp.ts b/src/vs/workbench/contrib/scm/browser/scmAccessibilityHelp.ts index 3126416f..18908231 100644 --- a/src/vs/workbench/contrib/scm/browser/scmAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/scm/browser/scmAccessibilityHelp.ts @@ -76,23 +76,23 @@ class SCMAccessibilityHelpContentProvider extends Disposable implements IAccessi content.push(localize('state-msg1', "Visible repositories: {0}", repositoryList)); } - const activeRepository = this._scmViewService.activeRepository.get(); - if (activeRepository) { - content.push(localize('state-msg2', "Repository: {0}", activeRepository.provider.name)); + const focusedRepository = this._scmViewService.focusedRepository; + if (focusedRepository) { + content.push(localize('state-msg2', "Repository: {0}", focusedRepository.provider.name)); // History Item Reference - const currentHistoryItemRef = activeRepository.provider.historyProvider.get()?.historyItemRef.get(); + const currentHistoryItemRef = focusedRepository.provider.historyProvider.get()?.historyItemRef.get(); if (currentHistoryItemRef) { content.push(localize('state-msg3', "History item reference: {0}", currentHistoryItemRef.name)); } // Commit Message - if (activeRepository.input.visible && activeRepository.input.enabled && activeRepository.input.value !== '') { - content.push(localize('state-msg4', "Commit message: {0}", activeRepository.input.value)); + if (focusedRepository.input.visible && focusedRepository.input.enabled && focusedRepository.input.value !== '') { + content.push(localize('state-msg4', "Commit message: {0}", focusedRepository.input.value)); } // Action Button - const actionButton = activeRepository.provider.actionButton.get(); + const actionButton = focusedRepository.provider.actionButton.get(); if (actionButton) { const label = actionButton.command.tooltip ?? actionButton.command.title; const enablementLabel = actionButton.enabled ? localize('enabled', "enabled") : localize('disabled', "disabled"); @@ -101,11 +101,11 @@ class SCMAccessibilityHelpContentProvider extends Disposable implements IAccessi // Resource Groups const resourceGroups: string[] = []; - for (const resourceGroup of activeRepository.provider.groups) { + for (const resourceGroup of focusedRepository.provider.groups) { resourceGroups.push(`${resourceGroup.label} (${resourceGroup.resources.length} resource(s))`); } - activeRepository.provider.groups.map(g => g.label).join(', '); + focusedRepository.provider.groups.map(g => g.label).join(', '); content.push(localize('state-msg6', "Resource groups: {0}", resourceGroups.join(', '))); } diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 64d04fc0..fdde018b 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -318,7 +318,7 @@ class HistoryItemRenderer implements ITreeRenderer('scm.graph.badges', 'filter', this._configurationService); + private readonly _badgesConfig: IObservable<'all' | 'filter'>; constructor( private readonly hoverDelegate: IHoverDelegate, @@ -328,7 +328,9 @@ class HistoryItemRenderer implements ITreeRenderer('scm.graph.badges', 'filter', this._configurationService); + } renderTemplate(container: HTMLElement): HistoryItemTemplate { // hack @@ -749,36 +751,13 @@ type RepositoryState = { }; class SCMHistoryViewModel extends Disposable { - - private readonly _closedRepository = observableFromEvent( - this, - this._scmService.onDidRemoveRepository, - repository => repository); - - private readonly _firstRepository = this._scmService.repositoryCount > 0 ? - constObservable(Iterable.first(this._scmService.repositories)) : - observableFromEvent( - this, - Event.once(this._scmService.onDidAddRepository), - repository => repository - ); - - private readonly _selectedRepository = observableValue<'auto' | ISCMRepository>(this, 'auto'); - - private readonly _graphRepository = derived(reader => { - const selectedRepository = this._selectedRepository.read(reader); - if (selectedRepository !== 'auto') { - return selectedRepository; - } - - return this._scmViewService.activeRepository.read(reader); - }); - /** * The active | selected repository takes precedence over the first repository when the observable * values are updated in the same transaction (or during the initial read of the observable value). */ - readonly repository = latestChangedValue(this, [this._firstRepository, this._graphRepository]); + readonly repository: IObservable; + private readonly _selectedRepository = observableValue<'auto' | ISCMRepository>(this, 'auto'); + readonly onDidChangeHistoryItemsFilter = observableSignal(this); readonly isViewModelEmpty = observableValue(this, false); @@ -804,9 +783,30 @@ class SCMHistoryViewModel extends Disposable { this._scmHistoryItemCountCtx = ContextKeys.SCMHistoryItemCount.bindTo(this._contextKeyService); + const firstRepository = this._scmService.repositoryCount > 0 + ? constObservable(Iterable.first(this._scmService.repositories)) + : observableFromEvent(this, + Event.once(this._scmService.onDidAddRepository), + repository => repository); + + const graphRepository = derived(reader => { + const selectedRepository = this._selectedRepository.read(reader); + if (selectedRepository !== 'auto') { + return selectedRepository; + } + + return this._scmViewService.activeRepository.read(reader); + }); + + this.repository = latestChangedValue(this, [firstRepository, graphRepository]); + + const closedRepository = observableFromEvent(this, + this._scmService.onDidRemoveRepository, + repository => repository); + // Closed repository cleanup this._register(autorun(reader => { - const repository = this._closedRepository.read(reader); + const repository = closedRepository.read(reader); if (!repository) { return; } @@ -1044,7 +1044,7 @@ class SCMHistoryViewModel extends Disposable { type RepositoryQuickPickItem = IQuickPickItem & { repository: 'auto' | ISCMRepository }; -class RepositoryPicker extends Disposable { +class RepositoryPicker { private readonly _autoQuickPickItem: RepositoryQuickPickItem = { label: localize('auto', "Auto"), description: localize('activeRepository', "Show the source control graph for the active repository"), @@ -1054,9 +1054,7 @@ class RepositoryPicker extends Disposable { constructor( @IQuickInputService private readonly _quickInputService: IQuickInputService, @ISCMViewService private readonly _scmViewService: ISCMViewService - ) { - super(); - } + ) { } async pickRepository(): Promise { const picks: (RepositoryQuickPickItem | IQuickPickSeparator)[] = [ diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index 254f02be..755aed69 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -92,6 +92,7 @@ export class SCMRepositoriesViewPane extends ViewPane { this._register(this.list); this._register(this.list.onDidChangeSelection(this.onListSelectionChange, this)); + this._register(this.list.onDidChangeFocus(this.onDidChangeFocus, this)); this._register(this.list.onContextMenu(this.onListContextMenu, this)); this._register(this.scmViewService.onDidChangeRepositories(this.onDidChangeRepositories, this)); @@ -169,6 +170,12 @@ export class SCMRepositoriesViewPane extends ViewPane { } } + private onDidChangeFocus(e: IListEvent): void { + if (e.browserEvent && e.elements.length > 0) { + this.scmViewService.focus(e.elements[0]); + } + } + private updateListSelection(): void { const oldSelection = this.list.getSelection(); const oldSet = new Set(Iterable.map(oldSelection, i => this.list.element(i))); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 013eeeac..83f8a154 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1498,6 +1498,8 @@ class SCMInputWidgetEditorOptions { e => { return e.affectsConfiguration('editor.accessibilitySupport') || e.affectsConfiguration('editor.cursorBlinking') || + e.affectsConfiguration('editor.cursorStyle') || + e.affectsConfiguration('editor.cursorWidth') || e.affectsConfiguration('editor.emptySelectionClipboard') || e.affectsConfiguration('editor.fontFamily') || e.affectsConfiguration('editor.rulers') || @@ -1515,7 +1517,6 @@ class SCMInputWidgetEditorOptions { return { ...getSimpleEditorOptions(this.configurationService), ...this.getEditorOptions(), - cursorWidth: 1, dragAndDrop: true, dropIntoEditor: { enabled: true }, formatOnType: true, @@ -1539,9 +1540,11 @@ class SCMInputWidgetEditorOptions { const lineHeight = this._getEditorLineHeight(fontSize); const accessibilitySupport = this.configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport'); const cursorBlinking = this.configurationService.getValue<'blink' | 'smooth' | 'phase' | 'expand' | 'solid'>('editor.cursorBlinking'); + const cursorStyle = this.configurationService.getValue('editor.cursorStyle'); + const cursorWidth = this.configurationService.getValue('editor.cursorWidth') ?? 1; const emptySelectionClipboard = this.configurationService.getValue('editor.emptySelectionClipboard') === true; - return { ...this._getEditorLanguageConfiguration(), accessibilitySupport, cursorBlinking, fontFamily, fontSize, lineHeight, emptySelectionClipboard }; + return { ...this._getEditorLanguageConfiguration(), accessibilitySupport, cursorBlinking, cursorStyle, cursorWidth, fontFamily, fontSize, lineHeight, emptySelectionClipboard }; } private _getEditorFontFamily(): string { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 52af3ec9..e5c16a6e 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -18,9 +18,14 @@ import { binarySearch } from '../../../../base/common/arrays.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { derivedObservableWithCache, derivedOpts, latestChangedValue, observableFromEventOpts } from '../../../../base/common/observable.js'; +import { derivedObservableWithCache, derivedOpts, IObservable, ISettableObservable, latestChangedValue, observableFromEventOpts, observableValue } from '../../../../base/common/observable.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { EditorResourceAccessor } from '../../../common/editor.js'; +import { EditorInput } from '../../../common/editor/editorInput.js'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { localize } from '../../../../nls.js'; function getProviderStorageKey(provider: ISCMProvider): string { return `${provider.contextValue}:${provider.label}${provider.rootUri ? `:${provider.rootUri.toString()}` : ''}`; @@ -39,6 +44,41 @@ export const RepositoryContextKeys = { RepositorySortKey: new RawContextKey('scmRepositorySortKey', ISCMRepositorySortKey.DiscoveryTime), }; +export type RepositoryQuickPickItem = IQuickPickItem & { repository: 'auto' | ISCMRepository }; + +export class RepositoryPicker { + private readonly _autoQuickPickItem: RepositoryQuickPickItem; + + constructor( + private readonly _placeHolder: string, + private readonly _autoQuickItemDescription: string, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @ISCMViewService private readonly _scmViewService: ISCMViewService + ) { + this._autoQuickPickItem = { + label: localize('auto', "Auto"), + description: this._autoQuickItemDescription, + repository: 'auto' + } satisfies RepositoryQuickPickItem; + } + + async pickRepository(): Promise { + const picks: (RepositoryQuickPickItem | IQuickPickSeparator)[] = [ + this._autoQuickPickItem, + { type: 'separator' } + ]; + + picks.push(...this._scmViewService.repositories.map(r => ({ + label: r.provider.name, + description: r.provider.rootUri?.fsPath, + iconClass: ThemeIcon.asClassName(Codicon.repo), + repository: r + }))); + + return this._quickInputService.pick(picks, { placeHolder: this._placeHolder }); + } +} + interface ISCMRepositoryView { readonly repository: ISCMRepository; readonly discoveryTime: number; @@ -157,49 +197,17 @@ export class SCMViewService implements ISCMViewService { private _onDidFocusRepository = new Emitter(); readonly onDidFocusRepository = this._onDidFocusRepository.event; - private readonly _focusedRepository = observableFromEventOpts( - { - owner: this, - equalsFn: () => false - }, this.onDidFocusRepository, - () => this.focusedRepository); - - private readonly _activeEditor = observableFromEventOpts( - { - owner: this, - equalsFn: () => false - }, this.editorService.onDidActiveEditorChange, - () => this.editorService.activeEditor); - - private readonly _activeEditorRepository = derivedObservableWithCache(this, - (reader, lastValue) => { - const activeEditor = this._activeEditor.read(reader); - const activeResource = EditorResourceAccessor.getOriginalUri(activeEditor); - if (!activeResource) { - return lastValue; - } - - const repository = this.scmService.getRepository(activeResource); - if (!repository) { - return lastValue; - } - - return Object.create(repository); - }); + readonly activeRepository: IObservable; + private readonly _activeEditorObs: IObservable; + private readonly _activeEditorRepositoryObs: IObservable; /** * The focused repository takes precedence over the active editor repository when the observable * values are updated in the same transaction (or during the initial read of the observable value). - */ - private readonly _activeRepository = latestChangedValue(this, [this._activeEditorRepository, this._focusedRepository]); - - /** - * Derived with a custom equality function - */ - readonly activeRepository = derivedOpts({ - owner: this, - equalsFn: (r1, r2) => r1?.id === r2?.id - }, reader => this._activeRepository.read(reader)); + */ + private readonly _activeRepositoryObs: IObservable; + private readonly _activeRepositoryPinnedObs: ISettableObservable; + private readonly _focusedRepositoryObs: IObservable; private _repositoriesSortKey: ISCMRepositorySortKey; private _sortKeyContextKey: IContextKey; @@ -216,6 +224,49 @@ export class SCMViewService implements ISCMViewService { ) { this.menus = instantiationService.createInstance(SCMMenus); + this._focusedRepositoryObs = observableFromEventOpts( + { + owner: this, + equalsFn: () => false + }, this.onDidFocusRepository, + () => this.focusedRepository); + + this._activeEditorObs = observableFromEventOpts( + { + owner: this, + equalsFn: () => false + }, this.editorService.onDidActiveEditorChange, + () => this.editorService.activeEditor); + + this._activeEditorRepositoryObs = derivedObservableWithCache(this, + (reader, lastValue) => { + const activeEditor = this._activeEditorObs.read(reader); + const activeResource = EditorResourceAccessor.getOriginalUri(activeEditor); + if (!activeResource) { + return lastValue; + } + + const repository = this.scmService.getRepository(activeResource); + if (!repository) { + return lastValue; + } + + return Object.create(repository); + }); + + this._activeRepositoryPinnedObs = observableValue(this, undefined); + this._activeRepositoryObs = latestChangedValue(this, [this._activeEditorRepositoryObs, this._focusedRepositoryObs]); + + this.activeRepository = derivedOpts({ + owner: this, + equalsFn: (r1, r2) => r1?.id === r2?.id + }, reader => { + const activeRepository = this._activeRepositoryObs.read(reader); + const activeRepositoryPinned = this._activeRepositoryPinnedObs.read(reader); + + return activeRepositoryPinned ?? activeRepository; + }); + try { this.previousState = JSON.parse(storageService.get('scm:view:visibleRepositories', StorageScope.WORKSPACE, '')); } catch { @@ -379,6 +430,10 @@ export class SCMViewService implements ISCMViewService { } } + pinActiveRepository(repository: ISCMRepository | undefined): void { + this._activeRepositoryPinnedObs.set(repository, undefined); + } + private compareRepositories(op1: ISCMRepositoryView, op2: ISCMRepositoryView): number { // Sort by discovery time if (this._repositoriesSortKey === ISCMRepositorySortKey.DiscoveryTime) { diff --git a/src/vs/workbench/contrib/scm/browser/workingSet.ts b/src/vs/workbench/contrib/scm/browser/workingSet.ts index 1185f8de..1f53f8d2 100644 --- a/src/vs/workbench/contrib/scm/browser/workingSet.ts +++ b/src/vs/workbench/contrib/scm/browser/workingSet.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { autorun, autorunWithStore, derived } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStore, derived, IObservable } from '../../../../base/common/observable.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; @@ -28,8 +28,8 @@ interface ISCMRepositoryWorkingSet { export class SCMWorkingSetController extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.scmWorkingSets'; + private _enabledConfig: IObservable; private _workingSets!: Map; - private _enabledConfig = observableConfigValue('scm.workingSets.enabled', false, this.configurationService); private readonly _repositoryDisposables = new DisposableMap(); @@ -42,6 +42,8 @@ export class SCMWorkingSetController extends Disposable implements IWorkbenchCon ) { super(); + this._enabledConfig = observableConfigValue('scm.workingSets.enabled', false, this.configurationService); + this._store.add(autorunWithStore((reader, store) => { if (!this._enabledConfig.read(reader)) { this.storageService.remove('scm.workingSets', StorageScope.WORKSPACE); diff --git a/src/vs/workbench/contrib/scm/common/quickDiff.ts b/src/vs/workbench/contrib/scm/common/quickDiff.ts index 55cd6956..60dd34b3 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiff.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiff.ts @@ -14,7 +14,10 @@ import { LineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; import { IColorTheme } from '../../../../platform/theme/common/themeService.js'; import { Color } from '../../../../base/common/color.js'; -import { editorErrorForeground, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; +import { + darken, editorBackground, editorForeground, listInactiveSelectionBackground, opaque, + editorErrorForeground, registerColor, transparent +} from '../../../../platform/theme/common/colorRegistry.js'; export const IQuickDiffService = createDecorator('quickDiff'); @@ -45,6 +48,12 @@ export const overviewRulerAddedForeground = registerColor('editorOverviewRuler.a export const overviewRulerDeletedForeground = registerColor('editorOverviewRuler.deletedForeground', transparent(editorGutterDeletedBackground, 0.6), nls.localize('overviewRulerDeletedForeground', 'Overview ruler marker color for deleted content.')); +export const editorGutterItemGlyphForeground = registerColor('editorGutter.itemGlyphForeground', + { dark: editorForeground, light: editorForeground, hcDark: Color.black, hcLight: Color.white }, + nls.localize('editorGutterItemGlyphForeground', 'Editor gutter decoration color for gutter item glyphs.') +); +export const editorGutterItemBackground = registerColor('editorGutter.itemBackground', { dark: opaque(listInactiveSelectionBackground, editorBackground), light: darken(opaque(listInactiveSelectionBackground, editorBackground), .05), hcDark: Color.white, hcLight: Color.black }, nls.localize('editorGutterItemBackground', 'Editor gutter decoration color for gutter item background. This color should be opaque.')); + export interface QuickDiffProvider { label: string; rootUri: URI | undefined; diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index d04c2b09..1987fedd 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -233,6 +233,7 @@ export interface ISCMViewService { * Focused repository or the repository for the active editor */ readonly activeRepository: IObservable; + pinActiveRepository(repository: ISCMRepository | undefined): void; } export const SCM_CHANGES_EDITOR_ID = 'workbench.editor.scmChangesEditor'; diff --git a/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts b/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts index df9d8a5d..c630a138 100644 --- a/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts +++ b/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts @@ -23,14 +23,18 @@ import { TextSearchHeadingImpl } from '../searchTreeModel/textSearchHeading.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { textSearchResultToMatches } from '../searchTreeModel/match.js'; import { ISearchTreeAIFileMatch } from './aiSearchModelBase.js'; +import { ResourceSet } from '../../../../../base/common/map.js'; export class AITextSearchHeadingImpl extends TextSearchHeadingImpl { + public override hidden: boolean; constructor( parent: ISearchResult, @IInstantiationService instantiationService: IInstantiationService, @IUriIdentityService uriIdentityService: IUriIdentityService ) { super(false, parent, instantiationService, uriIdentityService); + + this.hidden = true; } override name(): string { @@ -64,6 +68,20 @@ export class AITextSearchHeadingImpl extends TextSearchHeadingImpl this._query = query; } + override fileCount(): number { + const uniqueFileUris = new ResourceSet(); + for (const folderMatch of this.folderMatches()) { + if (folderMatch.isEmpty()) { + continue; + } + for (const fileMatch of folderMatch.allDownstreamFileMatches()) { + uniqueFileUris.add(fileMatch.resource); + } + } + + return uniqueFileUris.size; + } + private _createBaseFolderMatch(resource: URI, id: string, index: number, query: IAITextQuery): ISearchTreeFolderMatch { const folderMatch: ISearchTreeFolderMatch = this._register(this.createWorkspaceRootWithResourceImpl(resource, id, index, query)); const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); diff --git a/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts b/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts index ab63b6ec..ab06c9be 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts @@ -99,6 +99,35 @@ registerAction2(class CopyAllCommandAction extends Action2 { } }); +registerAction2(class GetSearchResultsAction extends Action2 { + constructor() { + super({ + id: Constants.SearchCommandIds.GetSearchResultsActionId, + title: nls.localize2('getSearchResultsLabel', "Get Search Results"), + category, + f1: false + }); + } + + override async run(accessor: ServicesAccessor): Promise { + const viewsService = accessor.get(IViewsService); + const labelService = accessor.get(ILabelService); + + const searchView = getSearchView(viewsService); + if (searchView) { + const root = searchView.searchResult; + const textSearchResult = allFolderMatchesToString(root.folderMatches(), labelService); + const aiSearchResult = allFolderMatchesToString(root.folderMatches(true), labelService); + + const text = `${textSearchResult}${lineDelimiter}${lineDelimiter}${aiSearchResult}`; + + return text; + } + + return undefined; + } +}); + //#endregion //#region Helpers diff --git a/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts b/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts index 0776c52e..7a0f2a21 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts @@ -15,7 +15,7 @@ import { VIEW_ID } from '../../../services/search/common/search.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { KeyCode } from '../../../../base/common/keyCodes.js'; +import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { SearchStateKey, SearchUIState } from '../common/search.js'; import { category, getSearchView } from './searchActionsBase.js'; import { isSearchTreeMatch, RenderableMatch, ISearchResult, isSearchTreeFolderMatch, isSearchTreeFolderMatchNoRoot, isSearchTreeFolderMatchWorkspaceRoot, isSearchResult, isTextSearchHeading, isSearchTreeFileMatch } from './searchTreeModel/searchTreeCommon.js'; @@ -205,6 +205,34 @@ registerAction2(class ViewAsListAction extends Action2 { } }); +registerAction2(class SearchWithAIAction extends Action2 { + constructor() { + super({ + id: Constants.SearchCommandIds.SearchWithAIActionId, + title: nls.localize2('SearchWithAIAction.label', "Search with AI"), + category, + f1: true, + precondition: Constants.SearchContext.hasAIResultProvider, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(Constants.SearchContext.hasAIResultProvider, Constants.SearchContext.SearchViewFocusedKey), + primary: KeyMod.CtrlCmd | KeyCode.KeyI + } + }); + } + + async run(accessor: ServicesAccessor, ...args: any[]) { + const searchView = getSearchView(accessor.get(IViewsService)); + if (searchView) { + const viewer = searchView.getControl(); + searchView.model.searchResult.aiTextSearchResult.hidden = false; + searchView.model.cancelAISearch(true); + searchView.model.clearAiSearchResults(); + await searchView.queueRefreshTree(); + await forcedExpandRecursively(viewer, searchView.model.searchResult.aiTextSearchResult); + } + } +}); //#endregion @@ -274,7 +302,7 @@ function cancelSearch(accessor: ServicesAccessor) { function refreshSearch(accessor: ServicesAccessor) { const viewsService = accessor.get(IViewsService); const searchView = getSearchView(viewsService); - searchView?.triggerQueryChange({ preserveFocus: false }); + searchView?.triggerQueryChange({ preserveFocus: false, shouldUpdateAISearch: !searchView.model.searchResult.aiTextSearchResult.hidden }); } function collapseDeepestExpandedLevel(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/search/browser/searchMessage.ts b/src/vs/workbench/contrib/search/browser/searchMessage.ts index 37a03f28..ea8990aa 100644 --- a/src/vs/workbench/contrib/search/browser/searchMessage.ts +++ b/src/vs/workbench/contrib/search/browser/searchMessage.ts @@ -10,7 +10,7 @@ import { parseLinkedText } from '../../../../base/common/linkedText.js'; import Severity from '../../../../base/common/severity.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { TextSearchCompleteMessage, TextSearchCompleteMessageType } from '../../../services/search/common/searchExtTypes.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { Schemas } from '../../../../base/common/network.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 55c599ca..f94b4227 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -144,7 +144,13 @@ export class TextSearchResultRenderer extends Disposable implements ICompressibl SearchContext.FileFocusKey.bindTo(templateData.contextKeyService).set(false); SearchContext.FolderFocusKey.bindTo(templateData.contextKeyService).set(false); } else { - const aiName = await node.element.parent().searchModel.getAITextResultProviderName(); + let aiName = 'Copilot'; + try { + aiName = (await node.element.parent().searchModel.getAITextResultProviderName()) || 'Copilot'; + } catch { + // ignore + } + const localizedLabel = nls.localize({ key: 'searchFolderMatch.aiText.label', comment: ['This is displayed before the AI text search results, where {0} will be in the place of the AI name (ie: Copilot)'] diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchModel.ts index 104a7ef8..df9c61ce 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchModel.ts @@ -398,6 +398,11 @@ export class SearchModelImpl extends Disposable implements ISearchModel { } return false; } + clearAiSearchResults(): void { + this._aiResultQueue.length = 0; + // it's not clear all as we are only clearing the AI results + this._searchResult.aiTextSearchResult.clear(false); + } override dispose(): void { this.cancelSearch(); this.cancelAISearch(); diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts index 6bdb676f..f2e0fa7e 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts @@ -197,7 +197,9 @@ export class SearchResultImpl extends Disposable implements ISearchResult { add(allRaw: IFileMatch[], searchInstanceID: string, ai: boolean, silent: boolean = false): void { this._plainTextSearchResult.hidden = false; - this._aiTextSearchResult.hidden = false; + if (ai) { + this._aiTextSearchResult.hidden = false; + } if (ai) { this._aiTextSearchResult.add(allRaw, searchInstanceID, silent); diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts index d473600c..bb05360e 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts @@ -107,6 +107,7 @@ export interface ISearchModel { }; cancelSearch(cancelledForNewSearch?: boolean): boolean; cancelAISearch(cancelledForNewSearch?: boolean): boolean; + clearAiSearchResults(): void; dispose(): void; } @@ -168,7 +169,7 @@ export interface ITextSearchHeading { rangeHighlightDecorations: RangeHighlightDecorations; fileCount(): number; count(): number; - clear(): void; + clear(clearAll: boolean): void; dispose(): void; } @@ -335,6 +336,13 @@ export function isSearchTreeMatch(obj: any): obj is ISearchTreeMatch { obj.id().startsWith(MATCH_PREFIX); } +export function isSearchHeader(obj: any): boolean { + return typeof obj === 'object' && + obj !== null && + typeof obj.id === 'function' && + obj.id().startsWith(TEXT_SEARCH_HEADING_PREFIX); +} + export function getFileMatches(matches: (ISearchTreeFileMatch | ISearchTreeFolderMatchWithResource)[]): ISearchTreeFileMatch[] { const folderMatches: ISearchTreeFolderMatchWithResource[] = []; diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/textSearchHeading.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/textSearchHeading.ts index ce7f9670..c301606b 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/textSearchHeading.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/textSearchHeading.ts @@ -249,9 +249,9 @@ export abstract class TextSearchHeadingImpl return this.matches().reduce((prev, match) => prev + match.count(), 0); } - clear(): void { + clear(clearAll: boolean = true): void { this.cachedSearchComplete = undefined; - this.folderMatches().forEach((folderMatch) => folderMatch.clear(true)); + this.folderMatches().forEach((folderMatch) => folderMatch.clear(clearAll)); this.disposeMatches(); this._folderMatches = []; this._otherFilesMatch = null; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index e46972d4..2a7f4d3c 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -71,7 +71,7 @@ import { createEditorFromSearchResult } from '../../searchEditor/browser/searchE import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { IPreferencesService, ISettingsEditorOptions } from '../../../services/preferences/common/preferences.js'; import { ITextQueryBuilderOptions, QueryBuilder } from '../../../services/search/common/queryBuilder.js'; -import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, SearchCompletionExitCode, SearchSortOrder, TextSearchCompleteMessageType, ViewMode } from '../../../services/search/common/search.js'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ISearchService, ITextQuery, SearchCompletionExitCode, SearchSortOrder, TextSearchCompleteMessageType, ViewMode } from '../../../services/search/common/search.js'; import { TextSearchCompleteMessage } from '../../../services/search/common/searchExtTypes.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { INotebookService } from '../../notebook/common/notebookService.js'; @@ -80,7 +80,7 @@ import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../pl import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { ISearchViewModelWorkbenchService } from './searchTreeModel/searchViewModelWorkbenchService.js'; -import { ISearchTreeMatch, isSearchTreeMatch, RenderableMatch, SearchModelLocation, IChangeEvent, FileMatchOrMatch, ISearchTreeFileMatch, ISearchTreeFolderMatch, ISearchModel, ISearchResult, isSearchTreeFileMatch, isSearchTreeFolderMatch, isSearchTreeFolderMatchNoRoot, isSearchTreeFolderMatchWithResource, isSearchTreeFolderMatchWorkspaceRoot, isSearchResult, isTextSearchHeading, ITextSearchHeading } from './searchTreeModel/searchTreeCommon.js'; +import { ISearchTreeMatch, isSearchTreeMatch, RenderableMatch, SearchModelLocation, IChangeEvent, FileMatchOrMatch, ISearchTreeFileMatch, ISearchTreeFolderMatch, ISearchModel, ISearchResult, isSearchTreeFileMatch, isSearchTreeFolderMatch, isSearchTreeFolderMatchNoRoot, isSearchTreeFolderMatchWithResource, isSearchTreeFolderMatchWorkspaceRoot, isSearchResult, isTextSearchHeading, ITextSearchHeading, isSearchHeader } from './searchTreeModel/searchTreeCommon.js'; import { INotebookFileInstanceMatch, isIMatchInNotebook } from './notebookSearch/notebookSearchModelBase.js'; import { searchMatchComparer } from './searchCompare.js'; import { AIFolderMatchWorkspaceRootImpl } from './AISearch/aiSearchModel.js'; @@ -117,6 +117,7 @@ export class SearchView extends ViewPane { private folderMatchFocused: IContextKey; private folderMatchWithResourceFocused: IContextKey; private matchFocused: IContextKey; + private searchResultHeaderFocused: IContextKey; private isEditableItem: IContextKey; private hasSearchResultsKey: IContextKey; private lastFocusState: 'input' | 'tree' = 'input'; @@ -194,6 +195,7 @@ export class SearchView extends ViewPane { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService private readonly storageService: IStorageService, + @ISearchService private readonly searchService: ISearchService, @IOpenerService openerService: IOpenerService, @IHoverService hoverService: IHoverService, @INotebookService private readonly notebookService: INotebookService, @@ -214,6 +216,7 @@ export class SearchView extends ViewPane { this.fileMatchFocused = Constants.SearchContext.FileFocusKey.bindTo(this.contextKeyService); this.folderMatchFocused = Constants.SearchContext.FolderFocusKey.bindTo(this.contextKeyService); this.folderMatchWithResourceFocused = Constants.SearchContext.ResourceFolderFocusKey.bindTo(this.contextKeyService); + this.searchResultHeaderFocused = Constants.SearchContext.SearchResultHeaderFocused.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.SearchContext.HasSearchResults.bindTo(this.contextKeyService); this.matchFocused = Constants.SearchContext.MatchFocusKey.bindTo(this.contextKeyService); this.searchStateKey = SearchStateKey.bindTo(this.contextKeyService); @@ -940,6 +943,7 @@ export class SearchView extends ViewPane { this.fileMatchOrFolderMatchFocus.set(isSearchTreeFileMatch(focus) || isSearchTreeFolderMatch(focus)); this.fileMatchOrFolderMatchWithResourceFocus.set(isSearchTreeFileMatch(focus) || isSearchTreeFolderMatchWithResource(focus)); this.folderMatchWithResourceFocused.set(isSearchTreeFolderMatchWithResource(focus)); + this.searchResultHeaderFocused.set(isSearchHeader(focus)); this.lastFocusState = 'tree'; } @@ -963,6 +967,7 @@ export class SearchView extends ViewPane { this.fileMatchOrFolderMatchFocus.reset(); this.fileMatchOrFolderMatchWithResourceFocus.reset(); this.folderMatchWithResourceFocused.reset(); + this.searchResultHeaderFocused.reset(); this.isEditableItem.reset(); })); } @@ -1310,7 +1315,7 @@ export class SearchView extends ViewPane { } cancelSearch(focus: boolean = true): boolean { - if (this.viewModel.cancelSearch()) { + if (this.viewModel.cancelSearch() && this.viewModel.cancelAISearch()) { if (focus) { this.searchWidget.focus(); } return true; } @@ -1463,7 +1468,7 @@ export class SearchView extends ViewPane { this.searchWidget.focus(false); } - triggerQueryChange(_options?: { preserveFocus?: boolean; triggeredOnType?: boolean; delay?: number; shouldKeepAIResults?: boolean }): void { + triggerQueryChange(_options?: { preserveFocus?: boolean; triggeredOnType?: boolean; delay?: number; shouldKeepAIResults?: boolean; shouldUpdateAISearch?: boolean }): void { const options = { preserveFocus: true, triggeredOnType: false, delay: 0, ..._options }; if (options.triggeredOnType && !this.searchConfig.searchOnType) { return; } @@ -1472,7 +1477,7 @@ export class SearchView extends ViewPane { const delay = options.triggeredOnType ? options.delay : 0; this.triggerQueryDelayer.trigger(() => { - this._onQueryChanged(options.preserveFocus, options.triggeredOnType, options.shouldKeepAIResults); + this._onQueryChanged(options.preserveFocus, options.triggeredOnType, options.shouldKeepAIResults, options.shouldUpdateAISearch); }, delay); } } @@ -1485,7 +1490,7 @@ export class SearchView extends ViewPane { return this.inputPatternIncludes.getValue().trim(); } - private _onQueryChanged(preserveFocus: boolean, triggeredOnType = false, shouldKeepAIResults = false): void { + private _onQueryChanged(preserveFocus: boolean, triggeredOnType = false, shouldKeepAIResults = false, shouldUpdateAISearch = false): void { if (!(this.searchWidget.searchInput?.inputBox.isInputValid())) { return; } @@ -1564,11 +1569,11 @@ export class SearchView extends ViewPane { this.validateQuery(query).then(() => { // ensure that the node is closed when a new search is triggered - if (!shouldKeepAIResults && this.tree.hasNode(this.searchResult.aiTextSearchResult)) { + if (!shouldKeepAIResults && !shouldUpdateAISearch && this.tree.hasNode(this.searchResult.aiTextSearchResult)) { this.tree.collapse(this.searchResult.aiTextSearchResult); } - this.onQueryTriggered(query, options, excludePatternText, includePatternText, triggeredOnType, shouldKeepAIResults); + this.onQueryTriggered(query, options, excludePatternText, includePatternText, triggeredOnType, shouldKeepAIResults, shouldUpdateAISearch); if (!preserveFocus) { this.searchWidget.focus(false, undefined, true); // focus back to input field @@ -1598,7 +1603,7 @@ export class SearchView extends ViewPane { }); } - private onQueryTriggered(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string, triggeredOnType: boolean, shouldKeepAIResults: boolean): void { + private onQueryTriggered(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string, triggeredOnType: boolean, shouldKeepAIResults: boolean, shouldUpdateAISearch: boolean): void { this.addToSearchHistoryDelayer.trigger(() => { this.searchWidget.searchInput?.onSearchSubmit(); this.inputPatternExcludes.onSearchSubmit(); @@ -1609,7 +1614,7 @@ export class SearchView extends ViewPane { this.viewModel.cancelAISearch(true); this.currentSearchQ = this.currentSearchQ - .then(() => this.doSearch(query, excludePatternText, includePatternText, triggeredOnType, shouldKeepAIResults)) + .then(() => this.doSearch(query, excludePatternText, includePatternText, triggeredOnType, shouldKeepAIResults, shouldUpdateAISearch)) .then(() => undefined, () => undefined); } @@ -1663,8 +1668,34 @@ export class SearchView extends ViewPane { } // Special case for when we have an AI provider registered - if (this.shouldShowAIResults()) { - Constants.SearchContext.AIResultsRequested.bindTo(this.contextKeyService).set(!!aiResults); + Constants.SearchContext.AIResultsRequested.bindTo(this.contextKeyService).set(this.shouldShowAIResults() && !!aiResults); + + if (this.shouldShowAIResults() && !allResults) { + const messageEl = this.clearMessage(); + const noResultsMessage = nls.localize('noResultsFallback', "No results found. "); + dom.append(messageEl, noResultsMessage); + + let aiName = 'Copilot'; + try { + aiName = (await this.searchService.getAIName()) || aiName; + } catch (e) { + // ignore + } + + if (aiName) { + const searchWithAIButtonTooltip = appendKeyBindingLabel( + nls.localize('triggerAISearch.tooltip', "Search with {0}", aiName), + this.keybindingService.lookupKeybinding(Constants.SearchCommandIds.SearchWithAIActionId) + ); + const searchWithAIButtonText = nls.localize('searchWithAIButtonTooltip', "Search with {0}.", aiName); + const searchWithAIButton = this.messageDisposables.add(new SearchLinkButton( + searchWithAIButtonText, + () => { + this.commandService.executeCommand(Constants.SearchCommandIds.SearchWithAIActionId); + }, this.hoverService, searchWithAIButtonTooltip)); + dom.append(messageEl, searchWithAIButton.element); + } + if (!aiResults) { return; } @@ -1789,6 +1820,7 @@ export class SearchView extends ViewPane { const result = this.viewModel.addAIResults(); return result.then((complete) => { clearTimeout(slowTimer); + this.updateSearchResultCount(this.viewModel.searchResult.query?.userDisabledExcludesAndIgnoreFiles, this.viewModel.searchResult.query?.onlyOpenEditors, false); return this.onSearchComplete(progressComplete, excludePatternText, includePatternText, complete, false); }, (e) => { clearTimeout(slowTimer); @@ -1796,7 +1828,7 @@ export class SearchView extends ViewPane { }); } - private doSearch(query: ITextQuery, excludePatternText: string, includePatternText: string, triggeredOnType: boolean, shouldKeepAIResults: boolean): Thenable { + private doSearch(query: ITextQuery, excludePatternText: string, includePatternText: string, triggeredOnType: boolean, shouldKeepAIResults: boolean, shouldUpdateAISearch: boolean): Thenable { let progressComplete: () => void; this.progressService.withProgress({ location: this.getProgressLocation(), delay: triggeredOnType ? 300 : 0 }, _progress => { return new Promise(resolve => progressComplete = resolve); @@ -1805,6 +1837,7 @@ export class SearchView extends ViewPane { this.searchWidget.searchInput?.clearMessage(); this.state = SearchUIState.Searching; this.showEmptyStage(); + this.model.searchResult.aiTextSearchResult.hidden = !shouldKeepAIResults && !shouldUpdateAISearch; const slowTimer = setTimeout(() => { this.state = SearchUIState.SlowSearch; @@ -1822,9 +1855,14 @@ export class SearchView extends ViewPane { this.viewModel.replaceString = this.searchWidget.getReplaceValue(); const result = this.viewModel.search(query); - if (!shouldKeepAIResults) { + if (!shouldKeepAIResults || shouldUpdateAISearch) { this.viewModel.searchResult.setAIQueryUsingTextQuery(query); } + + if (shouldUpdateAISearch) { + this.tree.updateChildren(this.searchResult.aiTextSearchResult); + } + return result.asyncResults.then((complete) => { clearTimeout(slowTimer); return this.onSearchComplete(progressComplete, excludePatternText, includePatternText, complete); @@ -2340,8 +2378,13 @@ class SearchViewDataSource implements IAsyncDataSource('folderMatchWithResourceFocus', false), IsEditableItemKey: new RawContextKey('isEditableItem', true), MatchFocusKey: new RawContextKey('matchFocus', false), + SearchResultHeaderFocused: new RawContextKey('searchResultHeaderFocused', false), ViewHasSearchPatternKey: new RawContextKey('viewHasSearchPattern', false), ViewHasReplacePatternKey: new RawContextKey('viewHasReplacePattern', false), ViewHasFilePatternKey: new RawContextKey('viewHasFilePattern', false), diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 2ef0de41..06c660a0 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -201,7 +201,7 @@ export class SearchEditor extends AbstractTextCodeEditor ariaLabel: localize('label.includes', 'Search Include Patterns'), inputBoxStyles: searchEditorInputboxStyles })); - this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerSearch({ resetCursor: false, delay: triggeredOnType ? this.searchConfig.searchOnTypeDebouncePeriod : 0 })); + this._register(this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerSearch({ resetCursor: false, delay: triggeredOnType ? this.searchConfig.searchOnTypeDebouncePeriod : 0 }))); this._register(this.inputPatternIncludes.onChangeSearchInEditorsBox(() => this.triggerSearch())); // Excludes diff --git a/src/vs/workbench/contrib/speech/browser/speechService.ts b/src/vs/workbench/contrib/speech/browser/speechService.ts index 8529260f..45d4b02a 100644 --- a/src/vs/workbench/contrib/speech/browser/speechService.ts +++ b/src/vs/workbench/contrib/speech/browser/speechService.ts @@ -7,7 +7,7 @@ import { localize } from '../../../../nls.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore, 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 { ILogService } from '../../../../platform/log/common/log.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { DeferredPromise } from '../../../../base/common/async.js'; @@ -58,11 +58,11 @@ export class SpeechService extends Disposable implements ISpeechService { private readonly providers = new Map(); private readonly providerDescriptors = new Map(); - private readonly hasSpeechProviderContext = HasSpeechProvider.bindTo(this.contextKeyService); + private readonly hasSpeechProviderContext: IContextKey; constructor( @ILogService private readonly logService: ILogService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IHostService private readonly hostService: IHostService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -70,6 +70,10 @@ export class SpeechService extends Disposable implements ISpeechService { ) { super(); + this.hasSpeechProviderContext = HasSpeechProvider.bindTo(contextKeyService); + this.textToSpeechInProgress = TextToSpeechInProgress.bindTo(contextKeyService); + this.speechToTextInProgress = SpeechToTextInProgress.bindTo(contextKeyService); + this.handleAndRegisterSpeechExtensions(); } @@ -136,7 +140,7 @@ export class SpeechService extends Disposable implements ISpeechService { private activeSpeechToTextSessions = 0; get hasActiveSpeechToTextSession() { return this.activeSpeechToTextSessions > 0; } - private readonly speechToTextInProgress = SpeechToTextInProgress.bindTo(this.contextKeyService); + private readonly speechToTextInProgress: IContextKey; async createSpeechToTextSession(token: CancellationToken, context: string = 'speech'): Promise { const provider = await this.getProvider(); @@ -249,7 +253,7 @@ export class SpeechService extends Disposable implements ISpeechService { private activeTextToSpeechSessions = 0; get hasActiveTextToSpeechSession() { return this.activeTextToSpeechSessions > 0; } - private readonly textToSpeechInProgress = TextToSpeechInProgress.bindTo(this.contextKeyService); + private readonly textToSpeechInProgress: IContextKey; async createTextToSpeechSession(token: CancellationToken, context: string = 'speech'): Promise { const provider = await this.getProvider(); diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index ba0e35ef..0602ca9c 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -15,6 +15,7 @@ import { Severity, INotificationService, NotificationPriority } from '../../../. import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { URI } from '../../../../base/common/uri.js'; import { platform } from '../../../../base/common/process.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; const PROBABILITY = 0.15; const SESSION_COUNT_KEY = 'nps/sessionCount'; @@ -29,9 +30,10 @@ class NPSContribution implements IWorkbenchContribution { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IOpenerService openerService: IOpenerService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService ) { - if (!productService.npsSurveyUrl) { + if (!productService.npsSurveyUrl || !configurationService.getValue('telemetry.feedback.enabled')) { return; } diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index d6a12341..25385286 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -47,7 +47,7 @@ import { ITextFileService } from '../../../services/textfile/common/textfiles.js import { ITerminalGroupService, ITerminalService } from '../../terminal/browser/terminal.js'; import { ITerminalProfileResolverService } from '../../terminal/common/terminal.js'; -import { ConfiguringTask, ContributedTask, CustomTask, ExecutionEngine, InMemoryTask, ITaskEvent, ITaskIdentifier, ITaskSet, JsonSchemaVersion, KeyedTaskIdentifier, RuntimeType, Task, TASK_RUNNING_STATE, TaskDefinition, TaskEventKind, TaskGroup, TaskRunSource, TaskSettingId, TaskSorter, TaskSourceKind, TasksSchemaProperties, USER_TASKS_GROUP_KEY } from '../common/tasks.js'; +import { ConfiguringTask, ContributedTask, CustomTask, ExecutionEngine, InMemoryTask, ITaskEvent, ITaskIdentifier, ITaskSet, JsonSchemaVersion, KeyedTaskIdentifier, RuntimeType, Task, TASK_RUNNING_STATE, TaskDefinition, TaskGroup, TaskRunSource, TaskSettingId, TaskSorter, TaskSourceKind, TasksSchemaProperties, USER_TASKS_GROUP_KEY, TaskEventKind } from '../common/tasks.js'; import { CustomExecutionSupportedContext, ICustomizationProperties, IProblemMatcherRunOptions, ITaskFilter, ITaskProvider, ITaskService, IWorkspaceFolderTaskResult, ProcessExecutionSupportedContext, ServerlessWebContext, ShellExecutionSupportedContext, TaskCommandsRegistered, TaskExecutionSupportedContext } from '../common/taskService.js'; import { ITaskExecuteResult, ITaskResolver, ITaskSummary, ITaskSystem, ITaskSystemInfo, ITaskTerminateResponse, TaskError, TaskErrors, TaskExecuteKind } from '../common/taskSystem.js'; import { getTemplates as getTaskTemplates } from '../common/taskTemplates.js'; @@ -85,6 +85,7 @@ import { IPreferencesService } from '../../../services/preferences/common/prefer import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; import { isCancellationError } from '../../../../base/common/errors.js'; + const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; const USE_SLOW_PICKER = 'task.quickOpen.showAll'; diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 98592cc0..8629aa38 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -20,7 +20,7 @@ import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatus import { IOutputChannelRegistry, Extensions as OutputExt } from '../../../services/output/common/output.js'; -import { ITaskEvent, TaskEventKind, TaskGroup, TaskSettingId, TASKS_CATEGORY, TASK_RUNNING_STATE, TASK_TERMINAL_ACTIVE } from '../common/tasks.js'; +import { ITaskEvent, TaskGroup, TaskSettingId, TASKS_CATEGORY, TASK_RUNNING_STATE, TASK_TERMINAL_ACTIVE, TaskEventKind } from '../common/tasks.js'; import { ITaskService, TaskCommandsRegistered, TaskExecutionSupportedContext } from '../common/taskService.js'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; diff --git a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts index 47813317..d00ad24c 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts @@ -94,7 +94,7 @@ export class TaskTerminalStatus extends Disposable { } terminalData.taskRunEnded = true; terminalData.terminal.statusList.remove(terminalData.status); - if ((event.exitCode === 0) && (terminalData.problemMatcher.numberOfMatches === 0)) { + if ((event.exitCode === 0) && (!terminalData.problemMatcher.maxMarkerSeverity || terminalData.problemMatcher.maxMarkerSeverity < MarkerSeverity.Warning)) { this._accessibilitySignalService.playSignal(AccessibilitySignal.taskCompleted); if (terminalData.task.configurationProperties.isBackground) { for (const status of terminalData.terminal.statusList.statuses) { @@ -103,7 +103,7 @@ export class TaskTerminalStatus extends Disposable { } else { terminalData.terminal.statusList.add(SUCCEEDED_TASK_STATUS); } - } else if (event.exitCode || terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Error) { + } else if (event.exitCode || (terminalData.problemMatcher.maxMarkerSeverity !== undefined && terminalData.problemMatcher.maxMarkerSeverity >= MarkerSeverity.Warning)) { this._accessibilitySignalService.playSignal(AccessibilitySignal.taskFailed); terminalData.terminal.statusList.add(FAILED_TASK_STATUS); } else if (terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Warning) { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 61d99672..2fcb9837 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -833,6 +833,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { eventCounter++; this._busyTasks[mapKey] = task; this._fireTaskEvent(TaskEvent.general(TaskEventKind.Active, task, terminal?.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherStarted, task, terminal?.instanceId)); } else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) { eventCounter--; if (this._busyTasks[mapKey]) { @@ -842,6 +843,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (eventCounter === 0) { if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherFoundErrors, task, terminal?.instanceId)); const reveal = task.command.presentation!.reveal; const revealProblems = task.command.presentation!.revealProblems; if (revealProblems === RevealProblemKind.OnProblem) { @@ -850,6 +852,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { this._terminalService.setActiveInstance(terminal!); this._terminalGroupService.showPanel(false); } + } else { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherEnded, task, terminal?.instanceId)); } } } @@ -981,7 +985,17 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const problemMatchers = await this._resolveMatchers(resolver, task.configurationProperties.problemMatchers); const startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this._markerService, this._modelService, ProblemHandlingStrategy.Clean, this._fileService); this._terminalStatusManager.addTerminal(task, terminal, startStopProblemMatcher); - + startStopProblemMatcher.onDidStateChange((event) => { + if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherStarted, task, terminal?.instanceId)); + } else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) { + if (startStopProblemMatcher.numberOfMatches && startStopProblemMatcher.maxMarkerSeverity && startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error) { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherFoundErrors, task, terminal?.instanceId)); + } else { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherEnded, task, terminal?.instanceId)); + } + } + }); let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { @@ -1044,6 +1058,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { delete this._busyTasks[mapKey]; } this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal?.instanceId)); + if (startStopProblemMatcher.numberOfMatches && startStopProblemMatcher.maxMarkerSeverity && startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error) { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherFoundErrors, task, terminal?.instanceId)); + } else { + this._fireTaskEvent(TaskEvent.general(TaskEventKind.ProblemMatcherEnded, task, terminal?.instanceId)); + } this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task, terminal?.instanceId)); resolve({ exitCode: exitCode ?? undefined }); }); diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 3cb325d2..594086ee 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -362,6 +362,8 @@ export class StartStopProblemCollector extends AbstractProblemCollector implemen private currentOwner: string | undefined; private currentResource: string | undefined; + private _hasStarted: boolean = false; + constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, _strategy: ProblemHandlingStrategy = ProblemHandlingStrategy.Clean, fileService?: IFileService) { super(problemMatchers, markerService, modelService, fileService); const ownerSet: { [key: string]: boolean } = Object.create(null); @@ -373,6 +375,10 @@ export class StartStopProblemCollector extends AbstractProblemCollector implemen } protected async processLineInternal(line: string): Promise { + if (!this._hasStarted) { + this._hasStarted = true; + this._onDidStateChange.fire(IProblemCollectorEvent.create(ProblemCollectorEventKind.BackgroundProcessingBegins)); + } const markerMatch = this.tryFindMarker(line); if (!markerMatch) { return; diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index ac2f0c34..ec687197 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -108,3 +108,8 @@ export interface ITaskService { extensionCallbackTaskComplete(task: Task, result: number | undefined): Promise; } + +export interface ITaskTerminalStatus { + terminalId: number; + status: string; +} diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index c78cb6d4..2c56cfc6 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -1102,18 +1102,6 @@ export class TaskSorter { } } -export const enum TaskEventKind { - DependsOnStarted = 'dependsOnStarted', - AcquiredInput = 'acquiredInput', - Start = 'start', - ProcessStarted = 'processStarted', - Active = 'active', - Inactive = 'inactive', - Changed = 'changed', - Terminated = 'terminated', - ProcessEnded = 'processEnded', - End = 'end' -} export const enum TaskRunType { @@ -1125,6 +1113,49 @@ export interface ITaskChangedEvent { kind: TaskEventKind.Changed; } + + +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' +} + interface ITaskCommon { taskId: string; runType: TaskRunType; @@ -1157,8 +1188,13 @@ export interface ITaskStartedEvent extends ITaskCommon { resolvedVariables: Map; } +export interface ITaskProblemMatcherEndedEvent extends ITaskCommon { + kind: TaskEventKind.ProblemMatcherEnded; + hasErrors: boolean; +} + export interface ITaskGeneralEvent extends ITaskCommon { - kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.End; + kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.End | TaskEventKind.ProblemMatcherEnded | TaskEventKind.ProblemMatcherStarted | TaskEventKind.ProblemMatcherFoundErrors; terminalId: number | undefined; } @@ -1168,7 +1204,8 @@ export type ITaskEvent = | ITaskProcessEndedEvent | ITaskTerminatedEvent | ITaskStartedEvent - | ITaskGeneralEvent; + | ITaskGeneralEvent + | ITaskProblemMatcherEndedEvent; export const enum TaskRunSource { System, @@ -1224,7 +1261,7 @@ export namespace TaskEvent { }; } - export function general(kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.End, task: Task, terminalId?: number): ITaskGeneralEvent { + export function general(kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.End | TaskEventKind.ProblemMatcherEnded | TaskEventKind.ProblemMatcherStarted | TaskEventKind.ProblemMatcherFoundErrors, task: Task, terminalId?: number): ITaskGeneralEvent { return { ...common(task), kind, diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 06e08fcd..15ffde58 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -154,15 +154,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr private onTextFileModelResolved(e: ITextFileResolveEvent): void { const settingsType = this.getTypeIfSettings(e.model.resource); - if (settingsType) { - type SettingsReadClassification = { - owner: 'isidorn'; - settingsType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of the settings file that was read.' }; - comment: 'Track when a settings file was read, for example from an editor.'; - }; - - this.telemetryService.publicLog2<{ settingsType: string }, SettingsReadClassification>('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data - } else { + if (!settingsType) { type FileGetClassification = { owner: 'isidorn'; comment: 'Track when a file was read, for example from an editor.'; @@ -174,14 +166,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr private onTextFileModelSaved(e: ITextFileSaveEvent): void { const settingsType = this.getTypeIfSettings(e.model.resource); - if (settingsType) { - type SettingsWrittenClassification = { - owner: 'isidorn'; - settingsType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of the settings file that was written to.' }; - comment: 'Track when a settings file was written to, for example from an editor.'; - }; - this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data - } else { + if (!settingsType) { type FilePutClassfication = { owner: 'isidorn'; comment: 'Track when a file was written to, for example from an editor.'; diff --git a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts index 1ec81c70..9da2be1b 100644 --- a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts +++ b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts @@ -87,7 +87,8 @@ export class EnvironmentVariableInfoChangesActive implements IEnvironmentVariabl return { id: TerminalStatus.EnvironmentVariableInfoChangesActive, severity: Severity.Info, - tooltip: this._getInfo(scope), + tooltip: undefined, // The action is present when details aren't shown + detailedTooltip: this._getInfo(scope), hoverActions: this._getActions(scope) }; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index cee30a6d..4a4236d5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getFontSnippets } from '../../../../base/browser/fonts.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { Schemas } from '../../../../base/common/network.js'; import { isIOS, isWindows } from '../../../../base/common/platform.js'; @@ -49,6 +50,7 @@ import { registerTerminalConfiguration } from '../common/terminalConfiguration.j import { TerminalContextKeyStrings, TerminalContextKeys } from '../common/terminalContextKey.js'; import { terminalStrings } from '../common/terminalStrings.js'; import { registerSendSequenceKeybinding } from './terminalKeybindings.js'; +import { TerminalTelemetryContribution } from './terminalTelemetry.js'; // Register services registerSingleton(ITerminalLogService, TerminalLogService, InstantiationType.Delayed); @@ -63,10 +65,11 @@ registerSingleton(ITerminalProfileService, TerminalProfileService, Instantiation // This contribution blocks startup as it's critical to enable the web embedder window.createTerminal API registerWorkbenchContribution2(TerminalMainContribution.ID, TerminalMainContribution, WorkbenchPhase.BlockStartup); registerWorkbenchContribution2(RemoteTerminalBackendContribution.ID, RemoteTerminalBackendContribution, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2(TerminalTelemetryContribution.ID, TerminalTelemetryContribution, WorkbenchPhase.AfterRestored); // Register configurations registerTerminalPlatformConfiguration(); -registerTerminalConfiguration(); +registerTerminalConfiguration(getFontSnippets); // Register editor/dnd contributions Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(TerminalEditorInput.ID, TerminalInputSerializer); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 664eea28..ff14b2b4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -42,7 +42,6 @@ import { IPreferencesService } from '../../../services/preferences/common/prefer import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; import { SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { isAbsolute } from '../../../../base/common/path.js'; -import { AbstractVariableResolverService } from '../../../services/configurationResolver/common/variableResolver.js'; import { ITerminalQuickPickItem } from './terminalProfileQuickpick.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { getIconId, getColorClass, getUriClasses } from './terminalIcon.js'; @@ -62,6 +61,7 @@ import { editorGroupToColumn } from '../../../services/editor/common/editorGroup import { InstanceContext } from './terminalContextMenu.js'; import { AccessibleViewProviderId } from '../../../../platform/accessibility/browser/accessibleView.js'; import { TerminalTabList } from './terminalTabsList.js'; +import { ConfigurationResolverExpression } from '../../../services/configurationResolver/common/configurationResolverExpression.js'; export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; export const switchTerminalShowTabsTitle = localize('showTerminalTabs', "Show Tabs"); @@ -904,7 +904,7 @@ export function registerTerminalActions() { registerActiveInstanceAction({ id: TerminalCommandId.SelectToPreviousCommand, - title: localize2('workbench.action.terminal.selectToPreviousCommand', 'Select To Previous Command'), + title: localize2('workbench.action.terminal.selectToPreviousCommand', 'Select to Previous Command'), keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow, when: TerminalContextKeys.focus, @@ -919,7 +919,7 @@ export function registerTerminalActions() { registerActiveInstanceAction({ id: TerminalCommandId.SelectToNextCommand, - title: localize2('workbench.action.terminal.selectToNextCommand', 'Select To Next Command'), + title: localize2('workbench.action.terminal.selectToNextCommand', 'Select to Next Command'), keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow, when: TerminalContextKeys.focus, @@ -934,7 +934,7 @@ export function registerTerminalActions() { registerActiveXtermAction({ id: TerminalCommandId.SelectToPreviousLine, - title: localize2('workbench.action.terminal.selectToPreviousLine', 'Select To Previous Line'), + title: localize2('workbench.action.terminal.selectToPreviousLine', 'Select to Previous Line'), precondition: sharedWhenClause.terminalAvailable, run: async (xterm, _, instance) => { xterm.markTracker.selectToPreviousLine(); @@ -945,7 +945,7 @@ export function registerTerminalActions() { registerActiveXtermAction({ id: TerminalCommandId.SelectToNextLine, - title: localize2('workbench.action.terminal.selectToNextLine', 'Select To Next Line'), + title: localize2('workbench.action.terminal.selectToNextLine', 'Select to Next Line'), precondition: sharedWhenClause.terminalAvailable, run: async (xterm, _, instance) => { xterm.markTracker.selectToNextLine(); @@ -1682,7 +1682,7 @@ async function resolveWorkspaceFolderCwd(folder: IWorkspaceFolder, configuration } const resolvedCwdConfig = await configurationResolverService.resolveAsync(folder, cwdConfig); - return isAbsolute(resolvedCwdConfig) || resolvedCwdConfig.startsWith(AbstractVariableResolverService.VARIABLE_LHS) + return isAbsolute(resolvedCwdConfig) || resolvedCwdConfig.startsWith(ConfigurationResolverExpression.VARIABLE_LHS) ? { folder, isAbsolute: true, isOverridden: true, cwd: URI.from({ ...folder.uri, path: resolvedCwdConfig }) } : { folder, isAbsolute: false, isOverridden: true, cwd: URI.joinPath(folder.uri, resolvedCwdConfig) }; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalIconPicker.ts b/src/vs/workbench/contrib/terminal/browser/terminalIconPicker.ts index 4e295a44..f5f78bd1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalIconPicker.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalIconPicker.ts @@ -58,7 +58,7 @@ export class TerminalIconPicker extends Disposable { this._iconSelectBox.dispose(); })); this._iconSelectBox.clearInput(); - const hoverWidget = this._hoverService.showHover({ + const hoverWidget = this._hoverService.showInstantHover({ content: this._iconSelectBox.domNode, target: getActiveDocument().body, position: { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index e0a7854c..8149a20e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -90,6 +90,7 @@ import type { IMenu } from '../../../../platform/actions/common/actions.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { TerminalContribCommandId } from '../terminalContribExports.js'; import type { IProgressState } from '@xterm/addon-progress'; +import { refreshShellIntegrationInfoStatus } from './terminalTooltip.js'; const enum Constants { /** @@ -469,11 +470,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (capability === TerminalCapability.CommandDetection) { const commandDetection = this.capabilities.get(capability); if (commandDetection) { + commandDetection.promptInputModel.setShellType(this.shellType); capabilityListeners.set(capability, Event.any( commandDetection.promptInputModel.onDidStartInput, commandDetection.promptInputModel.onDidChangeInput, commandDetection.promptInputModel.onDidFinishInput - )(() => this._labelComputer?.refreshLabel(this))); + )(() => { + this._labelComputer?.refreshLabel(this); + refreshShellIntegrationInfoStatus(this); + })); } } })); @@ -846,6 +851,18 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Init winpty compat and link handler after process creation as they rely on the // underlying process OS this._register(this._processManager.onProcessReady(async (processTraits) => { + // Respond to DA1 with basic conformance. Note that including this is required to avoid + // a long delay in conpty 1.22+ where it waits for the response. + // Reference: https://github.com/microsoft/terminal/blob/3760caed97fa9140a40777a8fbc1c95785e6d2ab/src/terminal/adapter/adaptDispatch.cpp#L1471-L1495 + if (processTraits?.windowsPty?.backend === 'conpty') { + this._register(xterm.raw.parser.registerCsiHandler({ final: 'c' }, params => { + if (params.length === 0 || params.length === 1 && params[0] === 0) { + this._processManager.write('\x1b[?61;4c'); + return true; + } + return false; + })); + } if (this._processManager.os) { lineDataEventAddon.setOperatingSystem(this._processManager.os); } @@ -860,6 +877,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { })); this._register(xterm.onDidChangeProgress(() => this._labelComputer?.refreshLabel(this))); + // Register and update the terminal's shell integration status + this._register(Event.runAndSubscribe(xterm.shellIntegration.onDidChangeSeenSequences, () => { + if (xterm.shellIntegration.seenSequences.size > 0) { + refreshShellIntegrationInfoStatus(this); + } + })); + // Set up updating of the process cwd on key press, this is only needed when the cwd // detection capability has not been registered if (!this.capabilities.has(TerminalCapability.CwdDetection)) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 42b3f065..c998d9c0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1171,13 +1171,16 @@ export class TerminalService extends Disposable implements ITerminalService { } protected _showBackgroundTerminal(instance: ITerminalInstance): void { + const index = this._backgroundedTerminalInstances.indexOf(instance); + if (index === -1) { + return; + } this._backgroundedTerminalInstances.splice(this._backgroundedTerminalInstances.indexOf(instance), 1); const disposables = this._backgroundedTerminalDisposables.get(instance.instanceId); if (disposables) { dispose(disposables); } this._backgroundedTerminalDisposables.delete(instance.instanceId); - instance.shellLaunchConfig.hideFromUser = false; this._terminalGroupService.createGroup(instance); // Make active automatically if it's the first instance diff --git a/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts b/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts index ecc8d382..bdbbe661 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalStatusList.ts @@ -24,6 +24,7 @@ export const enum TerminalStatus { Disconnected = 'disconnected', RelaunchNeeded = 'relaunch-needed', EnvironmentVariableInfoChangesActive = 'env-var-info-changes-active', + ShellIntegrationInfo = 'shell-integration-info', ShellIntegrationAttentionNeeded = 'shell-integration-attention-needed' } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 87239108..3213dd89 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -458,8 +458,8 @@ export class TerminalTabbedView extends Disposable { if (!instance) { return; } - this._hoverService.showHover({ - ...getInstanceHoverInfo(instance), + this._hoverService.showInstantHover({ + ...getInstanceHoverInfo(instance, this._storageService), target: this._terminalContainer, trapFocus: true }, true); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 5d8140d6..bf8e35c8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -53,6 +53,8 @@ import { TerminalContextActionRunner } from './terminalContextMenu.js'; import type { IHoverAction } from '../../../../base/browser/ui/hover/hover.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { IStorageService, StorageScope } from '../../../../platform/storage/common/storage.js'; +import { TerminalStorageKeys } from '../common/terminalStorageKeys.js'; const $ = DOM.$; @@ -82,6 +84,7 @@ export class TerminalTabList extends WorkbenchList { @IInstantiationService instantiationService: IInstantiationService, @IDecorationsService decorationsService: IDecorationsService, @IThemeService private readonly _themeService: IThemeService, + @IStorageService private readonly _storageService: IStorageService, @ILifecycleService lifecycleService: ILifecycleService, @IHoverService private readonly _hoverService: IHoverService, ) { @@ -128,7 +131,8 @@ export class TerminalTabList extends WorkbenchList { this.reveal(i); } this.refresh(); - }) + }), + this._storageService.onDidChangeValue(StorageScope.APPLICATION, TerminalStorageKeys.TabsShowDetailed, this.disposables)(() => this.refresh()), ]; // Dispose of instance listeners on shutdown to avoid extra work and so tabs don't disappear @@ -229,8 +233,8 @@ export class TerminalTabList extends WorkbenchList { return; } - this._hoverService.showHover({ - ...getInstanceHoverInfo(instance), + this._hoverService.showInstantHover({ + ...getInstanceHoverInfo(instance, this._storageService), target: this.getHTMLElement(), trapFocus: true }, true); @@ -257,6 +261,7 @@ class TerminalTabsRenderer extends Disposable implements IListRenderer { + // Wait for process ready so the shell launch config is fully resolved + await instance.processReady; + this._logCreateInstance(instance.shellLaunchConfig); + })); + } + + private _logCreateInstance(shellLaunchConfig: IShellLaunchConfig): void { + type TerminalCreationTelemetryData = { + shellType: string; + isReconnect: boolean; + isCustomPtyImplementation: boolean; + isLoginShell: boolean; + }; + type TerminalCreationTelemetryClassification = { + owner: 'tyriar'; + comment: 'Track details about terminal creation, such as the shell type'; + shellType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The path of the file as a hash.' }; + isReconnect: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the terminal is reconnecting to an existing instance.' }; + isCustomPtyImplementation: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the terminal was using a custom PTY implementation.' }; + isLoginShell: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the arguments contain -l or --login.' }; + }; + this._telemetryService.publicLog2('terminal/createInstance', { + shellType: getSanitizedShellType(shellLaunchConfig), + isReconnect: !!shellLaunchConfig.attachPersistentProcess, + isCustomPtyImplementation: !!shellLaunchConfig.customPtyImplementation, + isLoginShell: (typeof shellLaunchConfig.args === 'string' ? shellLaunchConfig.args.split(' ') : shellLaunchConfig.args)?.some(arg => arg === '-l' || arg === '--login') ?? false, + }); + } +} + +const enum AllowedShellType { + Unknown = 'unknown', + + // Windows only + CommandPrompt = 'cmd', + GitBash = 'git-bash', + WindowsPowerShell = 'windows-powershell', + Wsl = 'wsl', + + // All platforms + Bash = 'bash', + Csh = 'csh', + Dash = 'dash', + Fish = 'fish', + Ksh = 'ksh', + Nushell = 'nu', + Pwsh = 'pwsh', + Sh = 'sh', + Ssh = 'ssh', + Tcsh = 'tcsh', + Tmux = 'tmux', + Zsh = 'zsh', + + // Lanugage REPLs + Julia = 'julia', + Node = 'node', + Python = 'python', + RubyIrb = 'irb', +} + +// Types that match the executable name directly +const shellTypeExecutableAllowList: Set = new Set([ + AllowedShellType.CommandPrompt, + AllowedShellType.Wsl, + + AllowedShellType.Bash, + AllowedShellType.Csh, + AllowedShellType.Dash, + AllowedShellType.Fish, + AllowedShellType.Ksh, + AllowedShellType.Nushell, + AllowedShellType.Pwsh, + AllowedShellType.Sh, + AllowedShellType.Ssh, + AllowedShellType.Tcsh, + AllowedShellType.Tmux, + AllowedShellType.Zsh, + + AllowedShellType.Julia, + AllowedShellType.Node, + AllowedShellType.RubyIrb, +]) satisfies Set; + +// Dynamic executables that map to a single type +const shellTypeExecutableRegexAllowList: { regex: RegExp; type: AllowedShellType }[] = [ + { regex: /^python(?:\d+(?:\.\d+)?)?$/i, type: AllowedShellType.Python }, +]; + +// Path-based look ups +const shellTypePathRegexAllowList: { regex: RegExp; type: AllowedShellType }[] = [ + // Git bash uses bash.exe, so look up based on the path + { regex: /Git\\bin\\bash\.exe$/i, type: AllowedShellType.GitBash }, + // WindowsPowerShell should always be installed on this path, we cannot just look at the + // executable name since powershell is the CLI on other platforms sometimes (eg. snap package) + { regex: /WindowsPowerShell\\v1.0\\powershell.exe$/i, type: AllowedShellType.WindowsPowerShell }, + // WSL executables will represent some other shell in the end, but it's difficult to determine + // when we log + { regex: /Windows\\System32\\(?:bash|wsl)\.exe$/i, type: AllowedShellType.Wsl }, +]; + +function getSanitizedShellType(shellLaunchConfig: IShellLaunchConfig): AllowedShellType { + if (!shellLaunchConfig.executable) { + return AllowedShellType.Unknown; + } + const executableFile = basename(shellLaunchConfig.executable); + const executableFileWithoutExt = executableFile.replace(/\.[^\.]+$/, ''); + for (const entry of shellTypePathRegexAllowList) { + if (entry.regex.test(shellLaunchConfig.executable)) { + return entry.type; + } + } + for (const entry of shellTypeExecutableRegexAllowList) { + if (entry.regex.test(executableFileWithoutExt)) { + return entry.type; + } + } + if ((shellTypeExecutableAllowList).has(executableFileWithoutExt)) { + return executableFileWithoutExt as AllowedShellType; + } + return AllowedShellType.Unknown; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts b/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts index fd934009..3c232558 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTooltip.ts @@ -5,53 +5,51 @@ import { localize } from '../../../../nls.js'; import { ITerminalInstance } from './terminal.js'; -import { TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js'; import { asArray } from '../../../../base/common/arrays.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import type { IHoverAction } from '../../../../base/browser/ui/hover/hover.js'; +import { TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js'; +import { TerminalStatus } from './terminalStatusList.js'; +import Severity from '../../../../base/common/severity.js'; +import { StorageScope, StorageTarget, type IStorageService } from '../../../../platform/storage/common/storage.js'; +import { TerminalStorageKeys } from '../common/terminalStorageKeys.js'; +import type { ITerminalStatusHoverAction } from '../common/terminal.js'; +import { basename } from '../../../../base/common/path.js'; -export function getInstanceHoverInfo(instance: ITerminalInstance): { content: MarkdownString; actions: IHoverAction[] } { +export function getInstanceHoverInfo(instance: ITerminalInstance, storageService: IStorageService): { content: MarkdownString; actions: IHoverAction[] } { + const showDetailed = parseInt(storageService.get(TerminalStorageKeys.TabsShowDetailed, StorageScope.APPLICATION) ?? '0'); let statusString = ''; const statuses = instance.statusList.statuses; - const actions = []; + const actions: ITerminalStatusHoverAction[] = []; for (const status of statuses) { - statusString += `\n\n---\n\n${status.icon ? `$(${status.icon?.id}) ` : ''}${status.tooltip || status.id}`; + if (showDetailed) { + if (status.detailedTooltip ?? status.tooltip) { + statusString += `\n\n---\n\n${status.icon ? `$(${status.icon?.id}) ` : ''}` + (status.detailedTooltip ?? status.tooltip ?? ''); + } + } else { + if (status.tooltip) { + statusString += `\n\n---\n\n${status.icon ? `$(${status.icon?.id}) ` : ''}` + (status.tooltip ?? ''); + } + } if (status.hoverActions) { actions.push(...status.hoverActions); } } + actions.push({ + commandId: 'toggleDetailedInfo', + label: showDetailed ? localize('hideDetails', 'Hide Details') : localize('showDetails', 'Show Details'), + run() { + storageService.store(TerminalStorageKeys.TabsShowDetailed, (showDetailed + 1) % 2, StorageScope.APPLICATION, StorageTarget.USER); + }, + }); - const shellProcessString = getShellProcessTooltip(instance, true); - const shellIntegrationString = getShellIntegrationTooltip(instance, true); - const content = new MarkdownString(instance.title + shellProcessString + shellIntegrationString + statusString, { supportThemeIcons: true }); + const shellProcessString = getShellProcessTooltip(instance, !!showDetailed); + const content = new MarkdownString(instance.title + shellProcessString + statusString, { supportThemeIcons: true }); return { content, actions }; } -export function getShellIntegrationTooltip(instance: ITerminalInstance, markdown: boolean): string { - const shellIntegrationCapabilities: TerminalCapability[] = []; - if (instance.capabilities.has(TerminalCapability.CommandDetection)) { - shellIntegrationCapabilities.push(TerminalCapability.CommandDetection); - } - if (instance.capabilities.has(TerminalCapability.CwdDetection)) { - shellIntegrationCapabilities.push(TerminalCapability.CwdDetection); - } - let shellIntegrationString = ''; - if (shellIntegrationCapabilities.length > 0) { - shellIntegrationString += `${markdown ? '\n\n---\n\n' : '\n\n'}${localize('shellIntegration.enabled', "Shell integration activated")}`; - } else { - if (instance.shellLaunchConfig.ignoreShellIntegration) { - shellIntegrationString += `${markdown ? '\n\n---\n\n' : '\n\n'}${localize('launchFailed.exitCodeOnlyShellIntegration', "The terminal process failed to launch. Disabling shell integration with terminal.integrated.shellIntegration.enabled might help.")}`; - } else { - if (instance.usedShellIntegrationInjection) { - shellIntegrationString += `${markdown ? '\n\n---\n\n' : '\n\n'}${localize('shellIntegration.activationFailed', "Shell integration failed to activate")}`; - } - } - } - return shellIntegrationString; -} - -export function getShellProcessTooltip(instance: ITerminalInstance, markdown: boolean): string { +export function getShellProcessTooltip(instance: ITerminalInstance, showDetailed: boolean): string { const lines: string[] = []; if (instance.processId && instance.processId > 0) { @@ -59,8 +57,16 @@ export function getShellProcessTooltip(instance: ITerminalInstance, markdown: bo } if (instance.shellLaunchConfig.executable) { - let commandLine = instance.shellLaunchConfig.executable; - const args = asArray(instance.injectedArgs || instance.shellLaunchConfig.args || []).map(x => `'${x}'`).join(' '); + let commandLine = ''; + if (!showDetailed && instance.shellLaunchConfig.executable.length > 32) { + const base = basename(instance.shellLaunchConfig.executable); + const sepIndex = instance.shellLaunchConfig.executable.length - base.length - 1; + const sep = instance.shellLaunchConfig.executable.substring(sepIndex, sepIndex + 1); + commandLine += `…${sep}${base}`; + } else { + commandLine += instance.shellLaunchConfig.executable; + } + const args = asArray(instance.injectedArgs || instance.shellLaunchConfig.args || []).map(x => x.match(/\s/) ? `'${x}'` : x).join(' '); if (args) { commandLine += ` ${args}`; } @@ -68,5 +74,40 @@ export function getShellProcessTooltip(instance: ITerminalInstance, markdown: bo lines.push(localize('shellProcessTooltip.commandLine', 'Command line: {0}', commandLine)); } - return lines.length ? `${markdown ? '\n\n---\n\n' : '\n\n'}${lines.join('\n')}` : ''; + return lines.length ? `\n\n---\n\n${lines.join('\n')}` : ''; +} + +export function refreshShellIntegrationInfoStatus(instance: ITerminalInstance) { + if (!instance.xterm) { + return; + } + const cmdDetectionType = ( + instance.capabilities.get(TerminalCapability.CommandDetection)?.hasRichCommandDetection + ? localize('shellIntegration.rich', 'Rich') + : instance.capabilities.has(TerminalCapability.CommandDetection) + ? localize('shellIntegration.basic', 'Basic') + : instance.usedShellIntegrationInjection + ? localize('shellIntegration.injectionFailed', "Injection failed to activate") + : localize('shellIntegration.no', 'No') + ); + + const detailedAdditions: string[] = []; + const seenSequences = Array.from(instance.xterm.shellIntegration.seenSequences); + if (seenSequences.length > 0) { + detailedAdditions.push(`Seen sequences: ${seenSequences.map(e => `\`${e}\``).join(', ')}`); + } + const combinedString = instance.capabilities.get(TerminalCapability.CommandDetection)?.promptInputModel.getCombinedString(); + if (combinedString !== undefined) { + detailedAdditions.push(`Prompt input: \`${combinedString}\``); + } + const detailedAdditionsString = detailedAdditions.length > 0 + ? '\n\n' + detailedAdditions.map(e => `- ${e}`).join('\n') + : ''; + + instance.statusList.add({ + id: TerminalStatus.ShellIntegrationInfo, + severity: Severity.Info, + tooltip: `${localize('shellIntegration', "Shell integration")}: ${cmdDetectionType}`, + detailedTooltip: `${localize('shellIntegration', "Shell integration")}: ${cmdDetectionType}${detailedAdditionsString}` + }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 7e22be4d..42ff0ac1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -50,6 +50,7 @@ import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { InstanceContext, TerminalContextActionRunner } from './terminalContextMenu.js'; import { MicrotaskDelay } from '../../../../base/common/symbols.js'; +import { IStorageService } from '../../../../platform/storage/common/storage.js'; export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; @@ -660,7 +661,8 @@ class SingleTabHoverDelegate implements IHoverDelegate { constructor( @IConfigurationService private readonly _configurationService: IConfigurationService, @IHoverService private readonly _hoverService: IHoverService, - @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService + @IStorageService private readonly _storageService: IStorageService, + @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, ) { } @@ -675,8 +677,8 @@ class SingleTabHoverDelegate implements IHoverDelegate { if (!instance) { return; } - const hoverInfo = getInstanceHoverInfo(instance); - return this._hoverService.showHover({ + const hoverInfo = getInstanceHoverInfo(instance, this._storageService); + return this._hoverService.showInstantHover({ ...options, content: hoverInfo.content, actions: hoverInfo.actions diff --git a/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts b/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts index 235a4586..5f6aec12 100644 --- a/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/widgets/terminalHoverWidget.ts @@ -44,7 +44,7 @@ export class TerminalHover extends Disposable implements ITerminalWidget { return; } const target = new CellHoverTarget(container, this._targetOptions); - const hover = this._hoverService.showHover({ + const hover = this._hoverService.showInstantHover({ target, content: this._text, actions: this._actions, diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index e612cca0..55513eb4 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -7,7 +7,7 @@ import type { IDecoration, ITerminalAddon, Terminal } from '@xterm/xterm'; import * as dom from '../../../../../base/browser/dom.js'; import { IAction, Separator } from '../../../../../base/common/actions.js'; import { Emitter } from '../../../../../base/common/event.js'; -import { Disposable, DisposableStore, IDisposable, dispose, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableMap, DisposableStore, IDisposable, dispose, toDisposable } from '../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { localize } from '../../../../../nls.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; @@ -32,7 +32,7 @@ interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposa export class DecorationAddon extends Disposable implements ITerminalAddon, IDecorationAddon { protected _terminal: Terminal | undefined; - private _capabilityDisposables: Map = new Map(); + private _capabilityDisposables: DisposableMap = this._register(new DisposableMap()); private _decorations: Map = new Map(); private _placeholderDecoration: IDecoration | undefined; private _showGutterDecorations?: boolean; @@ -77,14 +77,6 @@ export class DecorationAddon extends Disposable implements ITerminalAddon, IDeco this._register(lifecycleService.onWillShutdown(() => this._disposeAllDecorations())); } - private _removeCapabilityDisposables(c: TerminalCapability): void { - const disposables = this._capabilityDisposables.get(c); - if (disposables) { - dispose(disposables); - } - this._capabilityDisposables.delete(c); - } - private _createCapabilityDisposables(c: TerminalCapability): void { const store = new DisposableStore(); const capability = this._capabilities.get(c); @@ -106,6 +98,10 @@ export class DecorationAddon extends Disposable implements ITerminalAddon, IDeco this._capabilityDisposables.set(c, store); } + private _removeCapabilityDisposables(c: TerminalCapability): void { + this._capabilityDisposables.deleteAndDispose(c); + } + registerMarkDecoration(mark: IMarkProperties): IDecoration | undefined { if (!this._terminal || (!this._showGutterDecorations && !this._showOverviewRulerDecorations)) { return undefined; @@ -212,11 +208,8 @@ export class DecorationAddon extends Disposable implements ITerminalAddon, IDeco } private _getCommandDetectionListeners(capability: ICommandDetectionCapability): IDisposable[] { - if (this._capabilityDisposables.has(TerminalCapability.CommandDetection)) { - const disposables = this._capabilityDisposables.get(TerminalCapability.CommandDetection)!; - dispose(disposables); - this._capabilityDisposables.delete(capability.type); - } + this._removeCapabilityDisposables(TerminalCapability.CommandDetection); + const commandDetectionListeners = []; // Command started if (capability.executingCommandObject?.marker) { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index b1a9006b..b835c0b5 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -256,7 +256,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach } })); - this._register(this._themeService.onDidColorThemeChange(e => this._updateTheme(e.theme))); + this._register(this._themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); this._register(this._logService.onDidChangeLogLevel(e => this.raw.options.logLevel = vscodeToXtermLogLevel(e))); // Refire events diff --git a/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts index 8def96e4..5aeedf77 100644 --- a/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts @@ -24,6 +24,7 @@ import { IPtyHostProcessReplayEvent } from '../../../../../platform/terminal/com import { ISerializableEnvironmentDescriptionMap as ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from '../../../../../platform/terminal/common/environmentVariable.js'; import type * as performance from '../../../../../base/common/performance.js'; import { RemoteTerminalChannelEvent, RemoteTerminalChannelRequest } from './terminal.js'; +import { ConfigurationResolverExpression } from '../../../../services/configurationResolver/common/configurationResolverExpression.js'; export const REMOTE_TERMINAL_CHANNEL_NAME = 'remoteterminal'; @@ -133,20 +134,15 @@ export class RemoteTerminalChannelClient implements IPtyHostController { // But then we will keep only some variables, since the rest need to be resolved on the remote side const resolvedVariables = Object.create(null); const lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) ?? undefined : undefined; - let allResolvedVariables: Map | undefined = undefined; + const expr = ConfigurationResolverExpression.parse({ shellLaunchConfig, configuration }); try { - allResolvedVariables = (await this._resolverService.resolveAnyMap(lastActiveWorkspace, { - shellLaunchConfig, - configuration - })).resolvedVariables; + await this._resolverService.resolveAsync(lastActiveWorkspace, expr); } catch (err) { this._logService.error(err); } - if (allResolvedVariables) { - for (const [name, value] of allResolvedVariables.entries()) { - if (/^config:/.test(name) || name === 'selectedText' || name === 'lineNumber') { - resolvedVariables[name] = value; - } + for (const [{ inner }, resolved] of expr.resolved()) { + if (/^config:/.test(inner) || inner === 'selectedText' || inner === 'lineNumber') { + resolvedVariables[inner] = resolved.value; } } diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh index b5f2e1f5..727dcb02 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh @@ -198,6 +198,9 @@ if [ "$__vsc_stable" = "0" ]; then builtin printf "\e]633;P;ContinuationPrompt=$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')\a" fi +# Report this shell supports rich command detection +builtin printf '\e]633;P;HasRichCommandDetection=True\a' + __vsc_report_prompt() { # Expand the original PS1 similarly to how bash would normally # See https://stackoverflow.com/a/37137981 for technique @@ -242,8 +245,12 @@ __updateEnvCacheAA() { __trackMissingEnvVarsAA() { if [ "$use_associative_array" = 1 ]; then declare -A currentEnvMap - while IFS='=' read -r key value; do - currentEnvMap["$key"]="$value" + while IFS= read -r line; do + if [[ "$line" == *"="* ]]; then + local key="${line%%=*}" + local value="${line#*=}" + currentEnvMap["$key"]="$value" + fi done < <(env) for key in "${!vsc_aa_env[@]}"; do @@ -309,34 +316,52 @@ __vsc_update_env() { if [ "$use_associative_array" = 1 ]; then if [ ${#vsc_aa_env[@]} -eq 0 ]; then # Associative array is empty, do not diff, just add - while IFS='=' read -r key value; do - vsc_aa_env["$key"]="$value" - builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce" - done < <(env) + # Use null byte instead of a newline to support multi-line values (e.g. PS1 values) + while IFS= read -r -d $'\0' line; do + if [[ "$line" == *"="* ]]; then + # %% removes longest match of =* Ensure we get everything before first equal sign. + local key="${line%%=*}" + # # removes shortest match of *= Ensure we get everything after first equal sign. Preserving additional equal signs. + local value="${line#*=}" + vsc_aa_env["$key"]="$value" + builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce" + fi + done < <(env -0) # env command with null bytes as separator instead of newlines else # Diff approach for associative array - while IFS='=' read -r key value; do - __updateEnvCacheAA "$key" "$value" - done < <(env) + while IFS= read -r -d $'\0' line; do + if [[ "$line" == *"="* ]]; then + local key="${line%%=*}" + local value="${line#*=}" + __updateEnvCacheAA "$key" "$value" + fi + done < <(env -0) __trackMissingEnvVarsAA fi else if [[ -z ${vsc_env_keys[@]} ]] && [[ -z ${vsc_env_values[@]} ]]; then - # Non associative arrays are both empty, do not diff, just add - while IFS='=' read -r key value; do - vsc_env_keys+=("$key") - vsc_env_values+=("$value") - builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce" - done < <(env) + # Non associative arrays are both empty, do not diff, just add + while IFS= read -r -d $'\0' line; do + if [[ "$line" == *"="* ]]; then + local key="${line%%=*}" + local value="${line#*=}" + vsc_env_keys+=("$key") + vsc_env_values+=("$value") + builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce" + fi + done < <(env -0) else # Diff approach for non-associative arrays - while IFS='=' read -r key value; do - __updateEnvCache "$key" "$value" - done < <(env) + while IFS= read -r -d $'\0' line; do + if [[ "$line" == *"="* ]]; then + local key="${line%%=*}" + local value="${line#*=}" + __updateEnvCache "$key" "$value" + fi + done < <(env -0) __trackMissingEnvVars fi - fi builtin printf '\e]633;EnvSingleEnd;%s;\a' $__vsc_nonce fi diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh index 43738d95..62b93ad9 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh @@ -80,18 +80,19 @@ __vsc_escape_value() { builtin emulate -L zsh # Process text byte by byte, not by codepoint. - builtin local LC_ALL=C str="$1" i byte token out='' + builtin local LC_ALL=C str="$1" i byte token out='' val for (( i = 0; i < ${#str}; ++i )); do + # Escape backslashes, semi-colons specially, then special ASCII chars below space (0x20). byte="${str:$i:1}" - - # Escape backslashes, semi-colons and newlines - if [ "$byte" = "\\" ]; then + val=$(printf "%d" "'$byte") + if (( val < 31 )); then + # For control characters, use hex encoding + token=$(printf "\\\\x%02x" "'$byte") + elif [ "$byte" = "\\" ]; then token="\\\\" elif [ "$byte" = ";" ]; then token="\\x3b" - elif [ "$byte" = $'\n' ]; then - token="\x0a" else token="$byte" fi @@ -112,6 +113,11 @@ unset VSCODE_NONCE __vscode_shell_env_reporting="$VSCODE_SHELL_ENV_REPORTING" unset VSCODE_SHELL_ENV_REPORTING +builtin printf "\e]633;P;ContinuationPrompt=%s\a" "$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')" + +# Report this shell supports rich command detection +builtin printf '\e]633;P;HasRichCommandDetection=True\a' + __vsc_prompt_start() { builtin printf '\e]633;A\a' } diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish index e67701b0..f486869b 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish @@ -35,6 +35,9 @@ set -e VSCODE_PATH_PREFIX set -g vsc_env_keys set -g vsc_env_values +# Tracks if the shell has been initialized, this prevents +set -g vsc_initialized 0 + set -g __vsc_applied_env_vars 0 function __vsc_apply_env_vars if test $__vsc_applied_env_vars -eq 1; @@ -109,6 +112,11 @@ end # Sent when a command line is cleared or reset, but no command was run. # Marks the cleared line with neither success nor failure. function __vsc_cmd_clear --on-event fish_cancel + if test $vsc_initialized -eq 0; + return + end + __vsc_esc E "" $__vsc_nonce + __vsc_esc C __vsc_esc D end @@ -170,6 +178,7 @@ function __vsc_fish_prompt_start # evaluated __vsc_apply_env_vars __vsc_esc A + set -g vsc_initialized 1 end # Sent at the end of the prompt. @@ -207,4 +216,8 @@ function __init_vscode_shell_integration end end end + +# Report this shell supports rich command detection +__vsc_esc P HasRichCommandDetection=True + __preserve_fish_prompt diff --git a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1 b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1 index de5e863b..4f4f5b72 100644 --- a/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1 +++ b/src/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1 @@ -16,6 +16,7 @@ if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") { $Global:__VSCodeOriginalPrompt = $function:Prompt $Global:__LastHistoryId = -1 +$Global:__VSCodeIsInExecution = $false # Store the nonce in script scope and unset the global $Nonce = $env:VSCODE_NONCE @@ -73,8 +74,10 @@ function Global:Prompt() { Set-StrictMode -Off $LastHistoryEntry = Get-History -Count 1 $Result = "" - # Skip finishing the command if the first command has not yet started - if ($Global:__LastHistoryId -ne -1) { + # Skip finishing the command if the first command has not yet started or an execution has not + # yet begun + if ($Global:__LastHistoryId -ne -1 -and $Global:__VSCodeIsInExecution -eq $true) { + $Global:__VSCodeIsInExecution = $false if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) { # Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command) $Result += "$([char]0x1b)]633;D`a" @@ -93,7 +96,7 @@ function Global:Prompt() { $Result += if ($pwd.Provider.Name -eq 'FileSystem') { "$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a" } # Send current environment variables as JSON - # OSC 633 ; Env ; ; + # OSC 633 ; EnvJson ; ; if ($__vscode_shell_env_reporting -eq "1") { $envMap = @{} Get-ChildItem Env: | ForEach-Object { $envMap[$_.Name] = $_.Value } @@ -124,12 +127,14 @@ function Global:Prompt() { # Only send the command executed sequence when PSReadLine is loaded, if not shell integration should # still work thanks to the command line sequence if (Get-Module -Name PSReadLine) { + [Console]::Write("$([char]0x1b)]633;P;HasRichCommandDetection=True`a") $__VSCodeOriginalPSConsoleHostReadLine = $function:PSConsoleHostReadLine function Global:PSConsoleHostReadLine { $CommandLine = $__VSCodeOriginalPSConsoleHostReadLine.Invoke() + $Global:__VSCodeIsInExecution = $true # Command line - # OSC 633 ; E ; ; ST + # OSC 633 ; E [; [; ]] ST $Result = "$([char]0x1b)]633;E;" $Result += $(__VSCode-Escape-Value $CommandLine) # Only send the nonce if the OS is not Windows 10 as it seems to echo to the terminal @@ -148,6 +153,12 @@ if (Get-Module -Name PSReadLine) { $CommandLine } + + # Set ContinuationPrompt property + $ContinuationPrompt = (Get-PSReadLineOption).ContinuationPrompt + if ($ContinuationPrompt) { + [Console]::Write("$([char]0x1b)]633;P;ContinuationPrompt=$(__VSCode-Escape-Value $ContinuationPrompt)`a") + } } # Set IsWindows property @@ -159,14 +170,6 @@ else { [Console]::Write("$([char]0x1b)]633;P;IsWindows=$IsWindows`a") } -# Set ContinuationPrompt property -if ($isStable -eq "0") { - $ContinuationPrompt = (Get-PSReadLineOption).ContinuationPrompt - if ($ContinuationPrompt) { - [Console]::Write("$([char]0x1b)]633;P;ContinuationPrompt=$(__VSCode-Escape-Value $ContinuationPrompt)`a") - } -} - # Set always on key handlers which map to default VS Code keybindings function Set-MappedKeyHandler { param ([string[]] $Chord, [string[]]$Sequence) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 2a717f4a..3683a60a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -370,6 +370,10 @@ export interface ITerminalStatus { * What to show for this status in the terminal's hover. */ tooltip?: string | undefined; + /** + * What to show for this status in the terminal's hover when details are toggled. + */ + detailedTooltip?: string | undefined; /** * Actions to expose on hover. */ diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 22c96907..28aa98cc 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from '../../../../base/common/codicons.js'; +import { IJSONSchemaSnippet } from '../../../../base/common/jsonSchema.js'; import { isMacintosh, isWindows } from '../../../../base/common/platform.js'; import { localize } from '../../../../nls.js'; import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; @@ -16,19 +17,19 @@ import { terminalContribConfiguration, TerminalContribSettingId } from '../termi import { DEFAULT_COMMANDS_TO_SKIP_SHELL, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MAXIMUM_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, SUGGESTIONS_FONT_WEIGHT } from './terminal.js'; const terminalDescriptors = '\n- ' + [ - '`\${cwd}`: ' + localize("cwd", "the terminal's current working directory"), + '`\${cwd}`: ' + localize("cwd", "the terminal's current working directory."), '`\${cwdFolder}`: ' + localize('cwdFolder', "the terminal's current working directory, displayed for multi-root workspaces or in a single root workspace when the value differs from the initial working directory. On Windows, this will only be displayed when shell integration is enabled."), - '`\${workspaceFolder}`: ' + localize('workspaceFolder', "the workspace in which the terminal was launched"), - '`\${workspaceFolderName}`: ' + localize('workspaceFolderName', "the `name` of the workspace in which the terminal was launched"), - '`\${local}`: ' + localize('local', "indicates a local terminal in a remote workspace"), - '`\${process}`: ' + localize('process', "the name of the terminal process"), - '`\${progress}`: ' + localize('progress', "the progress state as reported by the `OSC 9;4` sequence"), - '`\${separator}`: ' + localize('separator', "a conditional separator {0} that only shows when surrounded by variables with values or static text.", '(` - `)'), - '`\${sequence}`: ' + localize('sequence', "the name provided to the terminal by the process"), - '`\${task}`: ' + localize('task', "indicates this terminal is associated with a task"), - '`\${shellType}`: ' + localize('shellType', "the detected shell type"), - '`\${shellCommand}`: ' + localize('shellCommand', "the command being executed according to shell integration. This also requires high confidence in the detected command line which may not work in some prompt frameworks."), - '`\${shellPromptInput}`: ' + localize('shellPromptInput', "the shell's full prompt input according to shell integration"), + '`\${workspaceFolder}`: ' + localize('workspaceFolder', "the workspace in which the terminal was launched."), + '`\${workspaceFolderName}`: ' + localize('workspaceFolderName', "the `name` of the workspace in which the terminal was launched."), + '`\${local}`: ' + localize('local', "indicates a local terminal in a remote workspace."), + '`\${process}`: ' + localize('process', "the name of the terminal process."), + '`\${progress}`: ' + localize('progress', "the progress state as reported by the `OSC 9;4` sequence."), + '`\${separator}`: ' + localize('separator', "a conditional separator {0} that only shows when it's surrounded by variables with values or static text.", '(` - `)'), + '`\${sequence}`: ' + localize('sequence', "the name provided to the terminal by the process."), + '`\${task}`: ' + localize('task', "indicates this terminal is associated with a task."), + '`\${shellType}`: ' + localize('shellType', "the detected shell type."), + '`\${shellCommand}`: ' + localize('shellCommand', "the command being executed according to shell integration. This also requires high confidence in the detected command line, which may not work in some prompt frameworks."), + '`\${shellPromptInput}`: ' + localize('shellPromptInput', "the shell's full prompt input according to shell integration."), ].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations let terminalTitle = localize('terminalTitle', "Controls the terminal title. Variables are substituted based on the context:"); @@ -174,7 +175,7 @@ const terminalConfiguration: IConfigurationNode = { }, [TerminalSettingId.FontFamily]: { markdownDescription: localize('terminal.integrated.fontFamily', "Controls the font family of the terminal. Defaults to {0}'s value.", '`#editor.fontFamily#`'), - type: 'string' + type: 'string', }, [TerminalSettingId.FontLigaturesEnabled]: { markdownDescription: localize('terminal.integrated.fontLigatures.enabled', "Controls whether font ligatures are enabled in the terminal. Ligatures will only work if the configured {0} supports them.", `\`#${TerminalSettingId.FontFamily}#\``), @@ -643,9 +644,13 @@ const terminalConfiguration: IConfigurationNode = { } }; -export function registerTerminalConfiguration() { +export async function registerTerminalConfiguration(getFontSnippets: () => Promise) { const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration(terminalConfiguration); + const fontsSnippets = await getFontSnippets(); + if (terminalConfiguration.properties) { + terminalConfiguration.properties[TerminalSettingId.FontFamily].defaultSnippets = fontsSnippets; + } } Registry.as(WorkbenchExtensions.ConfigurationMigration) diff --git a/src/vs/workbench/contrib/terminal/common/terminalStorageKeys.ts b/src/vs/workbench/contrib/terminal/common/terminalStorageKeys.ts index 6a495eca..915da5be 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalStorageKeys.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalStorageKeys.ts @@ -7,6 +7,7 @@ export const enum TerminalStorageKeys { SuggestedRendererType = 'terminal.integrated.suggestedRendererType', TabsListWidthHorizontal = 'tabs-list-width-horizontal', TabsListWidthVertical = 'tabs-list-width-vertical', + TabsShowDetailed = 'terminal.integrated.tabs.showDetailed', DeprecatedEnvironmentVariableCollections = 'terminal.integrated.environmentVariableCollections', EnvironmentVariableCollections = 'terminal.integrated.environmentVariableCollectionsV2', TerminalBufferState = 'terminal.integrated.bufferState', diff --git a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts index 101fb7f7..ed00de1f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts @@ -36,9 +36,9 @@ export const terminalStrings = { rename: localize2('workbench.action.terminal.rename', "Rename..."), toggleSizeToContentWidth: localize2('workbench.action.terminal.sizeToContentWidthInstance', "Toggle Size to Content Width"), focusHover: localize2('workbench.action.terminal.focusHover', "Focus Hover"), - sendSequence: localize2('workbench.action.terminal.sendSequence', "Send Custom Sequence To Terminal"), + sendSequence: localize2('workbench.action.terminal.sendSequence', "Send Custom Sequence to Terminal"), newWithCwd: localize2('workbench.action.terminal.newWithCwd', "Create New Terminal Starting in a Custom Working Directory"), renameWithArgs: localize2('workbench.action.terminal.renameWithArg', "Rename the Currently Active Terminal"), - scrollToPreviousCommand: localize2('workbench.action.terminal.scrollToPreviousCommand', "Scroll To Previous Command"), - scrollToNextCommand: localize2('workbench.action.terminal.scrollToNextCommand', "Scroll To Next Command") + scrollToPreviousCommand: localize2('workbench.action.terminal.scrollToPreviousCommand', "Scroll to Previous Command"), + scrollToNextCommand: localize2('workbench.action.terminal.scrollToNextCommand', "Scroll to Next Command") }; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts index f29ac9f6..4bef1ee6 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts @@ -23,13 +23,14 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../../pla import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { ITerminalCapabilityStore, TerminalCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js'; import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js'; -import { ChatAgentLocation, IChatAgent, IChatAgentService } from '../../../chat/common/chatAgents.js'; +import { IChatAgent, IChatAgentService } from '../../../chat/common/chatAgents.js'; import { IDetachedTerminalInstance, ITerminalContribution, ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalService, IXtermTerminal } from '../../../terminal/browser/terminal.js'; import { registerTerminalContribution, type IDetachedCompatibleTerminalContributionContext, type ITerminalContributionContext } from '../../../terminal/browser/terminalExtensions.js'; import { TerminalInstance } from '../../../terminal/browser/terminalInstance.js'; import { TerminalInitialHintSettingId } from '../common/terminalInitialHintConfiguration.js'; import './media/terminalInitialHint.css'; import { TerminalChatCommandId } from './terminalChat.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 782aca3e..5e19dbde 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -9,8 +9,9 @@ import { localize2 } from '../../../../../nls.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IChatWidgetService } from '../../../chat/browser/chat.js'; -import { ChatAgentLocation } from '../../../chat/common/chatAgents.js'; +import { ChatContextKeys } from '../../../chat/common/chatContextKeys.js'; import { IChatService } from '../../../chat/common/chatService.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; import { AbstractInline1ChatAction } from '../../../inlineChat/browser/inlineChatActions.js'; import { isDetachedTerminalInstance } from '../../../terminal/browser/terminal.js'; import { registerActiveXtermAction } from '../../../terminal/browser/terminalActions.js'; @@ -30,6 +31,7 @@ registerActiveXtermAction({ }, f1: true, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.hasChatAgent ), @@ -74,7 +76,10 @@ registerActiveXtermAction({ }], icon: Codicon.close, f1: true, - precondition: TerminalChatContextKeys.visible, + precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, + TerminalChatContextKeys.visible, + ), run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { return; @@ -90,6 +95,7 @@ registerActiveXtermAction({ shortTitle: localize2('run', 'Run'), category: AbstractInline1ChatAction.category, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.requestActive.negate(), TerminalChatContextKeys.responseContainsCodeBlock, @@ -122,6 +128,7 @@ registerActiveXtermAction({ shortTitle: localize2('runFirst', 'Run First'), category: AbstractInline1ChatAction.category, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.requestActive.negate(), TerminalChatContextKeys.responseContainsMultipleCodeBlocks @@ -154,6 +161,7 @@ registerActiveXtermAction({ category: AbstractInline1ChatAction.category, icon: Codicon.insert, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.requestActive.negate(), TerminalChatContextKeys.responseContainsCodeBlock, @@ -186,6 +194,7 @@ registerActiveXtermAction({ shortTitle: localize2('insertFirst', 'Insert First'), category: AbstractInline1ChatAction.category, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.requestActive.negate(), TerminalChatContextKeys.responseContainsMultipleCodeBlocks @@ -218,6 +227,7 @@ registerActiveXtermAction({ icon: Codicon.refresh, category: AbstractInline1ChatAction.category, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.requestActive.negate(), ), @@ -259,6 +269,7 @@ registerActiveXtermAction({ title: localize2('viewInChat', 'View in Chat'), category: AbstractInline1ChatAction.category, precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalChatContextKeys.requestActive.negate(), ), diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts index 868a887e..cbdeafdd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; -import { IChatAgentService, ChatAgentLocation } from '../../../chat/common/chatAgents.js'; +import { IChatAgentService } from '../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; import { TerminalChatContextKeys } from './terminalChat.js'; - export class TerminalChatEnabler { static Id = 'terminalChat.enabler'; @@ -22,7 +23,7 @@ export class TerminalChatEnabler { @IContextKeyService contextKeyService: IContextKeyService, ) { this._ctxHasProvider = TerminalChatContextKeys.hasChatAgent.bindTo(contextKeyService); - this._store.add(chatAgentService.onDidChangeAgents(() => { + this._store.add(Event.runAndSubscribe(chatAgentService.onDidChangeAgents, () => { const hasTerminalAgent = Boolean(chatAgentService.getDefaultAgent(ChatAgentLocation.Terminal)); this._ctxHasProvider.set(hasTerminalAgent); })); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index b2be6193..3a8cde6a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -5,29 +5,30 @@ import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import { Dimension, getActiveWindow, IFocusTracker, trackFocus } from '../../../../../base/browser/dom.js'; +import { CancelablePromise, createCancelablePromise, DeferredPromise } from '../../../../../base/common/async.js'; +import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { autorun, observableValue, type IObservable } from '../../../../../base/common/observable.js'; import { MicrotaskDelay } from '../../../../../base/common/symbols.js'; -import './media/terminalChatWidget.css'; import { localize } from '../../../../../nls.js'; +import { MenuId } from '../../../../../platform/actions/common/actions.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { ChatAgentLocation } from '../../../chat/common/chatAgents.js'; -import { InlineChatWidget } from '../../../inlineChat/browser/inlineChatWidget.js'; -import { ITerminalInstance, type IXtermTerminal } from '../../../terminal/browser/terminal.js'; -import { MENU_TERMINAL_CHAT_WIDGET_INPUT_SIDE_TOOLBAR, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId, TerminalChatContextKeys } from './terminalChat.js'; -import { TerminalStickyScrollContribution } from '../../stickyScroll/browser/terminalStickyScrollContribution.js'; -import { MENU_INLINE_CHAT_WIDGET_SECONDARY } from '../../../inlineChat/common/inlineChat.js'; -import { CancelablePromise, createCancelablePromise, DeferredPromise } from '../../../../../base/common/async.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { IChatAcceptInputOptions, showChatView } from '../../../chat/browser/chat.js'; -import { ChatModel, IChatResponseModel, isCellTextEditOperation } from '../../../chat/common/chatModel.js'; -import { IChatService, IChatProgress } from '../../../chat/common/chatService.js'; -import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; -import { MenuId } from '../../../../../platform/actions/common/actions.js'; import type { IChatViewState } from '../../../chat/browser/chatWidget.js'; -import { autorun, observableValue, type IObservable } from '../../../../../base/common/observable.js'; +import { IChatAgentService } from '../../../chat/common/chatAgents.js'; +import { ChatModel, IChatResponseModel, isCellTextEditOperation } from '../../../chat/common/chatModel.js'; +import { IChatProgress, IChatService } from '../../../chat/common/chatService.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; +import { InlineChatWidget } from '../../../inlineChat/browser/inlineChatWidget.js'; +import { MENU_INLINE_CHAT_WIDGET_SECONDARY } from '../../../inlineChat/common/inlineChat.js'; +import { ITerminalInstance, type IXtermTerminal } from '../../../terminal/browser/terminal.js'; +import { TerminalStickyScrollContribution } from '../../stickyScroll/browser/terminalStickyScrollContribution.js'; +import './media/terminalChatWidget.css'; +import { MENU_TERMINAL_CHAT_WIDGET_INPUT_SIDE_TOOLBAR, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId, TerminalChatContextKeys } from './terminalChat.js'; const enum Constants { HorizontalMargin = 10, @@ -100,6 +101,7 @@ export class TerminalChatWidget extends Disposable { @IStorageService private readonly _storageService: IStorageService, @IViewsService private readonly _viewsService: IViewsService, @IInstantiationService instantiationService: IInstantiationService, + @IChatAgentService private readonly _chatAgentService: IChatAgentService, ) { super(); @@ -225,7 +227,8 @@ export class TerminalChatWidget extends Disposable { } private _resetPlaceholder() { - this.inlineChatWidget.placeholder = this._model.value?.welcomeMessage?.title ?? localize('askAI', 'Ask AI'); + const defaultAgent = this._chatAgentService.getDefaultAgent(ChatAgentLocation.Terminal); + this.inlineChatWidget.placeholder = defaultAgent?.description ?? localize('askAI', 'Ask AI'); } async reveal(viewState?: IChatViewState): Promise { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts b/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts index d69b250f..ddf420a3 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts @@ -13,8 +13,9 @@ import { getActiveDocument } from '../../../../../../base/browser/dom.js'; import { Emitter } from '../../../../../../base/common/event.js'; import { strictEqual } from 'assert'; import { ExtensionIdentifier } from '../../../../../../platform/extensions/common/extensions.js'; -import { ChatAgentLocation, IChatAgent } from '../../../../chat/common/chatAgents.js'; +import { IChatAgent } from '../../../../chat/common/chatAgents.js'; import { importAMDNodeModule } from '../../../../../../amdX.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; suite('Terminal Initial Hint Addon', () => { const store = ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/media/terminalSymbolIcons.css b/src/vs/workbench/contrib/terminalContrib/suggest/browser/media/terminalSymbolIcons.css index 6d918851..515978e5 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/media/terminalSymbolIcons.css +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/media/terminalSymbolIcons.css @@ -3,7 +3,29 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .codicon.codicon-symbol-method-arrow, -.monaco-workbench .codicon.codicon-symbol-method-arrow { color: var(--vscode-terminalSymbolIcon-aliasForeground); } -.monaco-editor .codicon.codicon-flag, -.monaco-workbench .codicon.codicon-flag { color: var(--vscode-terminalSymbolIcon-flagForeground); } +.monaco-editor .codicon.codicon-terminal-symbol-alias, +.monaco-workbench .codicon.codicon-terminal-symbol-alias { color: var(--vscode-terminalSymbolIcon-aliasForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-flag, +.monaco-workbench .codicon.codicon-terminal-symbol-flag { color: var(--vscode-terminalSymbolIcon-flagForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-option-value, +.monaco-workbench .codicon.codicon-terminal-symbol-option-value { color: var(--vscode-terminalSymbolIcon-optionValueForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-method, +.monaco-workbench .codicon.codicon-terminal-symbol-method { color: var(--vscode-terminalSymbolIcon-methodForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-argument, +.monaco-workbench .codicon.codicon-terminal-symbol-argument { color: var(--vscode-terminalSymbolIcon-argumentForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-option, +.monaco-workbench .codicon.codicon-terminal-symbol-option { color: var(--vscode-terminalSymbolIcon-optionForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-inline-suggestion, +.monaco-workbench .codicon.codicon-terminal-symbol-inline-suggestion { color: var(--vscode-terminalSymbolIcon-inlineSuggestionForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-file, +.monaco-workbench .codicon.codicon-terminal-symbol-file { color: var(--vscode-terminalSymbolIcon-fileForeground); } + +.monaco-editor .codicon.codicon-terminal-symbol-folder, +.monaco-workbench .codicon.codicon-terminal-symbol-folder { color: var(--vscode-terminalSymbolIcon-folderForeground); } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/pwshCompletionProviderAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/pwshCompletionProviderAddon.ts index 9928faaf..1b3429c3 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/pwshCompletionProviderAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/pwshCompletionProviderAddon.ts @@ -9,23 +9,21 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import { Event, Emitter } from '../../../../../base/common/event.js'; import { ShellIntegrationOscPs } from '../../../../../platform/terminal/common/xterm/shellIntegrationAddon.js'; import * as dom from '../../../../../base/browser/dom.js'; -import { IPromptInputModel, IPromptInputModelState } from '../../../../../platform/terminal/common/capabilities/commandDetection/promptInputModel.js'; +import { IPromptInputModel } from '../../../../../platform/terminal/common/capabilities/commandDetection/promptInputModel.js'; import { sep } from '../../../../../base/common/path.js'; import { SuggestAddon } from './terminalSuggestAddon.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; -import { ITerminalSuggestConfiguration, terminalSuggestConfigSection, TerminalSuggestSettingId } from '../common/terminalSuggestConfiguration.js'; +import { ITerminalSuggestConfiguration, terminalSuggestConfigSection } from '../common/terminalSuggestConfiguration.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { GeneralShellType } from '../../../../../platform/terminal/common/terminal.js'; import { ITerminalCapabilityStore, TerminalCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; import { DeferredPromise } from '../../../../../base/common/async.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { ITerminalCompletion, TerminalCompletionItemKind } from './terminalCompletionItem.js'; export const enum VSCodeSuggestOscPt { Completions = 'Completions', - CompletionsPwshCommands = 'CompletionsPwshCommands', } export type CompressedPwshCompletion = [ @@ -42,13 +40,8 @@ export type PwshCompletion = { CustomIcon?: string; }; -const enum Constants { - CachedPwshCommandsStorageKey = 'terminal.suggest.pwshCommands' -} - const enum RequestCompletionsSequence { Contextual = '\x1b[24~e', // F12,e - Global = '\x1b[24~f', // F12,f } export class PwshCompletionProviderAddon extends Disposable implements ITerminalAddon, ITerminalCompletionProvider { @@ -56,13 +49,11 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal triggerCharacters?: string[] | undefined; isBuiltin?: boolean = true; static readonly ID = 'pwsh-shell-integration'; - static cachedPwshCommands: Set; readonly shellTypes = [GeneralShellType.PowerShell]; private _lastUserDataTimestamp: number = 0; private _terminal?: Terminal; private _mostRecentCompletion?: ITerminalCompletion; private _promptInputModel?: IPromptInputModel; - private _currentPromptInputState?: IPromptInputModelState; private _enableWidget: boolean = true; isPasting: boolean = false; private _completionsDeferred: DeferredPromise | null = null; @@ -73,10 +64,8 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal readonly onDidRequestSendText = this._onDidRequestSendText.event; constructor( - providedPwshCommands: Set | undefined, capabilities: ITerminalCapabilityStore, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IStorageService private readonly _storageService: IStorageService ) { super(); this._register(Event.runAndSubscribe(Event.any( @@ -92,29 +81,6 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal this._promptInputModel = undefined; } })); - PwshCompletionProviderAddon.cachedPwshCommands = providedPwshCommands || new Set(); - - // Attempt to load cached pwsh commands if not already loaded - if (PwshCompletionProviderAddon.cachedPwshCommands.size === 0) { - const config = this._storageService.get(Constants.CachedPwshCommandsStorageKey, StorageScope.APPLICATION, undefined); - if (config !== undefined) { - const completions = JSON.parse(config); - for (const c of completions) { - PwshCompletionProviderAddon.cachedPwshCommands.add(c); - } - } - } - - this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(TerminalSuggestSettingId.Enabled)) { - this.clearSuggestCache(); - } - })); - } - - clearSuggestCache(): void { - PwshCompletionProviderAddon.cachedPwshCommands.clear(); - this._storageService.remove(Constants.CachedPwshCommandsStorageKey, StorageScope.APPLICATION); } activate(xterm: Terminal): void { @@ -143,8 +109,6 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal case VSCodeSuggestOscPt.Completions: this._handleCompletionsSequence(this._terminal, data, command, args); return true; - case VSCodeSuggestOscPt.CompletionsPwshCommands: - return this._handleCompletionsPwshCommandsSequence(this._terminal, data, command, args); } // Unrecognized sequence @@ -175,38 +139,14 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal let replacementIndex = 0; let replacementLength = this._promptInputModel.cursorIndex; - this._currentPromptInputState = { - value: this._promptInputModel.value, - prefix: this._promptInputModel.prefix, - suffix: this._promptInputModel.suffix, - cursorIndex: this._promptInputModel.cursorIndex, - ghostTextIndex: this._promptInputModel.ghostTextIndex - }; - - let leadingLineContent = this._currentPromptInputState.prefix.substring(replacementIndex, replacementIndex + replacementLength); - - const firstChar = leadingLineContent.length === 0 ? '' : leadingLineContent[0]; - const isGlobalCommand = !leadingLineContent.includes(' ') && firstChar !== '['; - // This is a TabExpansion2 result - if (!isGlobalCommand) { - replacementIndex = parseInt(args[0]); - replacementLength = parseInt(args[1]); - leadingLineContent = this._promptInputModel.prefix; - } + replacementIndex = parseInt(args[0]); + replacementLength = parseInt(args[1]); + const payload = data.slice(command.length + args[0].length + args[1].length + args[2].length + 4/*semi-colons*/); const rawCompletions: PwshCompletion | PwshCompletion[] | CompressedPwshCompletion[] | CompressedPwshCompletion = args.length === 0 || payload.length === 0 ? undefined : JSON.parse(payload); const completions = parseCompletionsFromShell(rawCompletions, replacementIndex, replacementLength); - // This is a global command, add cached commands list to completions - if (isGlobalCommand) { - for (const c of PwshCompletionProviderAddon.cachedPwshCommands) { - c.replacementIndex = replacementIndex; - c.replacementLength = replacementLength; - completions.push(c); - } - } - if (this._mostRecentCompletion?.kind === TerminalCompletionItemKind.Folder && completions.every(c => c.kind === TerminalCompletionItemKind.Folder)) { completions.push(this._mostRecentCompletion); } @@ -214,22 +154,6 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal this._resolveCompletions(completions); } - private async _handleCompletionsPwshCommandsSequence(terminal: Terminal, data: string, command: string, args: string[]): Promise { - const type = args[0]; - const rawCompletions: PwshCompletion | PwshCompletion[] | CompressedPwshCompletion[] | CompressedPwshCompletion = JSON.parse(data.slice(command.length + type.length + 2/*semi-colons*/)); - const completions = parseCompletionsFromShell(rawCompletions, 0, 0); - - const set = PwshCompletionProviderAddon.cachedPwshCommands; - set.clear(); - for (const c of completions) { - set.add(c); - } - - this._storageService.store(Constants.CachedPwshCommandsStorageKey, JSON.stringify(Array.from(set.values())), StorageScope.APPLICATION, StorageTarget.MACHINE); - - return true; - } - private _resolveCompletions(result: ITerminalCompletion[] | undefined) { if (!this._completionsDeferred) { return; @@ -251,11 +175,6 @@ export class PwshCompletionProviderAddon extends Disposable implements ITerminal return Promise.resolve(undefined); } - // Request global pwsh completions if there are none cached - if (PwshCompletionProviderAddon.cachedPwshCommands.size === 0) { - this._onDidRequestSendText.fire(RequestCompletionsSequence.Global); - } - // Ensure that a key has been pressed since the last accepted completion in order to prevent // completions being requested again right after accepting a completion if (this._lastUserDataTimestamp > SuggestAddon.lastAcceptedCompletionTimestamp) { diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts index 51ca3fed..ca79d22a 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { GeneralShellType, TerminalLocation } from '../../../../../platform/terminal/common/terminal.js'; import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from '../../../terminal/browser/terminal.js'; -import { registerActiveInstanceAction } from '../../../terminal/browser/terminalActions.js'; +import { registerActiveInstanceAction, registerTerminalAction } from '../../../terminal/browser/terminalActions.js'; import { registerTerminalContribution, type ITerminalContributionContext } from '../../../terminal/browser/terminalExtensions.js'; import { TerminalContextKeys } from '../../../terminal/common/terminalContextKey.js'; import { TerminalSuggestCommandId } from '../common/terminal.suggest.js'; @@ -93,8 +93,12 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo } private async _loadPwshCompletionAddon(xterm: RawXtermTerminal): Promise { - // Disable when shell type is not powershell - if (this._ctx.instance.shellType !== GeneralShellType.PowerShell) { + // Disable when shell type is not powershell. A naive check is done for Windows PowerShell + // as we don't differentiate it in shellType + if ( + this._ctx.instance.shellType !== GeneralShellType.PowerShell || + this._ctx.instance.shellLaunchConfig.executable?.endsWith('WindowsPowerShell\\v1.0\\powershell.exe') + ) { this._pwshAddon.clear(); return; } @@ -106,11 +110,10 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo return; } - const pwshCompletionProviderAddon = this._pwshAddon.value = this._instantiationService.createInstance(PwshCompletionProviderAddon, undefined, this._ctx.instance.capabilities); + const pwshCompletionProviderAddon = this._pwshAddon.value = this._instantiationService.createInstance(PwshCompletionProviderAddon, this._ctx.instance.capabilities); xterm.loadAddon(pwshCompletionProviderAddon); this.add(pwshCompletionProviderAddon); this.add(pwshCompletionProviderAddon.onDidRequestSendText(text => { - this._ctx.instance.focus(); this._ctx.instance.sendText(text, false); })); this.add(this._terminalCompletionService.registerTerminalCompletionProvider('builtinPwsh', pwshCompletionProviderAddon.id, pwshCompletionProviderAddon)); @@ -202,6 +205,23 @@ registerTerminalContribution(TerminalSuggestContribution.ID, TerminalSuggestCont // #region Actions +registerTerminalAction({ + id: TerminalSuggestCommandId.ConfigureSettings, + title: localize2('workbench.action.terminal.configureSuggestSettings', 'Configure'), + f1: false, + precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), + keybinding: { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Comma, + weight: KeybindingWeight.WorkbenchContrib + }, + menu: { + id: MenuId.MenubarTerminalSuggestStatusMenu, + group: 'right', + order: 1 + }, + run: (c, accessor) => accessor.get(IPreferencesService).openSettings({ query: terminalSuggestConfigSection }) +}); + registerActiveInstanceAction({ id: TerminalSuggestCommandId.RequestCompletions, title: localize2('workbench.action.terminal.requestCompletions', 'Request Completions'), @@ -228,7 +248,8 @@ registerActiveInstanceAction({ keybinding: { // Up is bound to other workbench keybindings that this needs to beat primary: KeyCode.UpArrow, - weight: KeybindingWeight.WorkbenchContrib + 1 + weight: KeybindingWeight.WorkbenchContrib + 1, + when: ContextKeyExpr.or(SimpleSuggestContext.HasNavigated, ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.UpArrowNavigatesHistory}`, false)) }, run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.selectPreviousSuggestion() }); @@ -360,27 +381,20 @@ registerActiveInstanceAction({ }); registerActiveInstanceAction({ - id: TerminalSuggestCommandId.ConfigureSettings, - title: localize2('workbench.action.terminal.configureSuggestSettings', 'Configure'), + id: TerminalSuggestCommandId.HideSuggestWidgetAndNavigateHistory, + title: localize2('workbench.action.terminal.hideSuggestWidgetAndNavigateHistory', 'Hide Suggest Widget and Navigate History'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), - keybinding: { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Comma, - weight: KeybindingWeight.WorkbenchContrib + keybinding: + { + primary: KeyCode.UpArrow, + when: ContextKeyExpr.and(SimpleSuggestContext.HasNavigated.negate(), ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.UpArrowNavigatesHistory}`, true)), + weight: KeybindingWeight.WorkbenchContrib + 2 }, - menu: { - id: MenuId.MenubarTerminalSuggestStatusMenu, - group: 'right', - order: 1 - }, - run: (activeInstance, c, accessor) => accessor.get(IPreferencesService).openSettings({ query: terminalSuggestConfigSection }) -}); - -registerActiveInstanceAction({ - id: TerminalSuggestCommandId.ClearSuggestCache, - title: localize2('workbench.action.terminal.clearSuggestCache', 'Clear Suggest Cache'), - f1: true, - run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.pwshAddon?.clearSuggestCache() + run: (activeInstance) => { + TerminalSuggestContribution.get(activeInstance)?.addon?.hideSuggestWidget(true); + activeInstance.sendText('\u001b[A', false); // Up arrow + } }); // #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionModel.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionModel.ts index c83bbe13..b6559e2a 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionModel.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionModel.ts @@ -47,7 +47,11 @@ const compareCompletionsFn = (leadingLineContent: string, a: TerminalCompletionI // Sort files of the same name by extension const isArg = leadingLineContent.includes(' '); - if (!isArg && a.labelLowExcludeFileExt === b.labelLowExcludeFileExt) { + if (!isArg && a.completion.kind === TerminalCompletionItemKind.File && b.completion.kind === TerminalCompletionItemKind.File) { + // If the file name excluding the extension is different, just do a regular sort + if (a.labelLowExcludeFileExt !== b.labelLowExcludeFileExt) { + return a.labelLowExcludeFileExt.localeCompare(b.labelLowExcludeFileExt, undefined, { ignorePunctuation: true }); + } // Then by label length ascending (excluding file extension if it's a file) score = a.labelLowExcludeFileExt.length - b.labelLowExcludeFileExt.length; if (score !== 0) { @@ -81,20 +85,38 @@ const compareCompletionsFn = (leadingLineContent: string, a: TerminalCompletionI } // Sort by folder depth (eg. `vscode/` should come before `vscode-.../`) - if (a.labelLowNormalizedPath && b.labelLowNormalizedPath) { - // Directories - // Count depth of path (number of / or \ occurrences) - score = count(a.labelLowNormalizedPath, '/') - count(b.labelLowNormalizedPath, '/'); - if (score !== 0) { - return score; - } + if (a.completion.kind === TerminalCompletionItemKind.Folder && b.completion.kind === TerminalCompletionItemKind.Folder) { + if (a.labelLowNormalizedPath && b.labelLowNormalizedPath) { + // Directories + // Count depth of path (number of / or \ occurrences) + score = count(a.labelLowNormalizedPath, '/') - count(b.labelLowNormalizedPath, '/'); + if (score !== 0) { + return score; + } - // Ensure shorter prefixes appear first - if (b.labelLowNormalizedPath.startsWith(a.labelLowNormalizedPath)) { - return -1; // `a` is a prefix of `b`, so `a` should come first + // Ensure shorter prefixes appear first + if (b.labelLowNormalizedPath.startsWith(a.labelLowNormalizedPath)) { + return -1; // `a` is a prefix of `b`, so `a` should come first + } + if (a.labelLowNormalizedPath.startsWith(b.labelLowNormalizedPath)) { + return 1; // `b` is a prefix of `a`, so `b` should come first + } } - if (a.labelLowNormalizedPath.startsWith(b.labelLowNormalizedPath)) { - return 1; // `b` is a prefix of `a`, so `b` should come first + } + + if (a.completion.kind !== b.completion.kind) { + // Sort by kind + if ((a.completion.kind === TerminalCompletionItemKind.Method || a.completion.kind === TerminalCompletionItemKind.Alias) && (b.completion.kind !== TerminalCompletionItemKind.Method && b.completion.kind !== TerminalCompletionItemKind.Alias)) { + return -1; // Methods and aliases should come first + } + if ((b.completion.kind === TerminalCompletionItemKind.Method || b.completion.kind === TerminalCompletionItemKind.Alias) && (a.completion.kind !== TerminalCompletionItemKind.Method && a.completion.kind !== TerminalCompletionItemKind.Alias)) { + return 1; // Methods and aliases should come first + } + if ((a.completion.kind === TerminalCompletionItemKind.File || a.completion.kind === TerminalCompletionItemKind.Folder) && (b.completion.kind !== TerminalCompletionItemKind.File && b.completion.kind !== TerminalCompletionItemKind.Folder)) { + return 1; // Resources should come last + } + if ((b.completion.kind === TerminalCompletionItemKind.File || b.completion.kind === TerminalCompletionItemKind.Folder) && (a.completion.kind !== TerminalCompletionItemKind.File && a.completion.kind !== TerminalCompletionItemKind.Folder)) { + return -1; // Resources should come last } } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts index eb7f998c..af55e2bf 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts @@ -16,6 +16,7 @@ import { TerminalSuggestSettingId } from '../common/terminalSuggestConfiguration import { TerminalCompletionItemKind, type ITerminalCompletion } from './terminalCompletionItem.js'; import { env as processEnv } from '../../../../../base/common/process.js'; import type { IProcessEnvironment } from '../../../../../base/common/platform.js'; +import { timeout } from '../../../../../base/common/async.js'; export const ITerminalCompletionService = createDecorator('terminalCompletionService'); @@ -166,7 +167,10 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo if (provider.shellTypes && !provider.shellTypes.includes(shellType)) { return undefined; } - const completions = await provider.provideCompletions(promptValue, cursorPosition, allowFallbackCompletions, token); + const completions = await Promise.race([ + provider.provideCompletions(promptValue, cursorPosition, allowFallbackCompletions, token), + timeout(5000) + ]); if (!completions) { return undefined; } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 693bfefc..7757d450 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -5,7 +5,6 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import * as dom from '../../../../../base/browser/dom.js'; -import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { combinedDisposable, Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { sep } from '../../../../../base/common/path.js'; @@ -36,6 +35,9 @@ import { GOLDEN_LINE_HEIGHT_RATIO, MINIMUM_LINE_HEIGHT } from '../../../../../ed import { TerminalCompletionModel } from './terminalCompletionModel.js'; import { TerminalCompletionItem, TerminalCompletionItemKind, type ITerminalCompletion } from './terminalCompletionItem.js'; import { IntervalTimer, TimeoutTimer } from '../../../../../base/common/async.js'; +import { localize } from '../../../../../nls.js'; +import { TerminalSuggestTelemetry } from './terminalSuggestTelemetry.js'; +import { terminalSymbolAliasIcon, terminalSymbolArgumentIcon, terminalSymbolEnumMember, terminalSymbolFileIcon, terminalSymbolFlagIcon, terminalSymbolInlineSuggestionIcon, terminalSymbolMethodIcon, terminalSymbolOptionIcon, terminalSymbolFolderIcon } from './terminalSymbolIcons.js'; export interface ISuggestController { isPasting: boolean; @@ -44,7 +46,7 @@ export interface ISuggestController { selectNextSuggestion(): void; selectNextPageSuggestion(): void; acceptSelectedSuggestion(suggestion?: Pick, 'item' | 'model'>): void; - hideSuggestWidget(cancelAnyRequests: boolean): void; + hideSuggestWidget(cancelAnyRequests: boolean, wasClosedByUser?: boolean): void; } export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggestController { private _terminal?: Terminal; @@ -89,16 +91,29 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest readonly onDidFontConfigurationChange = this._onDidFontConfigurationChange.event; private _kindToIconMap = new Map([ - [TerminalCompletionItemKind.File, Codicon.file], - [TerminalCompletionItemKind.Folder, Codicon.folder], - [TerminalCompletionItemKind.Method, Codicon.symbolMethod], - [TerminalCompletionItemKind.Alias, Codicon.symbolMethodArrow], - [TerminalCompletionItemKind.Argument, Codicon.symbolVariable], - [TerminalCompletionItemKind.Option, Codicon.symbolEnum], - [TerminalCompletionItemKind.OptionValue, Codicon.symbolEnumMember], - [TerminalCompletionItemKind.Flag, Codicon.flag], - [TerminalCompletionItemKind.InlineSuggestion, Codicon.star], - [TerminalCompletionItemKind.InlineSuggestionAlwaysOnTop, Codicon.star], + [TerminalCompletionItemKind.File, terminalSymbolFileIcon], + [TerminalCompletionItemKind.Folder, terminalSymbolFolderIcon], + [TerminalCompletionItemKind.Method, terminalSymbolMethodIcon], + [TerminalCompletionItemKind.Alias, terminalSymbolAliasIcon], + [TerminalCompletionItemKind.Argument, terminalSymbolArgumentIcon], + [TerminalCompletionItemKind.Option, terminalSymbolOptionIcon], + [TerminalCompletionItemKind.OptionValue, terminalSymbolEnumMember], + [TerminalCompletionItemKind.Flag, terminalSymbolFlagIcon], + [TerminalCompletionItemKind.InlineSuggestion, terminalSymbolInlineSuggestionIcon], + [TerminalCompletionItemKind.InlineSuggestionAlwaysOnTop, terminalSymbolInlineSuggestionIcon], + ]); + + private _kindToKindLabelMap = new Map([ + [TerminalCompletionItemKind.File, localize('file', 'File')], + [TerminalCompletionItemKind.Folder, localize('folder', 'Folder')], + [TerminalCompletionItemKind.Method, localize('method', 'Method')], + [TerminalCompletionItemKind.Alias, localize('alias', 'Alias')], + [TerminalCompletionItemKind.Argument, localize('argument', 'Argument')], + [TerminalCompletionItemKind.Option, localize('option', 'Option')], + [TerminalCompletionItemKind.OptionValue, localize('optionValue', 'Option Value')], + [TerminalCompletionItemKind.Flag, localize('flag', 'Flag')], + [TerminalCompletionItemKind.InlineSuggestion, localize('inlineSuggestion', 'Inline Suggestion')], + [TerminalCompletionItemKind.InlineSuggestionAlwaysOnTop, localize('inlineSuggestionAlwaysOnTop', 'Inline Suggestion')], ]); private readonly _inlineCompletion: ITerminalCompletion = { @@ -111,11 +126,13 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest provider: 'core', detail: 'Inline suggestion', kind: TerminalCompletionItemKind.InlineSuggestion, + kindLabel: 'Inline suggestion', icon: this._kindToIconMap.get(TerminalCompletionItemKind.InlineSuggestion), }; private readonly _inlineCompletionItem = new TerminalCompletionItem(this._inlineCompletion); private _shouldSyncWhenReady: boolean = false; + private _suggestTelemetry: TerminalSuggestTelemetry | undefined; constructor( shellType: TerminalShellType | undefined, @@ -160,10 +177,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest if (commandDetection) { if (this._promptInputModel !== commandDetection.promptInputModel) { this._promptInputModel = commandDetection.promptInputModel; + this._suggestTelemetry = this._register(this._instantiationService.createInstance(TerminalSuggestTelemetry, commandDetection, this._promptInputModel)); this._promptInputModelSubscriptions.value = combinedDisposable( this._promptInputModel.onDidChangeInput(e => this._sync(e)), this._promptInputModel.onDidFinishInput(() => { - this._mostRecentPromptInputState = undefined; this.hideSuggestWidget(true); }), ); @@ -203,6 +220,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._lastUserData = e.key; this._lastUserDataTimestamp = Date.now(); })); + this._register(xterm.onScroll(() => this.hideSuggestWidget(true))); } private async _handleCompletionProviders(terminal: Terminal | undefined, token: CancellationToken, explicitlyInvoked?: boolean): Promise { @@ -244,7 +262,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._requestedCompletionsIndex = this._currentPromptInputState.cursorIndex; const quickSuggestionsConfig = this._configurationService.getValue(terminalSuggestConfigSection).quickSuggestions; - const allowFallbackCompletions = explicitlyInvoked || quickSuggestionsConfig === true || typeof quickSuggestionsConfig === 'object' && quickSuggestionsConfig.unknown === 'on'; + const allowFallbackCompletions = explicitlyInvoked || quickSuggestionsConfig.unknown === 'on'; const providedCompletions = await this._terminalCompletionService.provideCompletions(this._currentPromptInputState.prefix, this._currentPromptInputState.cursorIndex, allowFallbackCompletions, this.shellType, this._capabilities, token, doNotRequestExtensionCompletions); if (token.isCancellationRequested) { @@ -257,6 +275,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest const completions = providedCompletions?.flat() || []; if (!explicitlyInvoked && !completions.length) { + this.hideSuggestWidget(true); return; } @@ -284,12 +303,13 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest // Add any "ghost text" suggestion suggested by the shell. This aligns with behavior of the // editor and how it interacts with inline completions. This object is tracked and reused as // it may change on input. - this._refreshInlineCompletion(); + this._refreshInlineCompletion(completions); // Add any missing icons based on the completion item kind for (const completion of completions) { if (!completion.icon && completion.kind !== undefined) { completion.icon = this._kindToIconMap.get(completion.kind); + completion.kindLabel = this._kindToKindLabelMap.get(completion.kind); } } @@ -349,11 +369,27 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest await this._handleCompletionProviders(this._terminal, token, explicitlyInvoked); } + private _addPropertiesToInlineCompletionItem(completions: ITerminalCompletion[]): void { + const inlineCompletionLabel = (typeof this._inlineCompletionItem.completion.label === 'string' ? this._inlineCompletionItem.completion.label : this._inlineCompletionItem.completion.label.label).trim(); + const inlineCompletionMatchIndex = completions.findIndex(c => typeof c.label === 'string' ? c.label === inlineCompletionLabel : c.label.label === inlineCompletionLabel); + if (inlineCompletionMatchIndex !== -1) { + // Remove the existing inline completion item from the completions list + const richCompletionMatchingInline = completions.splice(inlineCompletionMatchIndex, 1)[0]; + // Apply its properties to the inline completion item + this._inlineCompletionItem.completion.label = richCompletionMatchingInline.label; + this._inlineCompletionItem.completion.detail = richCompletionMatchingInline.detail; + this._inlineCompletionItem.completion.documentation = richCompletionMatchingInline.documentation; + } else if (this._inlineCompletionItem.completion) { + this._inlineCompletionItem.completion.detail = undefined; + this._inlineCompletionItem.completion.documentation = undefined; + } + } + private _requestTriggerCharQuickSuggestCompletions(): boolean { if (!this._wasLastInputVerticalArrowKey()) { // Only request on trigger character when it's a regular input, or on an arrow if the widget // is already visible - if (!this._wasLastInputArrowKey() || this._terminalSuggestWidgetVisibleContextKey.get()) { + if (!this._wasLastInputIncludedEscape() || this._terminalSuggestWidgetVisibleContextKey.get()) { this.requestCompletions(); return true; } @@ -369,6 +405,14 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest return !!this._lastUserData?.match(/^\x1b[\[O]?[A-B]$/); } + /** + * Whether the last input included the escape character. Typically this will mean it was more + * than just a simple character, such as arrow keys, home, end, etc. + */ + private _wasLastInputIncludedEscape(): boolean { + return !!this._lastUserData?.includes('\x1b'); + } + private _wasLastInputArrowKey(): boolean { // Never request completions if the last key sequence was up or down as the user was likely // navigating history @@ -386,9 +430,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest if (!this._terminalSuggestWidgetVisibleContextKey.get()) { const commandLineHasSpace = promptInputState.prefix.trim().match(/\s/); if ( - (typeof config.quickSuggestions === 'boolean' && config.quickSuggestions) || - (typeof config.quickSuggestions === 'object' && !commandLineHasSpace && config.quickSuggestions.commands !== 'off') || - (typeof config.quickSuggestions === 'object' && commandLineHasSpace && config.quickSuggestions.arguments !== 'off') + (!commandLineHasSpace && config.quickSuggestions.commands !== 'off') || + (commandLineHasSpace && config.quickSuggestions.arguments !== 'off') ) { if (promptInputState.prefix.match(/[^\s]$/)) { sent = this._requestTriggerCharQuickSuggestCompletions(); @@ -426,16 +469,20 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } // If the cursor moved to the left - if (this._mostRecentPromptInputState && promptInputState.cursorIndex < this._mostRecentPromptInputState.cursorIndex) { - // Backspace or left past a trigger character - if (config.suggestOnTriggerCharacters && !sent && this._mostRecentPromptInputState.cursorIndex > 0) { - const char = this._mostRecentPromptInputState.value[this._mostRecentPromptInputState.cursorIndex - 1]; - if ( - // Only trigger on `\` and `/` if it's a directory. Not doing so causes problems - // with git branches in particular - this._isFilteringDirectories && char.match(/[\\\/]$/) - ) { - sent = this._requestTriggerCharQuickSuggestCompletions(); + if (this._mostRecentPromptInputState && promptInputState.cursorIndex < this._mostRecentPromptInputState.cursorIndex && promptInputState.cursorIndex > 0) { + // We only want to refresh via trigger characters in this case if the widget is + // already visible + if (this._terminalSuggestWidgetVisibleContextKey.get()) { + // Backspace or left past a trigger character + if (config.suggestOnTriggerCharacters && !sent && this._mostRecentPromptInputState.cursorIndex > 0) { + const char = this._mostRecentPromptInputState.value[this._mostRecentPromptInputState.cursorIndex - 1]; + if ( + // Only trigger on `\` and `/` if it's a directory. Not doing so causes problems + // with git branches in particular + this._isFilteringDirectories && char.match(/[\\\/]$/) + ) { + sent = this._requestTriggerCharQuickSuggestCompletions(); + } } } } @@ -488,7 +535,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._suggestWidget.setLineContext(lineContext); } - this._refreshInlineCompletion(); + this._refreshInlineCompletion(this._model?.items.map(i => i.completion) || []); // Hide and clear model if there are no more items if (!this._suggestWidget.hasCompletions()) { @@ -501,14 +548,14 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest return; } const xtermBox = this._screen!.getBoundingClientRect(); - this._suggestWidget.showSuggestions(0, false, false, { + this._suggestWidget.showSuggestions(0, false, true, { left: xtermBox.left + this._terminal.buffer.active.cursorX * dimensions.width, top: xtermBox.top + this._terminal.buffer.active.cursorY * dimensions.height, height: dimensions.height }); } - private _refreshInlineCompletion() { + private _refreshInlineCompletion(completions: ITerminalCompletion[]): void { const oldIsInvalid = this._inlineCompletionItem.isInvalid; if (!this._currentPromptInputState || this._currentPromptInputState.ghostTextIndex === -1) { this._inlineCompletionItem.isInvalid = true; @@ -526,6 +573,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest // Reset the completion item as the object reference must remain the same but its // contents will differ across syncs. This is done so we don't need to reassign the // model and the slowdown/flickering that could potentially cause. + this._addPropertiesToInlineCompletionItem(completions); + const x = new TerminalCompletionItem(this._inlineCompletion); this._inlineCompletionItem.idx = x.idx; this._inlineCompletionItem.score = x.score; @@ -592,6 +641,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest return fontInfo; } + private _getAdvancedExplainModeDetails(): string | undefined { + return `promptInputModel: ${this._promptInputModel?.getCombinedString()}`; + } + private _showCompletions(model: TerminalCompletionModel, explicitlyInvoked?: boolean): void { if (!this._terminal?.element) { return; @@ -608,8 +661,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest return; } const xtermBox = this._screen!.getBoundingClientRect(); - - suggestWidget.showSuggestions(0, false, false, { + suggestWidget.showSuggestions(0, false, !explicitlyInvoked, { left: xtermBox.left + this._terminal.buffer.active.cursorX * dimensions.width, top: xtermBox.top + this._terminal.buffer.active.cursorY * dimensions.height, height: dimensions.height @@ -628,7 +680,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest showStatusBarSettingId: TerminalSuggestSettingId.ShowStatusBar }, this._getFontInfo.bind(this), - this._onDidFontConfigurationChange.event.bind(this) + this._onDidFontConfigurationChange.event.bind(this), + this._getAdvancedExplainModeDetails.bind(this) )) as any as SimpleSuggestWidget; this._suggestWidget.list.style(getListStyles({ listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, @@ -690,6 +743,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } const initialPromptInputState = this._mostRecentPromptInputState; if (!suggestion || !initialPromptInputState || this._leadingLineContent === undefined || !this._model) { + this._suggestTelemetry?.acceptCompletion(undefined, this._mostRecentPromptInputState?.value); return; } SuggestAddon.lastAcceptedCompletionTimestamp = Date.now(); @@ -775,6 +829,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest // Send the completion this._onAcceptedCompletion.fire(resultSequence); + this._suggestTelemetry?.acceptCompletion(completion, this._mostRecentPromptInputState?.value); this.hideSuggestWidget(true); } @@ -826,3 +881,4 @@ export function normalizePathSeparator(path: string, sep: string): string { } return path.replaceAll('/', '\\'); } + diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestTelemetry.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestTelemetry.ts new file mode 100644 index 00000000..4eef8c24 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestTelemetry.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; +import { ICommandDetectionCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js'; +import { IPromptInputModel } from '../../../../../platform/terminal/common/capabilities/commandDetection/promptInputModel.js'; +import { ITerminalCompletion, TerminalCompletionItemKind } from './terminalCompletionItem.js'; + +export class TerminalSuggestTelemetry extends Disposable { + private _acceptedCompletions: Array<{ label: string; kind?: string }> | undefined; + + private _kindMap = new Map([ + [TerminalCompletionItemKind.File, 'File'], + [TerminalCompletionItemKind.Folder, 'Folder'], + [TerminalCompletionItemKind.Method, 'Method'], + [TerminalCompletionItemKind.Alias, 'Alias'], + [TerminalCompletionItemKind.Argument, 'Argument'], + [TerminalCompletionItemKind.Option, 'Option'], + [TerminalCompletionItemKind.OptionValue, 'Option Value'], + [TerminalCompletionItemKind.Flag, 'Flag'], + [TerminalCompletionItemKind.InlineSuggestion, 'Inline Suggestion'], + [TerminalCompletionItemKind.InlineSuggestionAlwaysOnTop, 'Inline Suggestion'], + ]); + + constructor( + commandDetection: ICommandDetectionCapability, + private readonly _promptInputModel: IPromptInputModel, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + ) { + super(); + this._register(commandDetection.onCommandFinished((e) => { + this._sendTelemetryInfo(false, e.exitCode); + this._acceptedCompletions = undefined; + })); + this._register(this._promptInputModel.onDidInterrupt(() => { + this._sendTelemetryInfo(true); + this._acceptedCompletions = undefined; + })); + } + acceptCompletion(completion: ITerminalCompletion | undefined, commandLine?: string): void { + if (!completion || !commandLine) { + this._acceptedCompletions = undefined; + return; + } + this._acceptedCompletions = this._acceptedCompletions || []; + this._acceptedCompletions.push({ label: typeof completion.label === 'string' ? completion.label : completion.label.label, kind: this._kindMap.get(completion.kind!) }); + } + private _sendTelemetryInfo(fromInterrupt?: boolean, exitCode?: number): void { + const commandLine = this._promptInputModel?.value; + for (const completion of this._acceptedCompletions || []) { + const label = completion?.label; + const kind = completion?.kind; + + if (label === undefined || commandLine === undefined || kind === undefined) { + return; + } + + let outcome: string; + if (fromInterrupt) { + outcome = CompletionOutcome.Interrupted; + } else if (commandLine.trim() && commandLine.includes(label)) { + outcome = CompletionOutcome.Accepted; + } else if (inputContainsFirstHalfOfLabel(commandLine, label)) { + outcome = CompletionOutcome.AcceptedWithEdit; + } else { + outcome = CompletionOutcome.Deleted; + } + this._telemetryService.publicLog2<{ + kind: string | undefined; + outcome: string; + exitCode: number | undefined; + }, { + owner: 'meganrogge'; + comment: 'This data is collected to understand the outcome of a terminal completion acceptance.'; + kind: { + classification: 'SystemMetaData'; + purpose: 'FeatureInsight'; + comment: 'The completion item\'s kind'; + }; + outcome: { + classification: 'SystemMetaData'; + purpose: 'FeatureInsight'; + comment: 'The outcome of the accepted completion'; + }; + exitCode: { + classification: 'SystemMetaData'; + purpose: 'FeatureInsight'; + comment: 'The exit code from the command'; + }; + }>('terminal.suggest.acceptedCompletion', { + kind, + outcome, + exitCode + }); + } + } +} + +const enum CompletionOutcome { + Accepted = 'Accepted', + Deleted = 'Deleted', + AcceptedWithEdit = 'AcceptedWithEdit', + Interrupted = 'Interrupted' +} + +function inputContainsFirstHalfOfLabel(commandLine: string, label: string): boolean { + return commandLine.includes(label.substring(0, Math.ceil(label.length / 2))); +} + diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSymbolIcons.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSymbolIcons.ts index a2039d2c..5f216f62 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSymbolIcons.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSymbolIcons.ts @@ -4,10 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import './media/terminalSymbolIcons.css'; -import { SYMBOL_ICON_ENUMERATOR_FOREGROUND, SYMBOL_ICON_METHOD_FOREGROUND } from '../../../../../editor/contrib/symbolIcons/browser/symbolIcons.js'; +import { SYMBOL_ICON_ENUMERATOR_FOREGROUND, SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND, SYMBOL_ICON_METHOD_FOREGROUND, SYMBOL_ICON_VARIABLE_FOREGROUND, SYMBOL_ICON_FILE_FOREGROUND, SYMBOL_ICON_FOLDER_FOREGROUND } from '../../../../../editor/contrib/symbolIcons/browser/symbolIcons.js'; import { registerColor } from '../../../../../platform/theme/common/colorUtils.js'; import { localize } from '../../../../../nls.js'; +import { registerIcon } from '../../../../../platform/theme/common/iconRegistry.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; export const TERMINAL_SYMBOL_ICON_FLAG_FOREGROUND = registerColor('terminalSymbolIcon.flagForeground', SYMBOL_ICON_ENUMERATOR_FOREGROUND, localize('terminalSymbolIcon.flagForeground', 'The foreground color for an flag icon. These icons will appear in the terminal suggest widget.')); - export const TERMINAL_SYMBOL_ICON_ALIAS_FOREGROUND = registerColor('terminalSymbolIcon.aliasForeground', SYMBOL_ICON_METHOD_FOREGROUND, localize('terminalSymbolIcon.aliasForeground', 'The foreground color for an alias icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_OPTION_VALUE_FOREGROUND = registerColor('terminalSymbolIcon.optionValueForeground', SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND, localize('terminalSymbolIcon.enumMemberForeground', 'The foreground color for an enum member icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_METHOD_FOREGROUND = registerColor('terminalSymbolIcon.methodForeground', SYMBOL_ICON_METHOD_FOREGROUND, localize('terminalSymbolIcon.methodForeground', 'The foreground color for a method icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_ARGUMENT_FOREGROUND = registerColor('terminalSymbolIcon.argumentForeground', SYMBOL_ICON_VARIABLE_FOREGROUND, localize('terminalSymbolIcon.argumentForeground', 'The foreground color for an argument icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_OPTION_FOREGROUND = registerColor('terminalSymbolIcon.optionForeground', SYMBOL_ICON_ENUMERATOR_FOREGROUND, localize('terminalSymbolIcon.optionForeground', 'The foreground color for an option icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_INLINE_SUGGESTION_FOREGROUND = registerColor('terminalSymbolIcon.inlineSuggestionForeground', null, localize('terminalSymbolIcon.inlineSuggestionForeground', 'The foreground color for an inline suggestion icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_FILE_FOREGROUND = registerColor('terminalSymbolIcon.fileForeground', SYMBOL_ICON_FILE_FOREGROUND, localize('terminalSymbolIcon.fileForeground', 'The foreground color for a file icon. These icons will appear in the terminal suggest widget.')); +export const TERMINAL_SYMBOL_ICON_FOLDER_FOREGROUND = registerColor('terminalSymbolIcon.folderForeground', SYMBOL_ICON_FOLDER_FOREGROUND, localize('terminalSymbolIcon.folderForeground', 'The foreground color for a folder icon. These icons will appear in the terminal suggest widget.')); + +export const terminalSymbolFlagIcon = registerIcon('terminal-symbol-flag', Codicon.flag, localize('terminalSymbolFlagIcon', 'Icon for flags in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_FLAG_FOREGROUND); +export const terminalSymbolAliasIcon = registerIcon('terminal-symbol-alias', Codicon.symbolMethod, localize('terminalSymbolAliasIcon', 'Icon for aliases in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_ALIAS_FOREGROUND); +export const terminalSymbolEnumMember = registerIcon('terminal-symbol-option-value', Codicon.symbolEnumMember, localize('terminalSymbolOptionValue', 'Icon for enum members in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_OPTION_VALUE_FOREGROUND); +export const terminalSymbolMethodIcon = registerIcon('terminal-symbol-method', Codicon.symbolMethod, localize('terminalSymbolMethodIcon', 'Icon for methods in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_METHOD_FOREGROUND); +export const terminalSymbolArgumentIcon = registerIcon('terminal-symbol-argument', Codicon.symbolVariable, localize('terminalSymbolArgumentIcon', 'Icon for arguments in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_ARGUMENT_FOREGROUND); +export const terminalSymbolOptionIcon = registerIcon('terminal-symbol-option', Codicon.symbolEnum, localize('terminalSymbolOptionIcon', 'Icon for options in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_OPTION_FOREGROUND); +export const terminalSymbolInlineSuggestionIcon = registerIcon('terminal-symbol-inline-suggestion', Codicon.star, localize('terminalSymbolInlineSuggestionIcon', 'Icon for inline suggestions in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_INLINE_SUGGESTION_FOREGROUND); +export const terminalSymbolFileIcon = registerIcon('terminal-symbol-file', Codicon.symbolFile, localize('terminalSymbolFileIcon', 'Icon for files in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_FILE_FOREGROUND); +export const terminalSymbolFolderIcon = registerIcon('terminal-symbol-folder', Codicon.symbolFolder, localize('terminalSymbolFolderIcon', 'Icon for folders in the terminal suggest widget.'), TERMINAL_SYMBOL_ICON_FOLDER_FOREGROUND); diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts index 30d9c86a..f9d66c0a 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts @@ -11,7 +11,7 @@ export const enum TerminalSuggestCommandId { AcceptSelectedSuggestion = 'workbench.action.terminal.acceptSelectedSuggestion', AcceptSelectedSuggestionEnter = 'workbench.action.terminal.acceptSelectedSuggestionEnter', HideSuggestWidget = 'workbench.action.terminal.hideSuggestWidget', - ClearSuggestCache = 'workbench.action.terminal.clearSuggestCache', + HideSuggestWidgetAndNavigateHistory = 'workbench.action.terminal.hideSuggestWidgetAndNavigateHistory', RequestCompletions = 'workbench.action.terminal.requestCompletions', ResetWidgetSize = 'workbench.action.terminal.resetSuggestWidgetSize', ToggleDetails = 'workbench.action.terminal.suggestToggleDetails', @@ -27,7 +27,6 @@ export const defaultTerminalSuggestCommandsToSkipShell = [ TerminalSuggestCommandId.AcceptSelectedSuggestion, TerminalSuggestCommandId.AcceptSelectedSuggestionEnter, TerminalSuggestCommandId.HideSuggestWidget, - TerminalSuggestCommandId.ClearSuggestCache, TerminalSuggestCommandId.RequestCompletions, TerminalSuggestCommandId.ToggleDetails, TerminalSuggestCommandId.ToggleDetailsFocus, diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts index db600bb8..f5cf2b9a 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts @@ -18,6 +18,7 @@ export const enum TerminalSuggestSettingId { ShowStatusBar = 'terminal.integrated.suggest.showStatusBar', CdPath = 'terminal.integrated.suggest.cdPath', InlineSuggestion = 'terminal.integrated.suggest.inlineSuggestion', + UpArrowNavigatesHistory = 'terminal.integrated.suggest.upArrowNavigatesHistory', } export const windowsDefaultExecutableExtensions: string[] = [ @@ -43,7 +44,7 @@ export const terminalSuggestConfigSection = 'terminal.integrated.suggest'; export interface ITerminalSuggestConfiguration { enabled: boolean; - quickSuggestions: /*Legacy - was this when experimental*/boolean | { + quickSuggestions: { commands: 'off' | 'on'; arguments: 'off' | 'on'; unknown: 'off' | 'on'; @@ -169,7 +170,14 @@ export const terminalSuggestConfiguration: IStringDictionary injectionRange.endIndex)) && continueCursor) { + if (cursor.endIndex < injectionRange.startIndex) { + continueCursor = cursor.gotoNextSibling(); + } else { + continueCursor = cursor.gotoFirstChild(); + } + } + } + + private _treeSitterTokenize(textModelTreeSitter: ITextModelTreeSitter, tree: Parser.Tree, languageId: string): IToken[] { const cursor = tree.walk(); cursor.gotoFirstChild(); let cursorResult: boolean = true; const tokens: IToken[] = []; const tokenizationSupport = TreeSitterTokenizationRegistry.get(languageId); + const cursors: { cursor: Parser.TreeCursor; languageId: string; startOffset: number; endOffset: number }[] = [{ cursor, languageId, startOffset: 0, endOffset: textModelTreeSitter.textModel.getValueLength() }]; do { - if (cursor.currentNode.childCount === 0) { - const capture = tokenizationSupport?.captureAtPositionTree(cursor.currentNode.startPosition.row + 1, cursor.currentNode.startPosition.column + 1, tree); - tokens.push({ - c: cursor.currentNode.text.replace(/\r/g, ''), - t: capture && capture.length > 0 ? capture[capture.length - 1].name : '', - r: { - dark_plus: undefined, - light_plus: undefined, - dark_vs: undefined, - light_vs: undefined, - hc_black: undefined, - } - }); + const current = cursors[cursors.length - 1]; + const currentCursor = current.cursor; + const currentLanguageId = current.languageId; + const isOutsideRange: boolean = (currentCursor.currentNode.endIndex > current.endOffset); - while (!(cursorResult = cursor.gotoNextSibling())) { - if (!(cursorResult = cursor.gotoParent())) { - break; + if (!isOutsideRange && (currentCursor.currentNode.childCount === 0)) { + const range = new Range(currentCursor.currentNode.startPosition.row + 1, currentCursor.currentNode.startPosition.column + 1, currentCursor.currentNode.endPosition.row + 1, currentCursor.currentNode.endPosition.column + 1); + const injection = textModelTreeSitter.getInjection(currentCursor.currentNode.startIndex, currentLanguageId); + const treeSitterRange = injection?.ranges!.find(r => r.startIndex <= currentCursor.currentNode.startIndex && r.endIndex >= currentCursor.currentNode.endIndex); + if (injection?.tree && treeSitterRange && (treeSitterRange.startIndex === currentCursor.currentNode.startIndex)) { + const injectionLanguageId = injection.languageId; + const injectionTree = injection.tree; + const injectionCursor = injectionTree.walk(); + this._moveInjectionCursorToRange(injectionCursor, treeSitterRange); + cursors.push({ cursor: injectionCursor, languageId: injectionLanguageId, startOffset: treeSitterRange.startIndex, endOffset: treeSitterRange.endIndex }); + while ((currentCursor.endIndex <= treeSitterRange.endIndex) && (currentCursor.gotoNextSibling() || currentCursor.gotoParent())) { } + } else { + const capture = tokenizationSupport?.captureAtRangeTree(range, tree, textModelTreeSitter); + tokens.push({ + c: currentCursor.currentNode.text.replace(/\r/g, ''), + t: capture?.map(cap => cap.name).join(' ') ?? '', + r: { + dark_plus: undefined, + light_plus: undefined, + dark_vs: undefined, + light_vs: undefined, + hc_black: undefined, + } + }); + while (!(cursorResult = currentCursor.gotoNextSibling())) { + if (!(cursorResult = currentCursor.gotoParent())) { + break; + } } } + } else { - cursorResult = cursor.gotoFirstChild(); + cursorResult = currentCursor.gotoFirstChild(); + } + if (cursors.length > 1 && ((!cursorResult && currentCursor === cursors[cursors.length - 1].cursor) || isOutsideRange)) { + cursors.pop(); + cursorResult = true; } } while (cursorResult); return tokens; @@ -324,14 +362,32 @@ class Snapper { }); } - public async captureTreeSitterSyntaxTokens(fileName: string, content: string): Promise { - const languageId = this.languageService.guessLanguageIdByFilepathOrFirstLine(URI.file(fileName)); + public async captureTreeSitterSyntaxTokens(resource: URI, content: string): Promise { + const languageId = this.languageService.guessLanguageIdByFilepathOrFirstLine(resource); if (languageId) { - const tree = await this.treeSitterParserService.getTree(content, languageId!); + const hasLanguage = TreeSitterTokenizationRegistry.get(languageId); + if (!hasLanguage) { + return []; + } + const model = this.modelService.getModel(resource) ?? this.modelService.createModel(content, { languageId, onDidChange: Event.None }, resource); + let textModelTreeSitter = this.treeSitterParserService.getParseResult(model); + let tree = textModelTreeSitter?.parseResult?.tree; + if (!textModelTreeSitter) { + return []; + } + if (!tree) { + let e = await Event.toPromise(this.treeSitterParserService.onDidUpdateTree); + // Once more for injections + if (e.hasInjections) { + e = await Event.toPromise(this.treeSitterParserService.onDidUpdateTree); + } + textModelTreeSitter = e.tree; + tree = textModelTreeSitter.parseResult?.tree; + } if (!tree) { return []; } - const result = (await this._treeSitterTokenize(tree, languageId)).filter(t => t.c.length > 0); + const result = (await this._treeSitterTokenize(textModelTreeSitter, tree, languageId)).filter(t => t.c.length > 0); const themeTokens = await this._getTreeSitterThemesResult(result, languageId); this._enrichResult(result, themeTokens); return result; @@ -348,7 +404,7 @@ async function captureTokens(accessor: ServicesAccessor, resource: URI | undefin return fileService.readFile(resource).then(content => { if (treeSitter) { - return snapper.captureTreeSitterSyntaxTokens(fileName, content.value.toString()); + return snapper.captureTreeSitterSyntaxTokens(resource, content.value.toString()); } else { return snapper.captureSyntaxTokens(fileName, content.value.toString()); } @@ -377,6 +433,12 @@ CommandsRegistry.registerCommand('_workbench.captureSyntaxTokens', function (acc return captureTokens(accessor, resource); }); -CommandsRegistry.registerCommand('_workbench.captureTreeSitterSyntaxTokens', function (accessor: ServicesAccessor, resource: URI) { +CommandsRegistry.registerCommand('_workbench.captureTreeSitterSyntaxTokens', function (accessor: ServicesAccessor, resource?: URI) { + // If no resource is provided, use the active editor's resource + // This is useful for testing the command + if (!resource) { + const editorService = accessor.get(IEditorService); + resource = editorService.activeEditor?.resource; + } return captureTokens(accessor, resource, true); }); diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 42193a62..a2ef6a1c 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -5,7 +5,7 @@ import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { Emitter } from '../../../../base/common/event.js'; -import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableMap, IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, TimelinePaneId } from './timeline.js'; @@ -16,6 +16,7 @@ import { IContextKey, IContextKeyService, RawContextKey } from '../../../../plat export const TimelineHasProviderContext = new RawContextKey('timelineHasProvider', false); export class TimelineService extends Disposable implements ITimelineService { + declare readonly _serviceBrand: undefined; private readonly _onDidChangeProviders = this._register(new Emitter()); @@ -23,12 +24,13 @@ export class TimelineService extends Disposable implements ITimelineService { private readonly _onDidChangeTimeline = this._register(new Emitter()); readonly onDidChangeTimeline = this._onDidChangeTimeline.event; + private readonly _onDidChangeUri = this._register(new Emitter()); readonly onDidChangeUri = this._onDidChangeUri.event; private readonly hasProviderContext: IContextKey; private readonly providers = new Map(); - private readonly providerSubscriptions = new Map(); + private readonly providerSubscriptions = this._register(new DisposableMap()); constructor( @ILogService private readonly logService: ILogService, @@ -122,8 +124,7 @@ export class TimelineService extends Disposable implements ITimelineService { } this.providers.delete(id); - this.providerSubscriptions.get(id)?.dispose(); - this.providerSubscriptions.delete(id); + this.providerSubscriptions.deleteAndDispose(id); this.updateHasProviderContext(); diff --git a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts index df75f745..0af4bb62 100644 --- a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts +++ b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts @@ -97,7 +97,7 @@ export class TypeHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.create(); this._peekViewService.addExclusiveWidget(editor, this); this._applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(e => this._applyTheme(e.theme), this)); + this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); this._disposables.add(this._previewDisposable); } diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index d8c9fbd6..beedeb01 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -340,7 +340,6 @@ export class ReleaseNotesManager { padding-right: 3px; padding-top: 1px; padding-bottom: 1px; - margin-left: -5px; margin-top: -3px; } .codesetting:hover { @@ -363,7 +362,7 @@ export class ReleaseNotesManager { display: inline-block; background-color: var(--vscode-editor-background); font-size: 12px; - margin-right: 8px; + margin-right: 4px; } header { display: flex; align-items: center; padding-top: 1em; } diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index fd3452f5..3def92a9 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -29,7 +29,7 @@ import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys import { Promises } from '../../../../base/common/async.js'; import { IUserDataSyncWorkbenchService } from '../../../services/userDataSync/common/userDataSync.js'; import { Event } from '../../../../base/common/event.js'; -import { Action } from '../../../../base/common/actions.js'; +import { toAction } from '../../../../base/common/actions.js'; export const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Uninitialized); export const MAJOR_MINOR_UPDATE_AVAILABLE = new RawContextKey('majorMinorUpdateAvailable', false); @@ -215,8 +215,10 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu message: nls.localize('update service disabled', "Updates are disabled because you are running the user-scope installation of {0} as Administrator.", this.productService.nameLong), actions: { primary: [ - new Action('', nls.localize('learn more', "Learn More"), undefined, undefined, () => { - this.openerService.open('https://aka.ms/vscode-windows-setup'); + toAction({ + id: '', + label: nls.localize('learn more', "Learn More"), + run: () => this.openerService.open('https://aka.ms/vscode-windows-setup') }) ] }, diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainService.ts b/src/vs/workbench/contrib/url/browser/trustedDomainService.ts index 99cddd18..a5f3100c 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainService.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainService.ts @@ -10,13 +10,14 @@ import { URI } from '../../../../base/common/uri.js'; import { IInstantiationService, createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope } from '../../../../platform/storage/common/storage.js'; import { TRUSTED_DOMAINS_STORAGE_KEY, readStaticTrustedDomains } from './trustedDomains.js'; -import { testUrlMatchesGlob } from '../common/urlGlob.js'; +import { isURLDomainTrusted } from '../common/trustedDomains.js'; +import { Event, Emitter } from '../../../../base/common/event.js'; export const ITrustedDomainService = createDecorator('ITrustedDomainService'); export interface ITrustedDomainService { _serviceBrand: undefined; - + onDidChangeTrustedDomains: Event; isValid(resource: URI): boolean; } @@ -25,6 +26,9 @@ export class TrustedDomainService extends Disposable implements ITrustedDomainSe private _staticTrustedDomainsResult!: WindowIdleValue; + private _onDidChangeTrustedDomains: Emitter = this._register(new Emitter()); + onDidChangeTrustedDomains: Event = this._onDidChangeTrustedDomains.event; + constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @IStorageService private readonly _storageService: IStorageService, @@ -44,6 +48,7 @@ export class TrustedDomainService extends Disposable implements ITrustedDomainSe this._register(this._storageService.onDidChangeValue(StorageScope.APPLICATION, TRUSTED_DOMAINS_STORAGE_KEY, this._store)(() => { this._staticTrustedDomainsResult?.dispose(); this._staticTrustedDomainsResult = initStaticDomainsResult(); + this._onDidChangeTrustedDomains.fire(); })); } @@ -54,54 +59,3 @@ export class TrustedDomainService extends Disposable implements ITrustedDomainSe return isURLDomainTrusted(resource, allTrustedDomains); } } - -const rLocalhost = /^localhost(:\d+)?$/i; -const r127 = /^127.0.0.1(:\d+)?$/; - -function isLocalhostAuthority(authority: string) { - return rLocalhost.test(authority) || r127.test(authority); -} - -/** - * Case-normalize some case-insensitive URLs, such as github. - */ -function normalizeURL(url: string | URI): string { - const caseInsensitiveAuthorities = ['github.com']; - try { - const parsed = typeof url === 'string' ? URI.parse(url, true) : url; - if (caseInsensitiveAuthorities.includes(parsed.authority)) { - return parsed.with({ path: parsed.path.toLowerCase() }).toString(true); - } else { - return parsed.toString(true); - } - } catch { return url.toString(); } -} - -/** - * Check whether a domain like https://www.microsoft.com matches - * the list of trusted domains. - * - * - Schemes must match - * - There's no subdomain matching. For example https://microsoft.com doesn't match https://www.microsoft.com - * - Star matches all subdomains. For example https://*.microsoft.com matches https://www.microsoft.com and https://foo.bar.microsoft.com - */ -export function isURLDomainTrusted(url: URI, trustedDomains: string[]): boolean { - url = URI.parse(normalizeURL(url)); - trustedDomains = trustedDomains.map(normalizeURL); - - if (isLocalhostAuthority(url.authority)) { - return true; - } - - for (let i = 0; i < trustedDomains.length; i++) { - if (trustedDomains[i] === '*') { - return true; - } - - if (testUrlMatchesGlob(url, trustedDomains[i])) { - return true; - } - } - - return false; -} diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts index 3a4df27b..23f27810 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts @@ -18,7 +18,8 @@ import { IStorageService } from '../../../../platform/storage/common/storage.js' import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { ITrustedDomainService, isURLDomainTrusted } from './trustedDomainService.js'; +import { ITrustedDomainService } from './trustedDomainService.js'; +import { isURLDomainTrusted } from '../common/trustedDomains.js'; import { configureOpenerTrustedDomainsHandler, readStaticTrustedDomains } from './trustedDomains.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; diff --git a/src/vs/workbench/contrib/url/common/trustedDomains.ts b/src/vs/workbench/contrib/url/common/trustedDomains.ts new file mode 100644 index 00000000..850a8357 --- /dev/null +++ b/src/vs/workbench/contrib/url/common/trustedDomains.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { URI } from '../../../../base/common/uri.js'; +import { testUrlMatchesGlob } from './urlGlob.js'; + + +/** + * Check whether a domain like https://www.microsoft.com matches + * the list of trusted domains. + * + * - Schemes must match + * - There's no subdomain matching. For example https://microsoft.com doesn't match https://www.microsoft.com + * - Star matches all subdomains. For example https://*.microsoft.com matches https://www.microsoft.com and https://foo.bar.microsoft.com + */ + +export function isURLDomainTrusted(url: URI, trustedDomains: string[]): boolean { + url = URI.parse(normalizeURL(url)); + trustedDomains = trustedDomains.map(normalizeURL); + + if (isLocalhostAuthority(url.authority)) { + return true; + } + + for (let i = 0; i < trustedDomains.length; i++) { + if (trustedDomains[i] === '*') { + return true; + } + + if (testUrlMatchesGlob(url, trustedDomains[i])) { + return true; + } + } + + return false; +} +/** + * Case-normalize some case-insensitive URLs, such as github. + */ + +export function normalizeURL(url: string | URI): string { + const caseInsensitiveAuthorities = ['github.com']; + try { + const parsed = typeof url === 'string' ? URI.parse(url, true) : url; + if (caseInsensitiveAuthorities.includes(parsed.authority)) { + return parsed.with({ path: parsed.path.toLowerCase() }).toString(true); + } else { + return parsed.toString(true); + } + } catch { return url.toString(); } +} +const rLocalhost = /^localhost(:\d+)?$/i; +const r127 = /^127.0.0.1(:\d+)?$/; + +export function isLocalhostAuthority(authority: string) { + return rLocalhost.test(authority) || r127.test(authority); +} + diff --git a/src/vs/workbench/contrib/url/test/browser/mockTrustedDomainService.ts b/src/vs/workbench/contrib/url/test/browser/mockTrustedDomainService.ts index 020ac3a9..d36e2128 100644 --- a/src/vs/workbench/contrib/url/test/browser/mockTrustedDomainService.ts +++ b/src/vs/workbench/contrib/url/test/browser/mockTrustedDomainService.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from '../../../../../base/common/event.js'; import { URI } from '../../../../../base/common/uri.js'; -import { isURLDomainTrusted, ITrustedDomainService } from '../../browser/trustedDomainService.js'; +import { ITrustedDomainService } from '../../browser/trustedDomainService.js'; +import { isURLDomainTrusted } from '../../common/trustedDomains.js'; export class MockTrustedDomainService implements ITrustedDomainService { _serviceBrand: undefined; @@ -12,6 +14,8 @@ export class MockTrustedDomainService implements ITrustedDomainService { constructor(private readonly _trustedDomains: string[] = []) { } + onDidChangeTrustedDomains: Event = Event.None; + isValid(resource: URI): boolean { return isURLDomainTrusted(resource, this._trustedDomains); } diff --git a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts index aef79543..50b2af6a 100644 --- a/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts +++ b/src/vs/workbench/contrib/url/test/browser/trustedDomains.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { URI } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { isURLDomainTrusted } from '../../browser/trustedDomainService.js'; +import { isURLDomainTrusted } from '../../common/trustedDomains.js'; function linkAllowedByRules(link: string, rules: string[]) { assert.ok(isURLDomainTrusted(URI.parse(link), rules), `Link\n${link}\n should be allowed by rules\n${JSON.stringify(rules)}`); diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts index c4a03276..04a69674 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts @@ -5,7 +5,7 @@ import './media/userDataProfilesEditor.css'; import { $, addDisposableListener, append, clearNode, Dimension, EventHelper, EventType, IDomPosition, trackFocus } from '../../../../base/browser/dom.js'; -import { Action, IAction, IActionChangeEvent, Separator, SubmenuAction } from '../../../../base/common/actions.js'; +import { Action, IAction, IActionChangeEvent, Separator, SubmenuAction, toAction } from '../../../../base/common/actions.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; @@ -225,7 +225,11 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi actions.push(new SubmenuAction('from.template', localize('from template', "From Template"), this.getCreateFromTemplateActions())); actions.push(new Separator()); } - actions.push(new Action('importProfile', localize('importProfile', "Import Profile..."), undefined, true, () => this.importProfile())); + actions.push(toAction({ + id: 'importProfile', + label: localize('importProfile', "Import Profile..."), + run: () => this.importProfile() + })); return actions; } }, @@ -240,12 +244,11 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi private getCreateFromTemplateActions(): IAction[] { return this.templates.map(template => - new Action( - `template:${template.url}`, - template.name, - undefined, - true, - () => this.createNewProfile(URI.parse(template.url)))); + toAction({ + id: `template:${template.url}`, + label: template.name, + run: () => this.createNewProfile(URI.parse(template.url)) + })); } private registerListeners(): void { @@ -282,13 +285,21 @@ export class UserDataProfilesEditor extends EditorPane implements IUserDataProfi private getTreeContextMenuActions(): IAction[] { const actions: IAction[] = []; - actions.push(new Action('newProfile', localize('newProfile', "New Profile"), undefined, true, () => this.createNewProfile())); + actions.push(toAction({ + id: 'newProfile', + label: localize('newProfile', "New Profile"), + run: () => this.createNewProfile() + })); const templateActions = this.getCreateFromTemplateActions(); if (templateActions.length) { actions.push(new SubmenuAction('from.template', localize('new from template', "New Profile From Template"), templateActions)); } actions.push(new Separator()); - actions.push(new Action('importProfile', localize('importProfile', "Import Profile..."), undefined, true, () => this.importProfile())); + actions.push(toAction({ + id: 'importProfile', + label: localize('importProfile', "Import Profile..."), + run: () => this.importProfile() + })); return actions; } @@ -1036,7 +1047,7 @@ class ProfileIconRenderer extends ProfilePropertyRenderer { return; } iconSelectBox.clearInput(); - hoverWidget = this.hoverService.showHover({ + hoverWidget = this.hoverService.showInstantHover({ content: iconSelectBox.domNode, target: iconElement, position: { diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts index 6cd4321e..5ef8a898 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action, IAction, Separator } from '../../../../base/common/actions.js'; +import { Action, IAction, Separator, toAction } from '../../../../base/common/actions.js'; import { Emitter } from '../../../../base/common/event.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; @@ -262,11 +262,12 @@ export abstract class AbstractUserDataProfileElement extends Disposable { checkbox: undefined, resourceType: r, openAction: children.length - ? new Action('_open', - localize('open', "Open to the Side"), - ThemeIcon.asClassName(Codicon.goToFile), - true, - () => children[0]?.openAction?.run()) + ? toAction({ + id: '_open', + label: localize('open', "Open to the Side"), + class: ThemeIcon.asClassName(Codicon.goToFile), + run: () => children[0]?.openAction?.run() + }) : undefined }; })); @@ -309,11 +310,16 @@ export abstract class AbstractUserDataProfileElement extends Disposable { description: isString(child.description) ? child.description : undefined, resource: URI.revive(child.resourceUri), icon: child.themeIcon, - openAction: new Action('_openChild', localize('open', "Open to the Side"), ThemeIcon.asClassName(Codicon.goToFile), true, async () => { - if (child.parent.type === ProfileResourceType.Extensions) { - await this.commandService.executeCommand('extension.open', child.handle, undefined, true, undefined, true); - } else if (child.resourceUri) { - await this.commandService.executeCommand(API_OPEN_EDITOR_COMMAND_ID, child.resourceUri, [SIDE_GROUP], undefined); + openAction: toAction({ + id: '_openChild', + label: localize('open', "Open to the Side"), + class: ThemeIcon.asClassName(Codicon.goToFile), + run: async () => { + if (child.parent.type === ProfileResourceType.Extensions) { + await this.commandService.executeCommand('extension.open', child.handle, undefined, true, undefined, true); + } else if (child.resourceUri) { + await this.commandService.executeCommand(API_OPEN_EDITOR_COMMAND_ID, child.resourceUri, [SIDE_GROUP], undefined); + } } }), actions: { @@ -1036,13 +1042,13 @@ export class UserDataProfilesEditorModel extends EditorModel { )); primaryActions.push(createAction); if (isWeb && copyFrom instanceof URI && isProfileURL(copyFrom)) { - primaryActions.push(new Action( + primaryActions.push(disposables.add(new Action( 'userDataProfile.createInDesktop', localize('import in desktop', "Create in {0}", this.productService.nameLong), undefined, true, () => this.openerService.open(copyFrom, { openExternal: true }) - )); + ))); } const cancelAction = disposables.add(new Action( 'userDataProfile.cancel', diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 3da71ce1..10d8716c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -13,7 +13,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { localize } from '../../../../nls.js'; import { isWeb } from '../../../../base/common/platform.js'; import { UserDataSyncTrigger } from './userDataSyncTrigger.js'; -import { Action } from '../../../../base/common/actions.js'; +import { toAction } from '../../../../base/common/actions.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IHostService } from '../../../services/host/browser/host.js'; @@ -42,8 +42,16 @@ class UserDataSyncReportIssueContribution extends Disposable implements IWorkben message, actions: { primary: [ - new Action('Show Sync Logs', localize('show sync logs', "Show Log"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)), - new Action('Restart', isWeb ? localize('reload', "Reload") : localize('restart', "Restart"), undefined, true, () => this.hostService.restart()) + toAction({ + id: 'Show Sync Logs', + label: localize('show sync logs', "Show Log"), + run: () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID) + }), + toAction({ + id: 'Restart', + label: isWeb ? localize('reload', "Reload") : localize('restart', "Restart"), + run: () => this.hostService.restart() + }) ] } }); @@ -58,7 +66,11 @@ class UserDataSyncReportIssueContribution extends Disposable implements IWorkben source: error.operationId ? localize('settings sync', "Settings Sync. Operation Id: {0}", error.operationId) : undefined, actions: { primary: [ - new Action('Show Sync Logs', localize('show sync logs', "Show Log"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)), + toAction({ + id: 'Show Sync Logs', + label: localize('show sync logs', "Show Log"), + run: () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID) + }) ] } }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 8f2a4d37..21d1c9b2 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Action } from '../../../../base/common/actions.js'; +import { toAction } from '../../../../base/common/actions.js'; import { getErrorMessage, isCancellationError } from '../../../../base/common/errors.js'; import { Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore, MutableDisposable, toDisposable, IDisposable } from '../../../../base/common/lifecycle.js'; @@ -249,7 +249,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Info, message: localize('session expired', "Settings sync was turned off because current session is expired, please sign in again to turn on sync."), actions: { - primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] + primary: [toAction({ + id: 'turn on sync', + label: localize('turn on sync', "Turn on Settings Sync..."), + run: () => this.turnOn() + })] } }); break; @@ -258,7 +262,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Info, message: localize('turned off', "Settings sync was turned off from another device, please turn on sync again."), actions: { - primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] + primary: [toAction({ + id: 'turn on sync', + label: localize('turn on sync', "Turn on Settings Sync..."), + run: () => this.turnOn() + })] } }); break; @@ -292,8 +300,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo message: operationId ? `${message} ${operationId}` : message, actions: { primary: [ - new Action('Show Sync Logs', localize('show sync logs', "Show Log"), undefined, true, () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID)), - new Action('Report Issue', localize('report issue', "Report Issue"), undefined, true, () => this.workbenchIssueService.openReporter()) + toAction({ + id: 'Show Sync Logs', + label: localize('show sync logs', "Show Log"), + run: () => this.commandService.executeCommand(SHOW_SYNC_LOG_COMMAND_ID) + }), + toAction({ + id: 'Report Issue', + label: localize('report issue', "Report Issue"), + run: () => this.workbenchIssueService.openReporter() + }) ] } }); @@ -305,8 +321,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo message: localize('error reset required', "Settings sync is disabled because your data in the cloud is older than that of the client. Please clear your data in the cloud before turning on sync."), actions: { primary: [ - new Action('reset', localize('reset', "Clear Data in Cloud..."), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), - new Action('show synced data', localize('show synced data action', "Show Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.showSyncActivity()) + toAction({ + id: 'reset', + label: localize('reset', "Clear Data in Cloud..."), + run: () => this.userDataSyncWorkbenchService.resetSyncedData() + }), + toAction({ + id: 'show synced data', + label: localize('show synced data action', "Show Synced Data"), + run: () => this.userDataSyncWorkbenchService.showSyncActivity() + }) ] } }); @@ -337,7 +361,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Info, message: localize('service changed and turned off', "Settings sync was turned off because {0} now uses a separate service. Please turn on sync again.", this.productService.nameLong), actions: { - primary: [new Action('turn on sync', localize('turn on sync', "Turn on Settings Sync..."), undefined, true, () => this.turnOn())] + primary: [toAction({ + id: 'turn on sync', + label: localize('turn on sync', "Turn on Settings Sync..."), + run: () => this.turnOn() + })] } }); } @@ -351,8 +379,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Error, message: operationId ? `${message} ${operationId}` : message, actions: { - primary: [new Action('open sync file', localize('open file', "Open {0} File", getSyncAreaLabel(resource)), undefined, true, - () => resource === SyncResource.Settings ? this.preferencesService.openUserSettings({ jsonEditor: true }) : this.preferencesService.openGlobalKeybindingSettings(true))] + primary: [toAction({ + id: 'open sync file', + label: localize('open file', "Open {0} File", getSyncAreaLabel(resource)), + run: () => resource === SyncResource.Settings ? this.preferencesService.openUserSettings({ jsonEditor: true }) : this.preferencesService.openGlobalKeybindingSettings(true) + })] } }); } @@ -408,8 +439,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Error, message: localize('errorInvalidConfiguration', "Unable to sync {0} because the content in the file is not valid. Please open the file and correct it.", errorArea.toLowerCase()), actions: { - primary: [new Action('open sync file', localize('open file', "Open {0} File", errorArea), undefined, true, - () => source === SyncResource.Settings ? this.preferencesService.openUserSettings({ jsonEditor: true }) : this.preferencesService.openGlobalKeybindingSettings(true))] + primary: [toAction({ + id: 'open sync file', + label: localize('open file', "Open {0} File", errorArea), + run: () => source === SyncResource.Settings ? this.preferencesService.openUserSettings({ jsonEditor: true }) : this.preferencesService.openGlobalKeybindingSettings(true) + })] } }); this.invalidContentErrorDisposables.set(key, toDisposable(() => { @@ -494,8 +528,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo message: localize('error reset required while starting sync', "Settings sync cannot be turned on because your data in the cloud is older than that of the client. Please clear your data in the cloud before turning on sync."), actions: { primary: [ - new Action('reset', localize('reset', "Clear Data in Cloud..."), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), - new Action('show synced data', localize('show synced data action', "Show Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.showSyncActivity()) + toAction({ + id: 'reset', + label: localize('reset', "Clear Data in Cloud..."), + run: () => this.userDataSyncWorkbenchService.resetSyncedData() + }), + toAction({ + id: 'show synced data', + label: localize('show synced data action', "Show Synced Data"), + run: () => this.userDataSyncWorkbenchService.showSyncActivity() + }) ] } }); @@ -529,7 +571,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const items = this.getConfigureSyncQuickPickItems(); quickPick.items = items; - quickPick.selectedItems = items.filter(item => this.userDataSyncEnablementService.isResourceEnabled(item.id)); + quickPick.selectedItems = items.filter(item => this.userDataSyncEnablementService.isResourceEnabled(item.id, true)); let accepted: boolean = false; disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(() => { accepted = true; @@ -575,8 +617,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo label: getSyncAreaLabel(SyncResource.Profiles), }]; - // if the `reusable prompt` feature is enabled, add appropriate item to the list - if (PromptsConfig.enabled(this.configService)) { + // if the `reusable prompt` feature is enabled and in vscode + // insiders, add the `Prompts` resource item to the list + if (PromptsConfig.enabled(this.configService) === true) { result.push({ id: SyncResource.Prompts, label: getSyncAreaLabel(SyncResource.Prompts) @@ -761,7 +804,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: 'workbench.userData.actions.turningOn', - title: localize('turnin on sync', "Turning on Settings Sync..."), + title: localize('turning on sync', "Turning on Settings Sync..."), precondition: ContextKeyExpr.false(), menu: [{ group: '3_configuration', diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 8ced2a6f..eb5f2a8e 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -20,7 +20,7 @@ import { IDialogService, IFileDialogService } from '../../../../platform/dialogs import { Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { Codicon } from '../../../../base/common/codicons.js'; -import { Action } from '../../../../base/common/actions.js'; +import { toAction } from '../../../../base/common/actions.js'; import { IUserDataSyncWorkbenchService, CONTEXT_SYNC_STATE, getSyncAreaLabel, CONTEXT_ACCOUNT_STATE, AccountStatus, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_TITLE, SYNC_CONFLICTS_VIEW_ID, CONTEXT_ENABLE_SYNC_CONFLICTS_VIEW, CONTEXT_HAS_CONFLICTS } from '../../../services/userDataSync/common/userDataSync.js'; import { IUserDataSyncMachinesService, IUserDataSyncMachine, isWebPlatform } from '../../../../platform/userDataSync/common/userDataSyncMachines.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; @@ -380,7 +380,11 @@ abstract class UserDataSyncActivityViewDataProvider implements ITre message: error.message, actions: { primary: [ - new Action('reset', localize('reset', "Reset Synced Data"), undefined, true, () => this.userDataSyncWorkbenchService.resetSyncedData()), + toAction({ + id: 'reset', + label: localize('reset', "Reset Synced Data"), + run: () => this.userDataSyncWorkbenchService.resetSyncedData() + }), ] } }); diff --git a/src/vs/workbench/contrib/void/common/prompt/prompts.ts b/src/vs/workbench/contrib/void/common/prompt/prompts.ts index 7051ac76..f1305896 100644 --- a/src/vs/workbench/contrib/void/common/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/common/prompt/prompts.ts @@ -393,7 +393,6 @@ ${fileNameEditExample}.`) details.push(`NEVER write the FULL PATH of a file when speaking with the user. Just write the file name ONLY.`) details.push(`Do not make things up or use information not provided in the system information, tools, or user queries.`) - details.push(`Always use MARKDOWN to format lists, bullet points, etc. Do NOT write tables.`) details.push(`Today's date is ${new Date().toDateString()}.`) const importantDetails = (`Important notes: diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index 88f65d9c..40108569 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -75,7 +75,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private _isDisposed = false; private readonly _onDidDispose = this._register(new Emitter()); - public onDidDispose = this._onDidDispose.event; + public readonly onDidDispose = this._onDidDispose.event; override dispose() { this._isDisposed = true; diff --git a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html index ae571f8c..309df85a 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html @@ -230,7 +230,7 @@ return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.')); } - const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`); + const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&id=${ID}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`); navigator.serviceWorker.register(swPath) .then(async registration => { /** diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html index 6a1f3d45..e123bb92 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -5,7 +5,7 @@ + content="default-src 'none'; script-src 'sha256-nlLyDpnjtftJG2xvXh2vuy77l7xFTjfOz7Jnj1iXNmA=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> @@ -236,7 +236,7 @@ return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.')); } - const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`); + const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&id=${ID}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`); navigator.serviceWorker.register(swPath) .then(async registration => { /** diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js index e5fa674e..3d74103e 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -18,6 +18,8 @@ const searchParams = new URL(location.toString()).searchParams; const remoteAuthority = searchParams.get('remoteAuthority'); +const ID = searchParams.get('id'); + /** * Origin used for resources */ @@ -233,12 +235,19 @@ sw.addEventListener('activate', (event) => { */ async function processResourceRequest(event, requestUrlComponents) { const client = await sw.clients.get(event.clientId); + let webviewId; if (!client) { - console.error('Could not find inner client for request'); - return notFound(); + const workerClient = await getWorkerClientForId(event.clientId); + if (!workerClient) { + console.error('Could not find inner client for request'); + return notFound(); + } else { + webviewId = getWebviewIdForClient(workerClient); + } + } else { + webviewId = getWebviewIdForClient(client); } - const webviewId = getWebviewIdForClient(client); if (!webviewId) { console.error('Could not resolve webview id'); return notFound(); @@ -439,6 +448,15 @@ async function processLocalhostRequest(event, requestUrl) { * @returns {string | null} */ function getWebviewIdForClient(client) { + // Refs https://github.com/microsoft/vscode/issues/244143 + // With PlzDedicatedWorker, worker subresources and blob wokers + // will use clients different from the window client. + // Since we cannot different a worker main resource from a worker subresource + // we will use the global webview ID passed in at the time of + // service worker registration. + if (client.type === 'worker' || client.type === 'sharedworker') { + return ID; + } const requesterClientUrl = new URL(client.url); return requesterClientUrl.searchParams.get('id'); } @@ -455,3 +473,16 @@ async function getOuterIframeClient(webviewId) { return hasExpectedPathName && clientUrl.searchParams.get('id') === webviewId; }); } + +/** + * @param {string} clientId + * @returns {Promise} + */ +async function getWorkerClientForId(clientId) { + const allDedicatedWorkerClients = await sw.clients.matchAll({ type: 'worker' }); + const allSharedWorkerClients = await sw.clients.matchAll({ type: 'sharedworker' }); + const allWorkerClients = [...allDedicatedWorkerClients, ...allSharedWorkerClients]; + return allWorkerClients.find(client => { + return client.id === clientId; + }); +} diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts index e5b58995..a57f9c15 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -57,13 +57,16 @@ registerAction2(class extends Action2 { public run( accessor: ServicesAccessor, walkthroughID: string | { category: string; step: string } | undefined, - toSide: boolean | undefined + optionsOrToSide: { toSide?: boolean; inactive?: boolean } | boolean | undefined ) { const editorGroupsService = accessor.get(IEditorGroupsService); const instantiationService = accessor.get(IInstantiationService); const editorService = accessor.get(IEditorService); const commandService = accessor.get(ICommandService); + const toSide = typeof optionsOrToSide === 'object' ? optionsOrToSide.toSide : optionsOrToSide; + const inactive = typeof optionsOrToSide === 'object' ? optionsOrToSide.inactive : false; + if (walkthroughID) { const selectedCategory = typeof walkthroughID === 'string' ? walkthroughID : walkthroughID.category; let selectedStep: string | undefined; @@ -77,7 +80,7 @@ registerAction2(class extends Action2 { if (!selectedCategory && !selectedStep) { editorService.openEditor({ resource: GettingStartedInput.RESOURCE, - options: { preserveFocus: toSide ?? false } + options: { preserveFocus: toSide ?? false, inactive } }, toSide ? SIDE_GROUP : undefined); return; } @@ -101,7 +104,7 @@ registerAction2(class extends Action2 { editor.selectedCategory = selectedCategory; editor.selectedStep = selectedStep; editor.showWelcome = false; - group.openEditor(editor, { revealIfOpened: true }); + group.openEditor(editor, { revealIfOpened: true, inactive }); return; } } @@ -124,7 +127,7 @@ registerAction2(class extends Action2 { }]); } else { // else open respecting toSide - const options: GettingStartedEditorOptions = { selectedCategory: selectedCategory, selectedStep: selectedStep, showWelcome: false, preserveFocus: toSide ?? false }; + const options: GettingStartedEditorOptions = { selectedCategory: selectedCategory, selectedStep: selectedStep, showWelcome: false, preserveFocus: toSide ?? false, inactive }; editorService.openEditor({ resource: GettingStartedInput.RESOURCE, options @@ -136,7 +139,7 @@ registerAction2(class extends Action2 { } else { editorService.openEditor({ resource: GettingStartedInput.RESOURCE, - options: { preserveFocus: toSide ?? false } + options: { preserveFocus: toSide ?? false, inactive } }, toSide ? SIDE_GROUP : undefined); } } diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 6be205a3..01778b2f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -280,12 +280,14 @@ export class GettingStartedPage extends EditorPane { badgeelement.parentElement?.setAttribute('aria-checked', 'true'); badgeelement.classList.remove(...ThemeIcon.asClassNameArray(gettingStartedUncheckedCodicon)); badgeelement.classList.add('complete', ...ThemeIcon.asClassNameArray(gettingStartedCheckedCodicon)); + badgeelement.setAttribute('aria-label', localize('stepDone', "Checkbox for Step {0}: Completed", step.title)); } else { badgeelement.setAttribute('aria-checked', 'false'); badgeelement.parentElement?.setAttribute('aria-checked', 'false'); badgeelement.classList.remove('complete', ...ThemeIcon.asClassNameArray(gettingStartedCheckedCodicon)); badgeelement.classList.add(...ThemeIcon.asClassNameArray(gettingStartedUncheckedCodicon)); + badgeelement.setAttribute('aria-label', localize('stepNotDone', "Checkbox for Step {0}: Not completed", step.title)); } }); } @@ -719,8 +721,8 @@ export class GettingStartedPage extends EditorPane { const themeType = this.themeService.getColorTheme().type; const videoPath = media.path[themeType]; const videoPoster = media.poster ? media.poster[themeType] : undefined; - - const rawHTML = await this.detailsRenderer.renderVideo(videoPath, videoPoster); + const altText = media.altText ? media.altText : localize('videoAltText', "Video for {0}", stepToExpand.title); + const rawHTML = await this.detailsRenderer.renderVideo(videoPath, videoPoster, altText); this.webview.setHtml(rawHTML); let isDisposed = false; @@ -731,7 +733,7 @@ export class GettingStartedPage extends EditorPane { const themeType = this.themeService.getColorTheme().type; const videoPath = media.path[themeType]; const videoPoster = media.poster ? media.poster[themeType] : undefined; - const body = await this.detailsRenderer.renderVideo(videoPath, videoPoster); + const body = await this.detailsRenderer.renderVideo(videoPath, videoPoster, altText); if (!isDisposed) { // Make sure we weren't disposed of in the meantime this.webview.setHtml(body); @@ -867,7 +869,7 @@ export class GettingStartedPage extends EditorPane { const header = $('.header', {}, $('h1.product-name.caption', {}, this.productService.nameLong), - $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "The Open Source Code Editor.")), // Void + $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")) ); const leftColumn = $('.categories-column.categories-column-left', {},); @@ -1178,7 +1180,7 @@ export class GettingStartedPage extends EditorPane { this.container.classList.toggle('height-constrained', size.height <= 600); this.container.classList.toggle('width-constrained', size.width <= 400); - this.container.classList.toggle('width-semi-constrained', size.width <= 800); + this.container.classList.toggle('width-semi-constrained', size.width <= 950); this.categoriesPageScrollbar?.scanDomNode(); this.detailsPageScrollbar?.scanDomNode(); @@ -1462,7 +1464,10 @@ export class GettingStartedPage extends EditorPane { 'data-done-step-id': step.id, 'x-dispatch': 'toggleStepCompletion:' + step.id, 'role': 'checkbox', - 'aria-checked': step.done ? 'true' : 'false' + 'aria-checked': step.done ? 'true' : 'false', + 'aria-label': step.done + ? localize('stepDone', "Checkbox for Step {0}: Completed", step.title) + : localize('stepNotDone', "Checkbox for Step {0}: Not completed", step.title), }); const container = $('.step-description-container', { 'x-step-description-for': step.id }); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer.ts index 94907761..1ca5db0b 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer.ts @@ -201,7 +201,7 @@ export class GettingStartedDetailsRenderer { `; } - async renderVideo(path: URI, poster?: URI): Promise { + async renderVideo(path: URI, poster?: URI, description?: string): Promise { const nonce = generateUuid(); return ` @@ -218,7 +218,7 @@ export class GettingStartedDetailsRenderer { -

    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 hUJ@Z|=KbmfHw6Z}y@O1TaS?83{1OTCAu(SXG diff --git a/src/vs/workbench/browser/parts/editor/media/void_cube_noshadow.png b/src/vs/workbench/browser/parts/editor/media/void_cube_noshadow.png deleted file mode 100644 index 225179f8e161e281833b6a059240be36127ca51d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 813913 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