From 6bd0a17d3c038f120c0b8d6b1babd32c5d378624 Mon Sep 17 00:00:00 2001 From: Lewis Sanchez <87730006+lewis-sanchez@users.noreply.github.com> Date: Tue, 18 Apr 2023 18:28:58 -0700 Subject: [PATCH] Merge from vscode merge-base (#22769) * Merge from vscode merge-base * Turn off basic checks * Enable compilation, unit, and integration tests --- .eslintrc.json | 2 + .github/pull_request_template.md | 2 - .github/workflows/bad-tag.yml | 22 + .github/workflows/basic.yml | 177 + .github/workflows/ci.yml | 118 +- .github/workflows/pr-chat.yml | 26 + .nvmrc | 1 + .vscode/extensions.json | 2 +- .vscode/launch.json | 185 +- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/endgame.github-issues | 2 +- .vscode/notebooks/inbox.github-issues | 2 +- .vscode/notebooks/my-endgame.github-issues | 4 +- .vscode/notebooks/my-work.github-issues | 4 +- .vscode/notebooks/verification.github-issues | 2 +- .vscode/notebooks/vscode-dev.github-issues | 8 +- .vscode/settings.json | 7 + ThirdPartyNotices.txt | 1086 ++- build/.cachesalt | 2 +- build/.gitignore | 1 + build/.moduleignore | 8 + .../common/computeNodeModulesCacheKey.js | 4 +- .../common/computeNodeModulesCacheKey.ts | 4 +- build/azure-pipelines/common/createAsset.js | 2 +- build/azure-pipelines/common/createAsset.ts | 2 - build/azure-pipelines/common/createBuild.js | 2 +- build/azure-pipelines/common/createBuild.ts | 2 - .../azure-pipelines/common/listNodeModules.js | 2 +- .../azure-pipelines/common/listNodeModules.ts | 2 - build/azure-pipelines/common/releaseBuild.js | 2 +- build/azure-pipelines/common/releaseBuild.ts | 2 - build/azure-pipelines/common/retry.js | 2 +- build/azure-pipelines/common/retry.ts | 2 - .../darwin/product-build-darwin-sign.yml | 47 +- .../darwin/product-build-darwin-test.yml | 435 +- .../darwin/product-build-darwin-universal.yml | 44 + .../darwin/product-build-darwin.yml | 319 +- .../darwin/sql-product-build-darwin.yml | 25 +- build/azure-pipelines/distro-build.yml | 4 +- .../linux/product-build-alpine.yml | 4 +- .../linux/product-build-linux-client-test.yml | 272 + .../linux/product-build-linux-client.yml | 528 +- .../linux/product-build-linux-server.yml | 103 +- .../linux/sql-product-build-linux.yml | 92 +- build/azure-pipelines/mixin.js | 2 +- build/azure-pipelines/mixin.ts | 2 - .../product-build-pr-cache.yml | 59 + build/azure-pipelines/product-build-pr.yml | 189 + build/azure-pipelines/product-build.yml | 242 +- build/azure-pipelines/product-compile.yml | 177 +- build/azure-pipelines/product-publish.ps1 | 1 + build/azure-pipelines/product-publish.yml | 1 + .../publish-types/check-version.js | 2 +- .../publish-types/check-version.ts | 2 - .../publish-types/update-types.js | 2 +- .../publish-types/update-types.ts | 2 - build/azure-pipelines/upload-cdn.js | 2 +- build/azure-pipelines/upload-cdn.ts | 2 - build/azure-pipelines/upload-configuration.js | 4 +- build/azure-pipelines/upload-configuration.ts | 4 +- build/azure-pipelines/upload-nlsmetadata.js | 12 +- build/azure-pipelines/upload-nlsmetadata.ts | 14 +- build/azure-pipelines/upload-sourcemaps.js | 2 +- build/azure-pipelines/upload-sourcemaps.ts | 2 - .../azure-pipelines/web/product-build-web.yml | 1 + .../win32/product-build-win32-test.yml | 247 + .../win32/product-build-win32.yml | 521 +- build/darwin/create-universal-app.js | 4 +- build/darwin/create-universal-app.ts | 4 +- build/darwin/sign.js | 2 +- build/darwin/sign.ts | 2 - build/filters.js | 2 - build/gulpfile.editor.js | 57 +- build/gulpfile.extensions.js | 34 +- build/gulpfile.hygiene.js | 2 +- build/gulpfile.js | 6 +- build/gulpfile.reh.js | 20 +- build/gulpfile.vscode.js | 24 +- build/gulpfile.vscode.linux.js | 11 +- build/gulpfile.vscode.web.js | 4 +- build/hygiene.js | 6 +- build/lib/asar.js | 4 +- build/lib/asar.ts | 4 +- build/lib/builtInExtensions.js | 4 +- build/lib/builtInExtensions.ts | 4 +- build/lib/bundle.js | 36 +- build/lib/bundle.ts | 57 +- build/lib/compilation.js | 47 +- build/lib/compilation.ts | 45 +- build/lib/dependencies.js | 2 +- build/lib/dependencies.ts | 2 - build/lib/electron.js | 2 +- build/lib/electron.ts | 2 - build/lib/eslint/code-no-look-behind-regex.js | 2 +- build/lib/eslint/code-no-look-behind-regex.ts | 2 - .../eslint/code-no-unexternalized-strings.js | 4 +- .../eslint/code-no-unexternalized-strings.ts | 4 +- .../lib/eslint/code-no-unused-expressions.js | 8 +- .../lib/eslint/code-no-unused-expressions.ts | 2 - build/lib/eslint/vscode-dts-cancellation.js | 2 +- build/lib/eslint/vscode-dts-cancellation.ts | 2 +- build/lib/eslint/vscode-dts-event-naming.js | 2 +- build/lib/eslint/vscode-dts-event-naming.ts | 3 +- build/lib/extensions.js | 129 +- build/lib/extensions.ts | 10 +- build/lib/git.js | 8 +- build/lib/git.ts | 4 +- build/lib/i18n.js | 196 +- build/lib/i18n.resources.json | 26 +- build/lib/i18n.ts | 209 +- build/lib/layersChecker.js | 5 + build/lib/monaco-api.js | 84 +- build/lib/monaco-api.ts | 86 +- build/lib/optimize.js | 66 +- build/lib/optimize.ts | 79 +- build/lib/policies.js | 497 ++ build/lib/policies.ts | 697 ++ build/lib/preLaunch.js | 2 +- build/lib/preLaunch.ts | 2 - build/lib/reporter.js | 2 +- build/lib/reporter.ts | 2 - build/lib/snapshotLoader.js | 2 +- build/lib/snapshotLoader.ts | 2 - build/lib/standalone.js | 31 +- build/lib/standalone.ts | 31 +- build/lib/task.js | 2 +- build/lib/task.ts | 2 - build/lib/treeshaking.js | 70 +- build/lib/treeshaking.ts | 66 +- build/lib/tsb/builder.js | 491 + build/lib/tsb/builder.ts | 608 ++ build/lib/tsb/index.js | 130 + build/lib/tsb/index.ts | 164 + build/lib/tsb/transpiler.js | 220 + build/lib/tsb/transpiler.ts | 285 + build/lib/tsb/utils.js | 124 + build/lib/tsb/utils.ts | 140 + build/lib/typings/gulp-tsb.d.ts | 18 - build/lib/util.js | 13 +- build/lib/util.ts | 13 +- build/lib/watch/.gitignore | 1 - build/lib/watch/package.json | 12 - build/lib/watch/yarn.lock | 400 - build/linux/debian/dep-lists.js | 156 + build/linux/debian/dep-lists.ts | 157 + build/linux/debian/dependencies-generator.js | 129 + build/linux/debian/dependencies-generator.ts | 145 + build/linux/debian/install-sysroot.js | 81 + build/linux/debian/install-sysroot.ts | 90 + build/linux/debian/sysroots.js | 26 + build/linux/debian/sysroots.ts | 24 + build/linux/debian/types.js | 6 + .../css.d.ts => build/linux/debian/types.ts | 1 + build/linux/libcxx-fetcher.js | 4 +- build/linux/libcxx-fetcher.ts | 2 - build/linux/rpm/dep-lists.js | 6 - build/linux/rpm/dep-lists.ts | 6 - build/linux/rpm/dependencies-generator.js | 4 +- build/linux/rpm/dependencies-generator.ts | 4 +- build/monaco/monaco.d.ts.recipe | 2 +- build/npm/dirs.js | 2 +- build/npm/postinstall.js | 28 +- build/npm/preinstall.js | 7 +- build/npm/setupBuildYarnrc.js | 25 + build/package-lock.json | 7877 +++++++++++++++++ build/package.json | 19 +- build/tsconfig.json | 1 + build/win32/code.iss | 2 +- build/yarn.lock | 1301 +-- cglicenses.json | 53 + cgmanifest.json | 42 +- extensions/configuration-editing/package.json | 7 +- .../devContainer.codespaces.schema.json | 189 + .../devContainer.schema.generated.json | 920 +- .../schemas/devContainer.schema.src.json | 183 +- .../schemas/devContainer.vscode.schema.json | 56 + .../src/configurationEditingMain.ts | 60 +- .../src/extensionsProposals.ts | 8 +- .../src/settingsDocumentHelper.ts | 152 +- .../src/test/completion.test.ts | 594 ++ .../server/src/utils/validation.ts | 108 + .../typings/markdown-it-named-headers.d.ts | 4 +- extensions/fsharp/cgmanifest.json | 2 +- .../fsharp/syntaxes/fsharp.tmLanguage.json | 28 +- extensions/git-base/src/remoteSource.ts | 7 +- extensions/git/extension.webpack.config.js | 3 +- extensions/git/package.json | 394 +- extensions/git/package.nls.json | 65 +- extensions/git/src/actionButton.ts | 287 +- extensions/git/src/api/api1.ts | 100 +- extensions/git/src/api/git.d.ts | 13 +- extensions/git/src/askpass.ts | 50 +- extensions/git/src/commands.ts | 500 +- extensions/git/src/decorationProvider.ts | 2 +- extensions/git/src/encoding.ts | 2 +- extensions/git/src/git-editor-empty.sh | 1 + extensions/git/src/git-editor-main.ts | 21 + extensions/git/src/git-editor.sh | 4 + extensions/git/src/git.ts | 75 +- extensions/git/src/gitEditor.ts | 63 + extensions/git/src/ipc/ipcServer.ts | 9 +- extensions/git/src/log.ts | 113 +- extensions/git/src/main.ts | 55 +- extensions/git/src/model.ts | 75 +- extensions/git/src/postCommitCommands.ts | 32 + extensions/git/src/protocolHandler.ts | 10 +- extensions/git/src/repository.ts | 140 +- extensions/git/src/staging.ts | 2 +- extensions/git/src/statusbar.ts | 8 +- extensions/git/src/terminal.ts | 36 +- extensions/git/src/test/git.test.ts | 6 + extensions/git/src/uri.ts | 11 + extensions/git/src/util.ts | 14 +- extensions/git/tsconfig.json | 2 +- extensions/git/yarn.lock | 64 +- extensions/github-authentication/package.json | 6 +- .../github-authentication/src/common/utils.ts | 2 +- .../github-authentication/src/github.ts | 28 +- .../github-authentication/src/githubServer.ts | 3 + extensions/github-authentication/yarn.lock | 65 +- extensions/github/package.json | 46 + extensions/github/package.nls.json | 4 + extensions/github/src/auth.ts | 2 +- extensions/github/src/commands.ts | 38 + extensions/github/src/extension.ts | 20 +- extensions/github/src/links.ts | 135 + extensions/github/src/pushErrorHandler.ts | 21 +- extensions/github/src/remoteSourceProvider.ts | 14 +- extensions/github/src/test/github.test.ts | 22 +- extensions/github/src/util.ts | 16 + .../client/src/languageParticipants.ts | 87 + .../server/src/utils/validation.ts | 108 + extensions/image-preview/media/main.js | 2 +- extensions/image-preview/package.json | 4 +- extensions/image-preview/yarn.lock | 47 +- extensions/ipynb/.gitignore | 1 + extensions/ipynb/.vscodeignore | 2 +- extensions/ipynb/esbuild.js | 47 + extensions/ipynb/package.json | 16 +- .../ipynb/src/cellAttachmentRenderer.ts | 47 + extensions/ipynb/yarn.lock | 34 +- .../javascript-language-configuration.json | 2 +- .../snippets/javascript.code-snippets | 9 + .../syntaxes/JavaScript.tmLanguage.json | 44 +- .../syntaxes/JavaScriptReact.tmLanguage.json | 44 +- .../client/src/browser/jsonClientMain.ts | 15 +- .../client/src/jsonClient.ts | 417 +- .../client/src/languageStatus.ts | 94 +- .../client/src/node/jsonClientMain.ts | 13 +- .../json-language-features/package.json | 14 +- .../json-language-features/package.nls.json | 1 + .../json-language-features/server/README.md | 6 - .../server/package.json | 6 +- .../server/src/jsonServer.ts | 194 +- .../server/src/languageModelCache.ts | 22 +- .../server/src/utils/runner.ts | 4 +- .../server/src/utils/strings.ts | 2 +- .../server/src/utils/validation.ts | 108 + .../json-language-features/server/yarn.lock | 83 +- extensions/json-language-features/yarn.lock | 103 +- extensions/json/language-configuration.json | 3 +- extensions/markdown-basics/cgmanifest.json | 2 +- extensions/markdown-basics/package.json | 5 +- .../syntaxes/markdown.tmLanguage.json | 41 +- .../markdown-language-features/.vscodeignore | 9 +- .../extension-browser.webpack.config.js | 2 +- .../notebook/index.ts | 133 +- .../markdown-language-features/package.json | 112 +- .../package.nls.json | 14 +- .../preview-src/loading.ts | 4 +- .../server/.npmignore | 12 + .../server/.vscode/launch.json | 16 + .../server/.vscode/settings.json | 2 + .../server/.vscode/tasks.json | 27 + .../server/README.md | 120 + .../extension-browser.webpack.config.js | 24 + .../server/extension.webpack.config.js | 22 + .../server/package.json | 26 + .../server/src/browser/main.ts | 16 + .../server/src/config.ts | 24 + .../server/src/configuration.ts | 59 + .../src/languageFeatures/diagnostics.ts | 95 + .../server/src/logging.ts | 47 + .../server/src/node/main.ts | 19 + .../server/src/protocol.ts | 27 + .../server/src/server.ts | 252 + .../server/src/util/arrays.ts | 11 + .../server/src/util/dispose.ts | 80 + .../server/src/util/file.ts | 16 + .../server/src/util/limiter.ts | 67 + .../server/src/util/resourceMap.ts | 69 + .../server/src/util/schemes.ts | 8 + .../server/src/workspace.ts | 251 + .../server/tsconfig.json | 9 + .../server/yarn.lock | 69 + .../markdown-language-features/src/client.ts | 114 + .../src/commands/openDocumentLink.ts | 9 +- .../src/commands/refreshPreview.ts | 4 +- .../src/commands/reloadPlugins.ts | 4 +- .../src/commands/renderDocument.ts | 8 +- .../src/extension.browser.ts | 42 + .../src/extension.shared.ts | 90 + .../src/extension.ts | 119 +- .../src/languageFeatures/copyPaste.ts | 29 + .../languageFeatures/definitionProvider.ts | 21 - .../src/languageFeatures/diagnostics.ts | 319 +- .../languageFeatures/documentLinkProvider.ts | 421 - .../documentSymbolProvider.ts | 76 - .../src/languageFeatures/dropIntoEditor.ts | 81 +- .../src/languageFeatures/fileReferences.ts | 17 +- .../src/languageFeatures/foldingProvider.ts | 113 - .../src/languageFeatures/pathCompletions.ts | 353 - .../src/languageFeatures/references.ts | 308 - .../src/languageFeatures/rename.ts | 272 - .../src/languageFeatures/smartSelect.ts | 251 - .../src/languageFeatures/workspaceCache.ts | 61 - .../workspaceSymbolProvider.ts | 29 - .../src/{logger.ts => logging.ts} | 39 +- .../src/markdownEngine.ts | 60 +- ...ContentProvider.ts => documentRenderer.ts} | 43 +- .../src/preview/preview.ts | 88 +- .../src/preview/previewManager.ts | 31 +- .../src/preview/topmostLineMonitor.ts | 32 +- .../src/protocol.ts | 28 + .../markdown-language-features/src/slugify.ts | 2 +- .../src/tableOfContents.ts | 97 +- .../src/test/definitionProvider.test.ts | 137 - .../src/test/diagnostic.test.ts | 160 - .../src/test/documentLink.test.ts | 8 +- .../src/test/documentLinkProvider.test.ts | 270 - .../src/test/documentSymbolProvider.test.ts | 97 - .../src/test/engine.test.ts | 2 +- .../src/test/engine.ts | 7 +- .../src/test/fileReferences.test.ts | 118 - .../src/test/foldingProvider.test.ts | 223 - .../src/test/inMemoryWorkspace.ts | 66 +- .../src/test/nulLogging.ts | 12 + .../src/test/pathCompletion.test.ts | 169 - .../src/test/references.test.ts | 580 -- .../src/test/rename.test.ts | 616 -- .../src/test/smartSelect.test.ts | 726 -- .../src/test/tableOfContentsProvider.test.ts | 130 - .../src/test/util.ts | 35 +- .../src/test/workspaceSymbolProvider.test.ts | 103 - .../src/types/textDocument.ts | 22 + .../src/util/async.ts | 4 + .../src/util/cancellation.ts | 13 + .../src/util/dispose.ts | 52 +- .../src/util/dom.ts | 18 + .../src/util/file.ts | 17 + .../src/util/inMemoryDocument.ts | 16 +- .../src/util/openDocumentLink.ts | 72 +- .../src/util/resourceMap.ts | 68 + .../src/util/schemes.ts | 45 +- .../src/util/string.ts | 7 +- .../src/util/workspaceCache.ts | 116 + .../{workspaceContents.ts => workspace.ts} | 111 +- .../markdown-language-features/tsconfig.json | 2 +- .../markdown-language-features/yarn.lock | 162 +- extensions/markdown-math/.gitignore | 1 + extensions/markdown-math/notebook/katex.ts | 4 +- extensions/markdown-math/package.json | 8 +- .../markdown-math/preview-styles/index.css | 2 +- extensions/markdown-math/yarn.lock | 4 +- extensions/merge-conflict/package.json | 8 +- .../merge-conflict/src/codelensProvider.ts | 12 +- .../merge-conflict/src/commandHandler.ts | 14 +- .../merge-conflict/src/contentProvider.ts | 4 +- extensions/merge-conflict/src/delayer.ts | 4 +- .../src/documentMergeConflict.ts | 4 +- .../merge-conflict/src/documentTracker.ts | 12 +- .../merge-conflict/src/mergeConflictParser.ts | 8 +- .../merge-conflict/src/mergeDecorator.ts | 12 +- extensions/merge-conflict/src/services.ts | 16 +- .../microsoft-authentication/package.json | 4 +- .../microsoft-authentication/src/AADHelper.ts | 67 +- .../microsoft-authentication/src/extension.ts | 10 +- .../microsoft-authentication/src/utils.ts | 33 + extensions/microsoft-authentication/yarn.lock | 47 +- extensions/notebook-renderers/package.json | 2 +- extensions/notebook-renderers/src/index.ts | 34 +- extensions/package.json | 2 +- .../resource-deployment/src/test/utils.ts | 2 +- extensions/search-result/src/extension.ts | 4 +- extensions/shared.webpack.config.js | 10 +- extensions/simple-browser/.vscodeignore | 1 + extensions/simple-browser/package.json | 4 +- extensions/simple-browser/yarn.lock | 47 +- .../src/models/loginMigrationModel.ts | 2 +- extensions/sql/cgmanifest.json | 2 +- extensions/sql/syntaxes/sql.tmLanguage.json | 2 +- extensions/theme-defaults/themes/dark_vs.json | 2 +- .../theme-defaults/themes/light_vs.json | 3 +- .../theme-seti/build/update-icon-theme.js | 3 +- extensions/theme-seti/cgmanifest.json | 2 +- extensions/theme-seti/icons/seti.woff | Bin 37204 -> 37200 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 28 +- .../themes/solarized-light-color-theme.json | 3 + .../tomorrow-night-blue-color-theme.json | 2 +- .../src/experimentationService.ts | 93 + .../interactiveWindow.test.ts | 108 + .../vscode-test-resolver/src/extension.ts | 4 +- .../xml-language-features/tsconfig.json | 3 +- .../xml/xml.language-configuration.json | 3 +- .../xml/xsl.language-configuration.json | 3 +- extensions/yarn.lock | 26 +- package.json | 34 +- remote/.yarnrc | 2 +- remote/package.json | 11 +- remote/web/package.json | 9 +- remote/web/yarn.lock | 169 +- remote/yarn.lock | 247 +- resources/darwin/bin/code.sh | 9 + resources/linux/bin/code.sh | 15 +- resources/linux/debian/control.template | 3 +- resources/linux/rpm/code.spec.template | 12 +- resources/linux/snap/snapcraft.yaml | 2 - resources/win32/policies/Code.admx | 48 - resources/win32/policies/en-US/Code.adml | 23 - scripts/test-remote-integration.bat | 8 +- scripts/test-remote-integration.sh | 26 +- scripts/test-web-integration.bat | 20 +- scripts/test-web-integration.sh | 20 +- src/bootstrap-fork.js | 72 +- src/bootstrap.js | 2 +- src/buildfile.js | 13 +- src/main.js | 15 +- src/server-main.js | 2 +- src/sql/azdata.d.ts | 5 + src/sql/azdata.proposed.d.ts | 6 +- .../base/browser/globalPointerMoveMonitor.ts | 126 + .../base/browser/ui/panel/panel.component.ts | 2 + .../browser/menuEntryActionViewItem.ts | 13 +- .../connection/common/connectionStore.ts | 4 +- .../api/common/extHostModelViewTree.ts | 5 +- .../api/common/sqlExtHost.protocol.ts | 4 +- .../browser/designer/designerScriptEditor.ts | 14 +- .../declarativeTable.component.ts | 2 +- .../modelComponents/queryTextEditor.ts | 15 +- .../modelComponents/table.component.ts | 2 +- .../workbench/contrib/charts/browser/utils.ts | 4 +- .../common/configurationUpgrader.ts | 4 +- .../contents/webviewContent.component.ts | 13 +- .../insights/insightsWidget.component.ts | 4 +- .../webview/webviewWidget.component.ts | 11 +- .../editData/browser/editDataEditor.ts | 3 +- .../executionPlanComparisonEditorView.ts | 5 +- .../browser/executionPlanContribution.ts | 16 +- .../browser/executionPlanView.ts | 4 +- .../widgets/highlightExpensiveNodeWidget.ts | 4 +- .../browser/scenarioRecommendations.ts | 4 +- .../modelView/browser/webview.component.ts | 13 +- .../notebook/browser/cellViews/interfaces.ts | 4 +- .../notebook/browser/notebook.contribution.ts | 25 +- .../notebook/browser/notebookEditor.ts | 4 +- .../browser/markdownTextTransformer.test.ts | 2 +- .../test/browser/notebookEditor.test.ts | 3 +- .../contrib/notebook/test/testCommon.ts | 5 +- .../browser/connectionTreeActions.test.ts | 40 +- .../browser/profilerResourceEditor.ts | 15 +- .../contrib/query/browser/actions.ts | 2 +- .../query/browser/query.contribution.ts | 20 +- .../contrib/query/browser/queryEditor.ts | 4 +- .../contrib/webview/browser/webViewDialog.ts | 13 +- .../browser/abstractEnablePreviewFeatures.ts | 6 +- .../notifyEncryptionDialog.ts | 4 +- .../welcome/page/browser/welcomePage.ts | 2 +- .../browser/accountManagementService.ts | 2 +- .../browser/connectionManagementService.ts | 2 +- .../services/insights/common/insightsUtils.ts | 2 + .../notebook/browser/notebookService.ts | 4 +- .../notebook/browser/notebookServiceImpl.ts | 4 +- .../objectExplorer/browser/asyncServerTree.ts | 10 +- .../profiler/browser/profilerService.ts | 2 +- src/tsconfig.monaco.json | 9 +- src/tsconfig.vscode-dts.json | 1 + src/tsec.exemptions.json | 3 +- src/vs/base/browser/broadcast.ts | 69 + src/vs/base/browser/browser.ts | 4 +- src/vs/base/browser/defaultWorkerFactory.ts | 10 +- src/vs/base/browser/deviceAccess.ts | 108 + src/vs/base/browser/dom.ts | 316 +- src/vs/base/browser/formattedTextRenderer.ts | 2 +- .../base/browser/globalPointerMoveMonitor.ts | 50 +- src/vs/base/browser/history.ts | 8 + src/vs/base/browser/iframe.ts | 8 +- src/vs/base/browser/indexedDB.ts | 13 +- src/vs/base/browser/keyboardEvent.ts | 8 +- src/vs/base/browser/markdownRenderer.ts | 110 +- src/vs/base/browser/mouseEvent.ts | 6 +- src/vs/base/browser/touch.ts | 48 +- .../browser/ui/actionbar/actionViewItems.ts | 48 +- src/vs/base/browser/ui/actionbar/actionbar.ts | 4 +- .../ui/breadcrumbs/breadcrumbsWidget.ts | 16 +- src/vs/base/browser/ui/button/button.css | 32 +- src/vs/base/browser/ui/button/button.ts | 40 +- .../browser/ui/codicons/codicon/codicon.ttf | Bin 70800 -> 72504 bytes .../browser/ui/contextview/contextview.css | 2 - .../browser/ui/contextview/contextview.ts | 25 +- src/vs/base/browser/ui/dialog/dialog.ts | 10 +- src/vs/base/browser/ui/dropdown/dropdown.ts | 6 +- .../ui/dropdown/dropdownActionViewItem.ts | 13 + src/vs/base/browser/ui/findinput/findInput.ts | 56 +- .../base/browser/ui/findinput/replaceInput.ts | 14 +- src/vs/base/browser/ui/grid/grid.ts | 46 +- src/vs/base/browser/ui/grid/gridview.ts | 33 +- .../browser/ui/iconLabel/iconHoverDelegate.ts | 3 +- .../browser/ui/iconLabel/iconLabelHover.ts | 34 +- src/vs/base/browser/ui/inputbox/inputBox.ts | 30 +- .../ui/keybindingLabel/keybindingLabel.ts | 2 +- src/vs/base/browser/ui/list/list.css | 77 +- src/vs/base/browser/ui/list/listPaging.ts | 10 +- src/vs/base/browser/ui/list/listView.ts | 81 +- src/vs/base/browser/ui/list/listWidget.ts | 174 +- src/vs/base/browser/ui/list/rangeMap.ts | 8 +- src/vs/base/browser/ui/list/rowCache.ts | 4 +- src/vs/base/browser/ui/menu/menu.ts | 87 +- src/vs/base/browser/ui/menu/menubar.css | 46 +- src/vs/base/browser/ui/menu/menubar.ts | 79 +- .../browser/ui/resizable}/resizable.ts | 0 src/vs/base/browser/ui/sash/sash.ts | 2 +- .../browser/ui/scrollbar/abstractScrollbar.ts | 5 +- .../browser/ui/scrollbar/scrollableElement.ts | 4 +- .../browser/ui/scrollbar/scrollbarArrow.ts | 3 +- .../scrollbarVisibilityController.ts | 8 +- .../browser/ui/selectBox/selectBoxCustom.ts | 16 +- src/vs/base/browser/ui/splitview/paneview.css | 5 +- src/vs/base/browser/ui/splitview/paneview.ts | 4 +- src/vs/base/browser/ui/splitview/splitview.ts | 23 +- src/vs/base/browser/ui/table/tableWidget.ts | 8 +- src/vs/base/browser/ui/toggle/toggle.ts | 15 +- src/vs/base/browser/ui/toolbar/toolbar.ts | 6 +- src/vs/base/browser/ui/tree/abstractTree.ts | 664 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 60 +- src/vs/base/browser/ui/tree/dataTree.ts | 4 +- src/vs/base/browser/ui/tree/indexTreeModel.ts | 4 +- src/vs/base/browser/ui/tree/media/tree.css | 52 + src/vs/base/browser/ui/tree/objectTree.ts | 8 +- src/vs/base/browser/ui/tree/tree.ts | 3 +- src/vs/base/browser/ui/widget.ts | 4 +- src/vs/base/common/actions.ts | 5 +- src/vs/base/common/amd.ts | 6 +- src/vs/base/common/arrays.ts | 114 +- src/vs/base/common/async.ts | 16 +- src/vs/base/common/codicons.ts | 17 +- src/vs/base/common/collections.ts | 25 +- src/vs/base/common/comparers.ts | 2 +- src/vs/base/common/console.ts | 2 +- src/vs/base/common/dataTransfer.ts | 90 + src/vs/base/common/diff/diff.ts | 4 +- src/vs/base/common/errors.ts | 38 +- src/vs/base/common/event.ts | 101 +- src/vs/base/common/fuzzyScorer.ts | 2 +- src/vs/base/common/htmlContent.ts | 4 + src/vs/base/common/json.ts | 26 +- src/vs/base/common/jsonEdit.ts | 4 +- src/vs/base/common/jsonSchema.ts | 23 +- src/vs/base/common/keyCodes.ts | 4 +- src/vs/base/common/labels.ts | 30 +- src/vs/base/common/lifecycle.ts | 2 +- src/vs/base/common/map.ts | 30 +- src/vs/base/common/marked/cgmanifest.json | 4 +- src/vs/base/common/marked/marked.js | 252 +- src/vs/base/common/mime.ts | 16 +- src/vs/base/common/network.ts | 12 +- src/vs/base/common/objects.ts | 3 +- src/vs/base/common/observable.ts | 30 + src/vs/base/common/observableImpl/autorun.ts | 167 + src/vs/base/common/observableImpl/base.ts | 244 + src/vs/base/common/observableImpl/derived.ts | 167 + src/vs/base/common/observableImpl/logging.ts | 312 + src/vs/base/common/observableImpl/utils.ts | 281 + src/vs/base/common/observableValue.ts | 11 + src/vs/base/common/platform.ts | 20 +- src/vs/base/common/product.ts | 4 + src/vs/base/common/resources.ts | 3 +- src/vs/base/common/scrollable.ts | 4 +- src/vs/base/common/skipList.ts | 6 +- src/vs/base/common/strings.ts | 18 +- src/vs/base/common/stripComments.js | 10 +- src/vs/base/common/types.ts | 17 +- src/vs/base/common/uriIpc.ts | 4 +- src/vs/base/common/worker/simpleWorker.ts | 14 +- src/vs/base/node/id.ts | 2 +- src/vs/base/node/macAddress.ts | 2 +- src/vs/base/node/pfs.ts | 7 +- src/vs/base/node/processes.ts | 2 +- src/vs/base/node/ps.ts | 5 + .../electron-sandbox/contextmenu.ts | 8 +- src/vs/base/parts/ipc/common/ipc.net.ts | 5 +- src/vs/base/parts/ipc/common/ipc.ts | 24 +- src/vs/base/parts/ipc/node/ipc.cp.ts | 4 +- src/vs/base/parts/ipc/node/ipc.net.ts | 39 +- .../quickinput/browser/media/quickInput.css | 5 +- .../parts/quickinput/browser/quickInput.ts | 26 +- .../quickinput/browser/quickInputList.ts | 60 +- .../parts/quickinput/common/quickInput.ts | 7 + .../test/browser/quickinput.test.ts | 48 +- src/vs/base/parts/request/browser/request.ts | 2 +- .../parts/sandbox/electron-browser/preload.js | 8 +- .../parts/sandbox/electron-sandbox/globals.ts | 4 + src/vs/base/parts/storage/common/storage.ts | 8 +- src/vs/base/parts/storage/node/storage.ts | 12 +- .../parts/storage/test/node/storage.test.ts | 91 +- src/vs/base/test/browser/actionbar.test.ts | 20 +- src/vs/base/test/browser/dom.test.ts | 167 +- .../browser/formattedTextRenderer.test.ts | 22 +- .../test/browser/markdownRenderer.test.ts | 51 +- src/vs/base/test/browser/ui/grid/grid.test.ts | 38 +- .../test/browser/ui/list/listWidget.test.ts | 97 + .../ui/scrollbar/scrollbarState.test.ts | 4 +- .../browser/ui/splitview/splitview.test.ts | 2 +- .../browser/ui/tree/asyncDataTree.test.ts | 59 +- .../browser/ui/tree/indexTreeModel.test.ts | 2 +- .../browser/ui/tree/objectTreeModel.test.ts | 2 +- src/vs/base/test/common/arrays.test.ts | 40 +- src/vs/base/test/common/async.test.ts | 148 +- src/vs/base/test/common/buffer.test.ts | 38 +- src/vs/base/test/common/cancellation.test.ts | 12 +- src/vs/base/test/common/collections.test.ts | 28 +- src/vs/base/test/common/color.test.ts | 8 +- src/vs/base/test/common/diff/diff.test.ts | 18 +- src/vs/base/test/common/event.test.ts | 86 +- src/vs/base/test/common/filters.test.ts | 26 +- src/vs/base/test/common/fuzzyScorer.test.ts | 72 +- src/vs/base/test/common/glob.test.ts | 78 +- src/vs/base/test/common/history.test.ts | 2 +- src/vs/base/test/common/iconLabels.test.ts | 2 +- src/vs/base/test/common/json.test.ts | 22 +- src/vs/base/test/common/jsonEdit.test.ts | 52 +- src/vs/base/test/common/jsonFormatter.test.ts | 2 +- src/vs/base/test/common/labels.test.ts | 14 - src/vs/base/test/common/lifecycle.test.ts | 10 +- src/vs/base/test/common/linkedList.test.ts | 10 +- src/vs/base/test/common/map.test.ts | 88 +- src/vs/base/test/common/marshalling.test.ts | 6 +- src/vs/base/test/common/network.test.ts | 10 +- src/vs/base/test/common/objects.test.ts | 26 +- src/vs/base/test/common/observable.test.ts | 507 ++ src/vs/base/test/common/processes.test.ts | 2 +- src/vs/base/test/common/resourceTree.test.ts | 4 +- src/vs/base/test/common/resources.test.ts | 34 +- src/vs/base/test/common/scrollable.test.ts | 7 +- src/vs/base/test/common/skipList.test.ts | 26 +- src/vs/base/test/common/stream.test.ts | 4 +- src/vs/base/test/common/stripComments.test.ts | 22 + .../base/test/common/timeTravelScheduler.ts | 7 +- src/vs/base/test/common/troubleshooting.ts | 8 +- src/vs/base/test/common/uri.test.ts | 30 +- src/vs/base/test/node/css.build.test.ts | 313 + src/vs/base/test/node/pfs/pfs.test.ts | 24 +- src/vs/base/test/node/uri.test.perf.ts | 18 +- src/vs/base/worker/workerMain.ts | 31 +- src/vs/code/browser/workbench/workbench.html | 15 +- .../contrib/extensionsCleaner.ts | 169 +- .../contrib/languagePackCachedDataCleaner.ts | 2 +- .../contrib/localizationsUpdater.ts | 6 +- .../sharedProcess/sharedProcessMain.ts | 54 +- .../electron-browser/workbench/workbench.html | 19 - .../electron-browser/workbench/workbench.js | 214 - src/vs/code/electron-main/app.ts | 60 +- src/vs/code/electron-main/main.ts | 65 +- .../issue/issueReporterMain.ts | 17 +- .../issue/issueReporterModel.ts | 11 +- .../electron-sandbox/workbench/workbench.html | 2 +- .../electron-sandbox/workbench/workbench.js | 7 +- src/vs/code/node/cli.ts | 21 +- src/vs/code/node/cliProcessMain.ts | 58 +- .../issue/testReporterModel.test.ts | 42 +- src/vs/css.build.ts | 304 + src/vs/css.js | 4 - src/vs/css.ts | 81 + src/vs/editor/browser/config/domFontInfo.ts | 6 +- .../browser/config/editorConfiguration.ts | 6 - .../editor/browser/config/fontMeasurements.ts | 4 +- .../editor/browser/config/migrateOptions.ts | 19 +- .../editor/browser/controller/mouseHandler.ts | 68 +- .../editor/browser/controller/mouseTarget.ts | 16 +- .../browser/controller/pointerHandler.ts | 6 +- .../browser/controller/textAreaHandler.ts | 9 +- src/vs/editor/browser/coreCommands.ts | 67 +- src/vs/editor/browser/dnd.ts | 76 + src/vs/editor/browser/editorBrowser.ts | 26 +- src/vs/editor/browser/editorDom.ts | 51 +- src/vs/editor/browser/editorExtensions.ts | 6 +- .../services/abstractCodeEditorService.ts | 26 +- .../browser/services/bulkEditService.ts | 68 +- .../browser/services/codeEditorService.ts | 6 + .../browser/services/editorWorkerService.ts | 4 +- .../editor/browser/services/openerService.ts | 2 +- src/vs/editor/browser/view.ts | 5 + .../browser/view/viewUserInputEvents.ts | 44 +- .../blockDecorations/blockDecorations.css | 14 + .../blockDecorations/blockDecorations.ts | 103 + .../viewParts/lineNumbers/lineNumbers.ts | 65 +- .../browser/viewParts/lines/viewLine.ts | 8 +- .../browser/viewParts/lines/viewLines.ts | 3 +- .../browser/viewParts/minimap/minimap.css | 9 + .../browser/viewParts/minimap/minimap.ts | 28 +- .../overviewRuler/decorationsOverviewRuler.ts | 12 +- .../scrollDecoration/scrollDecoration.ts | 2 +- .../viewParts/viewCursors/viewCursor.ts | 8 +- .../editor/browser/widget/codeEditorWidget.ts | 228 +- .../editor/browser/widget/diffEditorWidget.ts | 194 +- .../browser/widget/media/diffEditor.css | 9 + .../config/editorConfigurationSchema.ts | 5 + src/vs/editor/common/config/editorOptions.ts | 253 +- src/vs/editor/common/config/fontInfo.ts | 5 +- src/vs/editor/common/core/indentation.ts | 3 +- src/vs/editor/common/core/range.ts | 8 +- src/vs/editor/common/core/wordHelper.ts | 30 +- src/vs/editor/common/cursor/cursor.ts | 2 +- .../common/cursor/cursorMoveCommands.ts | 2 +- .../common/cursor/cursorWordOperations.ts | 8 +- src/vs/editor/common/diff/diffComputer.ts | 116 +- src/vs/editor/common/editorCommon.ts | 46 +- .../editor/common/encodedTokenAttributes.ts | 193 + .../editor/common/languageFeatureRegistry.ts | 16 +- src/vs/editor/common/languageSelector.ts | 10 +- src/vs/editor/common/languages.ts | 262 +- .../common/languages/languageConfiguration.ts | 2 +- .../editor/common/languages/linkComputer.ts | 10 +- .../editor/common/languages/nullTokenize.ts | 3 +- src/vs/editor/common/languages/supports.ts | 2 +- .../supports/inplaceReplaceSupport.ts | 2 +- .../common/languages/supports/tokenization.ts | 4 +- .../common/languages/textToHtmlTokenizer.ts | 3 +- src/vs/editor/common/model.ts | 13 +- .../bracketPairsImpl.ts | 6 +- .../bracketPairsTree/bracketPairsTree.ts | 16 +- .../bracketPairsTree/brackets.ts | 16 +- .../bracketPairsTree/tokenizer.ts | 4 +- src/vs/editor/common/model/editStack.ts | 2 +- .../pieceTreeTextBuffer/pieceTreeBase.ts | 3 +- src/vs/editor/common/model/textModel.ts | 32 +- src/vs/editor/common/model/textModelSearch.ts | 6 +- src/vs/editor/common/model/textModelTokens.ts | 7 +- .../common/model/tokenizationTextModelPart.ts | 2 +- .../common/services/editorSimpleWorker.ts | 18 +- .../editor/common/services/getIconClasses.ts | 1 - .../services/languageFeatureDebounce.ts | 4 +- .../common/services/languageFeatures.ts | 4 +- .../services/languageFeaturesService.ts | 3 +- .../editor/common/services/languageService.ts | 4 +- .../common/services/languagesRegistry.ts | 9 +- .../services/markerDecorationsService.ts | 6 +- src/vs/editor/common/services/modelService.ts | 19 +- .../editor/common/services/resolverService.ts | 6 + .../services/semanticTokensProviderStyling.ts | 70 +- .../services/unicodeTextModelHighlighter.ts | 2 +- .../common/standalone/standaloneEnums.ts | 209 +- src/vs/editor/common/tokenizationRegistry.ts | 3 +- .../common/tokenizationTextModelPart.ts | 2 +- .../common/tokens/contiguousTokensStore.ts | 3 +- src/vs/editor/common/tokens/lineTokens.ts | 3 +- .../editor/common/tokens/sparseTokensStore.ts | 3 +- .../common/viewLayout/lineDecorations.ts | 2 +- src/vs/editor/common/viewLayout/linePart.ts | 36 + .../editor/common/viewLayout/linesLayout.ts | 18 +- src/vs/editor/common/viewLayout/viewLayout.ts | 11 +- .../common/viewLayout/viewLineRenderer.ts | 98 +- .../viewLayout/viewLinesViewportData.ts | 2 +- src/vs/editor/common/viewModel.ts | 7 +- .../viewModel/minimapTokensColorTracker.ts | 3 +- .../common/viewModel/modelLineProjection.ts | 2 +- .../viewModel/monospaceLineBreaksComputer.ts | 6 +- .../common/viewModel/viewModelDecorations.ts | 16 +- .../editor/common/viewModel/viewModelImpl.ts | 117 +- .../editor/common/viewModel/viewModelLines.ts | 124 +- .../anchorSelect/browser/anchorSelect.ts | 30 +- .../browser/bracketMatching.ts | 29 +- .../caretOperations/browser/transpose.ts | 26 +- .../contrib/clipboard/browser/clipboard.ts | 1 + .../contrib/codeAction/browser/codeAction.ts | 6 +- .../codeAction/browser/codeActionCommands.ts | 216 +- .../browser/codeActionContributions.ts | 5 +- .../codeAction/browser/codeActionMenu.ts | 426 +- .../codeAction/browser/codeActionModel.ts | 12 +- .../codeAction/browser/codeActionUi.ts | 46 +- .../browser/codeActionWidgetContribution.ts | 22 + .../codeAction/browser/media/action.css | 111 + .../contrib/codeAction/browser/types.ts | 17 + .../test/browser/codeAction.test.ts | 30 +- .../contrib/codelens/browser/codelens.ts | 2 +- .../codelens/browser/codelensController.ts | 22 +- .../codelens/browser/codelensWidget.ts | 10 +- .../contrib/colorPicker/browser/color.ts | 4 +- .../colorPicker/browser/colorDetector.ts | 29 +- .../browser/colorHoverParticipant.ts | 2 +- .../colorPicker/browser/colorPickerWidget.ts | 6 +- .../comment/browser/blockCommentCommand.ts | 4 +- .../comment/browser/lineCommentCommand.ts | 10 +- .../test/browser/lineCommentCommand.test.ts | 3 +- .../contextmenu/browser/contextmenu.ts | 126 +- .../browser/copyPasteContribution.ts | 25 + .../copyPaste/browser/copyPasteController.ts | 230 + src/vs/editor/contrib/dnd/browser/dnd.ts | 23 +- .../contrib/dnd/browser/dragAndDropCommand.ts | 2 +- .../documentSymbols/browser/outlineModel.ts | 28 +- .../test/browser/outlineModel.test.ts | 32 +- .../browser/dropIntoEditorContribution.ts | 178 + .../test/browser/editorState.test.ts | 18 +- .../contrib/find/browser/findController.ts | 14 +- .../contrib/find/browser/findDecorations.ts | 22 +- .../editor/contrib/find/browser/findModel.ts | 60 +- .../contrib/find/browser/findOptionsWidget.ts | 8 +- .../editor/contrib/find/browser/findState.ts | 4 +- .../editor/contrib/find/browser/findWidget.ts | 44 +- .../contrib/find/browser/replaceAllCommand.ts | 4 +- .../contrib/find/browser/replacePattern.ts | 20 +- .../contrib/find/test/browser/find.test.ts | 18 +- .../find/test/browser/findController.test.ts | 10 +- .../find/test/browser/findModel.test.ts | 182 +- .../find/test/browser/replacePattern.test.ts | 50 +- .../contrib/folding/browser/folding.css | 5 +- .../editor/contrib/folding/browser/folding.ts | 234 +- .../folding/browser/foldingDecorations.ts | 66 +- .../contrib/folding/browser/foldingModel.ts | 302 +- .../contrib/folding/browser/foldingRanges.ts | 279 +- .../folding/browser/hiddenRangeModel.ts | 42 +- .../folding/browser/indentRangeProvider.ts | 40 +- .../browser/intializingRangeProvider.ts | 65 - .../folding/browser/syntaxRangeProvider.ts | 36 +- .../folding/test/browser/foldingModel.test.ts | 318 +- .../test/browser/foldingRanges.test.ts | 131 +- .../test/browser/hiddenRangeModel.test.ts | 10 +- .../folding/test/browser/indentFold.test.ts | 26 +- .../test/browser/indentRangeProvider.test.ts | 16 +- .../folding/test/browser/syntaxFold.test.ts | 30 +- .../editor/contrib/format/browser/format.ts | 16 +- .../contrib/format/browser/formatActions.ts | 6 +- .../contrib/format/browser/formattingEdit.ts | 4 +- .../gotoError/browser/gotoErrorWidget.ts | 16 +- .../browser/markerNavigationService.ts | 4 +- .../gotoSymbol/browser/goToCommands.ts | 13 +- .../contrib/gotoSymbol/browser/goToSymbol.ts | 2 +- .../browser/link/goToDefinitionAtPosition.ts | 19 +- .../browser/peek/referencesController.ts | 12 +- .../gotoSymbol/browser/peek/referencesTree.ts | 2 +- .../browser/peek/referencesWidget.ts | 24 +- .../gotoSymbol/browser/referencesModel.ts | 10 +- .../contrib/hover/browser/contentHover.ts | 103 +- src/vs/editor/contrib/hover/browser/hover.ts | 2 +- .../hover/browser/markerHoverParticipant.ts | 5 +- .../hover/test/browser/contentHover.test.ts | 27 + .../inPlaceReplace/browser/inPlaceReplace.ts | 13 +- .../indentation/browser/indentUtils.ts | 2 +- .../indentation/browser/indentation.ts | 97 +- .../contrib/inlayHints/browser/inlayHints.ts | 2 +- .../browser/inlayHintsController.ts | 50 +- .../inlayHints/browser/inlayHintsHover.ts | 2 +- .../browser/inlineCompletionsModel.ts | 39 +- .../browser/inlineCompletionsProvider.test.ts | 4 +- .../test/browser/suggestWidgetModel.test.ts | 2 +- .../browser/copyLinesCommand.ts | 2 +- .../browser/linesOperations.ts | 175 +- .../browser/moveLinesCommand.ts | 86 +- .../browser/sortLinesCommand.ts | 10 +- .../test/browser/linesOperations.test.ts | 178 +- .../test/browser/moveLinesCommand.test.ts | 2 +- .../linkedEditing/browser/linkedEditing.ts | 37 +- .../editor/contrib/links/browser/getLinks.ts | 2 +- src/vs/editor/contrib/links/browser/links.ts | 18 +- .../message/browser/messageController.ts | 12 +- .../multicursor/browser/multicursor.ts | 20 +- .../test/browser/multicursor.test.ts | 54 +- .../browser/parameterHintsWidget.ts | 12 +- .../contrib/peekView/browser/peekView.ts | 6 +- .../browser/gotoSymbolQuickAccess.ts | 23 +- .../readOnlyMessage/browser/contribution.ts | 36 + .../editor/contrib/rename/browser/rename.ts | 4 +- .../rename/browser/renameInputField.ts | 8 +- .../smartSelect/browser/bracketSelections.ts | 16 +- .../smartSelect/browser/smartSelect.ts | 12 +- .../smartSelect/browser/wordSelections.ts | 8 +- .../test/browser/smartSelect.test.ts | 24 +- .../snippet/browser/snippetController2.ts | 55 +- .../contrib/snippet/browser/snippetParser.ts | 68 +- .../contrib/snippet/browser/snippetSession.ts | 133 +- .../snippet/browser/snippetVariables.ts | 4 +- .../browser/snippetController2.old.test.ts | 4 +- .../test/browser/snippetController2.test.ts | 167 +- .../test/browser/snippetParser.test.ts | 57 +- .../test/browser/snippetSession.test.ts | 63 +- .../test/browser/snippetVariables.test.ts | 3 +- .../stickyScroll/browser/stickyScroll.ts | 408 + .../suggest/browser/completionModel.ts | 8 +- .../editor/contrib/suggest/browser/suggest.ts | 22 +- .../suggest/browser/suggestAlternatives.ts | 2 +- .../suggest/browser/suggestController.ts | 57 +- .../contrib/suggest/browser/suggestMemory.ts | 18 +- .../contrib/suggest/browser/suggestModel.ts | 33 +- .../contrib/suggest/browser/suggestWidget.ts | 24 +- .../suggest/browser/suggestWidgetDetails.ts | 11 +- .../suggest/browser/suggestWidgetRenderer.ts | 10 +- .../suggest/browser/suggestWidgetStatus.ts | 2 +- .../contrib/suggest/browser/wordDistance.ts | 8 +- .../test/browser/completionModel.test.ts | 19 +- .../test/browser/suggestController.test.ts | 34 +- .../test/browser/suggestMemory.test.ts | 24 +- .../suggest/test/browser/suggestModel.test.ts | 99 +- .../suggest/test/browser/wordDistance.test.ts | 6 +- .../browser/bannerController.ts | 4 +- .../browser/unicodeHighlighter.ts | 63 +- .../browser/wordHighlighter.ts | 71 +- .../wordOperations/browser/wordOperations.ts | 4 +- .../test/browser/wordTestUtils.ts | 4 +- .../browser/wordPartOperations.ts | 4 +- .../contrib/zoneWidget/browser/zoneWidget.ts | 62 +- src/vs/editor/editor.all.ts | 4 + src/vs/editor/standalone/browser/colorizer.ts | 3 +- .../browser/inspectTokens/inspectTokens.ts | 3 +- .../standaloneCommandsQuickAccess.ts | 16 +- .../standaloneGotoLineQuickAccess.ts | 16 +- .../standaloneGotoSymbolQuickAccess.ts | 24 +- .../quickAccess/standaloneHelpQuickAccess.ts | 2 +- .../quickInput/standaloneQuickInputService.ts | 3 +- .../browser/standaloneCodeEditor.ts | 10 +- .../browser/standaloneCodeEditorService.ts | 16 +- .../standalone/browser/standaloneEditor.ts | 41 +- .../standalone/browser/standaloneLanguages.ts | 16 +- .../browser/standaloneLayoutService.ts | 5 +- .../standalone/browser/standaloneServices.ts | 45 +- .../browser/standaloneThemeService.ts | 70 +- .../toggleHighContrast/toggleHighContrast.ts | 12 +- .../common/monarch/monarchCompile.ts | 6 +- .../standalone/common/monarch/monarchLexer.ts | 31 +- .../standalone/test/browser/monarch.test.ts | 59 +- .../test/browser/standaloneLanguages.test.ts | 3 +- .../browser/commands/shiftCommand.test.ts | 20 +- .../test/browser/commands/sideEditing.test.ts | 2 +- .../trimTrailingWhitespaceCommand.test.ts | 6 +- .../config/editorConfiguration.test.ts | 54 +- .../config/editorLayoutProvider.test.ts | 1 + .../test/browser/controller/cursor.test.ts | 122 +- .../test/browser/controller/imeRecorder.ts | 2 +- .../test/browser/controller/imeTester.ts | 34 +- .../browser/controller/textAreaInput.test.ts | 4 +- .../browser/controller/textAreaState.test.ts | 18 +- .../editor/test/browser/editorTestServices.ts | 2 +- .../services/decorationRenderOptions.test.ts | 6 +- .../browser/services/openerService.test.ts | 2 +- src/vs/editor/test/browser/testCommand.ts | 4 +- .../browser/view/minimapCharRenderer.test.ts | 22 +- .../test/browser/view/viewLayer.test.ts | 18 +- .../viewModel/modelLineProjection.test.ts | 57 +- .../viewModel/viewModelDecorations.test.ts | 18 +- .../browser/viewModel/viewModelImpl.test.ts | 6 +- .../browser/widget/codeEditorWidget.test.ts | 4 +- .../common/core/characterClassifier.test.ts | 2 +- .../test/common/core/lineTokens.test.ts | 6 +- src/vs/editor/test/common/core/range.test.ts | 10 +- .../editor/test/common/core/testLineToken.ts | 4 +- .../test/common/diff/diffComputer.test.ts | 556 +- .../beforeEditPositionMapper.test.ts | 2 +- .../bracketPairColorizer/brackets.test.ts | 2 +- .../concat23Trees.test.ts | 2 +- .../getBracketPairsInRange.test.ts | 6 +- .../model/bracketPairColorizer/length.test.ts | 2 +- .../smallImmutableSet.test.ts | 2 +- .../bracketPairColorizer/tokenizer.test.ts | 5 +- .../common/model/editableTextModel.test.ts | 18 +- .../model/editableTextModelAuto.test.ts | 34 +- .../model/editableTextModelTestUtils.ts | 26 +- .../test/common/model/intervalTree.test.ts | 54 +- .../linesTextBuffer/linesTextBuffer.test.ts | 2 +- .../textBufferAutoTestUtils.ts | 54 +- .../test/common/model/model.line.test.ts | 20 +- src/vs/editor/test/common/model/model.test.ts | 7 +- .../common/model/modelDecorations.test.ts | 64 +- .../common/model/modelEditOperation.test.ts | 8 +- .../pieceTreeTextBuffer.test.ts | 244 +- .../test/common/model/textChange.test.ts | 46 +- .../test/common/model/textModel.test.ts | 91 +- .../test/common/model/textModelSearch.test.ts | 90 +- .../common/model/textModelWithTokens.test.ts | 65 +- .../test/common/model/tokensStore.test.ts | 22 +- .../modes/languageConfiguration.test.ts | 22 +- .../common/modes/languageSelector.test.ts | 152 +- .../test/common/modes/linkComputer.test.ts | 20 +- .../modes/supports/characterPair.test.ts | 16 +- .../modes/supports/electricCharacter.test.ts | 10 +- .../common/modes/supports/onEnter.test.ts | 24 +- .../modes/supports/richEditBrackets.test.ts | 20 +- .../modes/supports/tokenization.test.ts | 54 +- .../common/modes/textToHtmlTokenizer.test.ts | 33 +- src/vs/editor/test/common/modesTestUtils.ts | 10 +- .../services/editorSimpleWorker.test.ts | 20 +- .../test/common/services/modelService.test.ts | 58 +- .../semanticTokensProviderStyling.test.ts | 94 +- src/vs/editor/test/common/testTextModel.ts | 55 +- .../viewLayout/viewLineRenderer.test.ts | 2135 +++-- .../common/viewModel/lineBreakData.test.ts | 2 +- .../monospaceLineBreaksComputer.test.ts | 11 +- .../node/classification/typescript.test.ts | 2 +- src/vs/loader.d.ts | 35 + src/vs/loader.js | 26 +- src/vs/monaco.d.ts | 445 +- src/vs/nls.build.ts | 84 + src/vs/nls.js | 9 +- src/vs/nls.mock.ts | 4 + src/vs/nls.ts | 228 + .../dropdownWithPrimaryActionViewItem.ts | 10 +- .../browser/menuEntryActionViewItem.css | 20 - .../browser/menuEntryActionViewItem.ts | 128 +- .../actions/common/actions.contribution.ts | 14 + src/vs/platform/actions/common/actions.ts | 66 +- .../actions/common/menuResetAction.ts | 29 + src/vs/platform/actions/common/menuService.ts | 212 +- .../actions/test/common/menuService.test.ts | 15 +- .../platform/assignment/common/assignment.ts | 14 +- .../backup/electron-main/backupMainService.ts | 6 +- src/vs/platform/commands/common/commands.ts | 6 +- .../configuration/common/configuration.ts | 14 +- .../common/configurationModels.ts | 154 +- .../common/configurationRegistry.ts | 91 +- .../common/configurationService.ts | 36 +- .../configuration/common/configurations.ts | 190 + .../test/common/configuration.test.ts | 26 +- .../test/common/configurationModels.test.ts | 144 +- .../test/common/configurationRegistry.test.ts | 26 + .../test/common/configurationService.test.ts | 24 +- .../test/common/policyConfiguration.test.ts | 206 + .../contextkey/browser/contextKeyService.ts | 63 +- .../platform/contextkey/common/contextkey.ts | 84 +- .../platform/contextkey/common/contextkeys.ts | 1 + .../test/browser/contextkey.test.ts | 99 +- .../contextkey/test/common/contextkey.test.ts | 31 +- .../contextview/browser/contextMenuHandler.ts | 15 +- .../contextview/browser/contextMenuService.ts | 4 +- .../contextview/browser/contextViewService.ts | 5 +- .../diagnostics/node/diagnosticsService.ts | 16 +- src/vs/platform/dialogs/common/dialogs.ts | 5 + .../electron-main/dialogMainService.ts | 3 +- src/vs/platform/dnd/browser/dnd.ts | 345 + .../download/common/downloadService.ts | 2 +- src/vs/platform/driver/browser/driver.ts | 2 +- src/vs/platform/environment/common/argv.ts | 5 +- .../environment/common/environment.ts | 12 +- .../environment/common/environmentService.ts | 31 +- .../electron-main/environmentMainService.ts | 4 - src/vs/platform/environment/node/argv.ts | 70 +- .../platform/environment/node/argvHelper.ts | 3 + src/vs/platform/environment/node/stdin.ts | 23 +- .../abstractExtensionManagementService.ts | 190 +- .../common/extensionEnablementService.ts | 16 +- .../common/extensionGalleryService.ts | 128 +- .../common/extensionManagement.ts | 80 +- .../common/extensionManagementCLIService.ts | 12 +- .../common/extensionManagementIpc.ts | 72 +- .../common/extensionManagementUtil.ts | 14 +- .../common/extensionNls.ts | 4 +- .../common/extensionStorage.ts | 25 +- .../common/extensionTipsService.ts | 5 +- .../common/extensionsProfileScannerService.ts | 129 + .../common/extensionsScannerService.ts | 313 +- .../common/unsupportedExtensionsMigration.ts | 15 +- .../defaultExtensionsProfileInit.ts | 27 + .../defaultExtensionsProfileInit.ts | 45 + .../electron-sandbox/extensionTipsService.ts | 20 +- .../extensionsScannerService.ts | 8 +- .../node/extensionLifecycle.ts | 6 +- .../node/extensionManagementService.ts | 262 +- .../node/extensionsScannerService.ts | 8 +- .../common/extensionGalleryService.test.ts | 5 +- .../node/extensionsScannerService.test.ts | 14 +- .../extensions/common/extensionHostStarter.ts | 8 +- .../extensions/common/extensionValidator.ts | 24 +- .../platform/extensions/common/extensions.ts | 8 +- .../electron-main/extensionHostStarter.ts | 417 + .../workerMainProcessExtensionHostStarter.ts | 173 - .../node/extensionHostStarterWorker.ts | 243 - .../node/extensionHostStarterWorkerMain.ts | 66 - .../test/common/extensionValidator.test.ts | 6 +- .../externalServices/common/marketplace.ts | 13 +- .../common/serviceMachineId.ts | 8 +- .../node/externalTerminalService.ts | 8 +- .../files/browser/htmlFileSystemProvider.ts | 2 +- .../browser/indexedDBFileSystemProvider.ts | 203 +- src/vs/platform/files/common/files.ts | 3 +- .../common/inMemoryFilesystemProvider.ts | 38 +- src/vs/platform/files/common/watcher.ts | 27 +- .../files/node/diskFileSystemProvider.ts | 11 +- .../node/watcher/nodejs/nodejsWatcher.ts | 2 +- .../node/watcher/nodejs/nodejsWatcherLib.ts | 2 +- .../node/watcher/parcel/parcelWatcher.ts | 38 +- ...> indexedDBFileService.integrationTest.ts} | 94 +- .../files/test/common/watcher.test.ts | 2 +- .../files/test/node/diskFileService.test.ts | 21 +- .../node/nodejsWatcher.integrationTest.ts | 4 +- .../node/parcelWatcher.integrationTest.ts | 24 +- .../browser/contextScopedHistoryWidget.ts | 92 +- src/vs/platform/instantiation/common/graph.ts | 12 +- .../common/instantiationService.ts | 36 +- .../instantiation/common/serviceCollection.ts | 2 +- .../instantiation/test/common/graph.test.ts | 2 +- .../test/common/instantiationService.test.ts | 66 +- .../test/common/instantiationServiceMock.ts | 53 +- src/vs/platform/issue/common/issue.ts | 1 + .../issue/electron-main/issueMainService.ts | 4 +- .../common/abstractKeybindingService.ts | 11 +- .../platform/keybinding/common/keybinding.ts | 12 +- .../keybinding/common/keybindingResolver.ts | 17 +- .../keybinding/common/keybindingsRegistry.ts | 3 +- .../common/resolvedKeybindingItem.ts | 2 +- .../common/abstractKeybindingService.test.ts | 29 +- .../test/common/mockKeybindingService.ts | 17 +- .../keyboardLayout/common/keyboardLayout.ts | 6 +- .../keyboardLayoutMainService.ts | 14 +- src/vs/platform/label/common/label.ts | 7 + .../languagePacks/browser/languagePacks.ts | 13 + .../languagePacks/common/languagePacks.ts | 91 + .../common/localizedStrings.ts | 0 .../node/languagePacks.ts} | 67 +- .../launch/electron-main/launchMainService.ts | 1 + .../platform/layout/browser/layoutService.ts | 13 +- src/vs/platform/lifecycle/common/lifecycle.ts | 2 +- .../electron-main/lifecycleMainService.ts | 2 +- src/vs/platform/list/browser/listService.ts | 231 +- src/vs/platform/log/common/bufferLog.ts | 4 +- src/vs/platform/log/node/spdlogLog.ts | 4 +- .../platform/markers/common/markerService.ts | 18 +- src/vs/platform/markers/common/markers.ts | 2 +- .../markers/test/common/markerService.test.ts | 24 +- .../platform/menubar/electron-main/menubar.ts | 8 +- src/vs/platform/native/common/native.ts | 9 +- .../electron-main/nativeHostMainService.ts | 38 +- .../notification/common/notification.ts | 33 +- .../test/common/testNotificationService.ts | 8 +- src/vs/platform/opener/common/opener.ts | 3 +- .../opener/test/common/opener.test.ts | 2 +- .../policy/common/filePolicyService.ts | 82 + src/vs/platform/policy/common/policy.ts | 64 + src/vs/platform/policy/common/policyIpc.ts | 76 + .../policy/node/nativePolicyService.ts | 44 + src/vs/platform/progress/common/progress.ts | 1 + .../electron-main/protocolMainService.ts | 4 +- .../quickinput/browser/commandsQuickAccess.ts | 8 +- .../quickinput/browser/helpQuickAccess.ts | 38 +- .../platform/quickinput/browser/quickInput.ts | 8 +- .../platform/quickinput/common/quickAccess.ts | 4 +- .../registry/test/common/platform.test.ts | 2 +- .../remote/browser/browserSocketFactory.ts | 4 +- .../browser/remoteAuthorityResolverService.ts | 14 +- .../remote/common/remoteAgentConnection.ts | 72 +- src/vs/platform/remote/common/remoteHosts.ts | 48 + .../remoteAuthorityResolverService.ts | 10 +- .../platform/remote/node/nodeSocketFactory.ts | 4 +- .../remote/test/common/remoteHosts.test.ts | 42 + .../remoteAuthorityResolverService.test.ts | 5 +- src/vs/platform/request/common/request.ts | 2 +- src/vs/platform/request/common/requestIpc.ts | 17 +- .../sharedProcessRequestService.ts | 47 + .../platform/request/node/requestService.ts | 7 +- .../electron-main/sharedProcess.ts | 8 +- .../sharedProcess/node/sharedProcess.ts | 8 + src/vs/platform/state/electron-main/state.ts | 6 +- .../state/electron-main/stateMainService.ts | 179 +- src/vs/platform/state/node/state.ts | 17 + src/vs/platform/state/node/stateService.ts | 171 + .../state/test/electron-main/state.test.ts | 4 +- src/vs/platform/storage/common/storage.ts | 239 +- src/vs/platform/storage/common/storageIpc.ts | 92 +- .../storage/electron-main/storageIpc.ts | 65 +- .../storage/electron-main/storageMain.ts | 76 +- .../electron-main/storageMainService.ts | 206 +- .../electron-sandbox/storageService.ts | 147 +- .../test/common/storageService.test.ts | 56 +- .../electron-main/storageMainService.test.ts | 133 +- .../platform/telemetry/browser/1dsAppender.ts | 5 +- .../telemetry/browser/errorTelemetry.ts | 16 +- .../platform/telemetry/common/1dsAppender.ts | 20 +- .../telemetry/common/commonProperties.ts | 7 +- .../telemetry/common/errorTelemetry.ts | 10 +- src/vs/platform/telemetry/common/telemetry.ts | 10 +- .../telemetry/common/telemetryService.ts | 16 +- .../telemetry/common/telemetryUtils.ts | 28 +- src/vs/platform/telemetry/node/1dsAppender.ts | 5 +- .../test/browser/1dsAppender.test.ts | 18 +- .../test/browser/telemetryService.test.ts | 202 +- .../common/capabilities/capabilities.ts | 74 +- .../commandDetectionCapability.ts | 174 +- .../terminal/common/environmentVariable.ts | 3 + src/vs/platform/terminal/common/terminal.ts | 84 +- .../terminal/common/terminalDataBuffering.ts | 4 +- .../terminal/common/terminalEnvironment.ts | 23 + .../common/terminalPlatformConfiguration.ts | 42 +- .../terminal/common/terminalProcess.ts | 18 +- .../terminal/common/terminalProfiles.ts | 2 +- .../terminal/common/terminalStrings.ts | 34 + .../common/xterm/shellIntegrationAddon.ts | 137 +- src/vs/platform/terminal/node/ptyHostMain.ts | 22 +- .../platform/terminal/node/ptyHostService.ts | 6 +- src/vs/platform/terminal/node/ptyService.ts | 129 +- .../terminal/node/terminalEnvironment.ts | 30 +- .../platform/terminal/node/terminalProcess.ts | 13 +- .../terminal/node/terminalProfiles.ts | 3 +- .../test/common/terminalEnvironment.test.ts | 40 + .../test/node/terminalEnvironment.test.ts | 33 +- .../platform/theme/browser/iconsStyleSheet.ts | 4 +- src/vs/platform/theme/common/colorRegistry.ts | 49 +- src/vs/platform/theme/common/iconRegistry.ts | 31 +- src/vs/platform/theme/common/styler.ts | 30 +- src/vs/platform/theme/common/theme.ts | 4 + src/vs/platform/theme/common/themeService.ts | 4 +- .../common/tokenClassificationRegistry.ts | 14 +- .../theme/electron-main/themeMainService.ts | 3 +- .../theme/test/common/testThemeService.ts | 4 +- src/vs/platform/tunnel/common/tunnel.ts | 46 +- src/vs/platform/tunnel/node/tunnelService.ts | 3 +- .../undoRedo/common/undoRedoService.ts | 6 +- .../common/update.config.contribution.ts | 6 +- src/vs/platform/update/common/update.ts | 1 + src/vs/platform/update/common/updateIpc.ts | 5 + .../electron-main/abstractUpdateService.ts | 12 +- .../electron-main/updateService.darwin.ts | 4 +- .../electron-main/updateService.snap.ts | 5 + .../electron-main/updateService.win32.ts | 51 +- .../uriIdentity/common/uriIdentityService.ts | 2 +- .../test/common/uriIdentityService.test.ts | 22 +- .../userData/common/fileUserDataProvider.ts | 14 +- .../test/browser/fileUserDataProvider.test.ts | 54 +- .../browser/userDataProfile.ts | 88 + .../userDataProfile/common/userDataProfile.ts | 400 + .../electron-main/userDataProfile.ts | 64 + .../electron-sandbox/userDataProfile.ts | 70 + .../userDataProfile/node/userDataProfile.ts | 35 + .../common/userDataProfileService.test.ts | 65 + .../userDataProfileMainService.test.ts | 76 + .../common/abstractSynchronizer.ts | 33 +- .../userDataSync/common/extensionsMerge.ts | 4 +- .../userDataSync/common/extensionsSync.ts | 18 +- .../userDataSync/common/globalStateSync.ts | 44 +- .../userDataSync/common/keybindingsMerge.ts | 2 +- .../userDataSync/common/keybindingsSync.ts | 29 +- .../userDataSync/common/settingsSync.ts | 30 +- .../userDataSync/common/snippetsSync.ts | 52 +- .../platform/userDataSync/common/tasksSync.ts | 27 +- .../common/userDataAutoSyncService.ts | 36 +- .../userDataSync/common/userDataSync.ts | 7 +- .../common/userDataSyncEnablementService.ts | 14 +- .../common/userDataSyncMachines.ts | 4 +- .../common/userDataSyncService.ts | 23 +- .../common/userDataSyncServiceIpc.ts | 2 + .../common/userDataSyncStoreService.ts | 40 +- .../test/common/globalStateSync.test.ts | 8 +- .../test/common/keybindingsSync.test.ts | 37 +- .../test/common/settingsSync.test.ts | 25 +- .../test/common/snippetsSync.test.ts | 17 +- .../test/common/synchronizer.test.ts | 2 + .../test/common/tasksSync.test.ts | 66 +- .../common/userDataAutoSyncService.test.ts | 601 +- .../test/common/userDataSyncClient.ts | 29 +- .../test/common/userDataSyncService.test.ts | 61 +- .../common/userDataSyncStoreService.test.ts | 12 +- .../electron-main/webviewProtocolProvider.ts | 1 - src/vs/platform/window/common/window.ts | 30 +- .../platform/window/electron-main/window.ts | 6 +- .../platform/windows/electron-main/window.ts | 175 +- .../platform/windows/electron-main/windows.ts | 2 + .../electron-main/windowsMainService.ts | 65 +- .../test/electron-main/windowsFinder.test.ts | 2 +- .../workspace/test/common/workspace.test.ts | 10 +- .../platform/workspaces/common/workspaces.ts | 12 +- .../workspacesHistoryMainService.ts | 35 +- .../workspacesManagementMainService.ts | 7 + .../workspaces/test/common/workspaces.test.ts | 6 +- .../workspacesHistoryStorage.test.ts | 8 +- .../workspacesManagementMainService.test.ts | 12 +- src/vs/server/node/extensionHostConnection.ts | 152 +- .../server/node/extensionsScannerService.ts | 8 +- .../server/node/remoteAgentEnvironmentImpl.ts | 47 +- .../node/remoteExtensionHostAgentCli.ts | 26 +- .../node/remoteExtensionHostAgentServer.ts | 28 +- src/vs/server/node/remoteLanguagePacks.ts | 4 +- src/vs/server/node/remoteTerminalChannel.ts | 3 +- src/vs/server/node/server.cli.ts | 49 +- src/vs/server/node/server.main.ts | 9 +- .../server/node/serverEnvironmentService.ts | 2 + src/vs/server/node/serverServices.ts | 39 +- src/vs/server/node/webClientServer.ts | 30 +- .../api/browser/extensionHost.contribution.ts | 1 - .../api/browser/mainThreadAuthentication.ts | 25 +- .../api/browser/mainThreadBulkEdits.ts | 31 +- .../api/browser/mainThreadCodeInsets.ts | 11 +- .../api/browser/mainThreadCommands.ts | 13 +- .../api/browser/mainThreadComments.ts | 67 +- .../api/browser/mainThreadCustomEditors.ts | 1 + .../api/browser/mainThreadDebugService.ts | 17 +- .../api/browser/mainThreadDecorations.ts | 2 +- .../api/browser/mainThreadDiagnostics.ts | 4 +- .../api/browser/mainThreadDialogs.ts | 9 +- .../api/browser/mainThreadDocuments.ts | 8 +- .../browser/mainThreadDocumentsAndEditors.ts | 8 +- .../workbench/api/browser/mainThreadEditor.ts | 4 +- .../api/browser/mainThreadEditorTabs.ts | 24 +- .../api/browser/mainThreadEditors.ts | 6 +- .../workbench/api/browser/mainThreadErrors.ts | 4 +- .../api/browser/mainThreadExtensionService.ts | 3 - .../api/browser/mainThreadFileSystem.ts | 40 +- .../mainThreadFileSystemEventService.ts | 11 +- .../api/browser/mainThreadLabelService.ts | 2 +- .../api/browser/mainThreadLanguageFeatures.ts | 165 +- .../api/browser/mainThreadLanguages.ts | 2 +- .../api/browser/mainThreadNotebook.ts | 2 +- .../api/browser/mainThreadNotebookDto.ts | 1 + .../api/browser/mainThreadNotebookEditors.ts | 21 +- .../api/browser/mainThreadNotebookKernels.ts | 31 +- .../browser/mainThreadNotebookProxyKernels.ts | 130 - .../api/browser/mainThreadOutputService.ts | 2 +- .../api/browser/mainThreadProgress.ts | 4 +- src/vs/workbench/api/browser/mainThreadSCM.ts | 10 + .../workbench/api/browser/mainThreadSearch.ts | 4 +- .../api/browser/mainThreadStorage.ts | 2 +- .../workbench/api/browser/mainThreadTask.ts | 165 +- .../api/browser/mainThreadTerminalService.ts | 12 +- .../api/browser/mainThreadTesting.ts | 9 +- .../api/browser/mainThreadTreeViews.ts | 53 +- .../api/browser/mainThreadWebviewPanels.ts | 9 +- .../api/browser/mainThreadWebviews.ts | 2 +- .../api/browser/mainThreadWorkspace.ts | 2 +- .../api/browser/viewsExtensionPoint.ts | 58 +- .../api/common/configurationExtensionPoint.ts | 16 +- .../workbench/api/common/extHost.api.impl.ts | 66 +- .../workbench/api/common/extHost.protocol.ts | 159 +- .../api/common/extHostApiCommands.ts | 6 + .../api/common/extHostAuthentication.ts | 6 +- .../workbench/api/common/extHostBulkEdits.ts | 7 +- .../workbench/api/common/extHostCodeInsets.ts | 4 +- .../workbench/api/common/extHostCommands.ts | 23 +- .../workbench/api/common/extHostComments.ts | 47 +- .../api/common/extHostConfiguration.ts | 6 +- .../api/common/extHostConsoleForwarder.ts | 124 + .../api/common/extHostDebugService.ts | 8 +- .../api/common/extHostDecorations.ts | 10 +- .../api/common/extHostDiagnostics.ts | 33 +- .../api/common/extHostDocumentData.ts | 4 +- .../common/extHostDocumentSaveParticipant.ts | 10 +- .../workbench/api/common/extHostEditorTabs.ts | 18 +- .../api/common/extHostExtensionActivator.ts | 8 +- .../api/common/extHostExtensionService.ts | 33 +- .../workbench/api/common/extHostFileSystem.ts | 2 +- .../common/extHostFileSystemEventService.ts | 21 +- .../api/common/extHostInteractive.ts | 7 +- .../api/common/extHostLanguageFeatures.ts | 208 +- .../workbench/api/common/extHostLanguages.ts | 2 +- src/vs/workbench/api/common/extHostMemento.ts | 2 +- .../api/common/extHostMessageService.ts | 2 +- .../api/common/extHostNotebookDocument.ts | 2 +- .../api/common/extHostNotebookEditor.ts | 31 +- .../api/common/extHostNotebookEditors.ts | 33 +- .../api/common/extHostNotebookKernels.ts | 15 +- .../api/common/extHostNotebookProxyKernels.ts | 157 - .../api/common/extHostNotebookRenderers.ts | 18 +- .../workbench/api/common/extHostQuickOpen.ts | 32 +- .../api/common/extHostRequireInterceptor.ts | 14 +- src/vs/workbench/api/common/extHostSCM.ts | 29 +- src/vs/workbench/api/common/extHostTask.ts | 120 +- .../api/common/extHostTerminalService.ts | 22 +- src/vs/workbench/api/common/extHostTesting.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 34 +- .../api/common/extHostTypeConverters.ts | 138 +- src/vs/workbench/api/common/extHostTypes.ts | 265 +- .../workbench/api/common/extHostWorkspace.ts | 6 +- .../common/jsonValidationExtensionPoint.ts | 2 +- .../api/common/shared/dataTransfer.ts | 43 - .../api/common/shared/dataTransferCache.ts | 42 + src/vs/workbench/api/common/shared/tasks.ts | 71 +- src/vs/workbench/api/node/extHostCLIServer.ts | 5 +- .../api/node/extHostConsoleForwarder.ts | 64 + .../workbench/api/node/extHostDebugService.ts | 2 +- .../api/node/extHostExtensionService.ts | 21 +- src/vs/workbench/api/node/extHostSearch.ts | 4 +- .../workbench/api/node/extHostStoragePaths.ts | 4 +- src/vs/workbench/api/node/extHostTask.ts | 12 +- .../api/node/extHostTunnelService.ts | 23 +- .../api/node/extensionHostProcess.ts | 119 +- src/vs/workbench/api/node/proxyResolver.ts | 2 +- .../test/browser/extHostApiCommands.test.ts | 100 +- .../browser/extHostAuthentication.test.ts | 96 +- .../api/test/browser/extHostBulkEdits.test.ts | 20 +- .../api/test/browser/extHostCommands.test.ts | 24 + .../test/browser/extHostConfiguration.test.ts | 67 +- .../test/browser/extHostDecorations.test.ts | 2 +- .../test/browser/extHostDiagnostics.test.ts | 72 +- .../test/browser/extHostDocumentData.test.ts | 43 +- .../extHostDocumentSaveParticipant.test.ts | 58 +- .../test/browser/extHostEditorTabs.test.ts | 41 +- .../browser/extHostLanguageFeatures.test.ts | 85 +- .../browser/extHostMessagerService.test.ts | 15 +- .../api/test/browser/extHostNotebook.test.ts | 20 +- .../browser/extHostNotebookKernel.test.ts | 6 +- .../api/test/browser/extHostTesting.test.ts | 8 +- .../test/browser/extHostTextEditor.test.ts | 8 +- .../api/test/browser/extHostTreeViews.test.ts | 23 +- .../test/browser/extHostTypeConverter.test.ts | 15 +- .../api/test/browser/extHostTypes.test.ts | 72 +- .../api/test/browser/extHostWorkspace.test.ts | 12 +- .../browser/mainThreadConfiguration.test.ts | 2 +- .../browser/mainThreadDiagnostics.test.ts | 6 +- ...mainThreadDocumentContentProviders.test.ts | 6 +- .../test/browser/mainThreadDocuments.test.ts | 4 +- .../mainThreadDocumentsAndEditors.test.ts | 2 +- .../test/browser/mainThreadEditors.test.ts | 41 +- .../test/browser/mainThreadTreeViews.test.ts | 2 +- .../api/test/common/testRPCProtocol.ts | 8 +- .../api/test/node/extHostSearch.test.ts | 4 +- .../api/worker/extHostConsoleForwarder.ts | 22 + .../api/worker/extHostExtensionService.ts | 85 +- src/vs/workbench/browser/actions.ts | 2 +- .../browser/actions/developerActions.ts | 6 +- .../browser/actions/layoutActions.ts | 34 +- .../workbench/browser/actions/listCommands.ts | 54 +- .../browser/actions/quickAccessActions.ts | 5 +- .../browser/actions/windowActions.ts | 4 +- src/vs/workbench/browser/codeeditor.ts | 9 +- src/vs/workbench/browser/composite.ts | 4 +- src/vs/workbench/browser/contextkeys.ts | 7 +- src/vs/workbench/browser/dnd.ts | 376 +- src/vs/workbench/browser/labels.ts | 25 +- src/vs/workbench/browser/layout.ts | 157 +- src/vs/workbench/browser/layoutState.ts | 38 +- src/vs/workbench/browser/media/style.css | 48 +- src/vs/workbench/browser/panecomposite.ts | 2 +- src/vs/workbench/browser/part.ts | 2 +- .../parts/activitybar/activitybarActions.ts | 10 +- .../parts/activitybar/activitybarPart.ts | 39 +- .../parts/auxiliarybar/auxiliaryBarActions.ts | 18 +- .../browser/parts/banner/media/bannerpart.css | 2 + .../workbench/browser/parts/compositeBar.ts | 10 +- .../workbench/browser/parts/compositePart.ts | 12 +- .../browser/parts/editor/breadcrumbs.ts | 4 +- .../parts/editor/breadcrumbsControl.ts | 46 +- .../browser/parts/editor/breadcrumbsModel.ts | 15 +- .../browser/parts/editor/breadcrumbsPicker.ts | 2 +- .../parts/editor/editor.contribution.ts | 46 +- .../workbench/browser/parts/editor/editor.ts | 2 - .../browser/parts/editor/editorActions.ts | 104 +- .../browser/parts/editor/editorCommands.ts | 16 +- .../browser/parts/editor/editorDropTarget.ts | 21 +- .../browser/parts/editor/editorGroupView.ts | 80 +- .../browser/parts/editor/editorPane.ts | 2 +- .../browser/parts/editor/editorPart.ts | 40 +- .../browser/parts/editor/editorPlaceholder.ts | 8 +- .../browser/parts/editor/editorStatus.ts | 4 +- .../parts/editor/editorWithViewState.ts | 4 +- .../parts/editor/media/editorplaceholder.css | 4 +- .../parts/editor/media/notabstitlecontrol.css | 2 +- .../parts/editor/media/titlecontrol.css | 8 +- .../browser/parts/editor/tabsTitleControl.ts | 24 +- .../browser/parts/editor/textCodeEditor.ts | 112 + .../browser/parts/editor/textDiffEditor.ts | 149 +- .../browser/parts/editor/textEditor.ts | 191 +- .../parts/editor/textResourceEditor.ts | 65 +- .../notifications/notificationsActions.ts | 21 +- .../notifications/notificationsCenter.ts | 20 +- .../notifications/notificationsCommands.ts | 12 +- .../notifications/notificationsStatus.ts | 19 +- .../notifications/notificationsToasts.ts | 6 +- .../parts/panel/media/basepanelpart.css | 4 +- .../browser/parts/panel/panelActions.ts | 13 +- .../browser/parts/panel/panelPart.ts | 28 +- .../parts/statusbar/media/statusbarpart.css | 21 +- .../browser/parts/statusbar/statusbarItem.ts | 23 +- .../browser/parts/statusbar/statusbarModel.ts | 18 +- .../browser/parts/statusbar/statusbarPart.ts | 6 +- .../parts/titlebar/commandCenterControl.ts | 199 + .../parts/titlebar/media/titlebarpart.css | 158 +- .../browser/parts/titlebar/menubarControl.ts | 63 +- .../browser/parts/titlebar/titlebarPart.ts | 496 +- .../browser/parts/titlebar/windowTitle.ts | 249 + .../workbench/browser/parts/views/treeView.ts | 312 +- .../workbench/browser/parts/views/viewPane.ts | 6 +- .../browser/parts/views/viewPaneContainer.ts | 11 +- .../browser/parts/views/viewsViewlet.ts | 2 +- src/vs/workbench/browser/web.api.ts | 31 +- src/vs/workbench/browser/web.factory.ts | 24 +- src/vs/workbench/browser/web.main.ts | 100 +- src/vs/workbench/browser/window.ts | 26 +- .../browser/workbench.contribution.ts | 51 +- src/vs/workbench/browser/workbench.ts | 15 +- src/vs/workbench/common/actions.ts | 2 +- src/vs/workbench/common/configuration.ts | 91 + src/vs/workbench/common/contextkeys.ts | 20 +- src/vs/workbench/common/contributions.ts | 4 +- src/vs/workbench/common/editor.ts | 87 +- .../common/editor/editorGroupModel.ts | 4 +- src/vs/workbench/common/editor/editorInput.ts | 45 +- .../common/editor/sideBySideEditorInput.ts | 10 +- .../common/editor/textResourceEditorInput.ts | 4 +- src/vs/workbench/common/memento.ts | 82 +- src/vs/workbench/common/theme.ts | 6 +- src/vs/workbench/common/views.ts | 6 +- .../browser/audioCueDebuggerContribution.ts | 2 +- .../audioCueLineFeatureContribution.ts | 35 +- .../audioCues/browser/audioCueService.ts | 12 +- .../contrib/audioCues/browser/observable.ts | 614 -- ...ketPairColorizer2Telemetry.contribution.ts | 55 + .../contrib/bulkEdit/browser/bulkCellEdits.ts | 28 +- .../bulkEdit/browser/bulkEditService.ts | 12 +- .../contrib/bulkEdit/browser/bulkFileEdits.ts | 2 +- .../contrib/bulkEdit/browser/bulkTextEdits.ts | 60 +- .../contrib/bulkEdit/browser/conflicts.ts | 4 +- .../browser/preview/bulkEdit.contribution.ts | 17 +- .../bulkEdit/browser/preview/bulkEditPane.ts | 12 +- .../browser/preview/bulkEditPreview.ts | 14 +- .../bulkEdit/browser/preview/bulkEditTree.ts | 34 +- .../browser/callHierarchy.contribution.ts | 4 +- .../browser/callHierarchyPeek.ts | 12 +- .../browser/callHierarchyTree.ts | 3 +- .../browser/accessibility/accessibility.ts | 4 +- .../browser/editorSettingsMigration.ts | 75 +- .../browser/find/simpleFindWidget.css | 1 - .../browser/find/simpleFindWidget.ts | 31 +- .../inspectEditorTokens.ts | 45 +- .../languageConfigurationExtensionPoint.ts | 22 +- .../browser/outline/documentSymbolsOutline.ts | 19 +- .../quickaccess/gotoLineQuickAccess.ts | 25 +- .../quickaccess/gotoSymbolQuickAccess.ts | 32 +- .../codeEditor/browser/saveParticipants.ts | 11 +- .../suggestEnabledInput.ts | 48 +- .../codeEditor/browser/toggleMinimap.ts | 2 +- .../browser/untitledTextEditorHint.ts | 97 +- .../electron-sandbox/selectionClipboard.ts | 8 +- .../test/browser/saveParticipant.test.ts | 8 +- .../comments/browser/commentFormActions.ts | 10 +- .../comments/browser/commentGlyphWidget.ts | 16 +- .../contrib/comments/browser/commentNode.ts | 55 +- .../contrib/comments/browser/commentReply.ts | 41 +- .../comments/browser/commentService.ts | 41 +- .../comments/browser/commentThreadBody.ts | 26 +- .../comments/browser/commentThreadHeader.ts | 2 +- .../browser/commentThreadRangeDecorator.ts | 63 +- .../comments/browser/commentThreadWidget.ts | 13 +- .../browser/commentThreadZoneWidget.ts | 30 +- .../browser/commentsEditorContribution.ts | 217 +- .../comments/browser/commentsTreeViewer.ts | 23 +- .../contrib/comments/browser/commentsView.ts | 10 +- .../contrib/comments/browser/media/review.css | 9 +- .../comments/browser/reactionsAction.ts | 10 +- .../comments/browser/simpleCommentEditor.ts | 4 +- .../contrib/comments/common/commentModel.ts | 2 +- .../configurationExportHelper.ts | 8 +- .../customEditor/browser/customEditorInput.ts | 26 +- .../browser/customEditorInputFactory.ts | 26 +- .../customEditor/browser/customEditors.ts | 25 +- .../common/contributedCustomEditors.ts | 4 +- .../customEditor/common/extensionPoint.ts | 23 +- .../contrib/debug/browser/baseDebugView.ts | 30 +- .../browser/breakpointEditorContribution.ts | 153 +- .../contrib/debug/browser/breakpointWidget.ts | 2 +- .../contrib/debug/browser/breakpointsView.ts | 173 +- .../browser/callStackEditorContribution.ts | 6 +- .../contrib/debug/browser/callStackView.ts | 13 +- .../debug/browser/debug.contribution.ts | 57 +- .../debug/browser/debugAdapterManager.ts | 97 +- .../contrib/debug/browser/debugCommands.ts | 378 +- .../browser/debugConfigurationManager.ts | 41 +- .../debug/browser/debugConsoleQuickAccess.ts | 68 + .../debug/browser/debugEditorActions.ts | 81 +- .../debug/browser/debugEditorContribution.ts | 11 +- .../contrib/debug/browser/debugHover.ts | 10 +- .../contrib/debug/browser/debugIcons.ts | 1 + .../contrib/debug/browser/debugQuickAccess.ts | 8 +- .../contrib/debug/browser/debugService.ts | 29 +- .../contrib/debug/browser/debugSession.ts | 17 +- .../debug/browser/debugSessionPicker.ts | 124 + .../contrib/debug/browser/debugStatus.ts | 4 +- .../contrib/debug/browser/debugTaskRunner.ts | 14 +- .../contrib/debug/browser/debugToolBar.ts | 18 +- .../contrib/debug/browser/debugViewlet.ts | 2 +- .../contrib/debug/browser/disassemblyView.ts | 49 +- .../browser/extensionHostDebugService.ts | 6 +- .../contrib/debug/browser/linkDetector.ts | 21 +- .../debug/browser/loadedScriptsView.ts | 5 +- .../browser/media/debug.contribution.css | 22 +- .../debug/browser/media/debugViewlet.css | 13 +- .../debug/browser/media/exceptionWidget.css | 2 + .../contrib/debug/browser/media/repl.css | 12 +- .../workbench/contrib/debug/browser/repl.ts | 7 +- .../contrib/debug/browser/replViewer.ts | 1 + .../contrib/debug/browser/variablesView.ts | 5 +- .../debug/browser/watchExpressionsView.ts | 46 +- .../contrib/debug/browser/welcomeView.ts | 2 +- .../debug/common/abstractDebugAdapter.ts | 8 +- .../workbench/contrib/debug/common/debug.ts | 38 +- .../debug/common/debugContentProvider.ts | 4 +- .../contrib/debug/common/debugModel.ts | 46 +- .../contrib/debug/common/debugProtocol.d.ts | 755 +- .../contrib/debug/common/debugSchemas.ts | 5 + .../contrib/debug/common/debugTelemetry.ts | 2 + .../contrib/debug/common/debugUtils.ts | 4 +- .../contrib/debug/common/debugViewModel.ts | 2 +- .../contrib/debug/common/debugger.ts | 16 +- .../debug/common/loadedScriptsPicker.ts | 108 + .../contrib/debug/node/debugAdapter.ts | 4 +- .../workbench/contrib/debug/node/terminals.ts | 22 +- .../debug/test/browser/baseDebugView.test.ts | 2 +- .../debug/test/browser/breakpoints.test.ts | 23 +- .../contrib/debug/test/browser/mockDebug.ts | 4 + .../test/node/streamDebugAdapter.test.ts | 10 +- .../contrib/debug/test/node/terminals.test.ts | 118 + ...eprecatedExtensionMigrator.contribution.ts | 103 + .../browser/dropIntoEditor.contibution.ts | 151 - .../browser/editSessions.contribution.ts | 601 ++ .../browser/editSessionsContentProvider.ts | 34 + .../editSessions/browser/editSessionsViews.ts | 188 + .../browser/editSessionsWorkbenchService.ts | 388 + .../editSessions/common/editSessions.ts | 81 + .../common/editSessionsLogService.ts | 50 + .../test/browser/editSessions.test.ts | 151 + .../contrib/emmet/browser/emmetActions.ts | 6 +- .../emmet/test/browser/emmetAction.test.ts | 2 +- .../experiments/browser/experimentalPrompt.ts | 4 +- .../experiments/common/experimentService.ts | 46 +- .../experimentService.test.ts | 23 +- .../abstractRuntimeExtensionsEditor.ts | 24 +- .../dynamicWorkspaceRecommendations.ts | 6 +- .../extensions/browser/extensionEditor.ts | 219 +- ...ensionRecommendationNotificationService.ts | 16 +- .../extensionRecommendationsService.ts | 7 +- .../browser/extensions.contribution.ts | 61 +- .../extensions/browser/extensionsActions.ts | 412 +- .../extensions/browser/extensionsCleaner.ts | 19 - .../extensions/browser/extensionsIcons.ts | 1 + .../extensions/browser/extensionsList.ts | 43 +- .../extensions/browser/extensionsViewer.ts | 6 +- .../extensions/browser/extensionsViewlet.ts | 61 +- .../extensions/browser/extensionsViews.ts | 80 +- .../extensions/browser/extensionsWidgets.ts | 199 +- .../browser/extensionsWorkbenchService.ts | 192 +- .../browser/fileBasedRecommendations.ts | 34 +- .../extensions/browser/media/extension.css | 26 +- .../browser/media/extensionActions.css | 2 + .../browser/media/extensionEditor.css | 58 +- .../browser/media/extensionsWidgets.css | 5 - .../extensions/common/extensionQuery.ts | 2 +- .../contrib/extensions/common/extensions.ts | 16 +- .../extensionProfileService.ts | 4 +- .../extensionsAutoProfiler.ts | 24 +- .../electron-sandbox/remoteExtensionsInit.ts | 8 +- .../reportExtensionIssueAction.ts | 6 +- .../runtimeExtensionsEditor.ts | 6 +- .../test/common/extensionQuery.test.ts | 2 +- .../extensionRecommendationsService.test.ts | 37 +- .../extensionsActions.test.ts | 124 +- .../electron-browser/extensionsViews.test.ts | 13 +- .../extensionsWorkbenchService.test.ts | 52 +- .../externalTerminal.contribution.ts | 2 +- .../common/contributedOpeners.ts | 2 +- .../files/browser/editors/textFileEditor.ts | 65 +- .../editors/textFileSaveErrorHandler.ts | 6 +- .../contrib/files/browser/explorerService.ts | 8 +- .../contrib/files/browser/explorerViewlet.ts | 8 +- .../contrib/files/browser/fileActions.ts | 24 +- .../contrib/files/browser/fileCommands.ts | 46 +- .../contrib/files/browser/fileConstants.ts | 1 + .../contrib/files/browser/fileImportExport.ts | 8 +- .../files/browser/files.contribution.ts | 18 +- .../workbench/contrib/files/browser/files.ts | 4 +- .../views/explorerDecorationsProvider.ts | 4 +- .../files/browser/views/explorerView.ts | 58 +- .../files/browser/views/explorerViewer.ts | 105 +- .../files/browser/views/openEditorsView.ts | 13 +- .../contrib/files/common/explorerModel.ts | 18 +- .../workbench/contrib/files/common/files.ts | 3 + .../test/browser/fileEditorInput.test.ts | 4 +- .../format/browser/formatActionsMultiple.ts | 27 +- .../format/browser/formatActionsNone.ts | 5 +- .../contrib/format/browser/formatModified.ts | 2 +- .../browser/inlayHintsAccessibilty.ts | 14 +- .../browser/interactive.contribution.ts | 100 +- .../interactive/browser/interactiveCommon.ts | 5 + .../interactive/browser/interactiveEditor.ts | 147 +- .../browser/interactiveEditorInput.ts | 129 +- .../browser/languageDetection.contribution.ts | 8 +- .../browser/languageStatus.contribution.ts | 26 +- .../contrib/list/browser/list.contribution.ts | 12 +- .../browser/localHistoryCommands.ts | 18 +- .../localization/browser/localeService.ts | 64 + .../browser/localization.contribution.ts | 16 + .../browser/localizationsActions.ts | 93 + .../contrib/localization/common/locale.ts | 15 + .../electron-sandbox/localeService.ts | 147 + .../localization.contribution.ts} | 42 +- .../electron-sandbox}/minimalTranslations.ts | 0 .../browser/localizationsActions.ts | 87 - .../contrib/logs/common/logConstants.ts | 1 + .../contrib/logs/common/logs.contribution.ts | 1 + .../contrib/markers/browser/constants.ts | 31 - .../markers/browser/markers.contribution.ts | 177 +- .../contrib/markers/browser/markers.ts | 29 +- .../markers/browser/markersFileDecorations.ts | 4 +- .../contrib/markers/browser/markersModel.ts | 20 +- .../contrib/markers/browser/markersTable.ts | 562 ++ .../markers/browser/markersTreeViewer.ts | 51 +- .../contrib/markers/browser/markersView.ts | 644 +- .../markers/browser/markersViewActions.ts | 4 +- .../contrib/markers/browser/media/markers.css | 80 + .../contrib/markers/browser/messages.ts | 1 + .../contrib/markers/common/markers.ts | 39 + .../markers/test/browser/markersModel.test.ts | 6 +- .../mergeEditor/browser/commands/commands.ts | 449 + .../browser/commands/devCommands.ts | 166 + .../browser/mergeEditor.contribution.ts | 64 + .../mergeEditor/browser/mergeEditorInput.ts | 278 + .../browser/mergeEditorSerializer.ts | 53 + .../mergeEditor/browser/model/diffComputer.ts | 166 + .../mergeEditor/browser/model/editing.ts | 70 + .../mergeEditor/browser/model/lineRange.ts | 114 + .../mergeEditor/browser/model/mapping.ts | 268 + .../browser/model/mergeEditorModel.ts | 351 + .../browser/model/modifiedBaseRange.ts | 315 + .../browser/model/textModelDiffs.ts | 216 + .../contrib/mergeEditor/browser/utils.ts | 160 + .../mergeEditor/browser/view/colors.ts | 56 + .../mergeEditor/browser/view/editorGutter.ts | 146 + .../browser/view/editors/codeEditorView.ts | 115 + .../view/editors/inputCodeEditorView.ts | 378 + .../view/editors/resultCodeEditorView.ts | 144 + .../browser/view/media/mergeEditor.css | 123 + .../mergeEditor/browser/view/mergeEditor.ts | 655 ++ .../mergeEditor/browser/view/viewModel.ts | 159 + .../contrib/mergeEditor/common/mergeEditor.ts | 14 + .../mergeEditor/test/browser/model.test.ts | 367 + .../contrib/cellCommands/cellCommands.ts | 90 +- .../executionStatusBarItemController.ts | 53 +- .../cellStatusBar/statusBarProviders.ts | 46 +- .../contrib/clipboard/notebookClipboard.ts | 8 +- .../editorStatusBar/editorStatusBar.ts | 296 +- .../browser/contrib/find/notebookFind.ts | 48 +- .../find/notebookFindReplaceWidget.css | 7 +- .../contrib/find/notebookFindReplaceWidget.ts | 92 +- .../contrib/find/notebookFindWidget.ts | 4 +- .../browser/contrib/format/formatting.ts | 2 +- .../gettingStarted/notebookGettingStarted.ts | 9 +- .../browser/contrib/navigation/arrow.ts | 89 +- .../contrib/outline/notebookOutline.ts | 8 +- .../browser/contrib/troubleshoot/layout.ts | 82 +- .../viewportCustomMarkdown.ts | 2 +- .../notebook/browser/controller/apiActions.ts | 6 +- .../browser/controller/cellOperations.ts | 2 +- .../browser/controller/coreActions.ts | 13 +- .../browser/controller/editActions.ts | 44 +- .../browser/controller/executeActions.ts | 101 +- .../browser/controller/insertCellActions.ts | 14 +- .../browser/controller/layoutActions.ts | 20 +- .../notebook/browser/diff/diffComponents.ts | 6 +- .../browser/diff/notebookDiffEditorBrowser.ts | 4 +- .../browser/diff/notebookTextDiffEditor.ts | 9 +- .../browser/diff/notebookTextDiffList.ts | 23 +- .../cell-resize-above-viewport.drawio.svg | 406 +- .../notebook/browser/docs/notebook.layout.md | 9 +- .../notebook/browser/extensionPoint.ts | 213 +- .../browser/media/notebookToolbar.css | 4 + .../notebook/browser/notebook.contribution.ts | 5 +- .../notebook/browser/notebookBrowser.ts | 51 +- .../notebook/browser/notebookEditor.ts | 47 +- .../notebook/browser/notebookEditorService.ts | 5 - .../browser/notebookEditorServiceImpl.ts | 19 - .../notebook/browser/notebookEditorWidget.ts | 365 +- .../browser/notebookExecutionServiceImpl.ts | 88 +- .../notebookExecutionStateServiceImpl.ts | 51 +- .../browser/notebookKernelServiceImpl.ts | 109 +- .../notebook/browser/notebookServiceImpl.ts | 63 +- .../services/notebookKeymapServiceImpl.ts | 2 +- .../browser/view/cellParts/cellActionView.ts | 22 +- .../view/cellParts/cellDragRenderer.ts | 5 +- .../browser/view/cellParts/cellExecution.ts | 25 +- .../browser/view/cellParts/cellOutput.ts | 8 +- .../browser/view/cellParts/cellToolbars.ts | 51 +- .../browser/view/cellParts/codeCell.ts | 3 +- .../browser/view/cellParts/foldedCellHint.ts | 2 +- .../browser/view/cellParts/markdownCell.ts | 9 +- .../notebook/browser/view/notebookCellList.ts | 93 +- .../browser/view/notebookRenderingCommon.ts | 3 +- .../view/renderers/backLayerWebView.ts | 86 +- .../browser/view/renderers/cellRenderer.ts | 5 +- .../browser/view/renderers/webviewMessages.ts | 12 +- .../browser/view/renderers/webviewPreloads.ts | 160 +- .../browser/viewModel/baseCellViewModel.ts | 54 +- .../viewModel/notebookViewModelImpl.ts | 9 +- .../viewParts/notebookEditorDecorations.ts | 205 - .../viewParts/notebookEditorToolbar.ts | 10 +- .../notebookEditorWidgetContextKeys.ts | 24 +- .../viewParts/notebookKernelActionViewItem.ts | 68 +- .../viewParts/notebookTopCellToolbar.ts | 2 +- .../contrib/notebook/common/notebookCommon.ts | 18 +- .../notebook/common/notebookContextKeys.ts | 3 + .../notebook/common/notebookEditorInput.ts | 6 +- .../common/notebookExecutionService.ts | 1 + .../common/notebookExecutionStateService.ts | 8 +- .../notebook/common/notebookKernelService.ts | 43 +- .../notebook/common/notebookOptions.ts | 4 +- .../notebook/common/notebookPerformance.ts | 36 +- .../common/services/notebookSimpleWorker.ts | 4 +- .../notebook/test/browser/cellDnd.test.ts | 2 +- .../browser/notebookExecutionService.test.ts | 20 +- .../notebookExecutionStateService.test.ts | 24 +- .../browser/notebookKernelService.test.ts | 15 +- .../test/browser/notebookServiceImpl.test.ts | 4 +- .../test/browser/testNotebookEditor.ts | 14 +- .../contrib/outline/browser/outlinePane.ts | 15 +- .../outline/browser/outlineViewState.ts | 2 +- .../contrib/output/browser/logViewer.ts | 5 +- .../contrib/output/browser/outputServices.ts | 8 +- .../contrib/output/browser/outputView.ts | 12 +- .../output/common/outputChannelModel.ts | 28 +- .../test/browser/outputLinkProvider.test.ts | 12 +- .../browser/performance.web.contribution.ts | 6 +- .../performance/browser/perfviewEditor.ts | 16 +- .../preferences/browser/keybindingsEditor.ts | 36 +- .../browser/keybindingsEditorContribution.ts | 20 +- .../browser/keyboardLayoutPicker.ts | 18 +- .../browser/media/settingsEditor2.css | 42 +- .../browser/media/settingsWidgets.css | 3 +- .../browser/preferences.contribution.ts | 290 +- .../browser/preferencesRenderers.ts | 55 +- .../preferences/browser/preferencesSearch.ts | 2 +- .../preferences/browser/preferencesWidgets.ts | 41 +- .../preferences/browser/settingsEditor2.ts | 133 +- .../settingsEditorSettingIndicators.ts | 333 + .../preferences/browser/settingsSearchMenu.ts | 8 +- .../preferences/browser/settingsTree.ts | 337 +- .../preferences/browser/settingsTreeModels.ts | 164 +- .../contrib/preferences/browser/tocTree.ts | 7 +- .../contrib/preferences/common/preferences.ts | 4 + .../common/preferencesContribution.ts | 50 +- .../test/common/smartSnippetInserter.test.ts | 6 +- .../profiles/common/profilesActions.ts | 136 - .../browser/commandsQuickAccess.ts | 7 +- .../browser/quickAccess.contribution.ts | 6 +- .../quickaccess/browser/viewQuickAccess.ts | 21 +- .../browser/relauncher.contribution.ts | 27 +- .../remote/browser/explorerViewItems.ts | 4 +- .../contrib/remote/browser/remote.ts | 65 +- .../contrib/remote/browser/remoteExplorer.ts | 3 +- .../contrib/remote/browser/remoteIndicator.ts | 29 +- .../contrib/remote/browser/tunnelView.ts | 122 +- .../remote/common/remote.contribution.ts | 74 +- .../electron-sandbox/remote.contribution.ts | 25 +- .../workbench/contrib/scm/browser/activity.ts | 10 +- .../contrib/scm/browser/media/scm.css | 14 + .../contrib/scm/browser/scmViewPane.ts | 191 +- .../contrib/scm/browser/scmViewService.ts | 2 +- src/vs/workbench/contrib/scm/browser/util.ts | 2 +- src/vs/workbench/contrib/scm/common/scm.ts | 5 + .../contrib/scm/common/scmService.ts | 40 +- .../search/browser/media/searchview.css | 4 +- .../search/browser/search.contribution.ts | 70 +- .../contrib/search/browser/searchActions.ts | 314 +- .../search/browser/searchResultsView.ts | 2 +- .../contrib/search/browser/searchView.ts | 29 +- .../workbench/contrib/search/common/search.ts | 2 +- .../contrib/search/common/searchModel.ts | 148 +- .../search/test/browser/searchViewlet.test.ts | 95 +- .../search/test/common/searchModel.test.ts | 4 +- .../search/test/common/searchResult.test.ts | 3 + .../browser/searchEditor.contribution.ts | 47 +- .../searchEditor/browser/searchEditor.ts | 34 +- .../browser/searchEditorActions.ts | 4 +- .../searchEditor/browser/searchEditorInput.ts | 2 +- .../commands/abstractSnippetsActions.ts | 29 + .../{ => commands}/configureSnippets.ts | 49 +- .../browser/commands/fileTemplateSnippets.ts | 116 + .../browser/{ => commands}/insertSnippet.ts | 41 +- .../browser/commands/surroundWithSnippet.ts | 78 + .../browser/snippetCodeActionProvider.ts | 153 + .../browser/snippetCompletionProvider.ts | 24 +- .../contrib/snippets/browser/snippetPicker.ts | 7 +- .../snippets/browser/snippets.contribution.ts | 66 +- .../contrib/snippets/browser/snippets.ts | 33 + .../contrib/snippets/browser/snippetsFile.ts | 51 +- .../snippets/browser/snippetsService.ts | 162 +- .../snippets/browser/surroundWithSnippet.ts | 60 - .../contrib/snippets/browser/tabCompletion.ts | 2 +- .../snippets/test/browser/snippetFile.test.ts | 25 +- .../test/browser/snippetsRegistry.test.ts | 4 +- .../test/browser/snippetsRewrite.test.ts | 5 +- .../test/browser/snippetsService.test.ts | 207 +- .../surveys/browser/ces.contribution.ts | 15 +- .../browser/languageSurveys.contribution.ts | 38 +- .../surveys/browser/nps.contribution.ts | 26 +- .../tags/electron-sandbox/workspaceTags.ts | 5 +- .../electron-sandbox/workspaceTagsService.ts | 16 +- .../tasks/browser/abstractTaskService.ts | 1596 ++-- .../tasks/browser/runAutomaticTasks.ts | 113 +- .../tasks/browser/task.contribution.ts | 113 +- .../contrib/tasks/browser/taskQuickPick.ts | 194 +- .../contrib/tasks/browser/taskService.ts | 12 +- .../tasks/browser/taskTerminalStatus.ts | 72 +- .../contrib/tasks/browser/tasksQuickAccess.ts | 34 +- .../tasks/browser/terminalTaskSystem.ts | 1208 +-- .../contrib/tasks/common/jsonSchema_v1.ts | 8 +- .../contrib/tasks/common/jsonSchema_v2.ts | 67 +- .../contrib/tasks/common/problemCollectors.ts | 127 +- .../contrib/tasks/common/problemMatcher.ts | 294 +- .../contrib/tasks/common/taskConfiguration.ts | 523 +- .../tasks/common/taskDefinitionRegistry.ts | 45 +- .../contrib/tasks/common/taskService.ts | 39 +- .../contrib/tasks/common/taskSystem.ts | 55 +- .../contrib/tasks/common/taskTemplates.ts | 14 +- .../workbench/contrib/tasks/common/tasks.ts | 244 +- .../tasks/electron-sandbox/taskService.ts | 31 +- .../test/browser/taskTerminalStatus.test.ts | 140 + .../tasks/test/common/problemMatcher.test.ts | 60 +- ...tion.test.ts => taskConfiguration.test.ts} | 449 +- .../browser/telemetry.contribution.ts | 74 +- .../contrib/terminal/browser/links/links.ts | 16 +- .../links/terminalExternalLinkDetector.ts | 11 +- .../links/terminalLinkDetectorAdapter.ts | 11 +- .../browser/links/terminalLinkHelpers.ts | 4 + .../browser/links/terminalLinkManager.ts | 25 +- .../browser/links/terminalLinkOpeners.ts | 134 +- .../browser/links/terminalLinkQuickpick.ts | 21 +- .../links/terminalLocalLinkDetector.ts | 9 +- .../terminalShellIntegrationLinkDetector.ts | 70 - .../browser/links/terminalUriLinkDetector.ts | 30 +- .../browser/links/terminalWordLinkDetector.ts | 4 + .../browser/media/shellIntegration-bash.sh | 185 +- .../browser/media/shellIntegration-env.zsh | 8 +- .../browser/media/shellIntegration-login.zsh | 9 + .../media/shellIntegration-profile.zsh | 8 +- ...ntegration.zsh => shellIntegration-rc.zsh} | 44 +- .../browser/media/shellIntegration.ps1 | 25 +- .../terminal/browser/media/terminal.css | 35 +- .../contrib/terminal/browser/media/xterm.css | 11 +- .../contrib/terminal/browser/remotePty.ts | 4 +- .../terminal/browser/remoteTerminalBackend.ts | 14 + .../terminal/browser/terminal.contribution.ts | 31 +- .../contrib/terminal/browser/terminal.ts | 117 +- .../terminal/browser/terminalActions.ts | 158 +- .../terminal/browser/terminalConfigHelper.ts | 2 +- .../terminal/browser/terminalEditor.ts | 8 +- .../terminal/browser/terminalEditorInput.ts | 39 +- .../browser/terminalEditorSerializer.ts | 6 +- .../terminal/browser/terminalEditorService.ts | 11 +- .../browser/terminalEscapeSequences.ts | 111 + .../terminal/browser/terminalFindWidget.ts | 49 +- .../contrib/terminal/browser/terminalGroup.ts | 46 +- .../terminal/browser/terminalGroupService.ts | 45 +- .../contrib/terminal/browser/terminalIcon.ts | 7 +- .../terminal/browser/terminalInstance.ts | 541 +- .../browser/terminalInstanceService.ts | 6 + .../browser/terminalMainContribution.ts | 59 +- .../contrib/terminal/browser/terminalMenus.ts | 13 +- .../browser/terminalProcessManager.ts | 122 +- .../browser/terminalProfileQuickpick.ts | 5 +- .../browser/terminalProfileResolverService.ts | 23 +- .../browser/terminalProfileService.ts | 2 +- .../terminal/browser/terminalQuickAccess.ts | 22 +- .../terminal/browser/terminalService.ts | 82 +- .../terminal/browser/terminalStatusList.ts | 1 + .../terminal/browser/terminalTabbedView.ts | 4 +- .../terminal/browser/terminalTabsList.ts | 11 +- .../terminal/browser/terminalTooltip.ts | 25 +- .../browser/terminalTypeAheadAddon.ts | 1 + .../contrib/terminal/browser/terminalView.ts | 70 +- .../widgets/environmentVariableInfoWidget.ts | 2 +- .../browser/xterm/commandNavigationAddon.ts | 24 +- .../terminal/browser/xterm/decorationAddon.ts | 370 +- .../browser/xterm/navigationModeAddon.ts | 111 +- .../terminal/browser/xterm/xtermTerminal.ts | 93 +- .../terminal/common/environmentVariable.ts | 4 + .../common/environmentVariableCollection.ts | 4 +- .../common/environmentVariableShared.ts | 16 +- .../contrib/terminal/common/history.ts | 224 +- .../terminal/common/remoteTerminalChannel.ts | 4 + .../contrib/terminal/common/terminal.ts | 21 +- .../terminal/common/terminalColorRegistry.ts | 37 +- .../terminal/common/terminalConfiguration.ts | 56 +- .../terminal/common/terminalContextKey.ts | 14 + .../terminal/common/terminalStrings.ts | 8 - .../terminal/electron-sandbox/localPty.ts | 4 +- .../electron-sandbox/localTerminalBackend.ts | 10 + .../browser/links/terminalLinkManager.test.ts | 6 +- .../browser/links/terminalLinkOpeners.test.ts | 142 +- .../links/terminalLocalLinkDetector.test.ts | 3 + .../links/terminalUriLinkDetector.test.ts | 114 +- .../test/browser/terminalConfigHelper.test.ts | 4 +- .../browser/terminalProcessManager.test.ts | 2 +- .../browser/terminalProfileService.test.ts | 10 +- .../browser/xterm/decorationAddon.test.ts | 17 +- .../xterm/shellIntegrationAddon.test.ts | 2 +- .../test/browser/xterm/xtermTerminal.test.ts | 14 +- .../terminal/test/common/history.test.ts | 435 +- .../browser/explorerProjections/nodeHelper.ts | 6 +- .../contrib/testing/browser/media/testing.css | 18 + .../testing/browser/testExplorerActions.ts | 48 +- .../testing/browser/testing.contribution.ts | 1 - .../testing/browser/testingDecorations.ts | 39 +- .../testing/browser/testingExplorerFilter.ts | 22 +- .../testing/browser/testingExplorerView.ts | 50 +- .../testing/browser/testingOutputPeek.ts | 45 +- .../browser/testingProgressUiService.ts | 1 + .../contrib/testing/common/configuration.ts | 7 + .../contrib/testing/common/constants.ts | 3 +- .../common/mainThreadTestCollection.ts | 2 +- .../testing/common/testExplorerFilterState.ts | 2 +- .../contrib/testing/common/testId.ts | 2 +- .../testing/common/testItemCollection.ts | 24 +- .../testing/common/testingContextKeys.ts | 14 +- .../testing/test/browser/testObjectTree.ts | 2 +- .../themes/browser/themes.contribution.ts | 85 +- .../browser/themes.test.contribution.ts | 84 +- .../colorRegistry.releaseTest.ts | 42 +- .../contrib/timeline/browser/timelinePane.ts | 5 +- .../browser/typeHierarchy.contribution.ts | 4 +- .../browser/typeHierarchyPeek.ts | 12 +- .../browser/typeHierarchyTree.ts | 2 +- .../update/browser/releaseNotesEditor.ts | 22 +- .../update/browser/update.contribution.ts | 51 +- .../contrib/update/browser/update.ts | 24 +- .../contrib/url/browser/trustedDomains.ts | 6 +- .../trustedDomainsFileSystemProvider.ts | 6 +- .../url/browser/trustedDomainsValidator.ts | 8 +- .../browser/userDataProfile.contribution.ts | 13 + .../browser/userDataProfile.ts | 179 + .../common/userDataProfileActions.ts | 451 + .../userDataSync/browser/userDataSync.ts | 211 +- .../browser/userDataSyncTrigger.ts | 8 +- .../contrib/watermark/browser/watermark.ts | 8 +- .../contrib/webview/browser/overlayWebview.ts | 55 +- .../webview/browser/pre/index-no-csp.html | 1171 ++- .../contrib/webview/browser/pre/index.html | 1172 ++- .../contrib/webview/browser/pre/main.js | 1133 --- .../webview/browser/resourceLoading.ts | 2 +- .../contrib/webview/browser/themeing.ts | 6 +- .../webview/browser/webview.contribution.ts | 9 + .../contrib/webview/browser/webview.ts | 26 +- .../contrib/webview/browser/webviewElement.ts | 92 +- .../contrib/webview/browser/webviewService.ts | 22 +- .../electron-sandbox/webviewElement.ts | 19 +- .../electron-sandbox/webviewService.ts | 12 +- .../webviewPanel/browser/webviewEditor.ts | 17 +- .../browser/webviewEditorInput.ts | 22 +- .../browser/webviewEditorInputSerializer.ts | 18 +- .../browser/webviewWorkbenchService.ts | 89 +- .../webviewView/browser/webviewViewPane.ts | 27 +- .../browser/telemetryOptOut.ts | 4 +- .../browser/welcomeBanner.contribution.ts | 4 +- .../browser/gettingStarted.contribution.ts | 29 +- .../browser/gettingStarted.ts | 34 +- .../browser/gettingStartedList.ts | 4 +- .../browser/gettingStartedService.ts | 53 +- .../browser/media/gettingStarted.css | 7 +- .../browser/startupPage.ts | 8 +- .../welcomeOverlay/browser/welcomeOverlay.ts | 2 +- .../common/newFile.contribution.ts | 23 +- .../browser/walkThroughPart.ts | 10 +- .../browser/media/workspaceTrustEditor.css | 3 +- .../electron-sandbox/actions/windowActions.ts | 2 +- .../electron-sandbox/desktop.contribution.ts | 22 +- .../electron-sandbox/desktop.main.ts | 71 +- .../parts/titlebar/menubarControl.ts | 8 +- .../parts/titlebar/titlebarPart.ts | 58 +- src/vs/workbench/electron-sandbox/window.ts | 68 +- .../electron-sandbox/accessibilityService.ts | 1 + .../actions/common/menusExtensionPoint.ts | 104 +- .../assignment/common/assignmentService.ts | 3 +- .../browser/authenticationService.ts | 14 +- .../electron-sandbox/clipboardService.ts | 6 +- .../test/common/commandService.test.ts | 28 +- .../configuration/browser/configuration.ts | 78 +- .../browser/configurationService.ts | 213 +- .../configuration/common/configuration.ts | 16 +- .../common/configurationEditingService.ts | 39 +- .../common/configurationModels.ts | 4 +- .../common/jsonEditingService.ts | 2 +- .../configurationEditingService.test.ts | 72 +- .../test/browser/configurationService.test.ts | 384 +- .../test/common/configurationModels.test.ts | 8 +- .../baseConfigurationResolverService.ts | 35 +- .../common/variableResolver.ts | 6 +- .../configurationResolverService.test.ts | 43 +- .../electron-sandbox/contextmenuService.ts | 14 +- .../decorations/browser/decorationsService.ts | 19 +- .../test/browser/decorationsService.test.ts | 197 +- .../browser/abstractFileDialogService.ts | 7 +- .../dialogs/browser/fileDialogService.ts | 2 +- .../dialogs/browser/simpleFileDialog.ts | 6 +- .../editor/browser/codeEditorService.ts | 9 +- .../editor/browser/editorResolverService.ts | 92 +- .../services/editor/browser/editorService.ts | 62 +- .../editor/common/editorGroupFinder.ts | 4 +- .../editor/common/editorGroupsService.ts | 7 +- .../editor/common/editorResolverService.ts | 21 +- .../test/browser/editorGroupsService.test.ts | 10 +- .../browser/editorResolverService.test.ts | 144 +- .../editor/test/browser/editorService.test.ts | 329 +- .../environment/browser/environmentService.ts | 40 +- .../environment/common/environmentService.ts | 1 + .../electron-sandbox/environmentService.ts | 3 + .../builtinExtensionsScannerService.ts | 68 +- .../browser/extensionBisect.ts | 10 +- .../browser/extensionEnablementService.ts | 112 +- .../browser/webExtensionsScannerService.ts | 214 +- .../common/extensionManagement.ts | 27 +- .../extensionManagementServerService.ts | 7 +- .../common/extensionManagementService.ts | 57 +- .../common/webExtensionManagementService.ts | 88 +- .../extensionManagementServerService.ts | 19 +- .../extensionManagementService.ts | 10 +- .../nativeExtensionManagementService.ts | 85 + .../remoteExtensionManagementService.ts | 31 +- .../extensionEnablementService.test.ts | 65 +- .../extensionIgnoredRecommendationsService.ts | 6 +- .../common/extensionResourceLoader.ts | 5 +- .../extensions/browser/extensionService.ts | 35 +- .../extensions/browser/extensionUrlHandler.ts | 8 +- .../browser/webWorkerExtensionHost.ts | 25 +- .../common/abstractExtensionService.ts | 145 +- .../common/extensionDescriptionRegistry.ts | 49 +- .../extensions/common/extensionDevOptions.ts | 12 +- .../extensions/common/extensionHostEnv.ts | 93 + .../extensions/common/extensionHostManager.ts | 69 +- .../common/extensionHostProtocol.ts | 11 +- .../extensions/common/extensionHostProxy.ts | 1 - .../common/extensionStorageMigration.ts | 6 +- .../services/extensions/common/extensions.ts | 4 +- .../common/extensionsApiProposals.ts | 18 +- .../extensions/common/extensionsRegistry.ts | 22 +- .../extensions/common/extensionsUtil.ts | 13 +- .../services/extensions/common/lazyPromise.ts | 14 +- .../extensions/common/proxyIdentifier.ts | 3 +- .../extensions/common/remoteExtensionHost.ts | 17 +- .../services/extensions/common/rpcProtocol.ts | 70 +- .../cachedExtensionScanner.ts | 16 +- .../electronExtensionService.ts} | 102 +- .../electron-sandbox/extensionHostProfiler.ts | 26 +- .../localProcessExtensionHost.ts | 629 +- .../nativeLocalProcessExtensionHost.ts | 123 + .../sandboxExtensionService.ts | 22 + .../test/browser/extensionService.test.ts | 114 +- .../browser/extensionStorageMigration.test.ts | 28 +- .../extensionDescriptionRegistry.test.ts | 40 + .../test/common/rpcProtocol.test.ts | 22 +- .../worker/webWorkerExtensionHostIframe.html | 9 +- .../history/browser/historyService.ts | 8 +- .../test/browser/historyService.test.ts | 2 +- .../host/browser/browserHostService.ts | 34 +- .../services/hover/browser/hoverService.ts | 6 +- .../services/hover/browser/hoverWidget.ts | 20 +- .../electron-sandbox/integrityService.ts | 4 +- .../issue/electron-sandbox/issueService.ts | 13 +- .../keybinding/browser/keybindingService.ts | 68 +- .../browser/keyboardLayoutService.ts | 26 +- .../keybinding/common/keybindingEditing.ts | 19 +- .../keybinding/common/keybindingIO.ts | 6 +- .../services/keybinding/common/keymapInfo.ts | 34 +- .../common/macLinuxKeyboardMapper.ts | 35 +- .../common/windowsKeyboardMapper.ts | 8 +- .../browser/browserKeyboardMapper.test.ts | 10 +- .../test/browser/keybindingEditing.test.ts | 22 +- .../test/browser/keybindingIO.test.ts | 38 +- .../keyboardMapperTestUtils.ts | 14 +- .../{electron-browser => node}/linux_de_ch.js | 0 .../linux_de_ch.txt | 0 .../{electron-browser => node}/linux_en_uk.js | 0 .../linux_en_uk.txt | 0 .../{electron-browser => node}/linux_en_us.js | 0 .../linux_en_us.txt | 0 .../{electron-browser => node}/linux_ru.js | 0 .../{electron-browser => node}/linux_ru.txt | 0 .../macLinuxFallbackKeyboardMapper.test.ts | 6 +- .../macLinuxKeyboardMapper.test.ts | 8 +- .../{electron-browser => node}/mac_de_ch.js | 0 .../{electron-browser => node}/mac_de_ch.txt | 0 .../{electron-browser => node}/mac_en_us.js | 0 .../{electron-browser => node}/mac_en_us.txt | 0 .../{electron-browser => node}/mac_zh_hant.js | 0 .../mac_zh_hant.txt | 0 .../mac_zh_hant2.js | 0 .../mac_zh_hant2.txt | 0 .../{electron-browser => node}/win_de_ch.js | 0 .../{electron-browser => node}/win_de_ch.txt | 0 .../{electron-browser => node}/win_en_us.js | 0 .../{electron-browser => node}/win_en_us.txt | 0 .../{electron-browser => node}/win_por_ptb.js | 0 .../win_por_ptb.txt | 0 .../test/{electron-browser => node}/win_ru.js | 0 .../{electron-browser => node}/win_ru.txt | 0 .../windowsKeyboardMapper.test.ts | 2 +- .../services/label/common/labelService.ts | 73 +- .../services/label/test/browser/label.test.ts | 58 +- .../label/test/common/mockLabelService.ts | 41 + .../label/test/electron-browser/label.test.ts | 6 +- .../language/common/languageService.ts | 6 +- .../languageDetectionWorkerServiceImpl.ts | 18 +- .../common/languageDetectionWorkerService.ts | 23 +- .../electron-sandbox/languagePackService.ts} | 4 +- .../common/notificationService.ts | 67 +- .../outline/browser/outlineService.ts | 4 +- .../output/common/delayedLogChannel.ts | 33 + .../preferences/browser/preferencesService.ts | 29 +- .../preferences/common/preferences.ts | 3 +- .../preferences/common/preferencesModels.ts | 32 +- .../browser/keybindingsEditorModel.test.ts | 2 +- .../services/profiles/common/profile.ts | 44 - .../progress/browser/progressService.ts | 12 +- .../test/browser/progressIndicator.test.ts | 14 +- .../common/abstractRemoteAgentService.ts | 38 +- .../common/remoteAgentEnvironmentChannel.ts | 4 + .../remote/common/remoteAgentService.ts | 8 + .../remote/common/remoteExplorerService.ts | 14 +- .../request/browser/requestService.ts | 2 +- .../services/search/common/ignoreFile.ts | 21 +- .../services/search/common/searchExtTypes.ts | 2 +- .../services/search/common/searchService.ts | 17 +- .../services/search/node/fileSearch.ts | 7 +- .../services/search/node/rawSearchService.ts | 4 +- .../search/test/common/ignoreFile.test.ts | 12 +- .../search/test/common/replace.test.ts | 8 +- .../services/search/worker/localFileSearch.ts | 2 +- .../services/statusbar/browser/statusbar.ts | 5 +- .../storage/browser/storageService.ts | 205 +- .../electron-sandbox/storageService.ts | 30 + .../test/browser/storageService.test.ts | 36 +- .../telemetry/browser/telemetryService.ts | 40 +- .../browser/workbenchCommonProperties.ts | 18 +- .../electron-sandbox/telemetryService.ts | 5 +- .../workbenchCommonProperties.ts | 8 +- .../test/browser/commonProperties.test.ts | 6 +- .../electron-browser/commonProperties.test.ts | 10 +- .../browser/abstractTextMateService.ts | 23 +- .../textMate/browser/nativeTextMateService.ts | 2 +- .../textMate/browser/textMateWorker.ts | 6 +- .../textMate/common/TMGrammarFactory.ts | 6 +- .../services/textMate/common/TMHelper.ts | 16 +- .../textMate/common/TMScopeRegistry.ts | 2 +- .../textMate/common/TMTokenization.ts | 11 +- .../textfile/common/textEditorService.ts | 19 +- .../textfile/common/textFileEditorModel.ts | 2 +- .../browser/browserTextFileService.io.test.ts | 2 +- .../test/browser/textEditorService.test.ts | 2 +- .../test/browser/textFileEditorModel.test.ts | 10 +- .../textFileEditorModelManager.test.ts | 10 +- .../test/browser/textFileService.test.ts | 6 +- .../test/common/textFileService.io.test.ts | 4 +- .../common/textModelResolverService.ts | 41 +- .../browser/textModelResolverService.test.ts | 18 +- .../themes/browser/fileIconThemeData.ts | 4 +- .../themes/browser/productIconThemeData.ts | 41 +- .../themes/browser/workbenchThemeService.ts | 23 +- .../themes/common/colorExtensionPoint.ts | 4 +- .../services/themes/common/colorThemeData.ts | 94 +- .../themes/common/colorThemeSchema.ts | 6 +- .../themes/common/fileIconThemeSchema.ts | 2 +- .../themes/common/iconExtensionPoint.ts | 2 +- .../services/themes/common/plistParser.ts | 28 +- .../themes/common/productIconThemeSchema.ts | 5 +- .../themes/common/textMateScopeMatcher.ts | 4 +- .../themes/common/themeCompatibility.ts | 12 +- .../themes/common/themeConfiguration.ts | 8 +- .../themes/common/themeExtensionPoints.ts | 6 +- .../tokenClassificationExtensionPoint.ts | 4 +- .../nativeHostColorSchemeService.ts | 4 +- .../tokenStyleResolving.test.ts | 4 +- .../services/timer/browser/timerService.ts | 15 +- .../timer/electron-sandbox/timerService.ts | 4 +- .../services/title/common/titleService.ts | 10 + .../services/tunnel/browser/tunnelService.ts | 2 +- .../tunnel/electron-sandbox/tunnelService.ts | 2 +- .../common/untitledTextEditorModel.ts | 2 +- .../test/browser/untitledTextEditor.test.ts | 2 +- .../services/update/browser/updateService.ts | 4 + .../services/userData/browser/userDataInit.ts | 17 +- .../browser/userDataProfileManagement.ts | 114 + .../common/extensionsProfile.ts | 2 +- .../common/globalStateProfile.ts | 8 +- .../common/settingsProfile.ts | 13 +- .../userDataProfile/common/userDataProfile.ts | 80 + .../userDataProfileImportExportService.ts} | 49 +- .../common/userDataProfileService.ts | 66 + .../common/userDataProfileStorageRegistry.ts} | 0 .../browser/userDataSyncWorkbenchService.ts | 35 +- .../views/browser/treeViewsService.ts | 4 +- .../views/browser/viewDescriptorService.ts | 20 +- .../views/common/viewContainerModel.ts | 12 +- .../test/browser/viewContainerModel.test.ts | 36 +- .../browser/viewDescriptorService.test.ts | 4 +- .../common/abstractFileWorkingCopyManager.ts | 2 +- .../common/fileWorkingCopyManager.ts | 2 +- .../common/storedFileWorkingCopy.ts | 2 +- .../common/storedFileWorkingCopyManager.ts | 2 +- .../common/untitledFileWorkingCopyManager.ts | 2 +- .../common/workingCopyHistoryTracker.ts | 3 +- .../test/browser/resourceWorkingCopy.test.ts | 27 +- .../browser/storedFileWorkingCopy.test.ts | 224 +- .../storedFileWorkingCopyManager.test.ts | 4 +- .../browser/untitledFileWorkingCopy.test.ts | 2 +- .../browser/workingCopyBackupTracker.test.ts | 2 +- .../browser/workingCopyEditorService.test.ts | 2 +- .../browser/workingCopyFileService.test.ts | 20 +- .../workingCopyBackupService.test.ts | 50 +- .../workingCopyHistoryService.test.ts | 24 +- .../workingCopyHistoryTracker.test.ts | 68 +- .../abstractWorkspaceEditingService.ts | 49 +- .../browser/workspaceEditingService.ts | 12 +- .../workspaces/browser/workspacesService.ts | 8 +- .../workspaces/common/workspaceEditing.ts | 10 +- .../workspaces/common/workspaceTrust.ts | 35 +- .../workspaceEditingService.ts | 14 +- .../test/common/workspaceTrust.test.ts | 2 +- .../workbench/test/browser/codeeditor.test.ts | 10 +- src/vs/workbench/test/browser/part.test.ts | 18 +- .../parts/editor/breadcrumbModel.test.ts | 18 +- .../parts/editor/diffEditorInput.test.ts | 4 +- .../test/browser/parts/editor/editor.test.ts | 179 +- .../parts/editor/editorDiffModel.test.ts | 12 +- .../parts/editor/editorGroupModel.test.ts | 24 +- .../browser/parts/editor/editorInput.test.ts | 7 +- .../browser/parts/editor/editorModel.test.ts | 2 +- .../editor/sideBySideEditorInput.test.ts | 4 +- src/vs/workbench/test/browser/viewlet.test.ts | 6 +- .../test/browser/workbenchTestServices.ts | 159 +- src/vs/workbench/test/common/memento.test.ts | 128 +- .../test/common/notifications.test.ts | 40 +- .../electron-browser/workbenchTestServices.ts | 36 +- src/vs/workbench/workbench.common.main.ts | 34 +- src/vs/workbench/workbench.desktop.main.ts | 171 +- .../workbench.desktop.sandbox.main.ts | 38 - src/vs/workbench/workbench.sandbox.main.ts | 163 - src/vs/workbench/workbench.web.main.ts | 15 +- src/vscode-dts/vscode.d.ts | 516 +- .../vscode.proposed.contribEditSessions.d.ts | 6 + ...e.proposed.contribMergeEditorToolbar.d.ts} | 5 +- .../vscode.proposed.contribShareMenu.d.ts | 6 + .../vscode.proposed.contribViewSize.d.ts | 17 + ...vscode.proposed.contribWebviewContext.d.ts | 6 + .../vscode.proposed.documentPaste.d.ts | 75 + .../vscode.proposed.inlineCompletions.d.ts | 174 - ...e.proposed.inlineCompletionsAdditions.d.ts | 21 + .../vscode.proposed.inlineCompletionsNew.d.ts | 22 +- .../vscode.proposed.inputBoxSeverity.d.ts | 50 - .../vscode.proposed.interactiveWindow.d.ts | 25 + ...code.proposed.notebookContentProvider.d.ts | 10 +- .../vscode.proposed.notebookEditor.d.ts | 103 +- ...proposed.notebookEditorDecorationType.d.ts | 28 - .../vscode.proposed.notebookEditorEdit.d.ts | 39 +- ...vscode.proposed.notebookKernelSource.d.ts} | 3 +- ...code.proposed.notebookProxyController.d.ts | 56 - ...vscode.proposed.notebookWorkspaceEdit.d.ts | 85 + .../vscode.proposed.scmActionButton.d.ts | 2 + .../vscode.proposed.snippetWorkspaceEdit.d.ts | 15 + .../vscode.proposed.tabInputTextMerge.d.ts | 25 + .../vscode.proposed.terminalExitReason.d.ts | 47 + ...code.proposed.terminalNameChangeEvent.d.ts | 30 - .../vscode.proposed.testCoverage.d.ts | 2 +- .../vscode.proposed.textEditorDrop.d.ts | 47 - .../vscode.proposed.textSearchProvider.d.ts | 2 +- test/automation/src/application.ts | 6 +- test/automation/src/code.ts | 12 +- test/automation/src/editor.ts | 3 + test/automation/src/electron.ts | 10 +- test/automation/src/extensions.ts | 2 +- test/automation/src/index.ts | 1 + test/automation/src/playwrightBrowser.ts | 2 +- test/automation/src/problems.ts | 4 +- test/automation/src/settings.ts | 35 +- test/automation/src/task.ts | 86 + test/automation/src/terminal.ts | 73 +- test/automation/src/workbench.ts | 7 +- test/integration/browser/src/index.ts | 4 +- test/smoke/package.json | 2 +- test/smoke/src/areas/search/search.test.ts | 4 +- .../src/areas/statusbar/statusbar.test.ts | 4 +- .../src/areas/task/task-quick-pick.test.ts | 71 + test/smoke/src/areas/task/task.test.ts | 22 + .../areas/terminal/terminal-editors.test.ts | 16 +- .../src/areas/terminal/terminal-helpers.ts | 23 + .../src/areas/terminal/terminal-input.test.ts | 12 +- .../terminal/terminal-persistence.test.ts | 45 +- .../areas/terminal/terminal-profiles.test.ts | 21 +- .../terminal-shellIntegration.test.ts | 144 +- .../areas/terminal/terminal-splitCwd.test.ts | 14 +- .../src/areas/terminal/terminal-tabs.test.ts | 16 +- .../smoke/src/areas/terminal/terminal.test.ts | 12 +- .../src/areas/workbench/data-loss.test.ts | 19 +- .../src/areas/workbench/localization.test.ts | 1 - test/smoke/src/main.ts | 148 +- test/smoke/src/utils.ts | 8 +- test/smoke/yarn.lock | 8 +- test/unit/browser/index.js | 25 +- test/unit/coverage.js | 4 +- test/unit/electron/renderer.js | 60 +- test/unit/node/index.js | 8 +- test/unit/reporter.js | 2 +- yarn.lock | 1729 ++-- 2389 files changed, 92183 insertions(+), 42601 deletions(-) create mode 100644 .github/workflows/bad-tag.yml create mode 100644 .github/workflows/basic.yml create mode 100644 .github/workflows/pr-chat.yml create mode 100644 .nvmrc create mode 100644 build/.gitignore create mode 100644 build/azure-pipelines/linux/product-build-linux-client-test.yml create mode 100644 build/azure-pipelines/product-build-pr-cache.yml create mode 100644 build/azure-pipelines/product-build-pr.yml create mode 100644 build/azure-pipelines/win32/product-build-win32-test.yml create mode 100644 build/lib/policies.js create mode 100644 build/lib/policies.ts create mode 100644 build/lib/tsb/builder.js create mode 100644 build/lib/tsb/builder.ts create mode 100644 build/lib/tsb/index.js create mode 100644 build/lib/tsb/index.ts create mode 100644 build/lib/tsb/transpiler.js create mode 100644 build/lib/tsb/transpiler.ts create mode 100644 build/lib/tsb/utils.js create mode 100644 build/lib/tsb/utils.ts delete mode 100644 build/lib/typings/gulp-tsb.d.ts delete mode 100644 build/lib/watch/.gitignore delete mode 100644 build/lib/watch/package.json delete mode 100644 build/lib/watch/yarn.lock create mode 100644 build/linux/debian/dep-lists.js create mode 100644 build/linux/debian/dep-lists.ts create mode 100644 build/linux/debian/dependencies-generator.js create mode 100644 build/linux/debian/dependencies-generator.ts create mode 100644 build/linux/debian/install-sysroot.js create mode 100644 build/linux/debian/install-sysroot.ts create mode 100644 build/linux/debian/sysroots.js create mode 100644 build/linux/debian/sysroots.ts create mode 100644 build/linux/debian/types.js rename src/vs/css.d.ts => build/linux/debian/types.ts (86%) create mode 100644 build/npm/setupBuildYarnrc.js create mode 100644 build/package-lock.json create mode 100644 extensions/configuration-editing/schemas/devContainer.codespaces.schema.json create mode 100644 extensions/configuration-editing/schemas/devContainer.vscode.schema.json create mode 100644 extensions/configuration-editing/src/test/completion.test.ts create mode 100644 extensions/css-language-features/server/src/utils/validation.ts create mode 100755 extensions/git/src/git-editor-empty.sh create mode 100644 extensions/git/src/git-editor-main.ts create mode 100755 extensions/git/src/git-editor.sh create mode 100644 extensions/git/src/gitEditor.ts create mode 100644 extensions/git/src/postCommitCommands.ts create mode 100644 extensions/github/src/links.ts create mode 100644 extensions/html-language-features/client/src/languageParticipants.ts create mode 100644 extensions/html-language-features/server/src/utils/validation.ts create mode 100644 extensions/ipynb/esbuild.js create mode 100644 extensions/ipynb/src/cellAttachmentRenderer.ts create mode 100644 extensions/json-language-features/server/src/utils/validation.ts create mode 100644 extensions/markdown-language-features/server/.npmignore create mode 100644 extensions/markdown-language-features/server/.vscode/launch.json create mode 100644 extensions/markdown-language-features/server/.vscode/settings.json create mode 100644 extensions/markdown-language-features/server/.vscode/tasks.json create mode 100644 extensions/markdown-language-features/server/README.md create mode 100644 extensions/markdown-language-features/server/extension-browser.webpack.config.js create mode 100644 extensions/markdown-language-features/server/extension.webpack.config.js create mode 100644 extensions/markdown-language-features/server/package.json create mode 100644 extensions/markdown-language-features/server/src/browser/main.ts create mode 100644 extensions/markdown-language-features/server/src/config.ts create mode 100644 extensions/markdown-language-features/server/src/configuration.ts create mode 100644 extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts create mode 100644 extensions/markdown-language-features/server/src/logging.ts create mode 100644 extensions/markdown-language-features/server/src/node/main.ts create mode 100644 extensions/markdown-language-features/server/src/protocol.ts create mode 100644 extensions/markdown-language-features/server/src/server.ts create mode 100644 extensions/markdown-language-features/server/src/util/arrays.ts create mode 100644 extensions/markdown-language-features/server/src/util/dispose.ts create mode 100644 extensions/markdown-language-features/server/src/util/file.ts create mode 100644 extensions/markdown-language-features/server/src/util/limiter.ts create mode 100644 extensions/markdown-language-features/server/src/util/resourceMap.ts create mode 100644 extensions/markdown-language-features/server/src/util/schemes.ts create mode 100644 extensions/markdown-language-features/server/src/workspace.ts create mode 100644 extensions/markdown-language-features/server/tsconfig.json create mode 100644 extensions/markdown-language-features/server/yarn.lock create mode 100644 extensions/markdown-language-features/src/client.ts create mode 100644 extensions/markdown-language-features/src/extension.browser.ts create mode 100644 extensions/markdown-language-features/src/extension.shared.ts create mode 100644 extensions/markdown-language-features/src/languageFeatures/copyPaste.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/references.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/rename.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/smartSelect.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts delete mode 100644 extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts rename extensions/markdown-language-features/src/{logger.ts => logging.ts} (59%) rename extensions/markdown-language-features/src/preview/{previewContentProvider.ts => documentRenderer.ts} (91%) create mode 100644 extensions/markdown-language-features/src/protocol.ts delete mode 100644 extensions/markdown-language-features/src/test/definitionProvider.test.ts delete mode 100644 extensions/markdown-language-features/src/test/diagnostic.test.ts delete mode 100644 extensions/markdown-language-features/src/test/documentLinkProvider.test.ts delete mode 100644 extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts delete mode 100644 extensions/markdown-language-features/src/test/fileReferences.test.ts delete mode 100644 extensions/markdown-language-features/src/test/foldingProvider.test.ts create mode 100644 extensions/markdown-language-features/src/test/nulLogging.ts delete mode 100644 extensions/markdown-language-features/src/test/pathCompletion.test.ts delete mode 100644 extensions/markdown-language-features/src/test/references.test.ts delete mode 100644 extensions/markdown-language-features/src/test/rename.test.ts delete mode 100644 extensions/markdown-language-features/src/test/smartSelect.test.ts delete mode 100644 extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts delete mode 100644 extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts create mode 100644 extensions/markdown-language-features/src/types/textDocument.ts create mode 100644 extensions/markdown-language-features/src/util/cancellation.ts create mode 100644 extensions/markdown-language-features/src/util/dom.ts create mode 100644 extensions/markdown-language-features/src/util/resourceMap.ts rename src/vs/workbench/common/dnd.ts => extensions/markdown-language-features/src/util/string.ts (71%) create mode 100644 extensions/markdown-language-features/src/util/workspaceCache.ts rename extensions/markdown-language-features/src/{workspaceContents.ts => workspace.ts} (57%) create mode 100644 extensions/typescript-language-features/src/experimentationService.ts create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts delete mode 100644 resources/win32/policies/Code.admx delete mode 100644 resources/win32/policies/en-US/Code.adml create mode 100644 src/sql/base/browser/globalPointerMoveMonitor.ts create mode 100644 src/vs/base/browser/broadcast.ts create mode 100644 src/vs/base/browser/deviceAccess.ts rename src/vs/{editor/contrib/suggest/browser => base/browser/ui/resizable}/resizable.ts (100%) create mode 100644 src/vs/base/common/dataTransfer.ts create mode 100644 src/vs/base/common/observable.ts create mode 100644 src/vs/base/common/observableImpl/autorun.ts create mode 100644 src/vs/base/common/observableImpl/base.ts create mode 100644 src/vs/base/common/observableImpl/derived.ts create mode 100644 src/vs/base/common/observableImpl/logging.ts create mode 100644 src/vs/base/common/observableImpl/utils.ts create mode 100644 src/vs/base/test/browser/ui/list/listWidget.test.ts create mode 100644 src/vs/base/test/common/observable.test.ts create mode 100644 src/vs/base/test/node/css.build.test.ts delete mode 100644 src/vs/code/electron-browser/workbench/workbench.html delete mode 100644 src/vs/code/electron-browser/workbench/workbench.js create mode 100644 src/vs/css.build.ts create mode 100644 src/vs/css.ts create mode 100644 src/vs/editor/browser/dnd.ts create mode 100644 src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.css create mode 100644 src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts create mode 100644 src/vs/editor/common/encodedTokenAttributes.ts create mode 100644 src/vs/editor/common/viewLayout/linePart.ts create mode 100644 src/vs/editor/contrib/codeAction/browser/codeActionWidgetContribution.ts create mode 100644 src/vs/editor/contrib/codeAction/browser/media/action.css create mode 100644 src/vs/editor/contrib/copyPaste/browser/copyPasteContribution.ts create mode 100644 src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts create mode 100644 src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts delete mode 100644 src/vs/editor/contrib/folding/browser/intializingRangeProvider.ts create mode 100644 src/vs/editor/contrib/hover/test/browser/contentHover.test.ts create mode 100644 src/vs/editor/contrib/readOnlyMessage/browser/contribution.ts create mode 100644 src/vs/editor/contrib/stickyScroll/browser/stickyScroll.ts create mode 100644 src/vs/loader.d.ts create mode 100644 src/vs/nls.build.ts create mode 100644 src/vs/nls.ts create mode 100644 src/vs/platform/actions/common/actions.contribution.ts create mode 100644 src/vs/platform/actions/common/menuResetAction.ts create mode 100644 src/vs/platform/configuration/common/configurations.ts create mode 100644 src/vs/platform/configuration/test/common/policyConfiguration.test.ts create mode 100644 src/vs/platform/dnd/browser/dnd.ts create mode 100644 src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts create mode 100644 src/vs/platform/extensionManagement/electron-main/defaultExtensionsProfileInit.ts create mode 100644 src/vs/platform/extensionManagement/electron-sandbox/defaultExtensionsProfileInit.ts create mode 100644 src/vs/platform/extensions/electron-main/extensionHostStarter.ts delete mode 100644 src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts delete mode 100644 src/vs/platform/extensions/node/extensionHostStarterWorker.ts delete mode 100644 src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts rename src/vs/platform/files/test/browser/{indexedDBFileService.test.ts => indexedDBFileService.integrationTest.ts} (87%) create mode 100644 src/vs/platform/languagePacks/browser/languagePacks.ts create mode 100644 src/vs/platform/languagePacks/common/languagePacks.ts rename src/vs/platform/{localizations => languagePacks}/common/localizedStrings.ts (100%) rename src/vs/platform/{localizations/node/localizations.ts => languagePacks/node/languagePacks.ts} (76%) create mode 100644 src/vs/platform/policy/common/filePolicyService.ts create mode 100644 src/vs/platform/policy/common/policy.ts create mode 100644 src/vs/platform/policy/common/policyIpc.ts create mode 100644 src/vs/platform/policy/node/nativePolicyService.ts create mode 100644 src/vs/platform/remote/test/common/remoteHosts.test.ts create mode 100644 src/vs/platform/request/electron-browser/sharedProcessRequestService.ts create mode 100644 src/vs/platform/state/node/state.ts create mode 100644 src/vs/platform/state/node/stateService.ts create mode 100644 src/vs/platform/terminal/common/terminalStrings.ts create mode 100644 src/vs/platform/terminal/test/common/terminalEnvironment.test.ts create mode 100644 src/vs/platform/userDataProfile/browser/userDataProfile.ts create mode 100644 src/vs/platform/userDataProfile/common/userDataProfile.ts create mode 100644 src/vs/platform/userDataProfile/electron-main/userDataProfile.ts create mode 100644 src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts create mode 100644 src/vs/platform/userDataProfile/node/userDataProfile.ts create mode 100644 src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts create mode 100644 src/vs/platform/userDataProfile/test/electron-main/userDataProfileMainService.test.ts delete mode 100644 src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts create mode 100644 src/vs/workbench/api/common/extHostConsoleForwarder.ts delete mode 100644 src/vs/workbench/api/common/extHostNotebookProxyKernels.ts delete mode 100644 src/vs/workbench/api/common/shared/dataTransfer.ts create mode 100644 src/vs/workbench/api/common/shared/dataTransferCache.ts create mode 100644 src/vs/workbench/api/node/extHostConsoleForwarder.ts create mode 100644 src/vs/workbench/api/worker/extHostConsoleForwarder.ts create mode 100644 src/vs/workbench/browser/parts/editor/textCodeEditor.ts create mode 100644 src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts create mode 100644 src/vs/workbench/browser/parts/titlebar/windowTitle.ts delete mode 100644 src/vs/workbench/contrib/audioCues/browser/observable.ts create mode 100644 src/vs/workbench/contrib/bracketPairColorizer2Telemetry/browser/bracketPairColorizer2Telemetry.contribution.ts create mode 100644 src/vs/workbench/contrib/debug/browser/debugConsoleQuickAccess.ts create mode 100644 src/vs/workbench/contrib/debug/browser/debugSessionPicker.ts create mode 100644 src/vs/workbench/contrib/debug/common/loadedScriptsPicker.ts create mode 100644 src/vs/workbench/contrib/debug/test/node/terminals.test.ts create mode 100644 src/vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution.ts delete mode 100644 src/vs/workbench/contrib/dropIntoEditor/browser/dropIntoEditor.contibution.ts create mode 100644 src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts create mode 100644 src/vs/workbench/contrib/editSessions/browser/editSessionsContentProvider.ts create mode 100644 src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts create mode 100644 src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts create mode 100644 src/vs/workbench/contrib/editSessions/common/editSessions.ts create mode 100644 src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts create mode 100644 src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts delete mode 100644 src/vs/workbench/contrib/extensions/browser/extensionsCleaner.ts create mode 100644 src/vs/workbench/contrib/localization/browser/localeService.ts create mode 100644 src/vs/workbench/contrib/localization/browser/localization.contribution.ts create mode 100644 src/vs/workbench/contrib/localization/browser/localizationsActions.ts create mode 100644 src/vs/workbench/contrib/localization/common/locale.ts create mode 100644 src/vs/workbench/contrib/localization/electron-sandbox/localeService.ts rename src/vs/workbench/contrib/{localizations/browser/localizations.contribution.ts => localization/electron-sandbox/localization.contribution.ts} (89%) rename src/vs/workbench/contrib/{localizations/browser => localization/electron-sandbox}/minimalTranslations.ts (100%) delete mode 100644 src/vs/workbench/contrib/localizations/browser/localizationsActions.ts delete mode 100644 src/vs/workbench/contrib/markers/browser/constants.ts create mode 100644 src/vs/workbench/contrib/markers/browser/markersTable.ts create mode 100644 src/vs/workbench/contrib/markers/common/markers.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/utils.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorDecorations.ts create mode 100644 src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts delete mode 100644 src/vs/workbench/contrib/profiles/common/profilesActions.ts create mode 100644 src/vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions.ts rename src/vs/workbench/contrib/snippets/browser/{ => commands}/configureSnippets.ts (89%) create mode 100644 src/vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets.ts rename src/vs/workbench/contrib/snippets/browser/{ => commands}/insertSnippet.ts (83%) create mode 100644 src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts create mode 100644 src/vs/workbench/contrib/snippets/browser/snippetCodeActionProvider.ts create mode 100644 src/vs/workbench/contrib/snippets/browser/snippets.ts delete mode 100644 src/vs/workbench/contrib/snippets/browser/surroundWithSnippet.ts create mode 100644 src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts rename src/vs/workbench/contrib/tasks/test/common/{configuration.test.ts => taskConfiguration.test.ts} (73%) delete mode 100644 src/vs/workbench/contrib/terminal/browser/links/terminalShellIntegrationLinkDetector.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/media/shellIntegration-login.zsh rename src/vs/workbench/contrib/terminal/browser/media/{shellIntegration.zsh => shellIntegration-rc.zsh} (74%) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalEscapeSequences.ts create mode 100644 src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts create mode 100644 src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts create mode 100644 src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts delete mode 100644 src/vs/workbench/contrib/webview/browser/pre/main.js create mode 100644 src/vs/workbench/services/extensionManagement/electron-sandbox/nativeExtensionManagementService.ts create mode 100644 src/vs/workbench/services/extensions/common/extensionHostEnv.ts rename src/vs/workbench/services/extensions/{electron-browser/extensionService.ts => electron-sandbox/electronExtensionService.ts} (88%) rename src/vs/workbench/services/extensions/{electron-browser => electron-sandbox}/localProcessExtensionHost.ts (53%) create mode 100644 src/vs/workbench/services/extensions/electron-sandbox/nativeLocalProcessExtensionHost.ts create mode 100644 src/vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService.ts create mode 100644 src/vs/workbench/services/extensions/test/common/extensionDescriptionRegistry.test.ts rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/keyboardMapperTestUtils.ts (85%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_de_ch.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_de_ch.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_en_uk.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_en_uk.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_en_us.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_en_us.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_ru.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/linux_ru.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/macLinuxFallbackKeyboardMapper.test.ts (97%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/macLinuxKeyboardMapper.test.ts (99%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_de_ch.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_de_ch.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_en_us.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_en_us.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_zh_hant.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_zh_hant.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_zh_hant2.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/mac_zh_hant2.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_de_ch.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_de_ch.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_en_us.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_en_us.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_por_ptb.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_por_ptb.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_ru.js (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/win_ru.txt (100%) rename src/vs/workbench/services/keybinding/test/{electron-browser => node}/windowsKeyboardMapper.test.ts (99%) create mode 100644 src/vs/workbench/services/label/test/common/mockLabelService.ts rename src/vs/workbench/services/{localizations/electron-sandbox/localizationsService.ts => localization/electron-sandbox/languagePackService.ts} (68%) create mode 100644 src/vs/workbench/services/output/common/delayedLogChannel.ts delete mode 100644 src/vs/workbench/services/profiles/common/profile.ts rename src/vs/{platform => workbench/services}/storage/browser/storageService.ts (54%) create mode 100644 src/vs/workbench/services/storage/electron-sandbox/storageService.ts rename src/vs/{platform => workbench/services}/storage/test/browser/storageService.test.ts (78%) create mode 100644 src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts rename src/vs/workbench/services/{profiles => userDataProfile}/common/extensionsProfile.ts (98%) rename src/vs/workbench/services/{profiles => userDataProfile}/common/globalStateProfile.ts (89%) rename src/vs/workbench/services/{profiles => userDataProfile}/common/settingsProfile.ts (81%) create mode 100644 src/vs/workbench/services/userDataProfile/common/userDataProfile.ts rename src/vs/workbench/services/{profiles/common/profileService.ts => userDataProfile/common/userDataProfileImportExportService.ts} (51%) create mode 100644 src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts rename src/vs/workbench/services/{profiles/common/profileStorageRegistry.ts => userDataProfile/common/userDataProfileStorageRegistry.ts} (100%) delete mode 100644 src/vs/workbench/workbench.desktop.sandbox.main.ts delete mode 100644 src/vs/workbench/workbench.sandbox.main.ts create mode 100644 src/vscode-dts/vscode.proposed.contribEditSessions.d.ts rename src/{vs/platform/contextview/browser/contextMenuHandler.css => vscode-dts/vscode.proposed.contribMergeEditorToolbar.d.ts} (83%) create mode 100644 src/vscode-dts/vscode.proposed.contribShareMenu.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribViewSize.d.ts create mode 100644 src/vscode-dts/vscode.proposed.contribWebviewContext.d.ts create mode 100644 src/vscode-dts/vscode.proposed.documentPaste.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.inlineCompletions.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts create mode 100644 src/vscode-dts/vscode.proposed.interactiveWindow.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts rename src/{vs/workbench/contrib/profiles/common/profiles.contribution.ts => vscode-dts/vscode.proposed.notebookKernelSource.d.ts} (92%) delete mode 100644 src/vscode-dts/vscode.proposed.notebookProxyController.d.ts create mode 100644 src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts create mode 100644 src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts create mode 100644 src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts create mode 100644 src/vscode-dts/vscode.proposed.terminalExitReason.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.terminalNameChangeEvent.d.ts delete mode 100644 src/vscode-dts/vscode.proposed.textEditorDrop.d.ts create mode 100644 test/automation/src/task.ts create mode 100644 test/smoke/src/areas/task/task-quick-pick.test.ts create mode 100644 test/smoke/src/areas/task/task.test.ts create mode 100644 test/smoke/src/areas/terminal/terminal-helpers.ts diff --git a/.eslintrc.json b/.eslintrc.json index 48b1a9d982..481e18997c 100755 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -770,6 +770,7 @@ "chart.js", "plotly.js", "angular2-grid", + "kburtram-query-plan", "html-to-image", "turndown", "gridstack", @@ -1146,6 +1147,7 @@ "extensions/azuremonitor/src/prompts/**", "extensions/azuremonitor/src/typings/findRemove.d.ts", "extensions/kusto/src/prompts/**", + "extensions/mssql/src/hdfs/webhdfs.ts", "extensions/mssql/src/prompts/**", "extensions/mssql/src/typings/bufferStreamReader.d.ts", "extensions/mssql/src/typings/findRemove.d.ts", diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f041acf3c8..a8f9a23b95 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,5 +5,3 @@ * Ensure that the code is up-to-date with the `main` branch. * Include a description of the proposed changes and how to test them. --> - -This PR fixes # diff --git a/.github/workflows/bad-tag.yml b/.github/workflows/bad-tag.yml new file mode 100644 index 0000000000..e996639dfb --- /dev/null +++ b/.github/workflows/bad-tag.yml @@ -0,0 +1,22 @@ +name: Bad Tag +on: + create + +jobs: + main: + runs-on: ubuntu-latest + if: github.event.ref == '1.999.0' + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: "microsoft/vscode-github-triage-actions" + ref: stable + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run Bad Tag + uses: ./actions/tag-alert + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + tag-name: '1.999.0' diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml new file mode 100644 index 0000000000..308d839235 --- /dev/null +++ b/.github/workflows/basic.yml @@ -0,0 +1,177 @@ +name: Basic checks + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + main: + if: github.ref != 'refs/heads/main' + name: Compilation, Unit and Integration Tests + runs-on: ubuntu-latest + timeout-minutes: 40 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v3 + + # TODO: rename azure-pipelines/linux/xvfb.init to github-actions + - name: Setup Build Environment + run: | + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Compute node modules cache key + id: nodeModulesCacheKey + run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" + - name: Cache node modules + id: cacheNodeModules + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-cacheNodeModules23-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-cacheNodeModules23- + - name: Get yarn cache directory path + id: yarnCacheDirPath + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: Cache yarn directory + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + uses: actions/cache@v3 + with: + path: ${{ steps.yarnCacheDirPath.outputs.dir }} + key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-yarnCacheDir- + - name: Execute yarn + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + run: yarn --frozen-lockfile --network-timeout 180000 + + - name: Compile and Download + run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" + + - name: Run Unit Tests + id: electron-unit-tests + run: DISPLAY=:10 ./scripts/test.sh + + - name: Run Integration Tests (Electron) + id: electron-integration-tests + run: DISPLAY=:10 ./scripts/test-integration.sh + +# {{SQL CARBON TODO}} Bring back "Hygiene and Layering" and "Warm up node modules cache" +# hygiene: +# if: github.ref != 'refs/heads/main' +# name: Hygiene and Layering +# runs-on: ubuntu-latest +# timeout-minutes: 40 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# steps: +# - uses: actions/checkout@v3 + +# - uses: actions/setup-node@v3 +# with: +# node-version: 16 + +# - name: Compute node modules cache key +# id: nodeModulesCacheKey +# run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" +# - name: Cache node modules +# id: cacheNodeModules +# uses: actions/cache@v3 +# with: +# path: "**/node_modules" +# key: ${{ runner.os }}-cacheNodeModules23-${{ steps.nodeModulesCacheKey.outputs.value }} +# restore-keys: ${{ runner.os }}-cacheNodeModules23- +# - name: Get yarn cache directory path +# id: yarnCacheDirPath +# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} +# run: echo "::set-output name=dir::$(yarn cache dir)" +# - name: Cache yarn directory +# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} +# uses: actions/cache@v3 +# with: +# path: ${{ steps.yarnCacheDirPath.outputs.dir }} +# key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} +# restore-keys: ${{ runner.os }}-yarnCacheDir- +# - name: Execute yarn +# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} +# env: +# PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 +# ELECTRON_SKIP_BINARY_DOWNLOAD: 1 +# run: yarn --frozen-lockfile --network-timeout 180000 + +# - name: Run Hygiene Checks +# run: yarn gulp hygiene + +# - name: Run Valid Layers Checks +# run: yarn valid-layers-check + +# - name: Compile /build/ +# run: yarn --cwd build compile + +# - name: Check clean git state +# run: ./.github/workflows/check-clean-git-state.sh + +# - name: Run eslint +# run: yarn eslint + +# - name: Run vscode-dts Compile Checks +# run: yarn vscode-dts-compile-check + +# - name: Run Trusted Types Checks +# run: yarn tsec-compile-check + +# warm-cache: +# name: Warm up node modules cache +# if: github.ref == 'refs/heads/main' +# runs-on: ubuntu-latest +# timeout-minutes: 40 +# env: +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# steps: +# - uses: actions/checkout@v3 + +# - uses: actions/setup-node@v3 +# with: +# node-version: 16 + +# - name: Compute node modules cache key +# id: nodeModulesCacheKey +# run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" +# - name: Cache node modules +# id: cacheNodeModules +# uses: actions/cache@v3 +# with: +# path: "**/node_modules" +# key: ${{ runner.os }}-cacheNodeModules23-${{ steps.nodeModulesCacheKey.outputs.value }} +# restore-keys: ${{ runner.os }}-cacheNodeModules23- +# - name: Get yarn cache directory path +# id: yarnCacheDirPath +# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} +# run: echo "::set-output name=dir::$(yarn cache dir)" +# - name: Cache yarn directory +# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} +# uses: actions/cache@v3 +# with: +# path: ${{ steps.yarnCacheDirPath.outputs.dir }} +# key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} +# restore-keys: ${{ runner.os }}-yarnCacheDir- +# - name: Execute yarn +# if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} +# env: +# PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 +# ELECTRON_SKIP_BINARY_DOWNLOAD: 1 +# run: yarn --frozen-lockfile --network-timeout 180000 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a95a2ef83..f82bb50654 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,27 +1,29 @@ name: CI -on: - push: - branches: - - main - - release/* - pull_request: - branches: - - main - - release/* +on: workflow_dispatch + +# on: +# push: +# branches: +# - main +# - release/* +# pull_request: +# branches: +# - main +# - release/* jobs: windows: name: Windows - runs-on: windows-2019 - timeout-minutes: 30 + runs-on: windows-2022 + timeout-minutes: 60 env: CHILD_CONCURRENCY: "1" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: node-version: 16 @@ -248,8 +250,8 @@ jobs: uses: actions/cache@v2 with: path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModules14-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-cacheNodeModules14- + key: ${{ runner.os }}-cacheNodeModules23-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-cacheNodeModules23- - name: Get yarn cache directory path id: yarnCacheDirPath if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} @@ -273,24 +275,98 @@ jobs: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 run: yarn --frozen-lockfile --network-timeout 180000 + - name: Compile and Download + run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + + # This is required for keytar unittests, otherwise we hit + # https://github.com/atom/node-keytar/issues/76 + - name: Create temporary keychain + run: | + security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain + security default-keychain -s $RUNNER_TEMP/buildagent.keychain + security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain + + - name: Run Unit Tests (Electron) + run: DISPLAY=:10 ./scripts/test.sh + + - name: Run Unit Tests (node.js) + run: yarn test-node + + - name: Run Unit Tests (Browser, Chromium) + run: DISPLAY=:10 yarn test-browser-no-install --browser chromium + + - name: Run Integration Tests (Electron) + run: DISPLAY=:10 ./scripts/test-integration.sh + + - name: Run Integration Tests (Browser, Webkit) + run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser webkit + + - name: Run Integration Tests (Remote) + timeout-minutes: 15 + run: DISPLAY=:10 ./scripts/test-remote-integration.sh + + hygiene: + name: Hygiene and Layering + runs-on: ubuntu-latest + timeout-minutes: 40 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Compute node modules cache key + id: nodeModulesCacheKey + run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" + - name: Cache node modules + id: cacheNodeModules + uses: actions/cache@v2 + with: + path: "**/node_modules" + key: ${{ runner.os }}-cacheNodeModules23-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-cacheNodeModules23- + - name: Get yarn cache directory path + id: yarnCacheDirPath + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: Cache yarn directory + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + uses: actions/cache@v2 + with: + path: ${{ steps.yarnCacheDirPath.outputs.dir }} + key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-yarnCacheDir- + - name: Execute yarn + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + run: yarn --frozen-lockfile --network-timeout 180000 + + - name: Download Playwright + run: yarn playwright-install + - name: Run Hygiene Checks run: yarn gulp hygiene - name: Run Valid Layers Checks run: yarn valid-layers-check - # - name: Run Monaco Editor Checks {{SQL CARBON EDIT}} Remove Monaco checks - # run: yarn monaco-compile-check - - name: Compile /build/ run: yarn --cwd build compile + - name: Check clean git state + run: ./.github/workflows/check-clean-git-state.sh + - name: Run eslint run: yarn eslint - # {{SQL CARBON EDIT}} Don't need this - # - name: Run Monaco Editor Checks - # run: yarn monaco-compile-check # {{SQL CARBON EDIT}} Don't need this # - name: Run vscode-dts Compile Checks diff --git a/.github/workflows/pr-chat.yml b/.github/workflows/pr-chat.yml new file mode 100644 index 0000000000..da4538d5fd --- /dev/null +++ b/.github/workflows/pr-chat.yml @@ -0,0 +1,26 @@ +name: PR Chat +on: + pull_request_target: + types: [opened, ready_for_review, closed] + +jobs: + main: + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: "microsoft/vscode-github-triage-actions" + ref: stable + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run Code Review Chat + uses: ./actions/code-review-chat + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + slack_token: ${{ secrets.SLACK_TOKEN }} + slack_user_token: ${{ secrets.SLACK_USER_TOKEN }} + slack_bot_name: "VSCodeBot" + notification_channel: codereview diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..0cf077e6b4 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +16.14 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index fea1d8007a..20d53a66c6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,6 @@ "recommendations": [ "dbaeumer.vscode-eslint", "EditorConfig.EditorConfig", - "redhat.vscode-yaml" + "ms-vscode.vscode-selfhost-test-provider" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 78e04da078..b852ce65ee 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,14 +13,182 @@ }, { "type": "node", + "timeout": 0, + "port": 5870, + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "${workspaceFolder}/extensions/*/out/**/*.js" + ] + }, + { + "type": "pwa-chrome", + "request": "attach", + "name": "Attach to Shared Process", + "timeout": 30000, + "port": 9222, + "urlFilter": "*sharedProcess.html*", + "presentation": { + "hidden": true + } + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Search Process", + "port": 5876, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Pty Host Process", + "port": 5877, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to CLI Process", + "port": 5874, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Main Process", + "timeout": 30000, + "port": 5875, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "hidden": true, + } + }, + { + "type": "extensionHost", "request": "launch", - "name": "Launch Azure Data Studio", - "runtimeExecutable": "${workspaceFolder}/scripts/sql.sh", - "windows": { - "runtimeExecutable": "${workspaceFolder}/scripts/sql.bat", - }, - "runtimeArgs": [ - "--no-cached-data" + "name": "VS Code Emmet Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/emmet/test-workspace", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/emmet", + "--extensionTestsPath=${workspaceFolder}/extensions/emmet/out/test" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code Configuration Editing Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}/extensions/configuration-editing", + "--extensionTestsPath=${workspaceFolder}/extensions/configuration-editing/out/test" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code Git Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "/tmp/my4g9l", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/git", + "--extensionTestsPath=${workspaceFolder}/extensions/git/out/test" + ], + "outFiles": [ + "${workspaceFolder}/extensions/git/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code Github Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/github/testWorkspace", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/github", + "--extensionTestsPath=${workspaceFolder}/extensions/github/out/test" + ], + "outFiles": [ + "${workspaceFolder}/extensions/github/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code API Tests (single folder)", + "runtimeExecutable": "${execPath}", + "args": [ + // "${workspaceFolder}", // Uncomment for running out of sources. + "${workspaceFolder}/extensions/vscode-api-tests/testWorkspace", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-api-tests", + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/singlefolder-tests", + "--disable-extensions" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 3 + } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code API Tests (workspace)", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/vscode-api-tests/testworkspace.code-workspace", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-api-tests", + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-api-tests/out/workspace-tests" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 4 + } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code Tokenizer Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/vscode-colorize-tests/test", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-colorize-tests", + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-colorize-tests/out" ], "outFiles": [ "${workspaceFolder}/out/**/*.js" @@ -53,6 +221,9 @@ "runtimeArgs": [ "--inspect=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", ], "webRoot": "${workspaceFolder}", "cascadeTerminateToConfigurations": [ diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 1b3790c8cc..6ba33aa8ae 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:\"May 2022\"" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"July 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 90cc4971f4..d637ade8f2 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"April 2022\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"July 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index ad451a6f91..10d7fd63e5 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$inbox -label:\"needs more info\" sort:created-desc" + "value": "$inbox -label:\"info-needed\" sort:created-desc" }, { "kind": 2, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index e2356a2d8a..25b9625ef3 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\n\n$MILESTONE=milestone:\"April 2022\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\n\n$MILESTONE=milestone:\"July 2022\"\n\n$MINE=assignee:@me" }, { "kind": 1, @@ -147,7 +147,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:needs-triage -label:verification-found" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 3325493e44..84f67029d3 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce\n\n// current milestone name\n$milestone=milestone:\"May 2022\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce\n\n// current milestone name\n$milestone=milestone:\"July 2022\"" }, { "kind": 1, @@ -114,7 +114,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"needs more info\"", + "value": "$repos assignee:@me is:open label:\"needs more info\"" }, { "kind": 1, diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index b4a61ec261..7015a401be 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -12,7 +12,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\n$milestone=milestone:\"March 2022\"" + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\n$milestone=milestone:\"May 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/vscode-dev.github-issues b/.vscode/notebooks/vscode-dev.github-issues index 2178fa29d5..9266fd7654 100644 --- a/.vscode/notebooks/vscode-dev.github-issues +++ b/.vscode/notebooks/vscode-dev.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-dev milestone:\"December 2021\" is:open" + "value": "repo:microsoft/vscode-dev milestone:\"May 2022\" is:open" }, { "kind": 2, @@ -32,11 +32,11 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-remote-repositories-github milestone:\"December 2021\" is:open" + "value": "repo:microsoft/vscode-remote-repositories-github milestone:\"May 2022\" is:open" }, { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-remotehub milestone:\"December 2021\" is:open" + "value": "repo:microsoft/vscode-remotehub milestone:\"May 2022\" is:open" } -] +] \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e9104ae88..718cfd2205 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -67,6 +67,13 @@ } ], "git.ignoreLimitWarning": true, + "git.branchProtection": [ + "main", + "release/*" + ], + "git.branchProtectionPrompt": "alwaysCommitToNewBranch", + "git.branchRandomName.enable": true, + "git.mergeEditor": true, "remote.extensionKind": { "msjsdiag.debugger-for-chrome": "workspace" }, diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 5e87659ab6..7f299f04c7 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -112,47 +112,53 @@ Microsoft PROSE SDK: https://microsoft.github.io/prose 23. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 24. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 25. James-Yu/LaTeX-Workshop version 8.19.1 (https://github.com/James-Yu/LaTeX-Workshop) -26. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) -27. jeff-hykin/cpp-textmate-grammar version 1.15.5 (https://github.com/jeff-hykin/cpp-textmate-grammar) -28. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) -29. JuliaEditorSupport/atom-language-julia version 0.21.1 (https://github.com/JuliaEditorSupport/atom-language-julia) -30. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) -31. language-docker (https://github.com/moby/moby) -32. language-less version 0.34.2 (https://github.com/atom/language-less) -33. language-php version 0.46.2 (https://github.com/atom/language-php) -34. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) -35. marked version 1.1.0 (https://github.com/markedjs/marked) -36. mdn-data version 1.1.12 (https://github.com/mdn/data) -37. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) -38. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) -39. microsoft/vscode-markdown-tm-grammar version 1.0.0 (https://github.com/microsoft/vscode-markdown-tm-grammar) -40. microsoft/vscode-mssql version 1.9.0 (https://github.com/microsoft/vscode-mssql) -41. mmims/language-batchfile version 0.7.6 (https://github.com/mmims/language-batchfile) -42. NVIDIA/cuda-cpp-grammar (https://github.com/NVIDIA/cuda-cpp-grammar) -43. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) -44. rust-syntax version 0.5.0 (https://github.com/dustypomerleau/rust-syntax) -45. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -46. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -47. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -48. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -49. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -50. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -51. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -52. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -53. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -54. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -55. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) -56. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -57. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -58. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -59. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -60. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) -61. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) -62. Unicode version 12.0.0 (https://home.unicode.org/) -63. vscode-codicons version 0.0.14 (https://github.com/microsoft/vscode-codicons) -64. vscode-logfile-highlighter version 2.11.0 (https://github.com/emilast/vscode-logfile-highlighter) -65. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -66. Web Background Synchronization (https://github.com/WICG/background-sync) +26. jeff-hykin/better-c-syntax version 1.13.2 (https://github.com/jeff-hykin/better-c-syntax) +27. jeff-hykin/better-cpp-syntax version 1.15.18 (https://github.com/jeff-hykin/better-cpp-syntax) +28. jeff-hykin/better-objc-syntax version 0.2.0 (https://github.com/jeff-hykin/better-objc-syntax) +29. jeff-hykin/better-objcpp-syntax version 0.1.0 (https://github.com/jeff-hykin/better-objcpp-syntax) +30. jlelong/vscode-latex-basics version 1.3.0 (https://github.com/jlelong/vscode-latex-basics) +31. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) +32. JuliaEditorSupport/atom-language-julia version 0.22.1 (https://github.com/JuliaEditorSupport/atom-language-julia) +33. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) +34. language-docker (https://github.com/moby/moby) +35. language-less version 0.34.2 (https://github.com/atom/language-less) +36. language-php version 0.48.1 (https://github.com/atom/language-php) +37. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) +38. marked version 4.0.16 (https://github.com/markedjs/marked) +39. mdn-data version 1.1.12 (https://github.com/mdn/data) +40. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) +41. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +42. microsoft/vscode-markdown-tm-grammar version 1.0.0 (https://github.com/microsoft/vscode-markdown-tm-grammar) +43. microsoft/vscode-mssql version 1.16.0 (https://github.com/microsoft/vscode-mssql) +44. mmims/language-batchfile version 0.7.6 (https://github.com/mmims/language-batchfile) +45. NVIDIA/cuda-cpp-grammar (https://github.com/NVIDIA/cuda-cpp-grammar) +46. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) +47. rust-syntax version 0.5.0 (https://github.com/dustypomerleau/rust-syntax) +48. semver version 5.5.0 (https://github.com/npm/node-semver) +49. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +50. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) +51. sumneko/lua.tmbundle version 1.0.0 (https://github.com/sumneko/lua.tmbundle) +52. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) +53. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) +54. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) +55. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) +56. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) +57. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) +58. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) +59. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) +60. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) +61. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) +62. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) +63. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +64. trond-snekvik/vscode-rst version 1.5.1 (https://github.com/trond-snekvik/vscode-rst) +65. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) +66. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) +67. Unicode version 12.0.0 (https://home.unicode.org/) +68. vscode-codicons version 0.0.14 (https://github.com/microsoft/vscode-codicons) +69. vscode-logfile-highlighter version 2.15.0 (https://github.com/emilast/vscode-logfile-highlighter) +70. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +71. vscode-win32-app-container-tokens (https://github.com/microsoft/vscode-win32-app-container-tokens) +72. Web Background Synchronization (https://github.com/WICG/background-sync) %% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE @@ -1467,7 +1473,50 @@ END OF make-error NOTICES AND INFORMATION ========================================= The MIT License (MIT) -Copyright (c) 2021 REditorSupport +Copyright (c) 2014 Darin Morrison + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF freebroccolo/atom-language-swift NOTICES AND INFORMATION + +%% HTML 5.1 W3C Working Draft NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes material copied +from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.) + +THIS DOCUMENT IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF +THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY +PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE +DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF. + +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to this document or its contents +without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders. +========================================= +END OF HTML 5.1 W3C Working Draft NOTICES AND INFORMATION + +%% Ikuyadeu/vscode-R NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2022 REditorSupport Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1493,243 +1542,585 @@ END OF mark.js NOTICES AND INFORMATION ========================================= This software is released under the MIT license: -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF ionide/ionide-fsgrammar NOTICES AND INFORMATION + +%% James-Yu/LaTeX-Workshop NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2016 James Yu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ========================================= -END OF minimist NOTICES AND INFORMATION +END OF James-Yu/LaTeX-Workshop NOTICES AND INFORMATION -%% moment NOTICES AND INFORMATION BEGIN HERE +%% jeff-hykin/better-c-syntax NOTICES AND INFORMATION BEGIN HERE ========================================= -Copyright (c) JS Foundation and other contributors +MIT License -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: +Copyright (c) 2019 Jeff Hykin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF jeff-hykin/better-c-syntax NOTICES AND INFORMATION + +%% jeff-hykin/better-cpp-syntax NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Jeff Hykin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF jeff-hykin/better-cpp-syntax NOTICES AND INFORMATION + +%% jeff-hykin/better-objc-syntax NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Jeff Hykin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF jeff-hykin/better-objc-syntax NOTICES AND INFORMATION + +%% jeff-hykin/better-objcpp-syntax NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Jeff Hykin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +========================================= +END OF jeff-hykin/better-objcpp-syntax NOTICES AND INFORMATION + +%% jlelong/vscode-latex-basics NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) vscode-latex-basics authors + +If not otherwise specified (see below), files in this repository fall under the MIT License + + +The file syntaxes/LaTeX.tmLanguage.json is based on https://github.com/textmate/latex.tmbundle/blob/master/Syntaxes/LaTeX.plist +but has been largely modified. The original file falls under the following license + +Permission to copy, use, modify, sell and distribute this +software is granted. This software is provided "as is" without +express or implied warranty, and with no claim as to its +suitability for any purpose. + +The file syntaxes/markdown-latex-combined.tmLanguage.json is generated from the Markdown grammar +included in VSCode and falls under the license described in markdown-latex-combined-license.txt. + +The file syntaxes/cpp-grammar-bailout.tmLanguage.json is generated from https://github.com/jeff-hykin/better-cpp-syntax +and falls under the license described in cpp-bailout-license.txt. +========================================= +END OF jlelong/vscode-latex-basics NOTICES AND INFORMATION + +%% js-beautify NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF js-beautify NOTICES AND INFORMATION + +%% JuliaEditorSupport/atom-language-julia NOTICES AND INFORMATION BEGIN HERE +========================================= +The atom-language-julia package is licensed under the MIT "Expat" License: + +> Copyright (c) 2015 +> +> Permission is hereby granted, free of charge, to any person obtaining +> a copy of this software and associated documentation files (the +> "Software"), to deal in the Software without restriction, including +> without limitation the rights to use, copy, modify, merge, publish, +> distribute, sublicense, and/or sell copies of the Software, and to +> permit persons to whom the Software is furnished to do so, subject to +> the following conditions: +> +> The above copyright notice and this permission notice shall be +> included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +> CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF JuliaEditorSupport/atom-language-julia NOTICES AND INFORMATION + +%% Jxck/assert NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2011 Jxck + +Originally from node.js (http://nodejs.org) +Copyright Joyent, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +========================================= +END OF Jxck/assert NOTICES AND INFORMATION + +%% language-docker NOTICES AND INFORMATION BEGIN HERE +========================================= +Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2018 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF language-docker NOTICES AND INFORMATION + +%% language-less NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This package was derived from a TextMate bundle located at +https://github.com/textmate/less.tmbundle and distributed under the following +license, located in `LICENSE.md`: + +Copyright (c) 2010 Scott Kyle and Rasmus Andersson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. ========================================= -END OF moment NOTICES AND INFORMATION +END OF language-less NOTICES AND INFORMATION - -%% mxgraph NOTICES AND INFORMATION BEGIN HERE +%% language-php NOTICES AND INFORMATION BEGIN HERE ========================================= -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +The MIT License (MIT) -1. Definitions. +Copyright (c) 2014 GitHub Inc. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +This package was derived from a TextMate bundle located at +https://github.com/textmate/php.tmbundle and distributed under the following +license, located in `README.mdown`: - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - (e) Neither the Work nor Derivative Works may be used or form any - part of a larger work that integrates or is supposed to be - integrated with a product or service owned or marketed by an - Atlassian entity, including its successors and assignees in title. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -//SEIBERT/MEDIA GmbH, Wiesbaden, Germany is the exclusive licensee of -JGraph for software products based on this codebase within the Atlassian -ecosystem of products. +Permission to copy, use, modify, sell and distribute this +software is granted. This software is provided "as is" without +express or implied warranty, and with no claim as to its +suitability for any purpose. ========================================= -END OF mxgraph NOTICES AND INFORMATION +END OF language-php NOTICES AND INFORMATION -%% native-keymap NOTICES AND INFORMATION BEGIN HERE +%% MagicStack/MagicPython NOTICES AND INFORMATION BEGIN HERE ========================================= -Copyright (c) Microsoft Corporation +The MIT License +Copyright (c) 2015-present MagicStack Inc. http://magic.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +========================================= +END OF MagicStack/MagicPython NOTICES AND INFORMATION + +%% marked NOTICES AND INFORMATION BEGIN HERE +========================================= +information + +## Contribution License Agreement + +If you contribute code to this project, you are implicitly allowing your code +to be distributed under the MIT license. You are also implicitly verifying that +all code is your original work. `` + +## Marked + +Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/) +Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Markdown + +Copyright © 2004, John Gruber +http://daringfireball.net/ All rights reserved. MIT License @@ -1958,23 +2349,11 @@ THE SOFTWARE. ========================================= END OF optimist NOTICES AND INFORMATION -%% primeng NOTICES AND INFORMATION BEGIN HERE +%% vscode-swift NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) -Copyright (c) 2016-2017 PrimeTek - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= -END OF primeng NOTICES AND INFORMATION - -%% process-nextick-args NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright (c) 2012-2015, Christopher Jeffrey (https://github.com/chjj/) +Copyright (c) 2015 David Owens II Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1983,174 +2362,45 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= -END OF pty.js NOTICES AND INFORMATION +END OF vscode-swift NOTICES AND INFORMATION -%% PyZMQ NOTICES AND INFORMATION BEGIN HERE +%% vscode-win32-app-container-tokens NOTICES AND INFORMATION BEGIN HERE ========================================= -Copyright (c) 2009-2012, Brian Granger, Min Ragan-Kelley +MIT License -All rights reserved. +Copyright (c) Microsoft Corporation. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -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. - -Neither the name of PyZMQ nor the names of its contributors may be used to -endorse or promote products derived from this software without specific prior -written permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE ========================================= -END OF pyzmq NOTICES AND INFORMATION +END OF vscode-win32-app-container-tokens NOTICES AND INFORMATION -%% reflect-metadata NOTICES AND INFORMATION BEGIN HERE -========================================= -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS -========================================= -END OF reflect-metadata NOTICES AND INFORMATION - -%% request NOTICES AND INFORMATION BEGIN HERE -========================================= -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS -========================================= -END OF request NOTICES AND INFORMATION - -%% rxjs NOTICES AND INFORMATION BEGIN HERE +%% Web Background Synchronization NOTICES AND INFORMATION BEGIN HERE ========================================= Apache License Version 2.0, January 2004 diff --git a/build/.cachesalt b/build/.cachesalt index 3b2a71b972..c0d4fa40fc 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2022-10-06T02:27:18.022Z +2022-07-19T07:55:26.168Z diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000000..5a8136bb88 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1 @@ +.yarnrc diff --git a/build/.moduleignore b/build/.moduleignore index 986fe2a915..e018f82e09 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -139,6 +139,14 @@ vscode-encrypt/binding.gyp vscode-encrypt/README.md !vscode-encrypt/build/Release/vscode-encrypt-native.node +vscode-policy-watcher/build/** +vscode-policy-watcher/.husky/** +vscode-policy-watcher/src/** +vscode-policy-watcher/binding.gyp +vscode-policy-watcher/README.md +vscode-policy-watcher/index.d.ts +!vscode-policy-watcher/build/Release/vscode-policy-watcher.node + vscode-windows-ca-certs/**/* !vscode-windows-ca-certs/package.json !vscode-windows-ca-certs/**/*.node diff --git a/build/azure-pipelines/common/computeNodeModulesCacheKey.js b/build/azure-pipelines/common/computeNodeModulesCacheKey.js index ebe6d97415..61bbe9e27f 100644 --- a/build/azure-pipelines/common/computeNodeModulesCacheKey.js +++ b/build/azure-pipelines/common/computeNodeModulesCacheKey.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const path = require("path"); @@ -14,7 +14,7 @@ shasum.update(fs.readFileSync(path.join(ROOT, 'build/.cachesalt'))); shasum.update(fs.readFileSync(path.join(ROOT, '.yarnrc'))); shasum.update(fs.readFileSync(path.join(ROOT, 'remote/.yarnrc'))); // Add `package.json` and `yarn.lock` files -for (let dir of dirs) { +for (const dir of dirs) { const packageJsonPath = path.join(ROOT, dir, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); const relevantPackageJsonSections = { diff --git a/build/azure-pipelines/common/computeNodeModulesCacheKey.ts b/build/azure-pipelines/common/computeNodeModulesCacheKey.ts index 8594304df7..8b8869f378 100644 --- a/build/azure-pipelines/common/computeNodeModulesCacheKey.ts +++ b/build/azure-pipelines/common/computeNodeModulesCacheKey.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; @@ -19,7 +17,7 @@ shasum.update(fs.readFileSync(path.join(ROOT, '.yarnrc'))); shasum.update(fs.readFileSync(path.join(ROOT, 'remote/.yarnrc'))); // Add `package.json` and `yarn.lock` files -for (let dir of dirs) { +for (const dir of dirs) { const packageJsonPath = path.join(ROOT, dir, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); const relevantPackageJsonSections = { diff --git a/build/azure-pipelines/common/createAsset.js b/build/azure-pipelines/common/createAsset.js index 7f2821aaaf..b728d74d40 100644 --- a/build/azure-pipelines/common/createAsset.js +++ b/build/azure-pipelines/common/createAsset.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const crypto = require("crypto"); diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts index d0193ade67..fff05be615 100644 --- a/build/azure-pipelines/common/createAsset.ts +++ b/build/azure-pipelines/common/createAsset.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fs from 'fs'; import { Readable } from 'stream'; import * as crypto from 'crypto'; diff --git a/build/azure-pipelines/common/createBuild.js b/build/azure-pipelines/common/createBuild.js index 4093db2f9d..dcc6f969bf 100644 --- a/build/azure-pipelines/common/createBuild.js +++ b/build/azure-pipelines/common/createBuild.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const identity_1 = require("@azure/identity"); const cosmos_1 = require("@azure/cosmos"); diff --git a/build/azure-pipelines/common/createBuild.ts b/build/azure-pipelines/common/createBuild.ts index 46fcdf267d..9663d6b3ee 100644 --- a/build/azure-pipelines/common/createBuild.ts +++ b/build/azure-pipelines/common/createBuild.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { ClientSecretCredential } from '@azure/identity'; import { CosmosClient } from '@azure/cosmos'; import { retry } from './retry'; diff --git a/build/azure-pipelines/common/listNodeModules.js b/build/azure-pipelines/common/listNodeModules.js index 5fad20ec79..308f1882a9 100644 --- a/build/azure-pipelines/common/listNodeModules.js +++ b/build/azure-pipelines/common/listNodeModules.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const path = require("path"); diff --git a/build/azure-pipelines/common/listNodeModules.ts b/build/azure-pipelines/common/listNodeModules.ts index c96df52179..1331087bb9 100644 --- a/build/azure-pipelines/common/listNodeModules.ts +++ b/build/azure-pipelines/common/listNodeModules.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fs from 'fs'; import * as path from 'path'; diff --git a/build/azure-pipelines/common/releaseBuild.js b/build/azure-pipelines/common/releaseBuild.js index f631696131..3d7046de9d 100644 --- a/build/azure-pipelines/common/releaseBuild.js +++ b/build/azure-pipelines/common/releaseBuild.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const identity_1 = require("@azure/identity"); const cosmos_1 = require("@azure/cosmos"); diff --git a/build/azure-pipelines/common/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index 521267f938..1ea27a6964 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { ClientSecretCredential } from '@azure/identity'; import { CosmosClient } from '@azure/cosmos'; import { retry } from './retry'; diff --git a/build/azure-pipelines/common/retry.js b/build/azure-pipelines/common/retry.js index 06c016281c..d2cdab9c8c 100644 --- a/build/azure-pipelines/common/retry.js +++ b/build/azure-pipelines/common/retry.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.retry = void 0; async function retry(fn) { diff --git a/build/azure-pipelines/common/retry.ts b/build/azure-pipelines/common/retry.ts index 2f0afd925c..1703a4f97a 100644 --- a/build/azure-pipelines/common/retry.ts +++ b/build/azure-pipelines/common/retry.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - export async function retry(fn: () => Promise): Promise { let lastError: Error | undefined; diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index f82e7a8b98..059e848c0b 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -35,25 +35,66 @@ steps: git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") displayName: Merge distro + - script: | + mkdir -p .build + node build/azure-pipelines/common/computeNodeModulesCacheKey.js x64 $ENABLE_TERRAPIN > .build/yarnlockhash + displayName: Prepare yarn cache flags + + - task: Cache@2 + inputs: + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache + + - script: | + set -e + tar -xzf .build/node_modules_cache/cache.tgz + displayName: Extract node_modules cache + condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + npm install -g node-gyp@latest + node-gyp --version + displayName: Update node-gyp + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + - script: | set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 retryCountOnTaskFailure: 3 - condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages - script: | set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + for i in {1..3}; do # try 3 times, for Terrapin - yarn --cwd build --frozen-lockfile --check-files && break + yarn --frozen-lockfile --check-files && break if [ $i -eq 3 ]; then echo "Yarn failed too many times" >&2 exit 1 fi echo "Yarn failed $i, trying again..." done - displayName: Install build dependencies + env: + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + mkdir -p .build/node_modules_cache + tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Create node_modules archive - download: current artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index e28da6547e..1094b41ca2 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -1,270 +1,213 @@ +parameters: + - name: VSCODE_QUALITY + type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + steps: - - task: NodeTool@0 - inputs: - versionSpec: "16.x" - - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" - - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output - - - script: | - set -e - tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output - - # Set up the credentials to retrieve distro repo and setup git persona - # to create a merge commit for when we merge distro into oss - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF - - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling - - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit - - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro - - - script: | - mkdir -p .build - node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash - displayName: Prepare yarn cache flags - - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache - - - script: | - set -e - tar -xzf .build/node_modules_cache/cache.tgz - condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Extract node_modules cache - - - script: | - set -e - npm install -g node-gyp@latest - node-gyp --version - displayName: Update node-gyp - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - - script: | - set -e - npx https://aka.ms/enablesecurefeed standAlone - timeoutInMinutes: 5 - retryCountOnTaskFailure: 3 - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) - displayName: Switch to Terrapin packages - - - script: | - set -e - export npm_config_arch=$(VSCODE_ARCH) - export npm_config_node_gyp=$(which node-gyp) - - for i in {1..3}; do # try 3 times, for Terrapin - yarn --frozen-lockfile --check-files && break - if [ $i -eq 3 ]; then - echo "Yarn failed too many times" >&2 - exit 1 - fi - echo "Yarn failed $i, trying again..." - done - env: - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Install dependencies - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - - script: | - set -e - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -p .build/node_modules_cache - tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Create node_modules archive - - # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality - - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci - displayName: Build client - - - script: | - set -e - node build/azure-pipelines/mixin --server - displayName: Mix in server quality - - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci - displayName: Build Server - - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" displayName: Download Electron and Playwright - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - # Setting hardened entitlements is a requirement for: - # * Running tests on Big Sur (because Big Sur has additional security precautions) - - script: | - set -e - security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - security default-keychain -s $(agent.tempdirectory)/buildagent.keychain - security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 - security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain - VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js - displayName: Set Hardened Entitlements + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + ./scripts/test.sh --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 - - script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 + - script: | + set -e + yarn test-node + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 - - script: | - set -e - yarn test-node --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --sequential --browser chromium --browser webkit --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 - - script: | - set -e - DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium & Webkit) - timeoutInMinutes: 30 + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - script: | + set -e + yarn test-node --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser, Webkit) - timeoutInMinutes: 20 + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-remote-integration.sh - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + yarn gulp \ + compile-extension:configuration-editing \ + compile-extension:css-language-features-server \ + compile-extension:emmet \ + compile-extension:git \ + compile-extension:github-authentication \ + compile-extension:html-language-features-server \ + compile-extension:ipynb \ + compile-extension:json-language-features-server \ + compile-extension:markdown-language-features-server \ + compile-extension:markdown-language-features \ + compile-extension-media \ + compile-extension:microsoft-authentication \ + compile-extension:typescript-language-features \ + compile-extension:vscode-api-tests \ + compile-extension:vscode-colorize-tests \ + compile-extension:vscode-notebook-tests \ + compile-extension:vscode-test-resolver + displayName: Build integration tests - - script: | - set -e - ps -ef - displayName: Diagnostics before smoke test run - continueOnError: true - condition: succeededOrFailed() + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --web --tracing --headless - timeoutInMinutes: 10 - displayName: Run smoke tests (Browser, Chromium) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 20 - displayName: Run smoke tests (Electron) + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser, Webkit) + timeoutInMinutes: 20 - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 20 - displayName: Run smoke tests (Remote) + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-remote-integration.sh + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 - - script: | - set -e - ps -ef - displayName: Diagnostics after smoke test run - continueOnError: true - condition: succeededOrFailed() + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + ps -ef + displayName: Diagnostics before smoke test run + continueOnError: true + condition: succeededOrFailed() - - task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-macos-$(VSCODE_ARCH) - targetPath: .build/crashes - displayName: "Publish Crash Reports" - continueOnError: true - condition: failed() + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + yarn --cwd test/smoke compile + displayName: Compile smoke tests - # In order to properly symbolify above crash reports - # (if any), we need the compiled native modules too - - task: PublishPipelineArtifact@0 - inputs: - artifactName: node-modules-macos-$(VSCODE_ARCH) - targetPath: node_modules - displayName: "Publish Node Modules" - continueOnError: true - condition: failed() + - script: | + set -e + yarn smoketest-no-compile --tracing + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) - - task: PublishPipelineArtifact@0 - inputs: - artifactName: logs-macos-$(VSCODE_ARCH)-$(System.JobAttempt) - targetPath: .build/logs - displayName: "Publish Log Files" - continueOnError: true - condition: failed() + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME" + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) + + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --web --tracing --headless + timeoutInMinutes: 20 + displayName: Run smoke tests (Browser, Chromium) + + - script: | + set -e + yarn gulp compile-extension:vscode-test-resolver + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" + timeoutInMinutes: 20 + displayName: Run smoke tests (Remote) + + - script: | + set -e + ps -ef + displayName: Diagnostics after smoke test run + continueOnError: true + condition: succeededOrFailed() + + - ${{ if or(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + - task: PublishPipelineArtifact@0 + inputs: + targetPath: .build/crashes + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: crash-dump-macos-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: crash-dump-macos-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: crash-dump-macos-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() + + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + targetPath: node_modules + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: node-modules-macos-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: node-modules-macos-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: node-modules-macos-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + + - task: PublishPipelineArtifact@0 + inputs: + targetPath: .build/logs + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: logs-macos-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: logs-macos-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: logs-macos-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Log Files" + continueOnError: true + condition: succeededOrFailed() - task: PublishTestResults@2 displayName: Publish Tests Results diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index 1b8cfef673..929aaf4203 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -51,6 +51,50 @@ steps: set -e tar -xzf .build/node_modules_cache/cache.tgz displayName: Extract node_modules cache + condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + npm install -g node-gyp@latest + node-gyp --version + displayName: Update node-gyp + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + npx https://aka.ms/enablesecurefeed standAlone + timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) + displayName: Switch to Terrapin packages + + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + env: + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + mkdir -p .build/node_modules_cache + tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Create node_modules archive - script: | set -e diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 5c524dd42f..eda79c53cf 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -1,50 +1,73 @@ +parameters: + - name: VSCODE_PUBLISH + type: boolean + - name: VSCODE_QUALITY + type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + steps: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - checkout: self + fetchDepth: 1 + retryCountOnTaskFailure: 3 + - task: NodeTool@0 inputs: versionSpec: "16.x" - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Compilation + path: $(Build.ArtifactStagingDirectory) + displayName: Download compilation output - - script: | - set -e - tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz + displayName: Extract compilation output - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | mkdir -p .build @@ -64,13 +87,6 @@ steps: condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) displayName: Extract node_modules cache - - script: | - set -e - npm install -g node-gyp@latest - node-gyp --version - displayName: Update node-gyp - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | set -e npx https://aka.ms/enablesecurefeed standAlone @@ -107,115 +123,142 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) displayName: Create node_modules archive - # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci - displayName: Build client + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build client - - script: | - set -e - node build/azure-pipelines/mixin --server - displayName: Mix in server quality + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci - displayName: Build Server + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build Server - # Setting hardened entitlements is a requirement for: - # * Apple notarization - # * Running tests on Big Sur (because Big Sur has additional security precautions) - - script: | - set -e - security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - security default-keychain -s $(agent.tempdirectory)/buildagent.keychain - security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain - echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 - security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain - VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js - displayName: Set Hardened Entitlements + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp "transpile-client" "transpile-extensions" + displayName: Transpile - - script: | - set -e - pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd - displayName: Archive build + - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + - template: product-build-darwin-test.yml + parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} + VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - - script: | - set -e + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + # Setting hardened entitlements is a requirement for: + # * Apple notarization + # * Running tests on Big Sur (because Big Sur has additional security precautions) + - script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements - # package Remote Extension Host - pushd .. && mv vscode-reh-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH) && zip -Xry vscode-server-darwin-$(VSCODE_ARCH).zip vscode-server-darwin-$(VSCODE_ARCH) && popd + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - script: | + set -e + pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd + displayName: Archive build - # package Remote Extension Host (Web) - pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd - displayName: Prepare to publish servers + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - script: | + set -e - - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip - artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive - displayName: Publish client archive + # package Remote Extension Host + pushd .. && mv vscode-reh-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH) && zip -Xry vscode-server-darwin-$(VSCODE_ARCH).zip vscode-server-darwin-$(VSCODE_ARCH) && popd - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip - artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish server archive + # package Remote Extension Host (Web) + pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd + displayName: Prepare to publish servers - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip - artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish web server archive + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (client) + inputs: + BuildDropPath: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + PackageName: Visual Studio Code - - task: AzureCLI@2 - inputs: - azureSubscription: "vscode-builds-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom - - script: | - set -e - AZURE_STORAGE_ACCOUNT="ticino" \ - AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ - AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - VSCODE_ARCH="$(VSCODE_ARCH)" \ - node build/azure-pipelines/upload-configuration - displayName: Upload configuration (for Bing settings search) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), ne(variables['VSCODE_PUBLISH'], 'false')) - continueOnError: true + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (server) + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) + PackageName: Visual Studio Code Server - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (client) - inputs: - BuildDropPath: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - PackageName: Visual Studio Code - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (server) + artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom - - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip + artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + displayName: Publish client archive - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (server) - inputs: - BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) - PackageName: Visual Studio Code Server - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip + artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish server archive - - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (server) - artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip + artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish web server archive + + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + + - ${{ if and(eq(parameters.VSCODE_PUBLISH, true), eq(parameters.VSCODE_RUN_UNIT_TESTS, false), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + - script: | + set -e + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + VSCODE_ARCH="$(VSCODE_ARCH)" \ + node build/azure-pipelines/upload-configuration + displayName: Upload configuration (for Bing settings search) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + continueOnError: true diff --git a/build/azure-pipelines/darwin/sql-product-build-darwin.yml b/build/azure-pipelines/darwin/sql-product-build-darwin.yml index fb5082ecaf..b3e07e96df 100644 --- a/build/azure-pipelines/darwin/sql-product-build-darwin.yml +++ b/build/azure-pipelines/darwin/sql-product-build-darwin.yml @@ -112,18 +112,19 @@ steps: displayName: Run unit tests condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true')) - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin-x64 - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-darwin" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run core integration tests - condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true')) +# {{SQL CARBON TODO}} Reenable "Run Core Integration Tests" + # - script: | + # # Figure out the full absolute path of the product we just built + # # including the remote server and configure the integration tests + # # to run with these builds instead of running out of sources. + # set -e + # APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin-x64 + # APP_NAME="`ls $APP_ROOT | head -n 1`" + # INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + # VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-darwin" \ + # ./scripts/test-integration.sh --build --tfs "Integration Tests" + # displayName: Run core integration tests + # condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true')) - script: | set -e diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 19af20b090..db2d245cc5 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -4,9 +4,7 @@ pool: trigger: branches: include: ["main", "release/*"] -pr: - branches: - include: ["main", "release/*"] +pr: none steps: - task: NodeTool@0 diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index 74577b52a6..3aef727924 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -118,7 +118,9 @@ steps: - script: | set -e - docker run -e VSCODE_QUALITY -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:alpine-$(VSCODE_ARCH) /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh + docker run -e VSCODE_QUALITY -e GITHUB_TOKEN -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:alpine-$(VSCODE_ARCH) /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Prebuild - script: | diff --git a/build/azure-pipelines/linux/product-build-linux-client-test.yml b/build/azure-pipelines/linux/product-build-linux-client-test.yml new file mode 100644 index 0000000000..31d477e93a --- /dev/null +++ b/build/azure-pipelines/linux/product-build-linux-client-test.yml @@ -0,0 +1,272 @@ +parameters: + - name: VSCODE_QUALITY + type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + +steps: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" + displayName: Download Electron and Playwright + + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + displayName: Setup build environment + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + ELECTRON_ROOT=.build/electron + sudo chown root $APP_ROOT/chrome-sandbox + sudo chown root $ELECTRON_ROOT/chrome-sandbox + sudo chmod 4755 $APP_ROOT/chrome-sandbox + sudo chmod 4755 $ELECTRON_ROOT/chrome-sandbox + stat $APP_ROOT/chrome-sandbox + stat $ELECTRON_ROOT/chrome-sandbox + displayName: Change setuid helper binary permission + + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + DISPLAY=:10 ./scripts/test.sh --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + + - script: | + set -e + yarn test-node + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 15 + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + + - script: | + set -e + yarn test-node --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 15 + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + yarn gulp \ + compile-extension:configuration-editing \ + compile-extension:css-language-features-server \ + compile-extension:emmet \ + compile-extension:git \ + compile-extension:github-authentication \ + compile-extension:html-language-features-server \ + compile-extension:ipynb \ + compile-extension:json-language-features-server \ + compile-extension:markdown-language-features-server \ + compile-extension:markdown-language-features \ + compile-extension-media \ + compile-extension:microsoft-authentication \ + compile-extension:typescript-language-features \ + compile-extension:vscode-api-tests \ + compile-extension:vscode-colorize-tests \ + compile-extension:vscode-notebook-tests \ + compile-extension:vscode-test-resolver + displayName: Build integration tests + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + DISPLAY=:10 ./scripts/test-integration.sh --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + + - script: | + set -e + ./scripts/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser, Chromium) + timeoutInMinutes: 20 + + - script: | + set -e + ./scripts/test-remote-integration.sh + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + ./scripts/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser, Chromium) + timeoutInMinutes: 20 + + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + ./scripts/test-remote-integration.sh + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + ps -ef + cat /proc/sys/fs/inotify/max_user_watches + lsof | wc -l + displayName: Diagnostics before smoke test run (processes, max_user_watches, number of opened file handles) + continueOnError: true + condition: succeededOrFailed() + + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + yarn --cwd test/smoke compile + displayName: Compile smoke tests + + - script: | + set -e + yarn smoketest-no-compile --tracing + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) + + - script: | + set -e + yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" + timeoutInMinutes: 20 + displayName: Run smoke tests (Browser, Chromium) + + - script: | + set -e + yarn gulp compile-extension:vscode-test-resolver + yarn smoketest-no-compile --remote --tracing + timeoutInMinutes: 20 + displayName: Run smoke tests (Remote) + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + yarn smoketest-no-compile --tracing --build "$APP_PATH" + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) + + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" + timeoutInMinutes: 20 + displayName: Run smoke tests (Browser, Chromium) + + - script: | + set -e + yarn gulp compile-extension:vscode-test-resolver + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" + timeoutInMinutes: 20 + displayName: Run smoke tests (Remote) + + - script: | + set -e + ps -ef + cat /proc/sys/fs/inotify/max_user_watches + lsof | wc -l + displayName: Diagnostics after smoke test run (processes, max_user_watches, number of opened file handles) + continueOnError: true + condition: succeededOrFailed() + + - ${{ if or(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + - task: PublishPipelineArtifact@0 + inputs: + targetPath: .build/crashes + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: crash-dump-linux-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: crash-dump-linux-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: crash-dump-linux-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() + + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + targetPath: node_modules + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: node-modules-linux-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: node-modules-linux-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: node-modules-linux-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + + - task: PublishPipelineArtifact@0 + inputs: + targetPath: .build/logs + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: logs-linux-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: logs-linux-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: logs-linux-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Log Files" + continueOnError: true + condition: succeededOrFailed() + + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index b6472b5e57..97a9cf31d6 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -1,79 +1,113 @@ +parameters: + - name: VSCODE_PUBLISH + type: boolean + - name: VSCODE_QUALITY + type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + steps: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - checkout: self + fetchDepth: 1 + retryCountOnTaskFailure: 3 + - task: NodeTool@0 inputs: versionSpec: "16.x" - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Compilation + path: $(Build.ArtifactStagingDirectory) + displayName: Download compilation output - - task: DownloadPipelineArtifact@2 - inputs: - artifact: reh_node_modules-$(VSCODE_ARCH) - path: $(Build.ArtifactStagingDirectory) - displayName: Download server build dependencies - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: reh_node_modules-$(VSCODE_ARCH) + path: $(Build.ArtifactStagingDirectory) + displayName: Download server build dependencies + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) - - script: | - set -e - # Start X server - /etc/init.d/xvfb start - # Start dbus session - DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) - echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" - displayName: Setup system services - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + # Start X server + /etc/init.d/xvfb start + # Start dbus session + DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) + echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" + displayName: Setup system services + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) - - script: | - set -e - tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz + displayName: Extract compilation output - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | mkdir -p .build node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash displayName: Prepare yarn cache flags - - task: Cache@2 - inputs: - key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" - path: .build/node_modules_cache - cacheHitVar: NODE_MODULES_RESTORED - displayName: Restore node_modules cache + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - task: Cache@2 + inputs: + key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: Cache@2 + inputs: + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache - script: | set -e @@ -91,6 +125,7 @@ steps: - script: | set -e + node build/npm/setupBuildYarnrc for i in {1..3}; do # try 3 times, for Terrapin yarn --cwd build --frozen-lockfile --check-files && break if [ $i -eq 3 ]; then @@ -103,7 +138,16 @@ steps: - script: | set -e - export npm_config_arch=$(NPM_ARCH) + if [ "$NPM_ARCH" = "armv7l" ]; then + # There is no target_arch="armv7l" supported by node_gyp, + # arm versions for compilation are decided based on the CC + # macros. + # Mapping value is based on + # https://github.com/nodejs/node/blob/0903515e126c2697042d6546c6aa4b72e1a4b33e/configure.py#L49-L50 + export npm_config_arch="arm" + else + export npm_config_arch=$(NPM_ARCH) + fi if [ -z "$CC" ] || [ -z "$CXX" ]; then # Download clang based on chromium revision used by vscode @@ -143,12 +187,13 @@ steps: displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | - set -e - rm -rf remote/node_modules - tar -xzf $(Build.ArtifactStagingDirectory)/reh_node_modules-$(VSCODE_ARCH).tar.gz --directory $(Build.SourcesDirectory)/remote - displayName: Extract server node_modules output - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + rm -rf remote/node_modules + tar -xzf $(Build.ArtifactStagingDirectory)/reh_node_modules-$(VSCODE_ARCH).tar.gz --directory $(Build.SourcesDirectory)/remote + displayName: Extract server node_modules output + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) - script: | set -e @@ -158,267 +203,136 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) displayName: Create node_modules archive - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci - displayName: Build + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + displayName: Build - - script: | - set -e - node build/azure-pipelines/mixin --server - displayName: Mix in server quality + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci - displayName: Build Server + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + displayName: Build Server - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" - displayName: Download Electron and Playwright + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp "transpile-client" "transpile-extensions" + displayName: Transpile - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - ELECTRON_ROOT=.build/electron - sudo chown root $APP_ROOT/chrome-sandbox - sudo chown root $ELECTRON_ROOT/chrome-sandbox - sudo chmod 4755 $APP_ROOT/chrome-sandbox - sudo chmod 4755 $ELECTRON_ROOT/chrome-sandbox - stat $APP_ROOT/chrome-sandbox - stat $ELECTRON_ROOT/chrome-sandbox - displayName: Change setuid helper binary permission + - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + - template: product-build-linux-client-test.yml + parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} + VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - - script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + displayName: Build deb, rpm packages - - script: | - set -e - yarn test-node --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + displayName: Prepare snap package - - script: | - set -e - DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: UseDotNet@2 + inputs: + version: 2.x - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_APP_NAME="$APP_NAME" \ - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: EsrpClientTool@1 + displayName: Download ESRPClient - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - ./scripts/test-web-integration.sh --browser chromium - displayName: Run integration tests (Browser, Chromium) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - script: | + set -e + node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' + displayName: Codesign rpm - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_APP_NAME="$APP_NAME" \ - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - ./scripts/test-remote-integration.sh - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - script: | + set -e + VSCODE_ARCH="$(VSCODE_ARCH)" \ + ./build/azure-pipelines/linux/prepare-publish.sh + displayName: Prepare for Publish - - script: | - set -e - ps -ef - cat /proc/sys/fs/inotify/max_user_watches - lsof | wc -l - displayName: Diagnostics before smoke test run (processes, max_user_watches, number of opened file handles) - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (client) + inputs: + BuildDropPath: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + PackageName: Visual Studio Code - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" - timeoutInMinutes: 10 - displayName: Run smoke tests (Browser, Chromium) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: vscode_client_linux_$(VSCODE_ARCH)_sbom - - script: | - set -e - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - yarn smoketest-no-compile --tracing --build "$APP_PATH" - timeoutInMinutes: 20 - displayName: Run smoke tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (server) + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) + PackageName: Visual Studio Code Server - - script: | - set -e - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" - timeoutInMinutes: 20 - displayName: Run smoke tests (Remote) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (server) + artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom - - script: | - set -e - ps -ef - cat /proc/sys/fs/inotify/max_user_watches - lsof | wc -l - displayName: Diagnostics after smoke test run (processes, max_user_watches, number of opened file handles) - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(DEB_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package + displayName: Publish deb package - - task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-linux-$(VSCODE_ARCH) - targetPath: .build/crashes - displayName: "Publish Crash Reports" - continueOnError: true - condition: failed() + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(RPM_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package + displayName: Publish rpm package - # In order to properly symbolify above crash reports - # (if any), we need the compiled native modules too - - task: PublishPipelineArtifact@0 - inputs: - artifactName: node-modules-linux-$(VSCODE_ARCH) - targetPath: node_modules - displayName: "Publish Node Modules" - continueOnError: true - condition: failed() + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(TARBALL_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish client archive - - task: PublishPipelineArtifact@0 - inputs: - artifactName: logs-linux-$(VSCODE_ARCH)-$(System.JobAttempt) - targetPath: .build/logs - displayName: "Publish Log Files" - continueOnError: true - condition: and(failed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH).tar.gz + artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish server archive - - task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: "*-results.xml" - searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz + artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned + displayName: Publish web server archive - - script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" - yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" - displayName: Build deb, rpm packages - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - script: | - set -e - yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" - displayName: Prepare snap package - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: UseDotNet@2 - inputs: - version: 2.x - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: EsrpClientTool@1 - displayName: Download ESRPClient - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - script: | - set -e - node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' - displayName: Codesign rpm - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - script: | - set -e - VSCODE_ARCH="$(VSCODE_ARCH)" \ - ./build/azure-pipelines/linux/prepare-publish.sh - displayName: Prepare for Publish - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(DEB_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package - displayName: Publish deb package - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(RPM_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package - displayName: Publish rpm package - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(TARBALL_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish client archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH).tar.gz - artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz - artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: PublishPipelineArtifact@0 - displayName: "Publish Pipeline Artifact" - inputs: - artifactName: "snap-$(VSCODE_ARCH)" - targetPath: .build/linux/snap-tarball - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (client) - inputs: - BuildDropPath: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - PackageName: Visual Studio Code - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_linux_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (server) - inputs: - BuildDropPath: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) - PackageName: Visual Studio Code Server - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (server) - artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: PublishPipelineArtifact@0 + displayName: "Publish Pipeline Artifact" + inputs: + artifactName: "snap-$(VSCODE_ARCH)" + targetPath: .build/linux/snap-tarball diff --git a/build/azure-pipelines/linux/product-build-linux-server.yml b/build/azure-pipelines/linux/product-build-linux-server.yml index 07fa3e4649..8ab58da435 100644 --- a/build/azure-pipelines/linux/product-build-linux-server.yml +++ b/build/azure-pipelines/linux/product-build-linux-server.yml @@ -1,49 +1,58 @@ +parameters: + - name: VSCODE_QUALITY + type: string + steps: - task: NodeTool@0 inputs: versionSpec: "16.x" - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - - task: Docker@1 - displayName: "Pull Docker image" - inputs: - azureSubscriptionEndpoint: "vscode-builds-subscription" - azureContainerRegistry: vscodehub.azurecr.io - command: "Run an image" - imageName: "vscode-linux-build-agent:centos7-devtoolset8-arm64" - containerCommand: uname - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: Docker@1 + displayName: "Pull Docker image" + inputs: + azureSubscriptionEndpoint: "vscode-builds-subscription" + azureContainerRegistry: vscodehub.azurecr.io + command: "Run an image" + imageName: "vscode-linux-build-agent:centos7-devtoolset8-arm64" + containerCommand: uname + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | set -e @@ -61,17 +70,19 @@ steps: GITHUB_TOKEN: "$(github-distro-mixin-password)" condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) - - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - displayName: Register Docker QEMU - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + displayName: Register Docker QEMU + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - script: | - set -e - docker run -e VSCODE_QUALITY -e GITHUB_TOKEN -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh - displayName: Install dependencies via qemu - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + docker run -e VSCODE_QUALITY -e GITHUB_TOKEN -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh + displayName: Install dependencies via qemu + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - script: | set -e diff --git a/build/azure-pipelines/linux/sql-product-build-linux.yml b/build/azure-pipelines/linux/sql-product-build-linux.yml index 5f29358cc5..4d6d2fae20 100644 --- a/build/azure-pipelines/linux/sql-product-build-linux.yml +++ b/build/azure-pipelines/linux/sql-product-build-linux.yml @@ -136,54 +136,56 @@ steps: displayName: Run core integration tests condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'), ne(variables['EXTENSIONS_ONLY'], 'true')) - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the unit tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/azuredatastudio-linux-x64 - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - NO_CLEANUP=1 \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-linux-x64" \ - DISPLAY=:10 ./scripts/test-extensions-unit.sh --build --tfs "Extension Unit Tests" - displayName: Run Extension Unit Tests (Continue on Error) - continueOnError: true - condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), eq(variables['EXTENSION_UNIT_TESTS_FAIL_ON_ERROR'], 'false'))) +# {{SQL CARBON TODO}} Reenable "Run Extension Unit Tests (Continue on Error)" and "Run Extension Unit Tests (Fail on Error)" and "Archive Logs" + # - script: | + # # Figure out the full absolute path of the product we just built + # # including the remote server and configure the unit tests + # # to run with these builds instead of running out of sources. + # set -e + # APP_ROOT=$(agent.builddirectory)/azuredatastudio-linux-x64 + # APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + # INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + # NO_CLEANUP=1 \ + # VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-linux-x64" \ + # DISPLAY=:10 ./scripts/test-extensions-unit.sh --build --tfs "Extension Unit Tests" + # displayName: Run Extension Unit Tests (Continue on Error) + # continueOnError: true + # condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), eq(variables['EXTENSION_UNIT_TESTS_FAIL_ON_ERROR'], 'false'))) - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the unit tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/azuredatastudio-linux-x64 - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - NO_CLEANUP=1 \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-linux-x64" \ - DISPLAY=:10 ./scripts/test-extensions-unit.sh --build --tfs "Extension Unit Tests" - displayName: Run Extension Unit Tests (Fail on Error) - condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['EXTENSION_UNIT_TESTS_FAIL_ON_ERROR'], 'false'))) + # - script: | + # # Figure out the full absolute path of the product we just built + # # including the remote server and configure the unit tests + # # to run with these builds instead of running out of sources. + # set -e + # APP_ROOT=$(agent.builddirectory)/azuredatastudio-linux-x64 + # APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + # INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + # NO_CLEANUP=1 \ + # VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-linux-x64" \ + # DISPLAY=:10 ./scripts/test-extensions-unit.sh --build --tfs "Extension Unit Tests" + # displayName: Run Extension Unit Tests (Fail on Error) + # condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['EXTENSION_UNIT_TESTS_FAIL_ON_ERROR'], 'false'))) - - bash: | - set -e - mkdir -p $(Build.ArtifactStagingDirectory)/logs/linux-x64 - cd /tmp - for folder in adsuser*/ - do - folder=${folder%/} - # Only archive directories we want for debugging purposes - tar -czvf $(Build.ArtifactStagingDirectory)/logs/linux-x64/$folder.tar.gz $folder/User $folder/logs - done - displayName: Archive Logs - continueOnError: true - condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true')) + # - bash: | + # set -e + # mkdir -p $(Build.ArtifactStagingDirectory)/logs/linux-x64 + # cd /tmp + # for folder in adsuser*/ + # do + # folder=${folder%/} + # # Only archive directories we want for debugging purposes + # tar -czvf $(Build.ArtifactStagingDirectory)/logs/linux-x64/$folder.tar.gz $folder/User $folder/logs + # done + # displayName: Archive Logs + # continueOnError: true + # condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true')) - - script: | - set -e - yarn gulp vscode-linux-x64-build-deb - displayName: Build Deb - condition: and(succeeded(), ne(variables['EXTENSIONS_ONLY'], 'true')) + # {{SQL CARBON TODO}} - Reenable + # - script: | + # set -e + # yarn gulp vscode-linux-x64-build-deb + # displayName: Build Deb + # condition: and(succeeded(), ne(variables['EXTENSIONS_ONLY'], 'true')) - script: | set -e diff --git a/build/azure-pipelines/mixin.js b/build/azure-pipelines/mixin.js index 0a17e8008b..8e676d9983 100644 --- a/build/azure-pipelines/mixin.js +++ b/build/azure-pipelines/mixin.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const json = require("gulp-json-editor"); const buffer = require('gulp-buffer'); diff --git a/build/azure-pipelines/mixin.ts b/build/azure-pipelines/mixin.ts index bc174b7308..8658d98125 100644 --- a/build/azure-pipelines/mixin.ts +++ b/build/azure-pipelines/mixin.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as json from 'gulp-json-editor'; const buffer = require('gulp-buffer'); import * as filter from 'gulp-filter'; diff --git a/build/azure-pipelines/product-build-pr-cache.yml b/build/azure-pipelines/product-build-pr-cache.yml new file mode 100644 index 0000000000..067afa7492 --- /dev/null +++ b/build/azure-pipelines/product-build-pr-cache.yml @@ -0,0 +1,59 @@ +steps: + - checkout: self + fetchDepth: 1 + retryCountOnTaskFailure: 3 + + - task: NodeTool@0 + inputs: + versionSpec: "16.x" + + - script: | + mkdir -p .build + node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash + displayName: Prepare yarn cache flags + + - task: Cache@2 + inputs: + key: "genericNodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache + + - script: | + set -e + tar -xzf .build/node_modules_cache/cache.tgz + condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Extract node_modules cache + + - script: | + set -e + npx https://aka.ms/enablesecurefeed standAlone + timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) + displayName: Switch to Terrapin packages + + - script: | + set -e + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + env: + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + mkdir -p .build/node_modules_cache + tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Create node_modules archive diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml new file mode 100644 index 0000000000..8362da25ee --- /dev/null +++ b/build/azure-pipelines/product-build-pr.yml @@ -0,0 +1,189 @@ +trigger: + - main + - release/* + +pr: + branches: + include: ["main", "release/*"] + +variables: + - name: Codeql.SkipTaskAutoInjection + value: true + - name: skipComponentGovernanceDetection + value: true + - name: ENABLE_TERRAPIN + value: false + - name: VSCODE_CIBUILD + value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }} + - name: VSCODE_PUBLISH + value: false + - name: VSCODE_QUALITY + value: oss + - name: VSCODE_STEP_ON_IT + value: false + +jobs: + - ${{ if ne(variables['VSCODE_CIBUILD'], true) }}: + - job: Compile + displayName: Compile & Hygiene + pool: vscode-1es-vscode-linux-20.04 + timeoutInMinutes: 30 + variables: + VSCODE_ARCH: x64 + steps: + - template: product-compile.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + + - job: Linuxx64UnitTest + displayName: Linux (Unit Tests) + pool: vscode-1es-vscode-linux-20.04 + timeoutInMinutes: 30 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + + - job: Linuxx64IntegrationTest + displayName: Linux (Integration Tests) + pool: vscode-1es-vscode-linux-20.04 + timeoutInMinutes: 30 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + + - job: Linuxx64SmokeTest + displayName: Linux (Smoke Tests) + pool: vscode-1es-vscode-linux-20.04 + timeoutInMinutes: 30 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true + + - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: + - job: Linuxx64MaintainNodeModulesCache + displayName: Linux (Maintain node_modules cache) + pool: vscode-1es-vscode-linux-20.04 + timeoutInMinutes: 30 + variables: + VSCODE_ARCH: x64 + steps: + - template: product-build-pr-cache.yml + + # - job: macOSUnitTest + # displayName: macOS (Unit Tests) + # pool: + # vmImage: macOS-latest + # timeoutInMinutes: 60 + # variables: + # BUILDSECMON_OPT_IN: true + # VSCODE_ARCH: x64 + # steps: + # - template: darwin/product-build-darwin.yml + # parameters: + # VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_RUN_UNIT_TESTS: true + # VSCODE_RUN_INTEGRATION_TESTS: false + # VSCODE_RUN_SMOKE_TESTS: false + # - job: macOSIntegrationTest + # displayName: macOS (Integration Tests) + # pool: + # vmImage: macOS-latest + # timeoutInMinutes: 60 + # variables: + # BUILDSECMON_OPT_IN: true + # VSCODE_ARCH: x64 + # steps: + # - template: darwin/product-build-darwin.yml + # parameters: + # VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_RUN_UNIT_TESTS: false + # VSCODE_RUN_INTEGRATION_TESTS: true + # VSCODE_RUN_SMOKE_TESTS: false + # - job: macOSSmokeTest + # displayName: macOS (Smoke Tests) + # pool: + # vmImage: macOS-latest + # timeoutInMinutes: 60 + # variables: + # BUILDSECMON_OPT_IN: true + # VSCODE_ARCH: x64 + # steps: + # - template: darwin/product-build-darwin.yml + # parameters: + # VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_RUN_UNIT_TESTS: false + # VSCODE_RUN_INTEGRATION_TESTS: false + # VSCODE_RUN_SMOKE_TESTS: true + + # - job: WindowsUnitTests + # displayName: Windows (Unit Tests) + # pool: vscode-1es-vscode-windows-2019 + # timeoutInMinutes: 60 + # variables: + # VSCODE_ARCH: x64 + # steps: + # - template: win32/product-build-win32.yml + # parameters: + # VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_RUN_UNIT_TESTS: true + # VSCODE_RUN_INTEGRATION_TESTS: false + # VSCODE_RUN_SMOKE_TESTS: false + # - job: WindowsIntegrationTests + # displayName: Windows (Integration Tests) + # pool: vscode-1es-vscode-windows-2019 + # timeoutInMinutes: 60 + # variables: + # VSCODE_ARCH: x64 + # steps: + # - template: win32/product-build-win32.yml + # parameters: + # VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_RUN_UNIT_TESTS: false + # VSCODE_RUN_INTEGRATION_TESTS: true + # VSCODE_RUN_SMOKE_TESTS: false + # - job: WindowsSmokeTests + # displayName: Windows (Smoke Tests) + # pool: vscode-1es-vscode-windows-2019 + # timeoutInMinutes: 60 + # variables: + # VSCODE_ARCH: x64 + # steps: + # - template: win32/product-build-win32.yml + # parameters: + # VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_RUN_UNIT_TESTS: false + # VSCODE_RUN_INTEGRATION_TESTS: false + # VSCODE_RUN_SMOKE_TESTS: true diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index f642d0a680..958203ec56 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -8,6 +8,10 @@ schedules: - main - joao/web +trigger: + branches: + include: ["main", "release/*"] + parameters: - name: VSCODE_DISTRO_REF displayName: Distro Ref (Private build) @@ -104,9 +108,11 @@ variables: - name: VSCODE_BUILD_STAGE_WINDOWS value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_32BIT, 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), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true), eq(parameters.VSCODE_BUILD_WEB, true)) }} + value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE, true), eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_MACOS value: ${{ or(eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }} + - name: VSCODE_BUILD_STAGE_WEB + value: ${{ eq(parameters.VSCODE_BUILD_WEB, true) }} - name: VSCODE_CIBUILD value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }} - name: VSCODE_PUBLISH @@ -162,6 +168,8 @@ stages: VSCODE_ARCH: x64 steps: - template: product-compile.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_WINDOWS'], true)) }}: - stage: Windows @@ -169,13 +177,60 @@ stages: - Compile pool: vscode-1es-windows jobs: - - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: + - job: WindowsUnitTests + displayName: Unit Tests + timeoutInMinutes: 60 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + - job: WindowsIntegrationTests + displayName: Integration Tests + timeoutInMinutes: 60 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + - job: WindowsSmokeTests + displayName: Smoke Tests + timeoutInMinutes: 60 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32, true)) }}: - job: Windows timeoutInMinutes: 120 variables: VSCODE_ARCH: x64 steps: - template: win32/product-build-win32.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true)) }}: - job: Windows32 @@ -184,6 +239,12 @@ stages: VSCODE_ARCH: ia32 steps: - template: win32/product-build-win32.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - job: WindowsARM64 @@ -192,6 +253,12 @@ stages: VSCODE_ARCH: arm64 steps: - template: win32/product-build-win32.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX'], true)) }}: - stage: LinuxServerDependencies @@ -206,13 +273,17 @@ stages: NPM_ARCH: x64 steps: - template: linux/product-build-linux-server.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}: - job: arm64 variables: VSCODE_ARCH: arm64 steps: - template: linux/product-build-linux-server.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX'], true)) }}: - stage: Linux @@ -221,7 +292,54 @@ stages: - LinuxServerDependencies pool: vscode-1es-linux jobs: - - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: + - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: + - job: Linuxx64UnitTest + displayName: Unit Tests + container: vscode-bionic-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + - job: Linuxx64IntegrationTest + displayName: Integration Tests + container: vscode-bionic-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + - job: Linuxx64SmokeTest + displayName: Smoke Tests + container: vscode-bionic-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}: - job: Linuxx64 container: vscode-bionic-x64 variables: @@ -230,6 +348,12 @@ stages: DISPLAY: ":10" steps: - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true), ne(variables['VSCODE_PUBLISH'], 'false')) }}: - job: LinuxSnap @@ -249,6 +373,12 @@ stages: NPM_ARCH: armv7l steps: - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false # TODO@joaomoreno: We don't ship ARM snaps for now - ${{ if and(false, eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}: @@ -269,6 +399,12 @@ stages: NPM_ARCH: arm64 steps: - template: linux/product-build-linux-client.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false # TODO@joaomoreno: We don't ship ARM snaps for now - ${{ if and(false, eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}: @@ -296,13 +432,6 @@ stages: steps: - template: linux/product-build-alpine.yml - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WEB, true)) }}: - - job: LinuxWeb - variables: - VSCODE_ARCH: x64 - steps: - - template: web/product-build-web.yml - - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_MACOS'], true)) }}: - stage: macOS dependsOn: @@ -312,20 +441,76 @@ stages: variables: BUILDSECMON_OPT_IN: true jobs: - - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - - job: macOSTest + - ${{ if eq(variables['VSCODE_CIBUILD'], true) }}: + - job: macOSUnitTest + displayName: Unit Tests timeoutInMinutes: 90 variables: VSCODE_ARCH: x64 steps: - - template: darwin/product-build-darwin-test.yml - - ${{ if eq(variables['VSCODE_CIBUILD'], false) }}: - - job: macOS + - template: darwin/product-build-darwin.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + - job: macOSIntegrationTest + displayName: Integration Tests + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + - job: macOSSmokeTest + displayName: Smoke Tests + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS, true)) }}: + - job: macOS + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + + - ${{ if eq(parameters.VSCODE_STEP_ON_IT, false) }}: + - job: macOSTest timeoutInMinutes: 90 variables: VSCODE_ARCH: x64 steps: - template: darwin/product-build-darwin.yml + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + + - ${{ if eq(variables['VSCODE_PUBLISH'], true) }}: - job: macOSSign dependsOn: - macOS @@ -342,7 +527,14 @@ stages: VSCODE_ARCH: arm64 steps: - template: darwin/product-build-darwin.yml - - ${{ if eq(variables['VSCODE_CIBUILD'], false) }}: + parameters: + VSCODE_PUBLISH: ${{ variables.VSCODE_PUBLISH }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + + - ${{ if eq(variables['VSCODE_PUBLISH'], true) }}: - job: macOSARM64Sign dependsOn: - macOSARM64 @@ -362,7 +554,8 @@ stages: VSCODE_ARCH: universal steps: - template: darwin/product-build-darwin-universal.yml - - ${{ if eq(variables['VSCODE_CIBUILD'], false) }}: + + - ${{ if eq(variables['VSCODE_PUBLISH'], true) }}: - job: macOSUniversalSign dependsOn: - macOSUniversal @@ -372,6 +565,19 @@ stages: steps: - template: darwin/product-build-darwin-sign.yml + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_WEB'], true)) }}: + - stage: Web + dependsOn: + - Compile + pool: vscode-1es-linux + jobs: + - ${{ if eq(parameters.VSCODE_BUILD_WEB, true) }}: + - job: Web + variables: + VSCODE_ARCH: x64 + steps: + - template: web/product-build-web.yml + - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), ne(variables['VSCODE_PUBLISH'], 'false')) }}: - stage: Publish dependsOn: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 2b109f3cc5..381d49ee75 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -1,39 +1,47 @@ +parameters: + - name: VSCODE_QUALITY + type: string + steps: - task: NodeTool@0 inputs: versionSpec: "16.x" - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password" + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password" - - script: | - set -e - cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF - git config user.email "vscode@microsoft.com" - git config user.name "VSCode" - displayName: Prepare tooling + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling - - script: | - set -e - git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF - echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - git checkout FETCH_HEAD - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit - - script: | - set -e - git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") - displayName: Merge distro + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro - script: | mkdir -p .build @@ -94,11 +102,12 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) displayName: Create node_modules archive - # Mixin must run before optimize, because the CSS loader will inline small SVGs - - script: | - set -e - node build/azure-pipelines/mixin - displayName: Mix in quality + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + # Mixin must run before optimize, because the CSS loader will inline small SVGs + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - script: | set -e @@ -107,59 +116,65 @@ steps: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Compile & Hygiene - - script: | - set -e - yarn --cwd test/smoke compile - yarn --cwd test/integration/browser compile - displayName: Compile test suites - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + yarn --cwd test/smoke compile + yarn --cwd test/integration/browser compile + displayName: Compile test suites + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - task: AzureCLI@2 - inputs: - azureSubscription: "vscode-builds-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureCLI@2 + inputs: + azureSubscription: "vscode-builds-subscription" + scriptType: pscore + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" + Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" - - script: | - set -e - AZURE_STORAGE_ACCOUNT="ticino" \ - AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ - AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - node build/azure-pipelines/upload-sourcemaps - displayName: Upload sourcemaps - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + node build/azure-pipelines/upload-sourcemaps + displayName: Upload sourcemaps - - script: | - set - - ./build/azure-pipelines/common/extract-telemetry.sh - displayName: Extract Telemetry - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set - + ./build/azure-pipelines/common/extract-telemetry.sh + displayName: Extract Telemetry - - script: | - set -e - tar -cz --ignore-failed-read -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz .build out-* test/integration/browser/out test/smoke/out test/automation/out - displayName: Compress compilation artifact + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + tar -cz --ignore-failed-read -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz .build out-* test/integration/browser/out test/smoke/out test/automation/out + displayName: Compress compilation artifact - - task: PublishPipelineArtifact@1 - inputs: - targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz - artifactName: Compilation - displayName: Publish compilation artifact + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: PublishPipelineArtifact@1 + inputs: + targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz + artifactName: Compilation + displayName: Publish compilation artifact - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn download-builtin-extensions-cg - displayName: Built-in extensions component details + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn download-builtin-extensions-cg + displayName: Built-in extensions component details - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: "Component Detection" - inputs: - sourceScanPath: $(Build.SourcesDirectory) - continueOnError: true + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: "Component Detection" + inputs: + sourceScanPath: $(Build.SourcesDirectory) + continueOnError: true diff --git a/build/azure-pipelines/product-publish.ps1 b/build/azure-pipelines/product-publish.ps1 index 5abfed48dc..5006ec61a3 100644 --- a/build/azure-pipelines/product-publish.ps1 +++ b/build/azure-pipelines/product-publish.ps1 @@ -46,6 +46,7 @@ $stages = @( if ($env:VSCODE_BUILD_STAGE_WINDOWS -eq 'True') { 'Windows' } if ($env:VSCODE_BUILD_STAGE_LINUX -eq 'True') { 'Linux' } if ($env:VSCODE_BUILD_STAGE_MACOS -eq 'True') { 'macOS' } + if ($env:VSCODE_BUILD_STAGE_WEB -eq 'True') { 'Web' } ) do { diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 4d711aba12..80076fd666 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -109,6 +109,7 @@ steps: if ($env:VSCODE_BUILD_STAGE_WINDOWS -eq 'True') { 'Windows' } if ($env:VSCODE_BUILD_STAGE_LINUX -eq 'True') { 'Linux' } if ($env:VSCODE_BUILD_STAGE_MACOS -eq 'True') { 'macOS' } + if ($env:VSCODE_BUILD_STAGE_WEB -eq 'True') { 'Web' } ) Write-Host "Stages to check: $stages" diff --git a/build/azure-pipelines/publish-types/check-version.js b/build/azure-pipelines/publish-types/check-version.js index 95563bf95e..f787f897ae 100644 --- a/build/azure-pipelines/publish-types/check-version.js +++ b/build/azure-pipelines/publish-types/check-version.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const cp = require("child_process"); let tag = ''; diff --git a/build/azure-pipelines/publish-types/check-version.ts b/build/azure-pipelines/publish-types/check-version.ts index 3e3614a867..72bf428f47 100644 --- a/build/azure-pipelines/publish-types/check-version.ts +++ b/build/azure-pipelines/publish-types/check-version.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as cp from 'child_process'; let tag = ''; diff --git a/build/azure-pipelines/publish-types/update-types.js b/build/azure-pipelines/publish-types/update-types.js index 2da8ae32e9..6da5a12a86 100644 --- a/build/azure-pipelines/publish-types/update-types.js +++ b/build/azure-pipelines/publish-types/update-types.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const cp = require("child_process"); diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index c3ed3324a7..93b19a5b4a 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fs from 'fs'; import * as cp from 'child_process'; import * as path from 'path'; diff --git a/build/azure-pipelines/upload-cdn.js b/build/azure-pipelines/upload-cdn.js index 6f0f9c7194..5bf24c505a 100644 --- a/build/azure-pipelines/upload-cdn.js +++ b/build/azure-pipelines/upload-cdn.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); const Vinyl = require("vinyl"); diff --git a/build/azure-pipelines/upload-cdn.ts b/build/azure-pipelines/upload-cdn.ts index 1229a06ba6..d28f20ac52 100644 --- a/build/azure-pipelines/upload-cdn.ts +++ b/build/azure-pipelines/upload-cdn.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as es from 'event-stream'; import * as Vinyl from 'vinyl'; import * as vfs from 'vinyl-fs'; diff --git a/build/azure-pipelines/upload-configuration.js b/build/azure-pipelines/upload-configuration.js index bc641b7d49..a8739f80cd 100644 --- a/build/azure-pipelines/upload-configuration.js +++ b/build/azure-pipelines/upload-configuration.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSettingsSearchBuildId = exports.shouldSetupSettingsSearch = void 0; const path = require("path"); @@ -52,7 +52,7 @@ function generateVSCodeConfigurationTask() { const timer = setTimeout(() => { codeProc.kill(); reject(new Error('export-default-configuration process timed out')); - }, 12 * 1000); + }, 60 * 1000); codeProc.on('error', err => { clearTimeout(timer); reject(err); diff --git a/build/azure-pipelines/upload-configuration.ts b/build/azure-pipelines/upload-configuration.ts index 3cb5622c66..dbcc1b99b7 100644 --- a/build/azure-pipelines/upload-configuration.ts +++ b/build/azure-pipelines/upload-configuration.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as path from 'path'; import * as os from 'os'; import * as cp from 'child_process'; @@ -63,7 +61,7 @@ function generateVSCodeConfigurationTask(): Promise { const timer = setTimeout(() => { codeProc.kill(); reject(new Error('export-default-configuration process timed out')); - }, 12 * 1000); + }, 60 * 1000); codeProc.on('error', err => { clearTimeout(timer); diff --git a/build/azure-pipelines/upload-nlsmetadata.js b/build/azure-pipelines/upload-nlsmetadata.js index 7eddb74807..df30c0d159 100644 --- a/build/azure-pipelines/upload-nlsmetadata.js +++ b/build/azure-pipelines/upload-nlsmetadata.js @@ -1,14 +1,16 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); const vfs = require("vinyl-fs"); const merge = require("gulp-merge-json"); const gzip = require("gulp-gzip"); const identity_1 = require("@azure/identity"); +const path = require("path"); +const fs_1 = require("fs"); const azure = require('gulp-azure-storage'); const commit = process.env['VSCODE_DISTRO_COMMIT'] || process.env['BUILD_SOURCEVERSION']; const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); @@ -18,8 +20,8 @@ function main() { .pipe(merge({ fileName: 'combined.nls.metadata.json', jsonSpace: '', + concatArrays: true, edit: (parsedJson, file) => { - let key; if (file.base === 'out-vscode-web-min') { return { vscode: parsedJson }; } @@ -63,7 +65,11 @@ function main() { break; } } - key = 'vscode.' + file.relative.split('/')[0]; + // Get extension id and use that as the key + const folderPath = path.join(file.base, file.relative.split('/')[0]); + const manifest = (0, fs_1.readFileSync)(path.join(folderPath, 'package.json'), 'utf-8'); + const manifestJson = JSON.parse(manifest); + const key = manifestJson.publisher + '.' + manifestJson.name; return { [key]: parsedJson }; }, })) diff --git a/build/azure-pipelines/upload-nlsmetadata.ts b/build/azure-pipelines/upload-nlsmetadata.ts index dbb1bf7651..e2a95c782b 100644 --- a/build/azure-pipelines/upload-nlsmetadata.ts +++ b/build/azure-pipelines/upload-nlsmetadata.ts @@ -3,14 +3,14 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as es from 'event-stream'; import * as Vinyl from 'vinyl'; import * as vfs from 'vinyl-fs'; import * as merge from 'gulp-merge-json'; import * as gzip from 'gulp-gzip'; import { ClientSecretCredential } from '@azure/identity'; +import path = require('path'); +import { readFileSync } from 'fs'; const azure = require('gulp-azure-storage'); const commit = process.env['VSCODE_DISTRO_COMMIT'] || process.env['BUILD_SOURCEVERSION']; @@ -33,8 +33,8 @@ function main(): Promise { .pipe(merge({ fileName: 'combined.nls.metadata.json', jsonSpace: '', + concatArrays: true, edit: (parsedJson, file) => { - let key; if (file.base === 'out-vscode-web-min') { return { vscode: parsedJson }; } @@ -82,7 +82,12 @@ function main(): Promise { break; } } - key = 'vscode.' + file.relative.split('/')[0]; + + // Get extension id and use that as the key + const folderPath = path.join(file.base, file.relative.split('/')[0]); + const manifest = readFileSync(path.join(folderPath, 'package.json'), 'utf-8'); + const manifestJson = JSON.parse(manifest); + const key = manifestJson.publisher + '.' + manifestJson.name; return { [key]: parsedJson }; }, })) @@ -113,4 +118,3 @@ main().catch(err => { console.error(err); process.exit(1); }); - diff --git a/build/azure-pipelines/upload-sourcemaps.js b/build/azure-pipelines/upload-sourcemaps.js index 034fb8e753..7392cd2efe 100644 --- a/build/azure-pipelines/upload-sourcemaps.js +++ b/build/azure-pipelines/upload-sourcemaps.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const es = require("event-stream"); diff --git a/build/azure-pipelines/upload-sourcemaps.ts b/build/azure-pipelines/upload-sourcemaps.ts index bf3066ab83..b679938e8b 100644 --- a/build/azure-pipelines/upload-sourcemaps.ts +++ b/build/azure-pipelines/upload-sourcemaps.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as path from 'path'; import * as es from 'event-stream'; import * as Vinyl from 'vinyl'; diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index fa07a82305..376f14c6bc 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -163,6 +163,7 @@ steps: cd $ROOT && tar --owner=0 --group=0 -czf $WEB_TARBALL_PATH $WEB_BUILD_NAME displayName: Prepare for publish + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - publish: $(Agent.BuildDirectory)/vscode-web.tar.gz artifact: vscode_web_linux_standalone_archive-unsigned diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml new file mode 100644 index 0000000000..59c91cd2b1 --- /dev/null +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -0,0 +1,247 @@ +parameters: + - name: VSCODE_QUALITY + type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + +steps: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" } + displayName: Download Electron and Playwright + + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn electron $(VSCODE_ARCH) } + exec { .\scripts\test.bat --tfs "Unit Tests" } + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-node } + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node test/unit/browser/index.js --sequential --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser, Chromium & Firefox) + timeoutInMinutes: 20 + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn electron $(VSCODE_ARCH) } + exec { .\scripts\test.bat --build --tfs "Unit Tests" } + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-node --build } + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser, Chromium & Firefox) + timeoutInMinutes: 20 + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp ` + compile-extension:configuration-editing ` + compile-extension:css-language-features-server ` + compile-extension:emmet ` + compile-extension:git ` + compile-extension:github-authentication ` + compile-extension:html-language-features-server ` + compile-extension:ipynb ` + compile-extension:json-language-features-server ` + compile-extension:markdown-language-features-server ` + compile-extension:markdown-language-features ` + compile-extension-media ` + compile-extension:microsoft-authentication ` + compile-extension:typescript-language-features ` + compile-extension:vscode-api-tests ` + compile-extension:vscode-colorize-tests ` + compile-extension:vscode-notebook-tests ` + compile-extension:vscode-test-resolver ` + } + displayName: Build integration tests + + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { .\scripts\test-integration.bat --tfs "Integration Tests" } + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { .\scripts\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser, Firefox) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { .\scripts\test-remote-integration.bat } + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser, Firefox) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec {.\build\azure-pipelines\win32\listprocesses.bat } + displayName: Diagnostics before smoke test run + continueOnError: true + condition: succeededOrFailed() + + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn --cwd test/smoke compile } + displayName: Compile smoke tests + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn smoketest-no-compile --tracing } + displayName: Run smoke tests (Electron) + timeoutInMinutes: 20 + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + exec { yarn smoketest-no-compile --tracing --build "$AppRoot" } + displayName: Run smoke tests (Electron) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" + exec { yarn smoketest-no-compile --web --tracing --headless } + displayName: Run smoke tests (Browser, Chromium) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" + exec { yarn gulp compile-extension:vscode-test-resolver } + exec { yarn smoketest-no-compile --tracing --remote --build "$AppRoot" } + displayName: Run smoke tests (Remote) + timeoutInMinutes: 20 + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec {.\build\azure-pipelines\win32\listprocesses.bat } + displayName: Diagnostics after smoke test run + continueOnError: true + condition: succeededOrFailed() + + - ${{ if or(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + - task: PublishPipelineArtifact@0 + inputs: + targetPath: .build\crashes + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: crash-dump-windows-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: crash-dump-windows-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: crash-dump-windows-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() + + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + targetPath: node_modules + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: node-modules-windows-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: node-modules-windows-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: node-modules-windows-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + + - task: PublishPipelineArtifact@0 + inputs: + targetPath: .build\logs + ${{ if and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, false)) }}: + artifactName: logs-windows-$(VSCODE_ARCH)-integration-$(System.JobAttempt) + ${{ elseif and(eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, false), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + artifactName: logs-windows-$(VSCODE_ARCH)-smoke-$(System.JobAttempt) + ${{ else }}: + artifactName: logs-windows-$(VSCODE_ARCH)-$(System.JobAttempt) + displayName: "Publish Log Files" + continueOnError: true + condition: succeededOrFailed() + + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 99b39ee2b2..41f0a8da8c 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,4 +1,21 @@ +parameters: + - name: VSCODE_PUBLISH + type: boolean + - name: VSCODE_QUALITY + type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + steps: + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - checkout: self + fetchDepth: 1 + retryCountOnTaskFailure: 3 + - task: NodeTool@0 inputs: versionSpec: "16.x" @@ -8,51 +25,58 @@ steps: versionSpec: "3.x" addToPath: true - - task: AzureKeyVault@1 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Compilation + path: $(Build.ArtifactStagingDirectory) + displayName: Download compilation output - - task: ExtractFiles@1 - displayName: Extract compilation output - inputs: - archiveFilePatterns: "$(Build.ArtifactStagingDirectory)/compilation.tar.gz" - cleanDestinationFolder: false - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - - exec { git config user.email "vscode@microsoft.com" } - exec { git config user.name "VSCode" } - displayName: Prepare tooling - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - - exec { git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $(VSCODE_DISTRO_REF) } - Write-Host "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" - exec { git checkout FETCH_HEAD } - condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) - displayName: Checkout override commit - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") } - displayName: Merge distro + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: ExtractFiles@1 + displayName: Extract compilation output + inputs: + archiveFilePatterns: "$(Build.ArtifactStagingDirectory)/compilation.tar.gz" + cleanDestinationFolder: false + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + "machine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + + exec { git config user.email "vscode@microsoft.com" } + exec { git config user.name "VSCode" } + displayName: Prepare tooling + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + + exec { git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $(VSCODE_DISTRO_REF) } + Write-Host "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + exec { git checkout FETCH_HEAD } + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit + + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") } + displayName: Merge distro - powershell: | + if (!(Test-Path ".build")) { New-Item -Path ".build" -ItemType Directory } "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch "$env:ENABLE_TERRAPIN" | Out-File -Encoding ascii -NoNewLine .build\terrapin node build/azure-pipelines/common/computeNodeModulesCacheKey.js > .build/yarnlockhash @@ -104,291 +128,176 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) displayName: Create node_modules archive - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/mixin } - displayName: Mix in quality + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/mixin } + displayName: Mix in quality - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn npm-run-all -lp "electron $(VSCODE_ARCH)" } - displayName: Download Electron - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build\lib\policies } + displayName: Generate Group Policy definitions - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" - displayName: Build + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "transpile-client" "transpile-extensions" } + displayName: Transpile - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } - displayName: Prepare Package - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" + displayName: Build - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build/azure-pipelines/mixin --server } - displayName: Mix in quality + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } + displayName: Prepare Package - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } - exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)" - displayName: Build Server - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/mixin --server } + displayName: Mix in quality + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" - exec { yarn npm-run-all -lp "playwright-install" } - displayName: Download Playwright - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + exec { yarn gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)" + displayName: Build Server + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn electron $(VSCODE_ARCH) } - exec { .\scripts\test.bat --build --tfs "Unit Tests" } - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: + - template: product-build-win32-test.yml + parameters: + VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} + VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} + VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn test-node --build } - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: UseDotNet@2 + inputs: + version: 3.x + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } - displayName: Run unit tests (Browser, Chromium & Firefox) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: EsrpClientTool@1 + displayName: Download ESRPClient - - powershell: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $EsrpClientTool = (gci -directory -filter EsrpClientTool_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName + $EsrpCliZip = (gci -recurse -filter esrpcli.*.zip $EsrpClientTool | Select-Object -last 1).FullName + mkdir -p $(Agent.TempDirectory)\esrpcli + Expand-Archive -Path $EsrpCliZip -DestinationPath $(Agent.TempDirectory)\esrpcli + $EsrpCliDllPath = (gci -recurse -filter esrpcli.dll $(Agent.TempDirectory)\esrpcli | Select-Object -last 1).FullName + echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" + displayName: Find ESRP CLI - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } - displayName: Run integration tests (Browser, Firefox) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.dll,*.exe,*.node' } + displayName: Codesign - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-archive" } + displayName: Package archive - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - exec {.\build\azure-pipelines\win32\listprocesses.bat } - displayName: Diagnostics before smoke test run - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:ESRPPKI = "$(ESRP-PKI)" + $env:ESRPAADUsername = "$(esrp-aad-username)" + $env:ESRPAADPassword = "$(esrp-aad-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" --sign } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } + displayName: Package setups - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --web --tracing --headless } - displayName: Run smoke tests (Browser, Chromium) - timeoutInMinutes: 10 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + .\build\azure-pipelines\win32\prepare-publish.ps1 + displayName: Publish - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --tracing --build "$AppRoot" } - displayName: Run smoke tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (client) + inputs: + BuildDropPath: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH) + PackageName: Visual Studio Code - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --tracing --remote --build "$AppRoot" } - displayName: Run smoke tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (client) + artifact: vscode_client_win32_$(VSCODE_ARCH)_sbom - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - exec {.\build\azure-pipelines\win32\listprocesses.bat } - displayName: Diagnostics after smoke test run - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM (server) + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH) + PackageName: Visual Studio Code Server + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-windows-$(VSCODE_ARCH) - targetPath: .build\crashes - displayName: "Publish Crash Reports" - continueOnError: true - condition: failed() + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM (server) + artifact: vscode_server_win32_$(VSCODE_ARCH)_sbom + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - # In order to properly symbolify above crash reports - # (if any), we need the compiled native modules too - - task: PublishPipelineArtifact@0 - inputs: - artifactName: node-modules-windows-$(VSCODE_ARCH) - targetPath: node_modules - displayName: "Publish Node Modules" - continueOnError: true - condition: failed() + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\archive\$(ARCHIVE_NAME) + artifact: vscode_client_win32_$(VSCODE_ARCH)_archive + displayName: Publish archive - - task: PublishPipelineArtifact@0 - inputs: - artifactName: logs-windows-$(VSCODE_ARCH)-$(System.JobAttempt) - targetPath: .build\logs - displayName: "Publish Log Files" - continueOnError: true - condition: and(failed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\system-setup\$(SYSTEM_SETUP_NAME) + artifact: vscode_client_win32_$(VSCODE_ARCH)_setup + displayName: Publish system setup - - task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: "*-results.xml" - searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" - condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\user-setup\$(USER_SETUP_NAME) + artifact: vscode_client_win32_$(VSCODE_ARCH)_user-setup + displayName: Publish user setup + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - task: UseDotNet@2 - inputs: - version: 3.x - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH).zip + artifact: vscode_server_win32_$(VSCODE_ARCH)_archive + displayName: Publish server archive + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - task: EsrpClientTool@1 - displayName: Download ESRPClient - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $EsrpClientTool = (gci -directory -filter EsrpClientTool_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName - $EsrpCliZip = (gci -recurse -filter esrpcli.*.zip $EsrpClientTool | Select-Object -last 1).FullName - mkdir -p $(Agent.TempDirectory)\esrpcli - Expand-Archive -Path $EsrpCliZip -DestinationPath $(Agent.TempDirectory)\esrpcli - $EsrpCliDllPath = (gci -recurse -filter esrpcli.dll $(Agent.TempDirectory)\esrpcli | Select-Object -last 1).FullName - echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" - displayName: Find ESRP CLI - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.dll,*.exe,*.node' } - displayName: Codesign - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-archive" } - displayName: Package archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:ESRPPKI = "$(ESRP-PKI)" - $env:ESRPAADUsername = "$(esrp-aad-username)" - $env:ESRPAADPassword = "$(esrp-aad-password)" - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" --sign } - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } - displayName: Package setups - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - .\build\azure-pipelines\win32\prepare-publish.ps1 - displayName: Publish - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\archive\$(ARCHIVE_NAME) - artifact: vscode_client_win32_$(VSCODE_ARCH)_archive - displayName: Publish archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\system-setup\$(SYSTEM_SETUP_NAME) - artifact: vscode_client_win32_$(VSCODE_ARCH)_setup - displayName: Publish system setup - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\user-setup\$(USER_SETUP_NAME) - artifact: vscode_client_win32_$(VSCODE_ARCH)_user-setup - displayName: Publish user setup - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH).zip - artifact: vscode_server_win32_$(VSCODE_ARCH)_archive - displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH)-web.zip - artifact: vscode_web_win32_$(VSCODE_ARCH)_archive - displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (client) - inputs: - BuildDropPath: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH) - PackageName: Visual Studio Code - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - publish: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (client) - artifact: vscode_client_win32_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: Generate SBOM (server) - inputs: - BuildDropPath: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH) - PackageName: Visual Studio Code Server - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - publish: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)/_manifest - displayName: Publish SBOM (server) - artifact: vscode_server_win32_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_PUBLISH, true) }}: + - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH)-web.zip + artifact: vscode_web_win32_$(VSCODE_ARCH)_archive + displayName: Publish web server archive + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index a76d9df018..24476a1fbd 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const vscode_universal_bundler_1 = require("vscode-universal-bundler"); const cross_spawn_promise_1 = require("@malept/cross-spawn-promise"); @@ -71,7 +71,7 @@ async function main() { outAppPath, force: true }); - let productJson = await fs.readJson(productJsonPath); + const productJson = await fs.readJson(productJsonPath); Object.assign(productJson, { darwinUniversalAssetId: 'darwin-universal' }); diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index 66c935b2c6..56fc1722db 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { makeUniversalApp } from 'vscode-universal-bundler'; import { spawn } from '@malept/cross-spawn-promise'; import * as fs from 'fs-extra'; @@ -80,7 +78,7 @@ async function main() { force: true }); - let productJson = await fs.readJson(productJsonPath); + const productJson = await fs.readJson(productJsonPath); Object.assign(productJson, { darwinUniversalAssetId: 'darwin-universal' }); diff --git a/build/darwin/sign.js b/build/darwin/sign.js index b33fdc01bc..06d7ac039d 100644 --- a/build/darwin/sign.js +++ b/build/darwin/sign.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const codesign = require("electron-osx-sign"); const path = require("path"); diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts index 69bd06b783..03bc801677 100644 --- a/build/darwin/sign.ts +++ b/build/darwin/sign.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as codesign from 'electron-osx-sign'; import * as path from 'path'; import * as util from '../lib/util'; diff --git a/build/filters.js b/build/filters.js index 6573eb4678..c24a7a3eab 100644 --- a/build/filters.js +++ b/build/filters.js @@ -52,7 +52,6 @@ module.exports.unicodeFilter = [ '!extensions/typescript-language-features/test-workspace/**', '!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace2/**', - '!extensions/vscode-custom-editor-tests/test-workspace/**', '!extensions/**/dist/**', '!extensions/**/out/**', '!extensions/**/snippets/**', @@ -93,7 +92,6 @@ module.exports.indentationFilter = [ '!extensions/markdown-math/notebook-out/**', '!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace2/**', - '!extensions/vscode-custom-editor-tests/test-workspace/**', '!build/monaco/**', '!build/win32/**', diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index e0e026cd9e..9e2759c05d 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -17,34 +17,41 @@ const compilation = require('./lib/compilation'); const monacoapi = require('./lib/monaco-api'); const fs = require('fs'); -let root = path.dirname(__dirname); -let sha1 = util.getVersion(root); -let semver = require('./monaco/package.json').version; -let headerVersion = semver + '(' + sha1 + ')'; +const root = path.dirname(__dirname); +const sha1 = util.getVersion(root); +const semver = require('./monaco/package.json').version; +const headerVersion = semver + '(' + sha1 + ')'; // Build -let editorEntryPoints = [ +const editorEntryPoints = [ { name: 'vs/editor/editor.main', include: [], exclude: ['vs/css', 'vs/nls'], - prepend: ['out-editor-build/vs/css.js', 'out-editor-build/vs/nls.js'], + prepend: [ + { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' }, + { path: 'out-editor-build/vs/nls.js', amdModuleId: 'vs/nls' } + ], }, { name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], - prepend: ['vs/loader.js'], - append: ['vs/base/worker/workerMain'], + exclude: ['vs/nls'], + prepend: [ + { path: 'vs/loader.js' }, + { path: 'vs/nls.js', amdModuleId: 'vs/nls' }, + { path: 'vs/base/worker/workerMain.js' } + ], dest: 'vs/base/worker/workerMain.js' } ]; -let editorResources = [ +const editorResources = [ 'out-editor-build/vs/base/browser/ui/codicons/**/*.ttf' ]; -let BUNDLED_FILE_HEADER = [ +const BUNDLED_FILE_HEADER = [ '/*!-----------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Version: ' + headerVersion, @@ -109,12 +116,6 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => 'inlineEntryPoint:0.ts', 'inlineEntryPoint:1.ts', 'vs/loader.js', - 'vs/nls.ts', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/css.js', - 'vs/css.build.js', - 'vs/css.d.ts', 'vs/base/worker/workerMain.ts', ], renames: { @@ -224,7 +225,7 @@ const appendJSToESMImportsTask = task.define('append-js-to-esm-imports', () => { result.push(line); continue; } - let modifiedLine = ( + const modifiedLine = ( line .replace(/^import(.*)\'([^']+)\'/, `import$1'$2.js'`) .replace(/^export \* from \'([^']+)\'/, `export * from '$1.js'`) @@ -239,10 +240,10 @@ const appendJSToESMImportsTask = task.define('append-js-to-esm-imports', () => { * @param {string} contents */ function toExternalDTS(contents) { - let lines = contents.split(/\r\n|\r|\n/); + const lines = contents.split(/\r\n|\r|\n/); let killNextCloseCurlyBrace = false; for (let i = 0; i < lines.length; i++) { - let line = lines[i]; + const line = lines[i]; if (killNextCloseCurlyBrace) { if ('}' === line) { @@ -316,7 +317,7 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { // package.json gulp.src('build/monaco/package.json') .pipe(es.through(function (data) { - let json = JSON.parse(data.contents.toString()); + const json = JSON.parse(data.contents.toString()); json.private = false; data.contents = Buffer.from(JSON.stringify(json, null, ' ')); this.emit('data', data); @@ -360,10 +361,10 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { return; } - let relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map')); + const relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map')); let strContents = data.contents.toString(); - let newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); + const newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); strContents = strContents.replace(/\/\/# sourceMappingURL=[^ ]+$/, newStr); data.contents = Buffer.from(strContents); @@ -483,13 +484,13 @@ function createTscCompileTask(watch) { cwd: path.join(__dirname, '..'), // stdio: [null, 'pipe', 'inherit'] }); - let errors = []; - let reporter = createReporter('monaco'); + const errors = []; + const reporter = createReporter('monaco'); /** @type {NodeJS.ReadWriteStream | undefined} */ let report; // eslint-disable-next-line no-control-regex - let magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings + const magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings child.stdout.on('data', data => { let str = String(data); @@ -502,12 +503,12 @@ function createTscCompileTask(watch) { report.end(); } else if (str) { - let match = /(.*\(\d+,\d+\): )(.*: )(.*)/.exec(str); + const match = /(.*\(\d+,\d+\): )(.*: )(.*)/.exec(str); if (match) { // trying to massage the message so that it matches the gulp-tsb error messages // e.g. src/vs/base/common/strings.ts(663,5): error TS2322: Type '1234' is not assignable to type 'string'. - let fullpath = path.join(root, match[1]); - let message = match[3]; + const fullpath = path.join(root, match[1]); + const message = match[3]; reporter(fullpath + message); } else { reporter(str); diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index fe6272b7a9..d342c9d24f 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -56,6 +56,7 @@ const compilations = [ 'json-language-features/client/tsconfig.json', 'json-language-features/server/tsconfig.json', 'markdown-language-features/preview-src/tsconfig.json', + 'markdown-language-features/server/tsconfig.json', 'markdown-language-features/tsconfig.json', 'markdown-math/tsconfig.json', 'merge-conflict/tsconfig.json', @@ -63,12 +64,13 @@ const compilations = [ 'npm/tsconfig.json', 'php-language-features/tsconfig.json', 'search-result/tsconfig.json', + 'references-view/tsconfig.json', 'simple-browser/tsconfig.json', 'typescript-language-features/test-workspace/tsconfig.json', 'typescript-language-features/tsconfig.json', 'vscode-api-tests/tsconfig.json', 'vscode-colorize-tests/tsconfig.json', - 'vscode-custom-editor-tests/tsconfig.json', + 'vscode-notebook-tests/tsconfig.json', 'vscode-test-resolver/tsconfig.json' ]; */ @@ -93,7 +95,7 @@ const tasks = compilations.map(function (tsconfigFile) { const baseUrl = getBaseUrl(out); let headerId, headerOut; - let index = relativeDirname.indexOf('/'); + const index = relativeDirname.indexOf('/'); if (index < 0) { headerId = 'microsoft.' + relativeDirname; // {{SQL CARBON EDIT}} headerOut = 'out'; @@ -102,9 +104,9 @@ const tasks = compilations.map(function (tsconfigFile) { headerOut = relativeDirname.substr(index + 1) + '/out'; } - function createPipeline(build, emitError) { + function createPipeline(build, emitError, transpileOnly) { const nlsDev = require('vscode-nls-dev'); - const tsb = require('gulp-tsb'); + const tsb = require('./lib/tsb'); const sourcemaps = require('gulp-sourcemaps'); const reporter = createReporter('extensions'); @@ -112,7 +114,7 @@ const tasks = compilations.map(function (tsconfigFile) { overrideOptions.inlineSources = Boolean(build); overrideOptions.base = path.dirname(absolutePath); - const compilation = tsb.create(absolutePath, overrideOptions, false, err => reporter(err.toString())); + const compilation = tsb.create(absolutePath, overrideOptions, { verbose: false, transpileOnly, transpileOnlyIncludesDts: transpileOnly }, err => reporter(err.toString())); const pipeline = function () { const input = es.through(); @@ -154,6 +156,16 @@ const tasks = compilations.map(function (tsconfigFile) { const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out)); + const transpileTask = task.define(`transpile-extension:${name}`, task.series(cleanTask, () => { + const pipeline = createPipeline(false, true, true); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); + + return input + .pipe(pipeline()) + .pipe(gulp.dest(out)); + })); + const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false, true); const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); @@ -186,12 +198,16 @@ const tasks = compilations.map(function (tsconfigFile) { })); // Tasks + gulp.task(transpileTask); gulp.task(compileTask); gulp.task(watchTask); - return { compileTask, watchTask, compileBuildTask }; + return { transpileTask, compileTask, watchTask, compileBuildTask }; }); +const transpileExtensionsTask = task.define('transpile-extensions', task.parallel(...tasks.map(t => t.transpileTask))); +gulp.task(transpileExtensionsTask); + const compileExtensionsTask = task.define('compile-extensions', task.parallel(...tasks.map(t => t.compileTask))); gulp.task(compileExtensionsTask); exports.compileExtensionsTask = compileExtensionsTask; @@ -229,7 +245,11 @@ const compileExtensionsBuildTask = task.define('compile-extensions-build', task. )); gulp.task(compileExtensionsBuildTask); -gulp.task(task.define('extensions-ci', task.series(compileExtensionsBuildTask, compileExtensionMediaBuildTask))); +// {{SQL CARBON EDIT}} Needed to pass the "done" gulp callback function to fix "Did you roget to signal async completion" error +gulp.task(task.define('extensions-ci', (done) => { + task.series(compileExtensionsBuildTask, compileExtensionMediaBuildTask); + done(); +})); exports.compileExtensionsBuildTask = compileExtensionsBuildTask; diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index d2ccb4885f..7294d319ad 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -16,7 +16,7 @@ function checkPackageJSON(actualPath) { const actual = require(path.join(__dirname, '..', actualPath)); const rootPackageJSON = require('../package.json'); const checkIncluded = (set1, set2) => { - for (let depName in set1) { + for (const depName in set1) { if (depName === 'typescript') { continue; } diff --git a/build/gulpfile.js b/build/gulpfile.js index a4cccae498..06b1268cf7 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -11,7 +11,7 @@ require('events').EventEmitter.defaultMaxListeners = 100; const gulp = require('gulp'); const util = require('./lib/util'); const task = require('./lib/task'); -const { compileTask, watchTask, compileApiProposalNamesTask, watchApiProposalNamesTask } = require('./lib/compilation'); +const { transpileTask, compileTask, watchTask, compileApiProposalNamesTask, watchApiProposalNamesTask } = require('./lib/compilation'); const { monacoTypecheckTask/* , monacoTypecheckWatchTask */ } = require('./gulpfile.editor'); const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } = require('./gulpfile.extensions'); @@ -19,6 +19,10 @@ const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } gulp.task(compileApiProposalNamesTask); gulp.task(watchApiProposalNamesTask); +// Transpile only +const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), transpileTask('src', 'out'))); +gulp.task(transpileClientTask); + // Fast compile for development time const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileApiProposalNamesTask, compileTask('src', 'out', false))); gulp.task(compileClientTask); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 3a669fce9f..87fcfe27df 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -116,7 +116,7 @@ const serverEntryPoints = [ name: 'vs/workbench/api/node/extensionHostProcess', exclude: ['vs/css', 'vs/nls'] }, - { + { name: 'vs/platform/files/node/watcher/watcherMain', exclude: ['vs/css', 'vs/nls'] }, @@ -135,7 +135,7 @@ try { // Include workbench web ...vscodeWebEntryPoints - ]; + ]; } catch (err) { serverWithWebEntryPoints = [ // Include all of server @@ -356,14 +356,14 @@ function copyConfigTask(folder) { const json = require('gulp-json-editor'); return gulp.src(['remote/pkg-package.json'], { base: 'remote' }) - .pipe(rename(path => path.basename += '.' + folder)) - .pipe(json(obj => { - const pkg = obj.pkg; - pkg.scripts = pkg.scripts && pkg.scripts.map(p => path.join(destination, p)); - pkg.assets = pkg.assets && pkg.assets.map(p => path.join(destination, p)); - return obj; - })) - .pipe(vfs.dest('out-vscode-reh-pkg')); + .pipe(rename(path => path.basename += '.' + folder)) + .pipe(json(obj => { + const pkg = obj.pkg; + pkg.scripts = pkg.scripts && pkg.scripts.map(p => path.join(destination, p)); + pkg.assets = pkg.assets && pkg.assets.map(p => path.join(destination, p)); + return obj; + })) + .pipe(vfs.dest('out-vscode-reh-pkg')); }; } diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index be7e312349..877e088292 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -64,7 +64,6 @@ const vscodeResources = [ 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-browser/preload.js', 'out-build/vs/platform/environment/node/userDataPath.js', - 'out-build/vs/platform/extensions/node/extensionHostStarterWorkerMain.js', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', @@ -76,7 +75,7 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/tasks/**/*.json', 'out-build/vs/platform/files/**/*.exe', 'out-build/vs/platform/files/**/*.md', - 'out-build/vs/code/electron-browser/workbench/**', + 'out-build/vs/code/electron-sandbox/workbench/**', 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', 'out-build/vs/code/electron-sandbox/issue/issueReporter.js', 'out-build/sql/**/*.{svg,png,cur,html}', @@ -187,9 +186,9 @@ gulp.task(core); * @return {Object} A map of paths to checksums. */ function computeChecksums(out, filenames) { - let result = {}; + const result = {}; filenames.forEach(function (filename) { - let fullPath = path.join(process.cwd(), out, filename); + const fullPath = path.join(process.cwd(), out, filename); result[filename] = computeChecksum(fullPath); }); return result; @@ -202,9 +201,9 @@ function computeChecksums(out, filenames) { * @return {string} The checksum for `filename`. */ function computeChecksum(filename) { - let contents = fs.readFileSync(filename); + const contents = fs.readFileSync(filename); - let hash = crypto + const hash = crypto .createHash('md5') .update(contents) .digest('base64') @@ -230,8 +229,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op 'vs/workbench/workbench.desktop.main.js', 'vs/workbench/workbench.desktop.main.css', 'vs/workbench/api/node/extensionHostProcess.js', - 'vs/code/electron-browser/workbench/workbench.html', - 'vs/code/electron-browser/workbench/workbench.js' + 'vs/code/electron-sandbox/workbench/workbench.html', + 'vs/code/electron-sandbox/workbench/workbench.js' ]); const src = gulp.src(out + '/**', { base: '.' }) @@ -322,6 +321,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' })); } else if (platform === 'darwin') { const shortcut = gulp.src('resources/darwin/bin/code.sh') + .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename('bin/code')); all = es.merge(all, shortcut); @@ -366,10 +366,14 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' }) .pipe(rename(product.nameShort + '.VisualElementsManifest.xml'))); + + result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' }) + .pipe(rename(f => f.dirname = `policies/${f.dirname}`))); + } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) .pipe(replace('@@PRODNAME@@', product.nameLong)) - .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename('bin/' + product.applicationName))); } @@ -523,7 +527,7 @@ gulp.task(task.define( gulp.task('vscode-translations-pull', function () { return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => { - let includeDefault = !!innoSetupConfig[language.id].defaultInfo; + const includeDefault = !!innoSetupConfig[language.id].defaultInfo; return i18n.pullSetupXlfFiles(apiHostname, apiName, apiToken, language, includeDefault).pipe(vfs.dest(`../vscode-translations-import/${language.id}/setup`)); })); }); diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 2770151708..dc1b101c85 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -16,6 +16,9 @@ const task = require('./lib/task'); const packageJson = require('../package.json'); const product = require('../product.json'); const rpmDependenciesGenerator = require('./linux/rpm/dependencies-generator'); +const debianDependenciesGenerator = require('./linux/debian/dependencies-generator'); +const sysrootInstaller = require('./linux/debian/install-sysroot'); +const debianRecommendedDependencies = require('./linux/debian/dep-lists').recommendedDeps; const path = require('path'); const root = path.dirname(__dirname); const commit = util.getVersion(root); @@ -75,12 +78,16 @@ function prepareDebPackage(arch) { let size = 0; const control = code.pipe(es.through( function (f) { size += f.isDirectory() ? 4096 : f.contents.length; }, - function () { + async function () { const that = this; + const sysroot = await sysrootInstaller.getSysroot(debArch); + const dependencies = debianDependenciesGenerator.getDependencies(binaryDir, product.applicationName, debArch, sysroot); gulp.src('resources/linux/debian/control.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@VERSION@@', packageJson.version + '-' + linuxPackageRevision)) .pipe(replace('@@ARCHITECTURE@@', debArch)) + .pipe(replace('@@DEPENDS@@', dependencies.join(', '))) + .pipe(replace('@@RECOMMENDS@@', debianRecommendedDependencies.join(', '))) .pipe(replace('@@INSTALLEDSIZE@@', Math.ceil(size / 1024))) .pipe(rename('DEBIAN/control')) .pipe(es.through(function (f) { that.emit('data', f); }, function () { that.emit('end'); })); @@ -212,7 +219,7 @@ function buildRpmPackage(arch) { return shell.task([ 'mkdir -p ' + destination, - 'HOME="$(pwd)/' + destination + '" fakeroot rpmbuild -bb ' + rpmBuildPath + '/SPECS/' + product.applicationName + '.spec --target=' + rpmArch, + 'HOME="$(pwd)/' + destination + '" rpmbuild -bb ' + rpmBuildPath + '/SPECS/' + product.applicationName + '.spec --target=' + rpmArch, 'cp "' + rpmOut + '/$(ls ' + rpmOut + ')" ' + destination + '/' ]); } diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 2e82737c2f..9a00be9971 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -208,7 +208,7 @@ function packageTask(sourceFolderName, destinationFolderName) { gulp.src('resources/server/code-512.png', { base: 'resources/server' }) ); - let all = es.merge( + const all = es.merge( packageJsonStream, license, sources, @@ -218,7 +218,7 @@ function packageTask(sourceFolderName, destinationFolderName) { pwaicons ); - let result = all + const result = all .pipe(util.skipDirectories()) .pipe(util.fixWin32DirectoryPermissions()); diff --git a/build/hygiene.js b/build/hygiene.js index 9e08a8b001..a12329e5a8 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -53,7 +53,7 @@ function hygiene(some, linting = true) { const m = /([^\t\n\r\x20-\x7E⊃⊇✔︎✓🎯⚠️🛑🔴🚗🚙🚕🎉✨❗⇧⌥⌘×÷¦⋯…↑↓→→←↔⟷·•●◆▼⟪⟫┌└├⏎↩√φ]+)/g.exec(line); if (m) { console.error( - file.relative + `(${i + 1},${m.index + 1}): Unexpected unicode character: "${m[0]}". To suppress, use // allow-any-unicode-next-line` + file.relative + `(${i + 1},${m.index + 1}): Unexpected unicode character: "${m[0]}" (charCode: ${m[0].charCodeAt(0)}). To suppress, use // allow-any-unicode-next-line` ); errorCount++; } @@ -115,8 +115,8 @@ function hygiene(some, linting = true) { }) .then( (result) => { - let original = result.src.replace(/\r\n/gm, '\n'); - let formatted = result.dest.replace(/\r\n/gm, '\n'); + const original = result.src.replace(/\r\n/gm, '\n'); + const formatted = result.dest.replace(/\r\n/gm, '\n'); if (original !== formatted) { console.error( diff --git a/build/lib/asar.js b/build/lib/asar.js index c3ada32736..2a25780d44 100644 --- a/build/lib/asar.js +++ b/build/lib/asar.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAsar = void 0; const path = require("path"); @@ -81,7 +81,7 @@ function createAsar(folderPath, unpackGlobs, destFilename) { out.push(file.contents); } }, function () { - let finish = () => { + const finish = () => { { const headerPickle = pickle.createEmpty(); headerPickle.writeString(JSON.stringify(filesystem.header)); diff --git a/build/lib/asar.ts b/build/lib/asar.ts index 2a50b219ba..508339ff85 100644 --- a/build/lib/asar.ts +++ b/build/lib/asar.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as path from 'path'; import * as es from 'event-stream'; const pickle = require('chromium-pickle-js'); @@ -98,7 +96,7 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena } }, function () { - let finish = () => { + const finish = () => { { const headerPickle = pickle.createEmpty(); headerPickle.writeString(JSON.stringify(filesystem.header)); diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index 50d2dbb787..e0a4dc74a8 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -98,12 +98,12 @@ function writeControlFile(control) { fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } function getBuiltInExtensions() { - log('Syncronizing built-in extensions...'); + log('Synchronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); const streams = []; for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) { - let controlState = control[extension.name] || 'marketplace'; + const controlState = control[extension.name] || 'marketplace'; control[extension.name] = controlState; streams.push(syncExtension(extension, controlState)); } diff --git a/build/lib/builtInExtensions.ts b/build/lib/builtInExtensions.ts index f5daac1919..2373db4738 100644 --- a/build/lib/builtInExtensions.ts +++ b/build/lib/builtInExtensions.ts @@ -136,14 +136,14 @@ function writeControlFile(control: IControlFile): void { } export function getBuiltInExtensions(): Promise { - log('Syncronizing built-in extensions...'); + log('Synchronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); const streams: Stream[] = []; for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) { - let controlState = control[extension.name] || 'marketplace'; + const controlState = control[extension.name] || 'marketplace'; control[extension.name] = controlState; streams.push(syncExtension(extension, controlState)); diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 6ea3384ff3..571cbb27f7 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -43,14 +43,20 @@ function bundle(entryPoints, config, callback) { if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } + config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; + config.buildForceInvokeFactory['vs/nls'] = true; + config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire) => { - const resolvePath = (path) => { - const r = localRequire.toUrl(path); - if (!/\.js/.test(r)) { - return r + '.js'; + const resolvePath = (entry) => { + let r = localRequire.toUrl(entry.path); + if (!r.endsWith('.js')) { + r += '.js'; } - return r; + // avoid packaging the build version of plugins: + r = r.replace('vs/nls.build.js', 'vs/nls.js'); + 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]; @@ -299,9 +305,18 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, if (module.shim) { mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); } - else { + 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]; @@ -322,10 +337,13 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, plugin.writeFile(pluginName, entryPoint, req, write, {}); } }); - const toIFile = (path) => { - const contents = readFileAndRemoveBOM(path); + const toIFile = (entry) => { + let contents = readFileAndRemoveBOM(entry.path); + if (entry.amdModuleId) { + contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); + } return { - path: path, + path: entry.path, contents: contents }; }; diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 779a23e575..62a22b03bc 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -15,7 +15,7 @@ interface IPosition { interface IBuildModuleInfo { id: string; path: string; - defineLocation: IPosition; + defineLocation: IPosition | null; dependencies: string[]; shim: string; exports: any; @@ -42,12 +42,17 @@ interface ILoaderPluginReqFunc { toUrl(something: string): string; } +export interface IExtraFile { + path: string; + amdModuleId?: string; +} + export interface IEntryPoint { name: string; include?: string[]; exclude?: string[]; - prepend?: string[]; - append?: string[]; + prepend?: IExtraFile[]; + append?: IExtraFile[]; dest?: string; } @@ -92,6 +97,13 @@ interface IPartialBundleResult { 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; + }; } /** @@ -132,15 +144,21 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } + config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; + config.buildForceInvokeFactory['vs/nls'] = true; + config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire: any) => { - const resolvePath = (path: string) => { - const r = localRequire.toUrl(path); - if (!/\.js/.test(r)) { - return r + '.js'; + const resolvePath = (entry: IExtraFile) => { + let r = localRequire.toUrl(entry.path); + if (!r.endsWith('.js')) { + r += '.js'; } - return r; + // avoid packaging the build version of plugins: + r = r.replace('vs/nls.build.js', 'vs/nls.js'); + 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]; @@ -403,8 +421,8 @@ function emitEntryPoint( deps: IGraph, entryPoint: string, includedModules: string[], - prepend: string[], - append: string[], + prepend: IExtraFile[], + append: IExtraFile[], dest: string | undefined ): IEmitEntryPointResult { if (!dest) { @@ -444,8 +462,16 @@ function emitEntryPoint( if (module.shim) { mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } else { + } 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)}`); } }); @@ -470,10 +496,13 @@ function emitEntryPoint( } }); - const toIFile = (path: string): IFile => { - const contents = readFileAndRemoveBOM(path); + const toIFile = (entry: IExtraFile): IFile => { + let contents = readFileAndRemoveBOM(entry.path); + if (entry.amdModuleId) { + contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); + } return { - path: path, + path: entry.path, contents: contents }; }; diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 6e1ea4beb2..a6f8577f13 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -1,10 +1,10 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. + * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = exports.watchTask = exports.compileTask = void 0; +exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = exports.watchTask = exports.compileTask = exports.transpileTask = void 0; const es = require("event-stream"); const fs = require("fs"); const gulp = require("gulp"); @@ -22,7 +22,7 @@ const watch = require('./watch'); const reporter = (0, reporter_1.createReporter)(); function getTypeScriptCompilerOptions(src) { const rootDir = path.join(__dirname, `../../${src}`); - let options = {}; + const options = {}; options.verbose = false; options.sourceMap = true; if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry @@ -34,8 +34,8 @@ function getTypeScriptCompilerOptions(src) { options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1; return options; } -function createCompile(src, build, emitError) { - const tsb = require('gulp-tsb'); +function createCompile(src, build, emitError, transpileOnly) { + const tsb = require('./tsb'); const sourcemaps = require('gulp-sourcemaps'); const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) }); @@ -69,7 +69,7 @@ function createCompile(src, build, emitError) { .pipe(noDeclarationsFilter) .pipe(build ? nls.nls() : es.through()) .pipe(noDeclarationsFilter.restore) - .pipe(sourcemaps.write('.', { + .pipe(transpileOnly ? es.through() : sourcemaps.write('.', { addComment: false, includeContent: !!build, sourceRoot: overrideOptions.sourceRoot @@ -83,14 +83,24 @@ function createCompile(src, build, emitError) { }; return pipeline; } +function transpileTask(src, out) { + return function () { + const transpile = createCompile(src, false, true, true); + const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); + return srcPipe + .pipe(transpile()) + .pipe(gulp.dest(out)); + }; +} +exports.transpileTask = transpileTask; function compileTask(src, out, build) { return function () { if (os.totalmem() < 4000000000) { throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true); + const compile = createCompile(src, build, true, false); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); - let generator = new MonacoGenerator(false); + const generator = new MonacoGenerator(false); if (src === 'src') { generator.execute(); } @@ -103,10 +113,10 @@ function compileTask(src, out, build) { exports.compileTask = compileTask; function watchTask(out, build) { return function () { - const compile = createCompile('src', build); + const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); - let generator = new MonacoGenerator(true); + const generator = new MonacoGenerator(true); generator.execute(); return watchSrc .pipe(generator.stream) @@ -122,7 +132,7 @@ class MonacoGenerator { this._isWatch = isWatch; this.stream = es.through(); this._watchedFiles = {}; - let onWillReadFile = (moduleId, filePath) => { + const onWillReadFile = (moduleId, filePath) => { if (!this._isWatch) { return; } @@ -159,7 +169,7 @@ class MonacoGenerator { }, 20); } _run() { - let r = monacodts.run3(this._declarationResolver); + const r = monacodts.run3(this._declarationResolver); if (!r && !this._isWatch) { // The build must always be able to generate the monaco.d.ts throw new Error(`monaco.d.ts generation error - Cannot continue`); @@ -188,6 +198,15 @@ class MonacoGenerator { } } function generateApiProposalNames() { + let eol; + try { + const src = fs.readFileSync('src/vs/workbench/services/extensions/common/extensionsApiProposals.ts', 'utf-8'); + const match = /\r?\n/m.exec(src); + eol = match ? match[0] : os.EOL; + } + catch { + eol = os.EOL; + } const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts$/; const proposalNames = new Set(); const input = es.through(); @@ -214,7 +233,7 @@ function generateApiProposalNames() { '});', 'export type ApiProposalName = keyof typeof allApiProposals;', '', - ].join(os.EOL); + ].join(eol); this.emit('data', new File({ path: 'vs/workbench/services/extensions/common/extensionsApiProposals.ts', contents: Buffer.from(contents) diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 7a5a0bc2ff..a764c6a7a2 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -26,7 +26,7 @@ const reporter = createReporter(); function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { const rootDir = path.join(__dirname, `../../${src}`); - let options: ts.CompilerOptions = {}; + const options: ts.CompilerOptions = {}; options.verbose = false; options.sourceMap = true; if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry @@ -39,8 +39,8 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { return options; } -function createCompile(src: string, build: boolean, emitError?: boolean) { - const tsb = require('gulp-tsb') as typeof import('gulp-tsb'); +function createCompile(src: string, build: boolean, emitError: boolean, transpileOnly: boolean) { + const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); @@ -80,7 +80,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { .pipe(noDeclarationsFilter) .pipe(build ? nls.nls() : es.through()) .pipe(noDeclarationsFilter.restore) - .pipe(sourcemaps.write('.', { + .pipe(transpileOnly ? es.through() : sourcemaps.write('.', { addComment: false, includeContent: !!build, sourceRoot: overrideOptions.sourceRoot @@ -96,6 +96,19 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { return pipeline; } +export function transpileTask(src: string, out: string): () => NodeJS.ReadWriteStream { + + return function () { + + const transpile = createCompile(src, false, true, true); + const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); + + return srcPipe + .pipe(transpile()) + .pipe(gulp.dest(out)); + }; +} + export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { @@ -104,9 +117,9 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true); + const compile = createCompile(src, build, true, false); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); - let generator = new MonacoGenerator(false); + const generator = new MonacoGenerator(false); if (src === 'src') { generator.execute(); } @@ -121,12 +134,12 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile('src', build); + const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); - let generator = new MonacoGenerator(true); + const generator = new MonacoGenerator(true); generator.execute(); return watchSrc @@ -150,7 +163,7 @@ class MonacoGenerator { this._isWatch = isWatch; this.stream = es.through(); this._watchedFiles = {}; - let onWillReadFile = (moduleId: string, filePath: string) => { + const onWillReadFile = (moduleId: string, filePath: string) => { if (!this._isWatch) { return; } @@ -192,7 +205,7 @@ class MonacoGenerator { } private _run(): monacodts.IMonacoDeclarationResult | null { - let r = monacodts.run3(this._declarationResolver); + const r = monacodts.run3(this._declarationResolver); if (!r && !this._isWatch) { // The build must always be able to generate the monaco.d.ts throw new Error(`monaco.d.ts generation error - Cannot continue`); @@ -225,6 +238,16 @@ class MonacoGenerator { } function generateApiProposalNames() { + let eol: string; + + try { + const src = fs.readFileSync('src/vs/workbench/services/extensions/common/extensionsApiProposals.ts', 'utf-8'); + const match = /\r?\n/m.exec(src); + eol = match ? match[0] : os.EOL; + } catch { + eol = os.EOL; + } + const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts$/; const proposalNames = new Set(); @@ -253,7 +276,7 @@ function generateApiProposalNames() { '});', 'export type ApiProposalName = keyof typeof allApiProposals;', '', - ].join(os.EOL); + ].join(eol); this.emit('data', new File({ path: 'vs/workbench/services/extensions/common/extensionsApiProposals.ts', diff --git a/build/lib/dependencies.js b/build/lib/dependencies.js index 31a8c9450b..658d63a576 100644 --- a/build/lib/dependencies.js +++ b/build/lib/dependencies.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.getProductionDependencies = void 0; const path = require("path"); diff --git a/build/lib/dependencies.ts b/build/lib/dependencies.ts index 8292d92bdc..4ef161bc7c 100644 --- a/build/lib/dependencies.ts +++ b/build/lib/dependencies.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as path from 'path'; import * as cp from 'child_process'; import * as _ from 'underscore'; diff --git a/build/lib/electron.js b/build/lib/electron.js index 1c804081d3..c4060df064 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.config = void 0; const fs = require("fs"); diff --git a/build/lib/electron.ts b/build/lib/electron.ts index b2e11a07b8..7aedd39710 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fs from 'fs'; import * as path from 'path'; import * as vfs from 'vinyl-fs'; diff --git a/build/lib/eslint/code-no-look-behind-regex.js b/build/lib/eslint/code-no-look-behind-regex.js index 03212ee32e..065be7d9ff 100644 --- a/build/lib/eslint/code-no-look-behind-regex.js +++ b/build/lib/eslint/code-no-look-behind-regex.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); //------------------------------------------------------------------------------ // Rule Definition diff --git a/build/lib/eslint/code-no-look-behind-regex.ts b/build/lib/eslint/code-no-look-behind-regex.ts index 7d10462639..9e6d2bda50 100644 --- a/build/lib/eslint/code-no-look-behind-regex.ts +++ b/build/lib/eslint/code-no-look-behind-regex.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as eslint from 'eslint'; import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as ESTree from 'estree'; diff --git a/build/lib/eslint/code-no-unexternalized-strings.js b/build/lib/eslint/code-no-unexternalized-strings.js index 748778d151..e72ea0974b 100644 --- a/build/lib/eslint/code-no-unexternalized-strings.js +++ b/build/lib/eslint/code-no-unexternalized-strings.js @@ -41,7 +41,7 @@ module.exports = new (_a = class NoUnexternalizedStrings { key = keyNode.value; } else if (keyNode.type === experimental_utils_1.AST_NODE_TYPES.ObjectExpression) { - for (let property of keyNode.properties) { + for (const property of keyNode.properties) { if (property.type === experimental_utils_1.AST_NODE_TYPES.Property && !property.computed) { if (property.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier && property.key.name === 'key') { if (isStringLiteral(property.value)) { @@ -83,7 +83,7 @@ module.exports = new (_a = class NoUnexternalizedStrings { // (2) // report all invalid NLS keys if (!key.match(NoUnexternalizedStrings._rNlsKeys)) { - for (let value of values) { + for (const value of values) { context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } }); } } diff --git a/build/lib/eslint/code-no-unexternalized-strings.ts b/build/lib/eslint/code-no-unexternalized-strings.ts index ae9f6f8044..a0cacc63fc 100644 --- a/build/lib/eslint/code-no-unexternalized-strings.ts +++ b/build/lib/eslint/code-no-unexternalized-strings.ts @@ -51,7 +51,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { key = keyNode.value; } else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) { - for (let property of keyNode.properties) { + for (const property of keyNode.properties) { if (property.type === AST_NODE_TYPES.Property && !property.computed) { if (property.key.type === AST_NODE_TYPES.Identifier && property.key.name === 'key') { if (isStringLiteral(property.value)) { @@ -97,7 +97,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { // (2) // report all invalid NLS keys if (!key.match(NoUnexternalizedStrings._rNlsKeys)) { - for (let value of values) { + for (const value of values) { context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } }); } } diff --git a/build/lib/eslint/code-no-unused-expressions.js b/build/lib/eslint/code-no-unused-expressions.js index 30097ba58f..19c6e2b183 100644 --- a/build/lib/eslint/code-no-unused-expressions.js +++ b/build/lib/eslint/code-no-unused-expressions.js @@ -1,14 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// FORKED FROM https://github.com/eslint/eslint/blob/b23ad0d789a909baf8d7c41a35bc53df932eaf30/lib/rules/no-unused-expressions.js -// and added support for `OptionalCallExpression`, see https://github.com/facebook/create-react-app/issues/8107 and https://github.com/eslint/eslint/issues/12642 -/** - * @fileoverview Flag expressions in statement position that do not side effect - * @author Michael Ficarra - */ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); //------------------------------------------------------------------------------ // Rule Definition diff --git a/build/lib/eslint/code-no-unused-expressions.ts b/build/lib/eslint/code-no-unused-expressions.ts index b6122759fa..19ddb9fc35 100644 --- a/build/lib/eslint/code-no-unused-expressions.ts +++ b/build/lib/eslint/code-no-unused-expressions.ts @@ -11,8 +11,6 @@ * @author Michael Ficarra */ -'use strict'; - import * as eslint from 'eslint'; import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as ESTree from 'estree'; diff --git a/build/lib/eslint/vscode-dts-cancellation.js b/build/lib/eslint/vscode-dts-cancellation.js index 32e59a2344..10a4576d34 100644 --- a/build/lib/eslint/vscode-dts-cancellation.js +++ b/build/lib/eslint/vscode-dts-cancellation.js @@ -16,7 +16,7 @@ module.exports = new class ApiProviderNaming { return { ['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature[key.name=/^(provide|resolve).+/]']: (node) => { let found = false; - for (let param of node.params) { + for (const param of node.params) { if (param.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { found = found || param.name === 'token'; } diff --git a/build/lib/eslint/vscode-dts-cancellation.ts b/build/lib/eslint/vscode-dts-cancellation.ts index b78d023b4f..eabcc4f945 100644 --- a/build/lib/eslint/vscode-dts-cancellation.ts +++ b/build/lib/eslint/vscode-dts-cancellation.ts @@ -20,7 +20,7 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { ['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature[key.name=/^(provide|resolve).+/]']: (node: any) => { let found = false; - for (let param of (node).params) { + for (const param of (node).params) { if (param.type === AST_NODE_TYPES.Identifier) { found = found || param.name === 'token'; } diff --git a/build/lib/eslint/vscode-dts-event-naming.js b/build/lib/eslint/vscode-dts-event-naming.js index 55e1f62eba..26466baa9b 100644 --- a/build/lib/eslint/vscode-dts-event-naming.js +++ b/build/lib/eslint/vscode-dts-event-naming.js @@ -77,7 +77,7 @@ module.exports = new (_a = class ApiEventNaming { if (def.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { return def; } - else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.Property) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { + else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.PropertyDefinition) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { return def.key; } return this.getIdent(def.parent); diff --git a/build/lib/eslint/vscode-dts-event-naming.ts b/build/lib/eslint/vscode-dts-event-naming.ts index c72e659c19..906682cfa8 100644 --- a/build/lib/eslint/vscode-dts-event-naming.ts +++ b/build/lib/eslint/vscode-dts-event-naming.ts @@ -88,11 +88,10 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { if (def.type === AST_NODE_TYPES.Identifier) { return def; - } else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.Property) && def.key.type === AST_NODE_TYPES.Identifier) { + } else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.PropertyDefinition) && def.key.type === AST_NODE_TYPES.Identifier) { return def.key; } return this.getIdent(def.parent); } }; - diff --git a/build/lib/extensions.js b/build/lib/extensions.js index e0352f9703..924f76548c 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -34,14 +34,14 @@ function minifyExtensionResources(input) { .pipe(jsonFilter) .pipe(buffer()) .pipe(es.mapSync((f) => { - const errors = []; - const value = jsoncParser.parse(f.contents.toString('utf8'), errors); - if (errors.length === 0) { - // file parsed OK => just stringify to drop whitespace and comments - f.contents = Buffer.from(JSON.stringify(value)); - } - return f; - })) + const errors = []; + const value = jsoncParser.parse(f.contents.toString('utf8'), errors); + if (errors.length === 0) { + // file parsed OK => just stringify to drop whitespace and comments + f.contents = Buffer.from(JSON.stringify(value)); + } + return f; + })) .pipe(jsonFilter.restore); } function updateExtensionPackageJSON(input, update) { @@ -50,10 +50,10 @@ function updateExtensionPackageJSON(input, update) { .pipe(packageJsonFilter) .pipe(buffer()) .pipe(es.mapSync((f) => { - const data = JSON.parse(f.contents.toString('utf8')); - f.contents = Buffer.from(JSON.stringify(update(data))); - return f; - })) + const data = JSON.parse(f.contents.toString('utf8')); + f.contents = Buffer.from(JSON.stringify(update(data))); + return f; + })) .pipe(packageJsonFilter.restore); } function fromLocal(extensionPath, forWeb) { @@ -95,11 +95,11 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) { const files = fileNames .map(fileName => path.join(extensionPath, fileName)) .map(filePath => new File({ - path: filePath, - stat: fs.statSync(filePath), - base: extensionPath, - contents: fs.createReadStream(filePath) - })); + path: filePath, + stat: fs.statSync(filePath), + base: extensionPath, + contents: fs.createReadStream(filePath) + })); // check for a webpack configuration files, then invoke webpack // and merge its output with the files stream. const webpackConfigLocations = glob.sync(path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] }); @@ -123,20 +123,20 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) { const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path); return webpackGulp(webpackConfig, webpack, webpackDone) .pipe(es.through(function (data) { - data.stat = data.stat || {}; - data.base = extensionPath; - this.emit('data', data); - })) + data.stat = data.stat || {}; + data.base = extensionPath; + this.emit('data', data); + })) .pipe(es.through(function (data) { - // source map handling: - // * rewrite sourceMappingURL - // * save to disk so that upload-task picks this up - const contents = data.contents.toString('utf8'); - data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { - return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`; - }), 'utf8'); - this.emit('data', data); - })); + // source map handling: + // * rewrite sourceMappingURL + // * save to disk so that upload-task picks this up + const contents = data.contents.toString('utf8'); + data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { + return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`; + }), 'utf8'); + this.emit('data', data); + })); }); }); es.merge(...webpackStreams, es.readArray(files)) @@ -158,16 +158,16 @@ function fromLocalNormal(extensionPath) { const result = es.through(); vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn }) .then(fileNames => { - const files = fileNames - .map(fileName => path.join(extensionPath, fileName)) - .map(filePath => new File({ - path: filePath, - stat: fs.statSync(filePath), - base: extensionPath, - contents: fs.createReadStream(filePath) - })); - es.readArray(files).pipe(result); - }) + const files = fileNames + .map(fileName => path.join(extensionPath, fileName)) + .map(filePath => new File({ + path: filePath, + stat: fs.statSync(filePath), + base: extensionPath, + contents: fs.createReadStream(filePath) + })); + es.readArray(files).pipe(result); + }) .catch(err => result.emit('error', err)); return result.pipe((0, stats_1.createStatsStream)(path.basename(extensionPath))); } @@ -324,11 +324,11 @@ function isWebExtension(manifest) { function packageLocalExtensionsStream(forWeb) { const localExtensionsDescriptions = (glob.sync('extensions/*/package.json') .map(manifestPath => { - const absoluteManifestPath = path.join(root, manifestPath); - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; - }) + const absoluteManifestPath = path.join(root, manifestPath); + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; + }) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) .filter(({ name }) => externalExtensions.indexOf(name) === -1) // {{SQL CARBON EDIT}} Remove external Extensions with separate package @@ -359,15 +359,15 @@ function packageMarketplaceExtensionsStream(forWeb, galleryServiceUrl) { ]; const marketplaceExtensionsStream = minifyExtensionResources(es.merge(...marketplaceExtensionsDescriptions .map(extension => { - const input = (galleryServiceUrl ? fromMarketplace(galleryServiceUrl, extension) : fromGithub(extension)) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - return updateExtensionPackageJSON(input, (data) => { - delete data.scripts; - delete data.dependencies; - delete data.devDependencies; - return data; - }); - }))); + const input = (galleryServiceUrl ? fromMarketplace(galleryServiceUrl, extension) : fromGithub(extension)) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + return updateExtensionPackageJSON(input, (data) => { + delete data.scripts; + delete data.dependencies; + delete data.devDependencies; + return data; + }); + }))); return (marketplaceExtensionsStream .pipe(util2.setExecutableBit(['**/*.sh']))); } @@ -384,7 +384,7 @@ function scanBuiltinExtensions(extensionsRoot, exclude = []) { if (!fs.existsSync(packageJSONPath)) { continue; } - let packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); if (!isWebExtension(packageJSON)) { continue; } @@ -412,10 +412,10 @@ exports.scanBuiltinExtensions = scanBuiltinExtensions; function packageExternalExtensionsStream() { const extenalExtensionDescriptions = glob.sync('extensions/*/package.json') .map(manifestPath => { - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath }; - }) + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath }; + }) .filter(({ name }) => externalExtensions.indexOf(name) >= 0 || exports.vscodeExternalExtensions.indexOf(name) >= 0); const builtExtensions = extenalExtensionDescriptions.map(extension => { return fromLocal(extension.path, false) @@ -433,10 +433,10 @@ exports.cleanRebuildExtensions = cleanRebuildExtensions; function packageRebuildExtensionsStream() { const extenalExtensionDescriptions = glob.sync('extensions/*/package.json') .map(manifestPath => { - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath }; - }) + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath }; + }) .filter(({ name }) => rebuildExtensions.indexOf(name) >= 0); const builtExtensions = extenalExtensionDescriptions.map(extension => { return fromLocal(extension.path, false) @@ -450,7 +450,7 @@ function translatePackageJSON(packageJSON, packageNLSPath) { const CharCode_PC = '%'.charCodeAt(0); const packageNls = JSON.parse(fs.readFileSync(packageNLSPath).toString()); const translate = (obj) => { - for (let key in obj) { + for (const key in obj) { const val = obj[key]; if (Array.isArray(val)) { val.forEach(translate); @@ -477,6 +477,7 @@ const esbuildMediaScripts = [ 'markdown-language-features/esbuild-preview.js', 'markdown-math/esbuild.js', 'notebook-renderers/esbuild.js', + 'ipynb/esbuild.js', 'simple-browser/esbuild-preview.js', ]; async function webpackExtensions(taskName, isWatch, webpackConfigLocations) { diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index e3639249f8..60f8d9ccef 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -9,7 +9,7 @@ import * as cp from 'child_process'; import * as glob from 'glob'; import * as gulp from 'gulp'; import * as path from 'path'; -import * as through2 from 'through2'; +import * as through2 from 'through2' import got from 'got'; import { Stream } from 'stream'; import * as File from 'vinyl'; @@ -285,6 +285,7 @@ const excludedExtensions = [ 'ms-vscode.node-debug', 'ms-vscode.node-debug2', 'vscode-custom-editor-tests', + 'vscode-notebook-tests', 'integration-tests', // {{SQL CARBON EDIT}} ]; @@ -476,7 +477,7 @@ export function scanBuiltinExtensions(extensionsRoot: string, exclude: string[] if (!fs.existsSync(packageJSONPath)) { continue; } - let packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); if (!isWebExtension(packageJSON)) { continue; } @@ -549,7 +550,7 @@ export function translatePackageJSON(packageJSON: string, packageNLSPath: string const CharCode_PC = '%'.charCodeAt(0); const packageNls: NLSFormat = JSON.parse(fs.readFileSync(packageNLSPath).toString()); const translate = (obj: any) => { - for (let key in obj) { + for (const key in obj) { const val = obj[key]; if (Array.isArray(val)) { val.forEach(translate); @@ -575,6 +576,7 @@ const esbuildMediaScripts = [ 'markdown-language-features/esbuild-preview.js', 'markdown-math/esbuild.js', 'notebook-renderers/esbuild.js', + 'ipynb/esbuild.js', 'simple-browser/esbuild-preview.js', ]; @@ -588,7 +590,7 @@ export async function webpackExtensions(taskName: string, isWatch: boolean, webp function addConfig(configOrFn: webpack.Configuration | Function) { let config; if (typeof configOrFn === 'function') { - config = configOrFn({}, {}); + config = (configOrFn as Function)({}, {}); webpackConfigs.push(config); } else { config = configOrFn; diff --git a/build/lib/git.js b/build/lib/git.js index b3624674fa..0bdd2f247e 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -1,10 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getVersion = void 0; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getVersion = void 0; const path = require("path"); const fs = require("fs"); /** @@ -45,7 +45,7 @@ function getVersion(repo) { } const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; let refsMatch; - let refs = {}; + const refs = {}; while (refsMatch = refsRegex.exec(refsRaw)) { refs[refsMatch[2]] = refsMatch[1]; } diff --git a/build/lib/git.ts b/build/lib/git.ts index 366d642626..5e6e4d3dea 100644 --- a/build/lib/git.ts +++ b/build/lib/git.ts @@ -2,8 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as path from 'path'; import * as fs from 'fs'; @@ -51,7 +49,7 @@ export function getVersion(repo: string): string | undefined { const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; let refsMatch: RegExpExecArray | null; - let refs: { [ref: string]: string } = {}; + const refs: { [ref: string]: string } = {}; while (refsMatch = refsRegex.exec(refsRaw)) { refs[refsMatch[2]] = refsMatch[1]; diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 74fcbc4ba4..51cf249847 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -46,7 +46,7 @@ exports.externalExtensionsWithTranslations = { var LocalizeInfo; (function (LocalizeInfo) { function is(value) { - let candidate = value; + const candidate = value; return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(element => Is.string(element)))); } LocalizeInfo.is = is; @@ -57,8 +57,8 @@ var BundledFormat; if (Is.undef(value)) { return false; } - let candidate = value; - let length = Object.keys(value).length; + const candidate = value; + const length = Object.keys(value).length; return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); } BundledFormat.is = is; @@ -70,7 +70,7 @@ var PackageJsonFormat; return false; } return Object.keys(value).every(key => { - let element = value[key]; + const element = value[key]; return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); }); } @@ -133,9 +133,9 @@ class XLF { } this.numberOfMessages += keys.length; this.files[original] = []; - let existingKeys = new Set(); + const existingKeys = new Set(); for (let i = 0; i < keys.length; i++) { - let key = keys[i]; + const key = keys[i]; let realKey; let comment; if (Is.string(key)) { @@ -152,7 +152,7 @@ class XLF { continue; } existingKeys.add(realKey); - let message = encodeEntities(messages[i]); + const message = encodeEntities(messages[i]); this.files[original].push({ id: realKey, message: message, comment: comment }); } } @@ -178,7 +178,7 @@ class XLF { this.appendNewLine('', 0); } appendNewLine(content, indent) { - let line = new Line(indent); + const line = new Line(indent); line.append(content); this.buffer.push(line.toString()); } @@ -186,8 +186,8 @@ class XLF { exports.XLF = XLF; XLF.parsePseudo = function (xlfString) { return new Promise((resolve) => { - let parser = new xml2js.Parser(); - let files = []; + const parser = new xml2js.Parser(); + const files = []; parser.parseString(xlfString, function (_err, result) { const fileNodes = result['xliff']['file']; fileNodes.forEach(file => { @@ -211,8 +211,8 @@ XLF.parsePseudo = function (xlfString) { }; XLF.parse = function (xlfString) { return new Promise((resolve, reject) => { - let parser = new xml2js.Parser(); - let files = []; + const parser = new xml2js.Parser(); + const files = []; parser.parseString(xlfString, function (err, result) { if (err) { reject(new Error(`XLF parsing error: Failed to parse XLIFF string. ${err}`)); @@ -226,7 +226,7 @@ XLF.parse = function (xlfString) { if (!originalFilePath) { reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); } - let language = file.$['target-language']; + const language = file.$['target-language']; if (!language) { reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`)); } @@ -295,9 +295,10 @@ function stripComments(content) { // Second group matches a single quoted string // Third group matches a multi line comment // Forth group matches a single line comment - const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; - let result = content.replace(regexp, (match, _m1, _m2, m3, m4) => { - // Only one of m1, m2, m3, m4 matches + // Fifth group matches a trailing comma + const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))|(,\s*[}\]])/g; + const result = content.replace(regexp, (match, _m1, _m2, m3, m4, m5) => { + // Only one of m1, m2, m3, m4, m5 matches if (m3) { // A block comment. Replace with nothing return ''; @@ -313,6 +314,10 @@ function stripComments(content) { return ''; } } + else if (m5) { + // Remove the trailing comma + return match.substring(1); + } else { // We match a string return match; @@ -356,20 +361,20 @@ function escapeCharacters(value) { return result.join(''); } function processCoreBundleFormat(fileHeader, languages, json, emitter) { - let keysSection = json.keys; - let messageSection = json.messages; - let bundleSection = json.bundles; - let statistics = Object.create(null); - let defaultMessages = Object.create(null); - let modules = Object.keys(keysSection); + const keysSection = json.keys; + const messageSection = json.messages; + const bundleSection = json.bundles; + const statistics = Object.create(null); + const defaultMessages = Object.create(null); + const modules = Object.keys(keysSection); modules.forEach((module) => { - let keys = keysSection[module]; - let messages = messageSection[module]; + const keys = keysSection[module]; + const messages = messageSection[module]; if (!messages || keys.length !== messages.length) { emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); return; } - let messageMap = Object.create(null); + const messageMap = Object.create(null); defaultMessages[module] = messageMap; keys.map((key, i) => { if (typeof key === 'string') { @@ -380,27 +385,27 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { } }); }); - let languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); + const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); if (!fs.existsSync(languageDirectory)) { log(`No VS Code localization repository found. Looking at ${languageDirectory}`); log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); } - let sortedLanguages = sortLanguages(languages); + const sortedLanguages = sortLanguages(languages); sortedLanguages.forEach((language) => { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`Generating nls bundles for: ${language.id}`); } statistics[language.id] = 0; - let localizedModules = Object.create(null); - let languageFolderName = language.translationId || language.id; - let i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); + const localizedModules = Object.create(null); + const languageFolderName = language.translationId || language.id; + const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); let allMessages; if (fs.existsSync(i18nFile)) { - let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + const content = stripComments(fs.readFileSync(i18nFile, 'utf8')); allMessages = JSON.parse(content); } modules.forEach((module) => { - let order = keysSection[module]; + const order = keysSection[module]; let moduleMessage; if (allMessages) { moduleMessage = allMessages.contents[module]; @@ -412,7 +417,7 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { moduleMessage = defaultMessages[module]; statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; } - let localizedMessages = []; + const localizedMessages = []; order.forEach((keyInfo) => { let key = null; if (typeof keyInfo === 'string') { @@ -434,14 +439,14 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { localizedModules[module] = localizedMessages; }); Object.keys(bundleSection).forEach((bundle) => { - let modules = bundleSection[bundle]; - let contents = [ + const modules = bundleSection[bundle]; + const contents = [ fileHeader, `define("${bundle}.nls.${language.id}", {` ]; modules.forEach((module, index) => { contents.push(`\t"${module}": [`); - let messages = localizedModules[module]; + const messages = localizedModules[module]; if (!messages) { emitter.emit('error', `Didn't find messages for module ${module}.`); return; @@ -456,11 +461,11 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { }); }); Object.keys(statistics).forEach(key => { - let value = statistics[key]; + const value = statistics[key]; log(`${key} has ${value} untranslated strings.`); }); sortedLanguages.forEach(language => { - let stats = statistics[language.id]; + const stats = statistics[language.id]; if (Is.undef(stats)) { log(`\tNo translations found for language ${language.id}. Using default language instead.`); } @@ -468,7 +473,7 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { } function processNlsFiles(opts) { return (0, event_stream_1.through)(function (file) { - let fileName = path.basename(file.path); + const fileName = path.basename(file.path); if (fileName === 'nls.metadata.json') { let json = null; if (file.isBuffer()) { @@ -554,7 +559,7 @@ function createXlfFilesForCoreBundle() { xlf.addFile(`src/${coreModule}`, keys, messages); } } - for (let resource in xlfs) { + for (const resource in xlfs) { const xlf = xlfs[resource]; const filePath = `${xlf.project}/${resource.replace(/\//g, '_')}.xlf`; const xlfFile = new File({ @@ -586,7 +591,7 @@ function createXlfFilesForExtensions() { if (!stat.isDirectory()) { return; } - let extensionName = path.basename(extensionFolder.path); + const extensionName = path.basename(extensionFolder.path); if (extensionName === 'node_modules') { return; } @@ -604,17 +609,24 @@ function createXlfFilesForExtensions() { const basename = path.basename(file.path); if (basename === 'package.nls.json') { const json = JSON.parse(buffer.toString('utf8')); - const keys = Object.keys(json); - const messages = keys.map((key) => { + const keys = []; + const messages = []; + Object.keys(json).forEach((key) => { const value = json[key]; if (Is.string(value)) { - return value; + keys.push(key); + messages.push(value); } else if (value) { - return value.message; + keys.push({ + key, + comment: value.comment + }); + messages.push(value.message); } else { - return `Unknown message for key: ${key}`; + keys.push(key); + messages.push(`Unknown message for key: ${key}`); } }); getXlf().addFile(`extensions/${extensionName}/package`, keys, messages); @@ -622,7 +634,7 @@ function createXlfFilesForExtensions() { else if (basename === 'nls.metadata.json') { const json = JSON.parse(buffer.toString('utf8')); const relPath = path.relative(`.build/extensions/${extensionName}`, path.dirname(file.path)); - for (let file in json) { + for (const file in json) { const fileContent = json[file]; getXlf().addFile(`extensions/${extensionName}/${relPath}/${file}`, fileContent.keys, fileContent.messages); } @@ -634,7 +646,7 @@ function createXlfFilesForExtensions() { } }, function () { if (_xlf) { - let xlfFile = new File({ + const xlfFile = new File({ path: path.join(extensionsProject, extensionName + '.xlf'), contents: Buffer.from(_xlf.toString(), 'utf8') }); @@ -666,14 +678,14 @@ function createXlfFilesForIsl() { else { throw new Error(`Unknown input file ${file.path}`); } - let xlf = new XLF(projectName), keys = [], messages = []; - let model = new TextModel(file.contents.toString()); + const xlf = new XLF(projectName), keys = [], messages = []; + const model = new TextModel(file.contents.toString()); let inMessageSection = false; model.lines.forEach(line => { if (line.length === 0) { return; } - let firstChar = line.charAt(0); + const firstChar = line.charAt(0); switch (firstChar) { case ';': // Comment line; @@ -685,13 +697,13 @@ function createXlfFilesForIsl() { if (!inMessageSection) { return; } - let sections = line.split('='); + const sections = line.split('='); if (sections.length !== 2) { throw new Error(`Badly formatted message found: ${line}`); } else { - let key = sections[0]; - let value = sections[1]; + const key = sections[0]; + const value = sections[1]; if (key.length > 0 && value.length > 0) { keys.push(key); messages.push(value); @@ -708,8 +720,8 @@ function createXlfFilesForIsl() { } exports.createXlfFilesForIsl = createXlfFilesForIsl; function pushXlfFiles(apiHostname, username, password) { - let tryGetPromises = []; - let updateCreatePromises = []; + const tryGetPromises = []; + const updateCreatePromises = []; return (0, event_stream_1.through)(function (file) { const project = path.dirname(file.relative); const fileName = path.basename(file.path); @@ -747,11 +759,11 @@ function getAllResources(project, apiHostname, username, password) { method: 'GET' }; const request = https.request(options, (res) => { - let buffer = []; + const buffer = []; res.on('data', (chunk) => buffer.push(chunk)); res.on('end', () => { if (res.statusCode === 200) { - let json = JSON.parse(Buffer.concat(buffer).toString()); + const json = JSON.parse(Buffer.concat(buffer).toString()); if (Array.isArray(json)) { resolve(json.map(o => o.slug)); return; @@ -770,7 +782,7 @@ function getAllResources(project, apiHostname, username, password) { }); } function findObsoleteResources(apiHostname, username, password) { - let resourcesByProject = Object.create(null); + const resourcesByProject = Object.create(null); resourcesByProject[extensionsProject] = [].concat(exports.externalExtensionsWithTranslations); // clone return (0, event_stream_1.through)(function (file) { const project = path.dirname(file.relative); @@ -784,10 +796,10 @@ function findObsoleteResources(apiHostname, username, password) { this.push(file); }, function () { const json = JSON.parse(fs.readFileSync('./build/lib/i18n.resources.json', 'utf8')); - let i18Resources = [...json.editor, ...json.workbench].map((r) => r.project + '/' + r.name.replace(/\//g, '_')); - let extractedResources = []; - for (let project of [workbenchProject, editorProject]) { - for (let resource of resourcesByProject[project]) { + const i18Resources = [...json.editor, ...json.workbench].map((r) => r.project + '/' + r.name.replace(/\//g, '_')); + const extractedResources = []; + for (const project of [workbenchProject, editorProject]) { + for (const resource of resourcesByProject[project]) { if (resource !== 'setup_messages') { extractedResources.push(project + '/' + resource); } @@ -797,11 +809,11 @@ function findObsoleteResources(apiHostname, username, password) { console.log(`[i18n] Obsolete resources in file 'build/lib/i18n.resources.json': JSON.stringify(${i18Resources.filter(p => extractedResources.indexOf(p) === -1)})`); console.log(`[i18n] Missing resources in file 'build/lib/i18n.resources.json': JSON.stringify(${extractedResources.filter(p => i18Resources.indexOf(p) === -1)})`); } - let promises = []; - for (let project in resourcesByProject) { + const promises = []; + for (const project in resourcesByProject) { promises.push(getAllResources(project, apiHostname, username, password).then(resources => { - let expectedResources = resourcesByProject[project]; - let unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); + const expectedResources = resourcesByProject[project]; + const unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); if (unusedResources.length) { console.log(`[transifex] Obsolete resources in project '${project}': ${unusedResources.join(', ')}`); } @@ -856,7 +868,7 @@ function createResource(project, slug, xlfFile, apiHostname, credentials) { auth: credentials, method: 'POST' }; - let request = https.request(options, (res) => { + const request = https.request(options, (res) => { if (res.statusCode === 201) { log(`Resource ${project}/${slug} successfully created on Transifex.`); } @@ -888,7 +900,7 @@ function updateResource(project, slug, xlfFile, apiHostname, credentials) { auth: credentials, method: 'PUT' }; - let request = https.request(options, (res) => { + const request = https.request(options, (res) => { if (res.statusCode === 200) { res.setEncoding('utf8'); let responseBuffer = ''; @@ -913,7 +925,7 @@ function updateResource(project, slug, xlfFile, apiHostname, credentials) { }); } function pullSetupXlfFiles(apiHostname, username, password, language, includeDefault) { - let setupResources = [{ name: 'setup_messages', project: workbenchProject }]; + const setupResources = [{ name: 'setup_messages', project: workbenchProject }]; if (includeDefault) { setupResources.push({ name: 'setup_default', project: setupProject }); } @@ -922,7 +934,7 @@ function pullSetupXlfFiles(apiHostname, username, password, language, includeDef exports.pullSetupXlfFiles = pullSetupXlfFiles; function pullXlfFiles(apiHostname, username, password, language, resources) { const credentials = `${username}:${password}`; - let expectedTranslationsCount = resources.length; + const expectedTranslationsCount = resources.length; let translationsRetrieved = 0, called = false; return (0, event_stream_1.readable)(function (_count, callback) { // Mark end of stream when all resources were retrieved @@ -949,7 +961,7 @@ function retrieveResource(language, resource, apiHostname, credentials) { return limiter.queue(() => new Promise((resolve, reject) => { const slug = resource.name.replace(/\//g, '_'); const project = resource.project; - let transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; + const transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; const options = { hostname: apiHostname, path: `/api/2/project/${project}/resource/${slug}/translation/${transifexLanguageId}?file&mode=onlyreviewed`, @@ -958,8 +970,8 @@ function retrieveResource(language, resource, apiHostname, credentials) { method: 'GET' }; console.log('[transifex] Fetching ' + options.path); - let request = https.request(options, (res) => { - let xlfBuffer = []; + const request = https.request(options, (res) => { + const xlfBuffer = []; res.on('data', (chunk) => xlfBuffer.push(chunk)); res.on('end', () => { if (res.statusCode === 200) { @@ -981,14 +993,14 @@ function retrieveResource(language, resource, apiHostname, credentials) { })); } function prepareI18nFiles() { - let parsePromises = []; + const parsePromises = []; return (0, event_stream_1.through)(function (xlf) { - let stream = this; - let parsePromise = XLF.parse(xlf.contents.toString()); + const stream = this; + const parsePromise = XLF.parse(xlf.contents.toString()); parsePromises.push(parsePromise); parsePromise.then(resolvedFiles => { resolvedFiles.forEach(file => { - let translatedFile = createI18nFile(file.originalFilePath, file.messages); + const translatedFile = createI18nFile(file.originalFilePath, file.messages); stream.queue(translatedFile); }); }); @@ -1000,7 +1012,7 @@ function prepareI18nFiles() { } exports.prepareI18nFiles = prepareI18nFiles; function createI18nFile(originalFilePath, messages) { - let result = Object.create(null); + const result = Object.create(null); result[''] = [ '--------------------------------------------------------------------------------------------', 'Copyright (c) Microsoft Corporation. All rights reserved.', @@ -1008,7 +1020,7 @@ function createI18nFile(originalFilePath, messages) { '--------------------------------------------------------------------------------------------', 'Do not edit this file. It is machine generated.' ]; - for (let key of Object.keys(messages)) { + for (const key of Object.keys(messages)) { result[key] = messages[key]; } let content = JSON.stringify(result, null, '\t'); @@ -1068,7 +1080,7 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse const translatedMainFile = createI18nFile('./main', mainPack); resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' }); this.queue(translatedMainFile); - for (let extension in extensionsPacks) { + for (const extension in extensionsPacks) { const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); this.queue(translatedExtFile); const externalExtensionId = externalExtensions[extension]; @@ -1088,14 +1100,14 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse } exports.prepareI18nPackFiles = prepareI18nPackFiles; function prepareIslFiles(language, innoSetupConfig) { - let parsePromises = []; + const parsePromises = []; return (0, event_stream_1.through)(function (xlf) { - let stream = this; - let parsePromise = XLF.parse(xlf.contents.toString()); + const stream = this; + const parsePromise = XLF.parse(xlf.contents.toString()); parsePromises.push(parsePromise); parsePromise.then(resolvedFiles => { resolvedFiles.forEach(file => { - let translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); + const translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); stream.queue(translatedFile); }); }).catch(reason => { @@ -1111,7 +1123,7 @@ function prepareIslFiles(language, innoSetupConfig) { } exports.prepareIslFiles = prepareIslFiles; function createIslFile(originalFilePath, messages, language, innoSetup) { - let content = []; + const content = []; let originalContent; if (path.basename(originalFilePath) === 'Default') { originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); @@ -1121,16 +1133,16 @@ function createIslFile(originalFilePath, messages, language, innoSetup) { } originalContent.lines.forEach(line => { if (line.length > 0) { - let firstChar = line.charAt(0); + const firstChar = line.charAt(0); if (firstChar === '[' || firstChar === ';') { content.push(line); } else { - let sections = line.split('='); - let key = sections[0]; + const sections = line.split('='); + const key = sections[0]; let translated = line; if (key) { - let translatedMessage = messages[key]; + const translatedMessage = messages[key]; if (translatedMessage) { translated = `${key}=${translatedMessage}`; } @@ -1148,9 +1160,9 @@ function createIslFile(originalFilePath, messages, language, innoSetup) { }); } function encodeEntities(value) { - let result = []; + const result = []; for (let i = 0; i < value.length; i++) { - let ch = value[i]; + const ch = value[i]; switch (ch) { case '<': result.push('<'); diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 06bfbb2b50..dd949e618f 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -119,7 +119,11 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/localizations", + "name": "vs/workbench/contrib/mergeEditor", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/localization", "project": "vscode-workbench" }, { @@ -274,6 +278,10 @@ "name": "vs/workbench/contrib/userDataSync", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/editSessions", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/views", "project": "vscode-workbench" @@ -286,6 +294,14 @@ "name": "vs/workbench/contrib/audioCues", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/deprecatedExtensionMigrator", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/bracketPairColorizer2Telemetry", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/offline", "project": "vscode-workbench" @@ -422,6 +438,10 @@ "name": "vs/workbench/services/userDataSync", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/editSessions", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/views", "project": "vscode-workbench" @@ -451,11 +471,11 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/profiles", + "name": "vs/workbench/contrib/userDataProfile", "project": "vscode-profiles" }, { - "name": "vs/workbench/services/profiles", + "name": "vs/workbench/services/userDataProfile", "project": "vscode-profiles" } ] diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index b303820cc2..df146393b9 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -87,7 +87,7 @@ interface LocalizeInfo { module LocalizeInfo { export function is(value: any): value is LocalizeInfo { - let candidate = value as LocalizeInfo; + const candidate = value as LocalizeInfo; return Is.defined(candidate) && Is.string(candidate.key) && (Is.undef(candidate.comment) || (Is.array(candidate.comment) && candidate.comment.every(element => Is.string(element)))); } } @@ -104,8 +104,8 @@ module BundledFormat { return false; } - let candidate = value as BundledFormat; - let length = Object.keys(value).length; + const candidate = value as BundledFormat; + const length = Object.keys(value).length; return length === 3 && Is.defined(candidate.keys) && Is.defined(candidate.messages) && Is.defined(candidate.bundles); } @@ -126,7 +126,7 @@ module PackageJsonFormat { return false; } return Object.keys(value).every(key => { - let element = value[key]; + const element = value[key]; return Is.string(element) || (Is.object(element) && Is.defined(element.message) && Is.defined(element.comment)); }); } @@ -218,9 +218,9 @@ export class XLF { } this.numberOfMessages += keys.length; this.files[original] = []; - let existingKeys = new Set(); + const existingKeys = new Set(); for (let i = 0; i < keys.length; i++) { - let key = keys[i]; + const key = keys[i]; let realKey: string | undefined; let comment: string | undefined; if (Is.string(key)) { @@ -236,7 +236,7 @@ export class XLF { continue; } existingKeys.add(realKey); - let message: string = encodeEntities(messages[i]); + const message: string = encodeEntities(messages[i]); this.files[original].push({ id: realKey, message: message, comment: comment }); } } @@ -269,15 +269,15 @@ export class XLF { } private appendNewLine(content: string, indent?: number): void { - let line = new Line(indent); + const line = new Line(indent); line.append(content); this.buffer.push(line.toString()); } static parsePseudo = function (xlfString: string): Promise { return new Promise((resolve) => { - let parser = new xml2js.Parser(); - let files: { messages: Map; originalFilePath: string; language: string }[] = []; + const parser = new xml2js.Parser(); + const files: { messages: Map; originalFilePath: string; language: string }[] = []; parser.parseString(xlfString, function (_err: any, result: any) { const fileNodes: any[] = result['xliff']['file']; fileNodes.forEach(file => { @@ -302,9 +302,9 @@ export class XLF { static parse = function (xlfString: string): Promise { return new Promise((resolve, reject) => { - let parser = new xml2js.Parser(); + const parser = new xml2js.Parser(); - let files: { messages: Map; originalFilePath: string; language: string }[] = []; + const files: { messages: Map; originalFilePath: string; language: string }[] = []; parser.parseString(xlfString, function (err: any, result: any) { if (err) { @@ -321,7 +321,7 @@ export class XLF { if (!originalFilePath) { reject(new Error(`XLF parsing error: XLIFF file node does not contain original attribute to determine the original location of the resource file.`)); } - let language = file.$['target-language']; + const language = file.$['target-language']; if (!language) { reject(new Error(`XLF parsing error: XLIFF file node does not contain target-language attribute to determine translated language.`)); } @@ -412,9 +412,10 @@ function stripComments(content: string): string { // Second group matches a single quoted string // Third group matches a multi line comment // Forth group matches a single line comment - const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; - let result = content.replace(regexp, (match, _m1: string, _m2: string, m3: string, m4: string) => { - // Only one of m1, m2, m3, m4 matches + // Fifth group matches a trailing comma + const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))|(,\s*[}\]])/g; + const result = content.replace(regexp, (match, _m1: string, _m2: string, m3: string, m4: string, m5: string) => { + // Only one of m1, m2, m3, m4, m5 matches if (m3) { // A block comment. Replace with nothing return ''; @@ -427,6 +428,9 @@ function stripComments(content: string): string { } else { return ''; } + } else if (m5) { + // Remove the trailing comma + return match.substring(1); } else { // We match a string return match; @@ -472,22 +476,22 @@ function escapeCharacters(value: string): string { } function processCoreBundleFormat(fileHeader: string, languages: Language[], json: BundledFormat, emitter: ThroughStream) { - let keysSection = json.keys; - let messageSection = json.messages; - let bundleSection = json.bundles; + const keysSection = json.keys; + const messageSection = json.messages; + const bundleSection = json.bundles; - let statistics: Map = Object.create(null); + const statistics: Map = Object.create(null); - let defaultMessages: Map> = Object.create(null); - let modules = Object.keys(keysSection); + const defaultMessages: Map> = Object.create(null); + const modules = Object.keys(keysSection); modules.forEach((module) => { - let keys = keysSection[module]; - let messages = messageSection[module]; + const keys = keysSection[module]; + const messages = messageSection[module]; if (!messages || keys.length !== messages.length) { emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); return; } - let messageMap: Map = Object.create(null); + const messageMap: Map = Object.create(null); defaultMessages[module] = messageMap; keys.map((key, i) => { if (typeof key === 'string') { @@ -498,28 +502,28 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json }); }); - let languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); + const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); if (!fs.existsSync(languageDirectory)) { log(`No VS Code localization repository found. Looking at ${languageDirectory}`); log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); } - let sortedLanguages = sortLanguages(languages); + const sortedLanguages = sortLanguages(languages); sortedLanguages.forEach((language) => { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`Generating nls bundles for: ${language.id}`); } statistics[language.id] = 0; - let localizedModules: Map = Object.create(null); - let languageFolderName = language.translationId || language.id; - let i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); + const localizedModules: Map = Object.create(null); + const languageFolderName = language.translationId || language.id; + const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); let allMessages: I18nFormat | undefined; if (fs.existsSync(i18nFile)) { - let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + const content = stripComments(fs.readFileSync(i18nFile, 'utf8')); allMessages = JSON.parse(content); } modules.forEach((module) => { - let order = keysSection[module]; + const order = keysSection[module]; let moduleMessage: { [messageKey: string]: string } | undefined; if (allMessages) { moduleMessage = allMessages.contents[module]; @@ -531,7 +535,7 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json moduleMessage = defaultMessages[module]; statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; } - let localizedMessages: string[] = []; + const localizedMessages: string[] = []; order.forEach((keyInfo) => { let key: string | null = null; if (typeof keyInfo === 'string') { @@ -552,14 +556,14 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json localizedModules[module] = localizedMessages; }); Object.keys(bundleSection).forEach((bundle) => { - let modules = bundleSection[bundle]; - let contents: string[] = [ + const modules = bundleSection[bundle]; + const contents: string[] = [ fileHeader, `define("${bundle}.nls.${language.id}", {` ]; modules.forEach((module, index) => { contents.push(`\t"${module}": [`); - let messages = localizedModules[module]; + const messages = localizedModules[module]; if (!messages) { emitter.emit('error', `Didn't find messages for module ${module}.`); return; @@ -574,11 +578,11 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json }); }); Object.keys(statistics).forEach(key => { - let value = statistics[key]; + const value = statistics[key]; log(`${key} has ${value} untranslated strings.`); }); sortedLanguages.forEach(language => { - let stats = statistics[language.id]; + const stats = statistics[language.id]; if (Is.undef(stats)) { log(`\tNo translations found for language ${language.id}. Using default language instead.`); } @@ -587,7 +591,7 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json export function processNlsFiles(opts: { fileHeader: string; languages: Language[] }): ThroughStream { return through(function (this: ThroughStream, file: File) { - let fileName = path.basename(file.path); + const fileName = path.basename(file.path); if (fileName === 'nls.metadata.json') { let json = null; if (file.isBuffer()) { @@ -674,7 +678,7 @@ export function createXlfFilesForCoreBundle(): ThroughStream { xlf.addFile(`src/${coreModule}`, keys, messages); } } - for (let resource in xlfs) { + for (const resource in xlfs) { const xlf = xlfs[resource]; const filePath = `${xlf.project}/${resource.replace(/\//g, '_')}.xlf`; const xlfFile = new File({ @@ -704,7 +708,7 @@ export function createXlfFilesForExtensions(): ThroughStream { if (!stat.isDirectory()) { return; } - let extensionName = path.basename(extensionFolder.path); + const extensionName = path.basename(extensionFolder.path); if (extensionName === 'node_modules') { return; } @@ -722,22 +726,29 @@ export function createXlfFilesForExtensions(): ThroughStream { const basename = path.basename(file.path); if (basename === 'package.nls.json') { const json: PackageJsonFormat = JSON.parse(buffer.toString('utf8')); - const keys = Object.keys(json); - const messages = keys.map((key) => { + const keys: Array = []; + const messages: string[] = []; + Object.keys(json).forEach((key) => { const value = json[key]; if (Is.string(value)) { - return value; + keys.push(key); + messages.push(value); } else if (value) { - return value.message; + keys.push({ + key, + comment: value.comment + }); + messages.push(value.message); } else { - return `Unknown message for key: ${key}`; + keys.push(key); + messages.push(`Unknown message for key: ${key}`); } }); getXlf().addFile(`extensions/${extensionName}/package`, keys, messages); } else if (basename === 'nls.metadata.json') { const json: BundledExtensionFormat = JSON.parse(buffer.toString('utf8')); const relPath = path.relative(`.build/extensions/${extensionName}`, path.dirname(file.path)); - for (let file in json) { + for (const file in json) { const fileContent = json[file]; getXlf().addFile(`extensions/${extensionName}/${relPath}/${file}`, fileContent.keys, fileContent.messages); } @@ -748,7 +759,7 @@ export function createXlfFilesForExtensions(): ThroughStream { } }, function () { if (_xlf) { - let xlfFile = new File({ + const xlfFile = new File({ path: path.join(extensionsProject, extensionName + '.xlf'), contents: Buffer.from(_xlf.toString(), 'utf8') }); @@ -781,17 +792,17 @@ export function createXlfFilesForIsl(): ThroughStream { throw new Error(`Unknown input file ${file.path}`); } - let xlf = new XLF(projectName), + const xlf = new XLF(projectName), keys: string[] = [], messages: string[] = []; - let model = new TextModel(file.contents.toString()); + const model = new TextModel(file.contents.toString()); let inMessageSection = false; model.lines.forEach(line => { if (line.length === 0) { return; } - let firstChar = line.charAt(0); + const firstChar = line.charAt(0); switch (firstChar) { case ';': // Comment line; @@ -803,12 +814,12 @@ export function createXlfFilesForIsl(): ThroughStream { if (!inMessageSection) { return; } - let sections: string[] = line.split('='); + const sections: string[] = line.split('='); if (sections.length !== 2) { throw new Error(`Badly formatted message found: ${line}`); } else { - let key = sections[0]; - let value = sections[1]; + const key = sections[0]; + const value = sections[1]; if (key.length > 0 && value.length > 0) { keys.push(key); messages.push(value); @@ -827,8 +838,8 @@ export function createXlfFilesForIsl(): ThroughStream { } export function pushXlfFiles(apiHostname: string, username: string, password: string): ThroughStream { - let tryGetPromises: Array> = []; - let updateCreatePromises: Array> = []; + const tryGetPromises: Array> = []; + const updateCreatePromises: Array> = []; return through(function (this: ThroughStream, file: File) { const project = path.dirname(file.relative); @@ -869,11 +880,11 @@ function getAllResources(project: string, apiHostname: string, username: string, }; const request = https.request(options, (res) => { - let buffer: Buffer[] = []; + const buffer: Buffer[] = []; res.on('data', (chunk: Buffer) => buffer.push(chunk)); res.on('end', () => { if (res.statusCode === 200) { - let json = JSON.parse(Buffer.concat(buffer).toString()); + const json = JSON.parse(Buffer.concat(buffer).toString()); if (Array.isArray(json)) { resolve(json.map(o => o.slug)); return; @@ -892,7 +903,7 @@ function getAllResources(project: string, apiHostname: string, username: string, } export function findObsoleteResources(apiHostname: string, username: string, password: string): ThroughStream { - let resourcesByProject: Map = Object.create(null); + const resourcesByProject: Map = Object.create(null); resourcesByProject[extensionsProject] = ([] as any[]).concat(externalExtensionsWithTranslations); // clone return through(function (this: ThroughStream, file: File) { @@ -909,10 +920,10 @@ export function findObsoleteResources(apiHostname: string, username: string, pas }, function () { const json = JSON.parse(fs.readFileSync('./build/lib/i18n.resources.json', 'utf8')); - let i18Resources = [...json.editor, ...json.workbench].map((r: Resource) => r.project + '/' + r.name.replace(/\//g, '_')); - let extractedResources: string[] = []; - for (let project of [workbenchProject, editorProject]) { - for (let resource of resourcesByProject[project]) { + const i18Resources = [...json.editor, ...json.workbench].map((r: Resource) => r.project + '/' + r.name.replace(/\//g, '_')); + const extractedResources: string[] = []; + for (const project of [workbenchProject, editorProject]) { + for (const resource of resourcesByProject[project]) { if (resource !== 'setup_messages') { extractedResources.push(project + '/' + resource); } @@ -923,12 +934,12 @@ export function findObsoleteResources(apiHostname: string, username: string, pas console.log(`[i18n] Missing resources in file 'build/lib/i18n.resources.json': JSON.stringify(${extractedResources.filter(p => i18Resources.indexOf(p) === -1)})`); } - let promises: Array> = []; - for (let project in resourcesByProject) { + const promises: Array> = []; + for (const project in resourcesByProject) { promises.push( getAllResources(project, apiHostname, username, password).then(resources => { - let expectedResources = resourcesByProject[project]; - let unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); + const expectedResources = resourcesByProject[project]; + const unusedResources = resources.filter(resource => resource && expectedResources.indexOf(resource) === -1); if (unusedResources.length) { console.log(`[transifex] Obsolete resources in project '${project}': ${unusedResources.join(', ')}`); } @@ -986,7 +997,7 @@ function createResource(project: string, slug: string, xlfFile: File, apiHostnam method: 'POST' }; - let request = https.request(options, (res) => { + const request = https.request(options, (res) => { if (res.statusCode === 201) { log(`Resource ${project}/${slug} successfully created on Transifex.`); } else { @@ -1020,7 +1031,7 @@ function updateResource(project: string, slug: string, xlfFile: File, apiHostnam method: 'PUT' }; - let request = https.request(options, (res) => { + const request = https.request(options, (res) => { if (res.statusCode === 200) { res.setEncoding('utf8'); @@ -1047,7 +1058,7 @@ function updateResource(project: string, slug: string, xlfFile: File, apiHostnam } export function pullSetupXlfFiles(apiHostname: string, username: string, password: string, language: Language, includeDefault: boolean): NodeJS.ReadableStream { - let setupResources = [{ name: 'setup_messages', project: workbenchProject }]; + const setupResources = [{ name: 'setup_messages', project: workbenchProject }]; if (includeDefault) { setupResources.push({ name: 'setup_default', project: setupProject }); } @@ -1056,7 +1067,7 @@ export function pullSetupXlfFiles(apiHostname: string, username: string, passwor function pullXlfFiles(apiHostname: string, username: string, password: string, language: Language, resources: Resource[]): NodeJS.ReadableStream { const credentials = `${username}:${password}`; - let expectedTranslationsCount = resources.length; + const expectedTranslationsCount = resources.length; let translationsRetrieved = 0, called = false; return readable(function (_count: any, callback: any) { @@ -1087,7 +1098,7 @@ function retrieveResource(language: Language, resource: Resource, apiHostname: s return limiter.queue(() => new Promise((resolve, reject) => { const slug = resource.name.replace(/\//g, '_'); const project = resource.project; - let transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; + const transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; const options = { hostname: apiHostname, path: `/api/2/project/${project}/resource/${slug}/translation/${transifexLanguageId}?file&mode=onlyreviewed`, @@ -1097,8 +1108,8 @@ function retrieveResource(language: Language, resource: Resource, apiHostname: s }; console.log('[transifex] Fetching ' + options.path); - let request = https.request(options, (res) => { - let xlfBuffer: Buffer[] = []; + const request = https.request(options, (res) => { + const xlfBuffer: Buffer[] = []; res.on('data', (chunk: Buffer) => xlfBuffer.push(chunk)); res.on('end', () => { if (res.statusCode === 200) { @@ -1119,16 +1130,16 @@ function retrieveResource(language: Language, resource: Resource, apiHostname: s } export function prepareI18nFiles(): ThroughStream { - let parsePromises: Promise[] = []; + const parsePromises: Promise[] = []; return through(function (this: ThroughStream, xlf: File) { - let stream = this; - let parsePromise = XLF.parse(xlf.contents.toString()); + const stream = this; + const parsePromise = XLF.parse(xlf.contents.toString()); parsePromises.push(parsePromise); parsePromise.then( resolvedFiles => { resolvedFiles.forEach(file => { - let translatedFile = createI18nFile(file.originalFilePath, file.messages); + const translatedFile = createI18nFile(file.originalFilePath, file.messages); stream.queue(translatedFile); }); } @@ -1149,7 +1160,7 @@ export function createI18nFile(originalFilePath: string, messages: any): File { '--------------------------------------------------------------------------------------------', 'Do not edit this file. It is machine generated.' ]; - for (let key of Object.keys(messages)) { + for (const key of Object.keys(messages)) { result[key] = messages[key]; } @@ -1178,16 +1189,16 @@ export interface TranslationPath { } export function prepareI18nPackFiles(externalExtensions: Map, resultingTranslationPaths: TranslationPath[], pseudo = false): NodeJS.ReadWriteStream { - let parsePromises: Promise[] = []; - let mainPack: I18nPack = { version: i18nPackVersion, contents: {} }; - let extensionsPacks: Map = {}; - let errors: any[] = []; + const parsePromises: Promise[] = []; + const mainPack: I18nPack = { version: i18nPackVersion, contents: {} }; + const extensionsPacks: Map = {}; + const errors: any[] = []; return through(function (this: ThroughStream, xlf: File) { - let project = path.basename(path.dirname(path.dirname(xlf.relative))); - let resource = path.basename(xlf.relative, '.xlf'); - let contents = xlf.contents.toString(); + const project = path.basename(path.dirname(path.dirname(xlf.relative))); + const resource = path.basename(xlf.relative, '.xlf'); + const contents = xlf.contents.toString(); log(`Found ${project}: ${resource}`); - let parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); + const parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); parsePromises.push(parsePromise); parsePromise.then( resolvedFiles => { @@ -1225,7 +1236,7 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' }); this.queue(translatedMainFile); - for (let extension in extensionsPacks) { + for (const extension in extensionsPacks) { const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); this.queue(translatedExtFile); @@ -1246,16 +1257,16 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT } export function prepareIslFiles(language: Language, innoSetupConfig: InnoSetup): ThroughStream { - let parsePromises: Promise[] = []; + const parsePromises: Promise[] = []; return through(function (this: ThroughStream, xlf: File) { - let stream = this; - let parsePromise = XLF.parse(xlf.contents.toString()); + const stream = this; + const parsePromise = XLF.parse(xlf.contents.toString()); parsePromises.push(parsePromise); parsePromise.then( resolvedFiles => { resolvedFiles.forEach(file => { - let translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); + const translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); stream.queue(translatedFile); }); } @@ -1272,7 +1283,7 @@ export function prepareIslFiles(language: Language, innoSetupConfig: InnoSetup): } function createIslFile(originalFilePath: string, messages: Map, language: Language, innoSetup: InnoSetup): File { - let content: string[] = []; + const content: string[] = []; let originalContent: TextModel; if (path.basename(originalFilePath) === 'Default') { originalContent = new TextModel(fs.readFileSync(originalFilePath + '.isl', 'utf8')); @@ -1281,15 +1292,15 @@ function createIslFile(originalFilePath: string, messages: Map, language } originalContent.lines.forEach(line => { if (line.length > 0) { - let firstChar = line.charAt(0); + const firstChar = line.charAt(0); if (firstChar === '[' || firstChar === ';') { content.push(line); } else { - let sections: string[] = line.split('='); - let key = sections[0]; + const sections: string[] = line.split('='); + const key = sections[0]; let translated = line; if (key) { - let translatedMessage = messages[key]; + const translatedMessage = messages[key]; if (translatedMessage) { translated = `${key}=${translatedMessage}`; } @@ -1311,9 +1322,9 @@ function createIslFile(originalFilePath: string, messages: Map, language } function encodeEntities(value: string): string { - let result: string[] = []; + const result: string[] = []; for (let i = 0; i < value.length; i++) { - let ch = value[i]; + const ch = value[i]; switch (ch) { case '<': result.push('<'); diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index 60d594f494..7d66ceba4f 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -72,6 +72,11 @@ const RULES = [ target: '**/{vs,sql}/**/test/**', skip: true // -> skip all test files }, + // TODO@bpasero remove me once electron utility process has landed + { + target: '**/vs/workbench/services/extensions/electron-sandbox/nativeLocalProcessExtensionHost.ts', + skip: true + }, // Common: vs/base/common/platform.ts { target: '**/{vs,sql}/base/common/platform.ts', diff --git a/build/lib/monaco-api.js b/build/lib/monaco-api.js index 151b90218e..7dcdcfbdbb 100644 --- a/build/lib/monaco-api.js +++ b/build/lib/monaco-api.js @@ -27,7 +27,7 @@ function isDeclaration(ts, a) { } function visitTopLevelDeclarations(ts, sourceFile, visitor) { let stop = false; - let visit = (node) => { + const visit = (node) => { if (stop) { return; } @@ -49,19 +49,19 @@ function visitTopLevelDeclarations(ts, sourceFile, visitor) { visit(sourceFile); } function getAllTopLevelDeclarations(ts, sourceFile) { - let all = []; + const all = []; visitTopLevelDeclarations(ts, sourceFile, (node) => { if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { - let interfaceDeclaration = node; - let triviaStart = interfaceDeclaration.pos; - let triviaEnd = interfaceDeclaration.name.pos; - let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); + const interfaceDeclaration = node; + const triviaStart = interfaceDeclaration.pos; + const triviaEnd = interfaceDeclaration.name.pos; + const triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); if (triviaText.indexOf('@internal') === -1) { all.push(node); } } else { - let nodeText = getNodeText(sourceFile, node); + const nodeText = getNodeText(sourceFile, node); if (nodeText.indexOf('@internal') === -1) { all.push(node); } @@ -95,7 +95,7 @@ function getNodeText(sourceFile, node) { function hasModifier(modifiers, kind) { if (modifiers) { for (let i = 0; i < modifiers.length; i++) { - let mod = modifiers[i]; + const mod = modifiers[i]; if (mod.kind === kind) { return true; } @@ -113,14 +113,14 @@ function isDefaultExport(ts, declaration) { function getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums) { let result = getNodeText(sourceFile, declaration); if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { - let interfaceDeclaration = declaration; + const interfaceDeclaration = declaration; const staticTypeName = (isDefaultExport(ts, interfaceDeclaration) ? `${importName}.default` : `${importName}.${declaration.name.text}`); let instanceTypeName = staticTypeName; const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); if (typeParametersCnt > 0) { - let arr = []; + const arr = []; for (let i = 0; i < typeParametersCnt; i++) { arr.push('any'); } @@ -129,7 +129,7 @@ function getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importN const members = interfaceDeclaration.members; members.forEach((member) => { try { - let memberText = getNodeText(sourceFile, member); + const memberText = getNodeText(sourceFile, member); if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { result = result.replace(memberText, ''); } @@ -152,7 +152,7 @@ function getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importN result = result.replace(/export default /g, 'export '); result = result.replace(/export declare /g, 'export '); result = result.replace(/declare /g, ''); - let lines = result.split(/\r\n|\r|\n/); + const lines = result.split(/\r\n|\r|\n/); for (let i = 0; i < lines.length; i++) { if (/\s*\*/.test(lines[i])) { // very likely a comment @@ -177,9 +177,9 @@ function format(ts, text, endl) { return text; } // Parse the source text - let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); + const sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); // Get the formatting edits on the input sources - let edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); + const edits = ts.formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); // Apply the edits on the input code return applyEdits(text, edits); function countParensCurly(text) { @@ -202,7 +202,7 @@ function format(ts, text, endl) { return r; } function preformat(text, endl) { - let lines = text.split(endl); + const lines = text.split(endl); let inComment = false; let inCommentDeltaIndent = 0; let indent = 0; @@ -282,9 +282,9 @@ function format(ts, text, endl) { // Apply edits in reverse on the existing text let result = text; for (let i = edits.length - 1; i >= 0; i--) { - let change = edits[i]; - let head = result.slice(0, change.span.start); - let tail = result.slice(change.span.start + change.span.length); + const change = edits[i]; + const head = result.slice(0, change.span.start); + const tail = result.slice(change.span.start + change.span.length); result = head + change.newText + tail; } return result; @@ -300,15 +300,15 @@ function createReplacerFromDirectives(directives) { } function createReplacer(data) { data = data || ''; - let rawDirectives = data.split(';'); - let directives = []; + const rawDirectives = data.split(';'); + const directives = []; rawDirectives.forEach((rawDirective) => { if (rawDirective.length === 0) { return; } - let pieces = rawDirective.split('=>'); + const pieces = rawDirective.split('=>'); let findStr = pieces[0]; - let replaceStr = pieces[1]; + const replaceStr = pieces[1]; findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); findStr = '\\b' + findStr + '\\b'; directives.push([new RegExp(findStr, 'g'), replaceStr]); @@ -317,32 +317,32 @@ function createReplacer(data) { } function generateDeclarationFile(ts, recipe, sourceFileGetter) { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; - let lines = recipe.split(endl); - let result = []; + const lines = recipe.split(endl); + const result = []; let usageCounter = 0; - let usageImports = []; - let usage = []; + const usageImports = []; + const usage = []; let failed = false; usage.push(`var a: any;`); usage.push(`var b: any;`); const generateUsageImport = (moduleId) => { - let importName = 'm' + (++usageCounter); + const importName = 'm' + (++usageCounter); usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`); return importName; }; - let enums = []; + const enums = []; let version = null; lines.forEach(line => { if (failed) { return; } - let m0 = line.match(/^\/\/dtsv=(\d+)$/); + const m0 = line.match(/^\/\/dtsv=(\d+)$/); if (m0) { version = m0[1]; } - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + const m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { - let moduleId = m1[1]; + const moduleId = m1[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { logErr(`While handling ${line}`); @@ -351,14 +351,14 @@ function generateDeclarationFile(ts, recipe, sourceFileGetter) { return; } const importName = generateUsageImport(moduleId); - let replacer = createReplacer(m1[2]); - let typeNames = m1[3].split(/,/); + const replacer = createReplacer(m1[2]); + const typeNames = m1[3].split(/,/); typeNames.forEach((typeName) => { typeName = typeName.trim(); if (typeName.length === 0) { return; } - let declaration = getTopLevelDeclaration(ts, sourceFile, typeName); + const declaration = getTopLevelDeclaration(ts, sourceFile, typeName); if (!declaration) { logErr(`While handling ${line}`); logErr(`Cannot find ${typeName}`); @@ -369,9 +369,9 @@ function generateDeclarationFile(ts, recipe, sourceFileGetter) { }); return; } - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + const m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { - let moduleId = m2[1]; + const moduleId = m2[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { logErr(`While handling ${line}`); @@ -380,10 +380,10 @@ function generateDeclarationFile(ts, recipe, sourceFileGetter) { return; } const importName = generateUsageImport(moduleId); - let replacer = createReplacer(m2[2]); - let typeNames = m2[3].split(/,/); - let typesToExcludeMap = {}; - let typesToExcludeArr = []; + const replacer = createReplacer(m2[2]); + const typeNames = m2[3].split(/,/); + const typesToExcludeMap = {}; + const typesToExcludeArr = []; typeNames.forEach((typeName) => { typeName = typeName.trim(); if (typeName.length === 0) { @@ -400,7 +400,7 @@ function generateDeclarationFile(ts, recipe, sourceFileGetter) { } else { // node is ts.VariableStatement - let nodeText = getNodeText(sourceFile, declaration); + const nodeText = getNodeText(sourceFile, declaration); for (let i = 0; i < typesToExcludeArr.length; i++) { if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) { return; @@ -605,7 +605,7 @@ class TypeScriptLanguageServiceHost { } } function execute() { - let r = run3(new DeclarationResolver(new FSProvider())); + const r = run3(new DeclarationResolver(new FSProvider())); if (!r) { throw new Error(`monaco.d.ts generation error - Cannot continue`); } diff --git a/build/lib/monaco-api.ts b/build/lib/monaco-api.ts index 6bcce808b5..4f6fce7a42 100644 --- a/build/lib/monaco-api.ts +++ b/build/lib/monaco-api.ts @@ -40,7 +40,7 @@ function isDeclaration(ts: typeof import('typescript'), a: TSTopLevelDeclare): a function visitTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: ts.SourceFile, visitor: (node: TSTopLevelDeclare) => boolean): void { let stop = false; - let visit = (node: ts.Node): void => { + const visit = (node: ts.Node): void => { if (stop) { return; } @@ -67,19 +67,19 @@ function visitTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: function getAllTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: ts.SourceFile): TSTopLevelDeclare[] { - let all: TSTopLevelDeclare[] = []; + const all: TSTopLevelDeclare[] = []; visitTopLevelDeclarations(ts, sourceFile, (node) => { if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) { - let interfaceDeclaration = node; - let triviaStart = interfaceDeclaration.pos; - let triviaEnd = interfaceDeclaration.name.pos; - let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); + const interfaceDeclaration = node; + const triviaStart = interfaceDeclaration.pos; + const triviaEnd = interfaceDeclaration.name.pos; + const triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd }); if (triviaText.indexOf('@internal') === -1) { all.push(node); } } else { - let nodeText = getNodeText(sourceFile, node); + const nodeText = getNodeText(sourceFile, node); if (nodeText.indexOf('@internal') === -1) { all.push(node); } @@ -115,10 +115,10 @@ function getNodeText(sourceFile: ts.SourceFile, node: { pos: number; end: number return sourceFile.getFullText().substring(node.pos, node.end); } -function hasModifier(modifiers: ts.NodeArray | undefined, kind: ts.SyntaxKind): boolean { +function hasModifier(modifiers: ts.NodeArray | undefined, kind: ts.SyntaxKind): boolean { if (modifiers) { for (let i = 0; i < modifiers.length; i++) { - let mod = modifiers[i]; + const mod = modifiers[i]; if (mod.kind === kind) { return true; } @@ -141,7 +141,7 @@ function isDefaultExport(ts: typeof import('typescript'), declaration: ts.Interf function getMassagedTopLevelDeclarationText(ts: typeof import('typescript'), sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string { let result = getNodeText(sourceFile, declaration); if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { - let interfaceDeclaration = declaration; + const interfaceDeclaration = declaration; const staticTypeName = ( isDefaultExport(ts, interfaceDeclaration) @@ -152,7 +152,7 @@ function getMassagedTopLevelDeclarationText(ts: typeof import('typescript'), sou let instanceTypeName = staticTypeName; const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); if (typeParametersCnt > 0) { - let arr: string[] = []; + const arr: string[] = []; for (let i = 0; i < typeParametersCnt; i++) { arr.push('any'); } @@ -162,7 +162,7 @@ function getMassagedTopLevelDeclarationText(ts: typeof import('typescript'), sou const members: ts.NodeArray = interfaceDeclaration.members; members.forEach((member) => { try { - let memberText = getNodeText(sourceFile, member); + const memberText = getNodeText(sourceFile, member); if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) { result = result.replace(memberText, ''); } else { @@ -182,7 +182,7 @@ function getMassagedTopLevelDeclarationText(ts: typeof import('typescript'), sou result = result.replace(/export default /g, 'export '); result = result.replace(/export declare /g, 'export '); result = result.replace(/declare /g, ''); - let lines = result.split(/\r\n|\r|\n/); + const lines = result.split(/\r\n|\r|\n/); for (let i = 0; i < lines.length; i++) { if (/\s*\*/.test(lines[i])) { // very likely a comment @@ -212,10 +212,10 @@ function format(ts: typeof import('typescript'), text: string, endl: string): st } // Parse the source text - let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); + const sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true); // Get the formatting edits on the input sources - let edits = (ts).formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); + const edits = (ts).formatting.formatDocument(sourceFile, getRuleProvider(tsfmt), tsfmt); // Apply the edits on the input code return applyEdits(text, edits); @@ -242,7 +242,7 @@ function format(ts: typeof import('typescript'), text: string, endl: string): st } function preformat(text: string, endl: string): string { - let lines = text.split(endl); + const lines = text.split(endl); let inComment = false; let inCommentDeltaIndent = 0; let indent = 0; @@ -328,9 +328,9 @@ function format(ts: typeof import('typescript'), text: string, endl: string): st // Apply edits in reverse on the existing text let result = text; for (let i = edits.length - 1; i >= 0; i--) { - let change = edits[i]; - let head = result.slice(0, change.span.start); - let tail = result.slice(change.span.start + change.span.length); + const change = edits[i]; + const head = result.slice(0, change.span.start); + const tail = result.slice(change.span.start + change.span.length); result = head + change.newText + tail; } return result; @@ -348,15 +348,15 @@ function createReplacerFromDirectives(directives: [RegExp, string][]): (str: str function createReplacer(data: string): (str: string) => string { data = data || ''; - let rawDirectives = data.split(';'); - let directives: [RegExp, string][] = []; + const rawDirectives = data.split(';'); + const directives: [RegExp, string][] = []; rawDirectives.forEach((rawDirective) => { if (rawDirective.length === 0) { return; } - let pieces = rawDirective.split('=>'); + const pieces = rawDirective.split('=>'); let findStr = pieces[0]; - let replaceStr = pieces[1]; + const replaceStr = pieces[1]; findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&'); findStr = '\\b' + findStr + '\\b'; @@ -380,12 +380,12 @@ interface IEnumEntry { function generateDeclarationFile(ts: typeof import('typescript'), recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; - let lines = recipe.split(endl); - let result: string[] = []; + const lines = recipe.split(endl); + const result: string[] = []; let usageCounter = 0; - let usageImports: string[] = []; - let usage: string[] = []; + const usageImports: string[] = []; + const usage: string[] = []; let failed = false; @@ -393,12 +393,12 @@ function generateDeclarationFile(ts: typeof import('typescript'), recipe: string usage.push(`var b: any;`); const generateUsageImport = (moduleId: string) => { - let importName = 'm' + (++usageCounter); + const importName = 'm' + (++usageCounter); usageImports.push(`import * as ${importName} from './${moduleId.replace(/\.d\.ts$/, '')}';`); return importName; }; - let enums: IEnumEntry[] = []; + const enums: IEnumEntry[] = []; let version: string | null = null; lines.forEach(line => { @@ -407,14 +407,14 @@ function generateDeclarationFile(ts: typeof import('typescript'), recipe: string return; } - let m0 = line.match(/^\/\/dtsv=(\d+)$/); + const m0 = line.match(/^\/\/dtsv=(\d+)$/); if (m0) { version = m0[1]; } - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + const m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { - let moduleId = m1[1]; + const moduleId = m1[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { logErr(`While handling ${line}`); @@ -425,15 +425,15 @@ function generateDeclarationFile(ts: typeof import('typescript'), recipe: string const importName = generateUsageImport(moduleId); - let replacer = createReplacer(m1[2]); + const replacer = createReplacer(m1[2]); - let typeNames = m1[3].split(/,/); + const typeNames = m1[3].split(/,/); typeNames.forEach((typeName) => { typeName = typeName.trim(); if (typeName.length === 0) { return; } - let declaration = getTopLevelDeclaration(ts, sourceFile, typeName); + const declaration = getTopLevelDeclaration(ts, sourceFile, typeName); if (!declaration) { logErr(`While handling ${line}`); logErr(`Cannot find ${typeName}`); @@ -445,9 +445,9 @@ function generateDeclarationFile(ts: typeof import('typescript'), recipe: string return; } - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); + const m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { - let moduleId = m2[1]; + const moduleId = m2[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { logErr(`While handling ${line}`); @@ -458,11 +458,11 @@ function generateDeclarationFile(ts: typeof import('typescript'), recipe: string const importName = generateUsageImport(moduleId); - let replacer = createReplacer(m2[2]); + const replacer = createReplacer(m2[2]); - let typeNames = m2[3].split(/,/); - let typesToExcludeMap: { [typeName: string]: boolean } = {}; - let typesToExcludeArr: string[] = []; + const typeNames = m2[3].split(/,/); + const typesToExcludeMap: { [typeName: string]: boolean } = {}; + const typesToExcludeArr: string[] = []; typeNames.forEach((typeName) => { typeName = typeName.trim(); if (typeName.length === 0) { @@ -479,7 +479,7 @@ function generateDeclarationFile(ts: typeof import('typescript'), recipe: string } } else { // node is ts.VariableStatement - let nodeText = getNodeText(sourceFile, declaration); + const nodeText = getNodeText(sourceFile, declaration); for (let i = 0; i < typesToExcludeArr.length; i++) { if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) { return; @@ -732,7 +732,7 @@ class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { } export function execute(): IMonacoDeclarationResult { - let r = run3(new DeclarationResolver(new FSProvider())); + const r = run3(new DeclarationResolver(new FSProvider())); if (!r) { throw new Error(`monaco.d.ts generation error - Cannot continue`); } diff --git a/build/lib/optimize.js b/build/lib/optimize.js index dfc644dfed..f359028f8d 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.minifyTask = exports.optimizeTask = exports.loaderConfig = void 0; const es = require("event-stream"); @@ -37,40 +37,58 @@ function loaderConfig() { } exports.loaderConfig = loaderConfig; const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { - let sources = [ - `${src}/vs/loader.js` - ]; - if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); - } - let isFirst = true; +function loaderPlugin(src, base, amdModuleId) { return (gulp - .src(sources, { base: `${src}` }) + .src(src, { base }) .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); + if (amdModuleId) { + let contents = data.contents.toString('utf8'); + contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); + data.contents = Buffer.from(contents); } - else { - this.emit('data', data); + this.emit('data', data); + }))); +} +function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { + let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); + if (bundleLoader) { + loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls')); + } + const files = []; + const order = (f) => { + if (f.path.endsWith('loader.js')) { + return 0; } + if (f.path.endsWith('css.js')) { + return 1; + } + if (f.path.endsWith('nls.js')) { + return 2; + } + return 3; + }; + return (loaderStream + .pipe(es.through(function (data) { + files.push(data); }, function () { + files.sort((a, b) => { + return order(a) - order(b); + }); + files.unshift(new VinylFile({ + path: 'fake', + base: '.', + contents: Buffer.from(bundledFileHeader) + })); if (externalLoaderInfo !== undefined) { - this.emit('data', new VinylFile({ + files.push(new VinylFile({ path: 'fake2', base: '.', contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`) })); } + for (const file of files) { + this.emit('data', file); + } this.emit('end'); })) .pipe(concat('vs/loader.js'))); diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index cfda628c34..16ba020199 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as es from 'event-stream'; import * as gulp from 'gulp'; import * as concat from 'gulp-concat'; @@ -43,41 +41,68 @@ export function loaderConfig() { const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: any): NodeJS.ReadWriteStream { - let sources = [ - `${src}/vs/loader.js` - ]; - if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); - } - - let isFirst = true; +function loaderPlugin(src: string, base: string, amdModuleId: string | undefined): NodeJS.ReadWriteStream { return ( gulp - .src(sources, { base: `${src}` }) - .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); - } else { - this.emit('data', data); + .src(src, { base }) + .pipe(es.through(function (data: VinylFile) { + if (amdModuleId) { + let contents = data.contents.toString('utf8'); + contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); + data.contents = Buffer.from(contents); } + this.emit('data', data); + })) + ); +} + +function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: any): NodeJS.ReadWriteStream { + let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); + if (bundleLoader) { + loaderStream = es.merge( + loaderStream, + loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), + loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls'), + ); + } + + const files: VinylFile[] = []; + const order = (f: VinylFile) => { + if (f.path.endsWith('loader.js')) { + return 0; + } + if (f.path.endsWith('css.js')) { + return 1; + } + if (f.path.endsWith('nls.js')) { + return 2; + } + return 3; + }; + + return ( + loaderStream + .pipe(es.through(function (data) { + files.push(data); }, function () { + files.sort((a, b) => { + return order(a) - order(b); + }); + files.unshift(new VinylFile({ + path: 'fake', + base: '.', + contents: Buffer.from(bundledFileHeader) + })); if (externalLoaderInfo !== undefined) { - this.emit('data', new VinylFile({ + files.push(new VinylFile({ path: 'fake2', base: '.', contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`) })); } + for (const file of files) { + this.emit('data', file); + } this.emit('end'); })) .pipe(concat('vs/loader.js')) diff --git a/build/lib/policies.js b/build/lib/policies.js new file mode 100644 index 0000000000..7b1bbdf394 --- /dev/null +++ b/build/lib/policies.js @@ -0,0 +1,497 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const child_process_1 = require("child_process"); +const fs_1 = require("fs"); +const path = require("path"); +const byline = require("byline"); +const ripgrep_1 = require("@vscode/ripgrep"); +const Parser = require("tree-sitter"); +const node_fetch_1 = require("node-fetch"); +const { typescript } = require('tree-sitter-typescript'); +const product = require('../../product.json'); +function isNlsString(value) { + return value ? typeof value !== 'string' : false; +} +function isStringArray(value) { + return !value.some(s => isNlsString(s)); +} +function isNlsStringArray(value) { + return value.every(s => isNlsString(s)); +} +var PolicyType; +(function (PolicyType) { + PolicyType[PolicyType["StringEnum"] = 0] = "StringEnum"; +})(PolicyType || (PolicyType = {})); +function renderADMLString(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 { + constructor(policyType, name, category, minimumVersion, description, moduleName) { + this.policyType = policyType; + this.name = name; + this.category = category; + this.minimumVersion = minimumVersion; + this.description = description; + this.moduleName = moduleName; + } + renderADMLString(nlsString, translations) { + return renderADMLString(this.name, this.moduleName, nlsString, translations); + } + renderADMX(regKey) { + return [ + ``, + ` `, + ` `, + ` `, + ...this.renderADMXElements(), + ` `, + `` + ]; + } + renderADMLStrings(translations) { + return [ + `${this.name}`, + this.renderADMLString(this.description, translations) + ]; + } + renderADMLPresentation() { + return `${this.renderADMLPresentationContents()}`; + } +} +class BooleanPolicy extends BasePolicy { + static from(name, category, minimumVersion, description, moduleName, settingNode) { + const type = getStringProperty(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); + } + renderADMXElements() { + return [ + ``, + ` `, + `` + ]; + } + renderADMLPresentationContents() { + return `${this.name}`; + } +} +class IntPolicy extends BasePolicy { + constructor(name, category, minimumVersion, description, moduleName, defaultValue) { + super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + this.defaultValue = defaultValue; + } + static from(name, category, minimumVersion, description, moduleName, settingNode) { + const type = getStringProperty(settingNode, 'type'); + if (type !== 'number') { + return undefined; + } + const defaultValue = getIntProperty(settingNode, 'default'); + if (typeof defaultValue === 'undefined') { + throw new Error(`Missing required 'default' property.`); + } + return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue); + } + renderADMXElements() { + return [ + `` + // `` + ]; + } + renderADMLPresentationContents() { + return `${this.name}`; + } +} +class StringPolicy extends BasePolicy { + static from(name, category, minimumVersion, description, moduleName, settingNode) { + const type = getStringProperty(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); + } + renderADMXElements() { + return [``]; + } + renderADMLPresentationContents() { + return ``; + } +} +class StringEnumPolicy extends BasePolicy { + constructor(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions) { + super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + this.enum_ = enum_; + this.enumDescriptions = enumDescriptions; + } + static from(name, category, minimumVersion, description, moduleName, settingNode) { + const type = getStringProperty(settingNode, 'type'); + if (type !== 'string') { + return undefined; + } + const enum_ = getStringArrayProperty(settingNode, 'enum'); + if (!enum_) { + return undefined; + } + if (!isStringArray(enum_)) { + throw new Error(`Property 'enum' should not be localized.`); + } + const enumDescriptions = getStringArrayProperty(settingNode, 'enumDescriptions'); + if (!enumDescriptions) { + throw new Error(`Missing required 'enumDescriptions' property.`); + } + else if (!isNlsStringArray(enumDescriptions)) { + throw new Error(`Property 'enumDescriptions' should be localized.`); + } + return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions); + } + renderADMXElements() { + return [ + ``, + ...this.enum_.map((value, index) => ` ${value}`), + `` + ]; + } + renderADMLStrings(translations) { + return [ + ...super.renderADMLStrings(translations), + ...this.enumDescriptions.map(e => this.renderADMLString(e, translations)) + ]; + } + renderADMLPresentationContents() { + return ``; + } +} +const IntQ = { + Q: `(number) @value`, + value(matches) { + const match = matches[0]; + if (!match) { + return undefined; + } + const value = match.captures.filter(c => c.name === 'value')[0]?.node.text; + if (!value) { + throw new Error(`Missing required 'value' property.`); + } + return parseInt(value); + } +}; +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)) + ]`, + value(matches) { + const match = matches[0]; + if (!match) { + return undefined; + } + const value = match.captures.filter(c => c.name === 'value')[0]?.node.text; + if (!value) { + throw new Error(`Missing required 'value' property.`); + } + const nlsKey = match.captures.filter(c => c.name === 'nlsKey')[0]?.node.text; + if (nlsKey) { + return { value, nlsKey }; + } + else { + return value; + } + } +}; +const StringArrayQ = { + Q: `(array ${StringQ.Q})`, + value(matches) { + if (matches.length === 0) { + return undefined; + } + return matches.map(match => { + return StringQ.value([match]); + }); + } +}; +function getProperty(qtype, node, key) { + const query = new Parser.Query(typescript, `( + (pair + key: [(property_identifier)(string)] @key + value: ${qtype.Q} + ) + (#eq? @key ${key}) + )`); + return qtype.value(query.matches(node)); +} +function getIntProperty(node, key) { + return getProperty(IntQ, node, key); +} +function getStringProperty(node, key) { + return getProperty(StringQ, node, key); +} +function getStringArrayProperty(node, key) { + return getProperty(StringArrayQ, node, key); +} +// TODO: add more policy types +const PolicyTypes = [ + BooleanPolicy, + IntPolicy, + StringEnumPolicy, + StringPolicy, +]; +function getPolicy(moduleName, configurationNode, settingNode, policyNode, categories) { + const name = getStringProperty(policyNode, 'name'); + if (!name) { + throw new Error(`Missing required 'name' property.`); + } + else if (isNlsString(name)) { + throw new Error(`Property 'name' should be a literal string.`); + } + const categoryName = getStringProperty(configurationNode, 'title'); + if (!categoryName) { + throw new Error(`Missing required 'title' property.`); + } + else if (!isNlsString(categoryName)) { + throw new Error(`Property 'title' should be localized.`); + } + const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; + let category = categories.get(categoryKey); + if (!category) { + category = { moduleName, name: categoryName }; + categories.set(categoryKey, category); + } + const minimumVersion = getStringProperty(policyNode, 'minimumVersion'); + if (!minimumVersion) { + throw new Error(`Missing required 'minimumVersion' property.`); + } + else if (isNlsString(minimumVersion)) { + throw new Error(`Property 'minimumVersion' should be a literal string.`); + } + const description = getStringProperty(settingNode, 'description'); + if (!description) { + throw new Error(`Missing required 'description' property.`); + } + if (!isNlsString(description)) { + throw new Error(`Property 'description' should be localized.`); + } + let result; + for (const policyType of PolicyTypes) { + if (result = policyType.from(name, category, minimumVersion, description, moduleName, settingNode)) { + break; + } + } + if (!result) { + throw new Error(`Failed to parse policy '${name}'.`); + } + return result; +} +function getPolicies(moduleName, node) { + const query = new Parser.Query(typescript, ` + ( + (call_expression + function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration) + arguments: (arguments (object (pair + key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey properties) + value: (object (pair + key: [(property_identifier)(string)] + value: (object (pair + key: [(property_identifier)(string)] @policyKey (#eq? @policyKey policy) + value: (object) @policy + )) @setting + )) + )) @configuration) + ) + ) + `); + const categories = new Map(); + return query.matches(node).map(m => { + const configurationNode = m.captures.filter(c => c.name === 'configuration')[0].node; + const settingNode = m.captures.filter(c => c.name === 'setting')[0].node; + const policyNode = m.captures.filter(c => c.name === 'policy')[0].node; + return getPolicy(moduleName, configurationNode, settingNode, policyNode, categories); + }); +} +async function getFiles(root) { + return new Promise((c, e) => { + const result = []; + const rg = (0, child_process_1.spawn)(ripgrep_1.rgPath, ['-l', 'registerConfiguration\\(', '-g', 'src/**/*.ts', '-g', '!src/**/test/**', root]); + const stream = byline(rg.stdout.setEncoding('utf8')); + stream.on('data', path => result.push(path)); + stream.on('error', err => e(err)); + stream.on('end', () => c(result)); + }); +} +function renderADMX(regKey, versions, categories, policies) { + versions = versions.map(v => v.replace(/\./g, '_')); + return ` + + + + + + + + ${versions.map(v => ``).join(`\n `)} + + + + + ${categories.map(c => ``).join(`\n `)} + + + ${policies.map(p => p.renderADMX(regKey)).flat().join(`\n `)} + + +`; +} +function renderADML(appName, versions, categories, policies, translations) { + return ` + + + + + + ${appName} + ${versions.map(v => `${appName} >= ${v}`)} + ${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations))} + ${policies.map(p => p.renderADMLStrings(translations)).flat().join(`\n `)} + + + ${policies.map(p => p.renderADMLPresentation()).join(`\n `)} + + + +`; +} +function renderGP(policies, translations) { + const appName = product.nameLong; + const regKey = product.win32RegValueName; + const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort(); + const categories = [...new Set(policies.map(p => p.category))]; + return { + admx: renderADMX(regKey, versions, categories, policies), + adml: [ + { languageId: 'en-us', contents: renderADML(appName, versions, categories, policies) }, + ...translations.map(({ languageId, languageTranslations }) => ({ languageId, contents: renderADML(appName, versions, categories, policies, languageTranslations) })) + ] + }; +} +const Languages = { + 'fr': 'fr-fr', + 'it': 'it-it', + 'de': 'de-de', + 'es': 'es-es', + 'ru': 'ru-ru', + 'zh-hans': 'zh-cn', + 'zh-hant': 'zh-tw', + 'ja': 'ja-jp', + 'ko': 'ko-kr', + 'cs': 'cs-cz', + 'pt-br': 'pt-br', + 'tr': 'tr-tr', + 'pl': 'pl-pl', +}; +async function getLatestStableVersion(updateUrl) { + const res = await (0, node_fetch_1.default)(`${updateUrl}/api/update/darwin/stable/latest`); + const { name: version } = await res.json(); + return version; +} +async function getSpecificNLS(resourceUrlTemplate, languageId, version) { + const resource = { + publisher: 'ms-ceintl', + name: `vscode-language-pack-${languageId}`, + version, + path: 'extension/translations/main.i18n.json' + }; + const url = resourceUrlTemplate.replace(/\{([^}]+)\}/g, (_, key) => resource[key]); + const res = await (0, node_fetch_1.default)(url); + if (res.status !== 200) { + throw new Error(`[${res.status}] Error downloading language pack ${languageId}@${version}`); + } + const { contents: result } = await res.json(); + return result; +} +function previousVersion(version) { + const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)$/.exec(version); + return `${major}.${parseInt(minor) - 1}.${patch}`; +} +async function getNLS(resourceUrlTemplate, languageId, version) { + try { + return await getSpecificNLS(resourceUrlTemplate, languageId, version); + } + catch (err) { + if (/\[404\]/.test(err.message)) { + console.warn(`Language pack ${languageId}@${version} is missing. Downloading previous version...`); + return await getSpecificNLS(resourceUrlTemplate, languageId, previousVersion(version)); + } + else { + throw err; + } + } +} +async function parsePolicies() { + const parser = new Parser(); + parser.setLanguage(typescript); + const files = await getFiles(process.cwd()); + const base = path.join(process.cwd(), 'src'); + const policies = []; + for (const file of files) { + const moduleName = path.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/'); + const contents = await fs_1.promises.readFile(file, { encoding: 'utf8' }); + const tree = parser.parse(contents); + policies.push(...getPolicies(moduleName, tree.rootNode)); + } + return policies; +} +async function getTranslations() { + const updateUrl = product.updateUrl; + if (!updateUrl) { + console.warn(`Skipping policy localization: No 'updateUrl' found in 'product.json'.`); + return []; + } + const resourceUrlTemplate = product.extensionsGallery?.resourceUrlTemplate; + if (!resourceUrlTemplate) { + console.warn(`Skipping policy localization: No 'resourceUrlTemplate' found in 'product.json'.`); + return []; + } + const version = await getLatestStableVersion(updateUrl); + const languageIds = Object.keys(Languages); + return await Promise.all(languageIds.map(languageId => getNLS(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); + const root = '.build/policies/win32'; + await fs_1.promises.rm(root, { recursive: true, force: true }); + await fs_1.promises.mkdir(root, { recursive: true }); + await fs_1.promises.writeFile(path.join(root, `${product.win32RegValueName}.admx`), admx.replace(/\r?\n/g, '\n')); + for (const { languageId, contents } of adml) { + const languagePath = path.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]); + await fs_1.promises.mkdir(languagePath, { recursive: true }); + await fs_1.promises.writeFile(path.join(languagePath, `${product.win32RegValueName}.adml`), contents.replace(/\r?\n/g, '\n')); + } +} +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/lib/policies.ts b/build/lib/policies.ts new file mode 100644 index 0000000000..487ef611fb --- /dev/null +++ b/build/lib/policies.ts @@ -0,0 +1,697 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { spawn } from 'child_process'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as byline from 'byline'; +import { rgPath } from '@vscode/ripgrep'; +import * as Parser from 'tree-sitter'; +import fetch from 'node-fetch'; +const { typescript } = require('tree-sitter-typescript'); +const product = require('../../product.json'); + +type NlsString = { value: string; nlsKey: string }; + +function isNlsString(value: string | NlsString | undefined): value is NlsString { + return value ? typeof value !== 'string' : false; +} + +function isStringArray(value: (string | NlsString)[]): value is string[] { + return !value.some(s => isNlsString(s)); +} + +function isNlsStringArray(value: (string | NlsString)[]): value is NlsString[] { + return value.every(s => isNlsString(s)); +} + +interface Category { + readonly moduleName: string; + readonly name: NlsString; +} + +enum PolicyType { + StringEnum +} + +interface Policy { + readonly category: Category; + readonly minimumVersion: string; + renderADMX(regKey: string): string[]; + renderADMLStrings(translations?: LanguageTranslations): string[]; + renderADMLPresentation(): string; +} + +function renderADMLString(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 category: Category, + readonly minimumVersion: string, + protected description: NlsString, + protected moduleName: string, + ) { } + + protected renderADMLString(nlsString: NlsString, translations?: LanguageTranslations): string { + return renderADMLString(this.name, this.moduleName, nlsString, translations); + } + + renderADMX(regKey: string) { + return [ + ``, + ` `, + ` `, + ` `, + ...this.renderADMXElements(), + ` `, + `` + ]; + } + + protected abstract renderADMXElements(): string[]; + + renderADMLStrings(translations?: LanguageTranslations) { + return [ + `${this.name}`, + this.renderADMLString(this.description, translations) + ]; + } + + renderADMLPresentation(): string { + return `${this.renderADMLPresentationContents()}`; + } + + protected abstract renderADMLPresentationContents(): string; +} + +class BooleanPolicy extends BasePolicy { + + static from( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + settingNode: Parser.SyntaxNode + ): BooleanPolicy | undefined { + const type = getStringProperty(settingNode, 'type'); + + if (type !== 'boolean') { + return undefined; + } + + return new BooleanPolicy(name, category, minimumVersion, description, moduleName); + } + + private constructor( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + ) { + super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + } + + protected renderADMXElements(): string[] { + return [ + ``, + ` `, + `` + ]; + } + + renderADMLPresentationContents() { + return `${this.name}`; + } +} + +class IntPolicy extends BasePolicy { + + static from( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + settingNode: Parser.SyntaxNode + ): IntPolicy | undefined { + const type = getStringProperty(settingNode, 'type'); + + if (type !== 'number') { + return undefined; + } + + const defaultValue = getIntProperty(settingNode, 'default'); + + if (typeof defaultValue === 'undefined') { + throw new Error(`Missing required 'default' property.`); + } + + return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue); + } + + private constructor( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + protected readonly defaultValue: number, + ) { + super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + } + + protected renderADMXElements(): string[] { + return [ + `` + // `` + ]; + } + + renderADMLPresentationContents() { + return `${this.name}`; + } +} + +class StringPolicy extends BasePolicy { + + static from( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + settingNode: Parser.SyntaxNode + ): StringPolicy | undefined { + const type = getStringProperty(settingNode, 'type'); + + if (type !== 'string') { + return undefined; + } + + return new StringPolicy(name, category, minimumVersion, description, moduleName); + } + + private constructor( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + ) { + super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + } + + protected renderADMXElements(): string[] { + return [``]; + } + + renderADMLPresentationContents() { + return ``; + } +} + +class StringEnumPolicy extends BasePolicy { + + static from( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + settingNode: Parser.SyntaxNode + ): StringEnumPolicy | undefined { + const type = getStringProperty(settingNode, 'type'); + + if (type !== 'string') { + return undefined; + } + + const enum_ = getStringArrayProperty(settingNode, 'enum'); + + if (!enum_) { + return undefined; + } + + if (!isStringArray(enum_)) { + throw new Error(`Property 'enum' should not be localized.`); + } + + const enumDescriptions = getStringArrayProperty(settingNode, 'enumDescriptions'); + + if (!enumDescriptions) { + throw new Error(`Missing required 'enumDescriptions' property.`); + } else if (!isNlsStringArray(enumDescriptions)) { + throw new Error(`Property 'enumDescriptions' should be localized.`); + } + + return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions); + } + + private constructor( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + protected enum_: string[], + protected enumDescriptions: NlsString[], + ) { + super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + } + + protected renderADMXElements(): string[] { + return [ + ``, + ...this.enum_.map((value, index) => ` ${value}`), + `` + ]; + } + + renderADMLStrings(translations?: LanguageTranslations) { + return [ + ...super.renderADMLStrings(translations), + ...this.enumDescriptions.map(e => this.renderADMLString(e, translations)) + ]; + } + + renderADMLPresentationContents() { + return ``; + } +} + +interface QType { + Q: string; + value(matches: Parser.QueryMatch[]): T | undefined; +} + +const IntQ: QType = { + Q: `(number) @value`, + + value(matches: Parser.QueryMatch[]): number | undefined { + const match = matches[0]; + + if (!match) { + return undefined; + } + + const value = match.captures.filter(c => c.name === 'value')[0]?.node.text; + + if (!value) { + throw new Error(`Missing required 'value' property.`); + } + + return parseInt(value); + } +}; + +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)) + ]`, + + value(matches: Parser.QueryMatch[]): string | NlsString | undefined { + const match = matches[0]; + + if (!match) { + return undefined; + } + + const value = match.captures.filter(c => c.name === 'value')[0]?.node.text; + + if (!value) { + throw new Error(`Missing required 'value' property.`); + } + + const nlsKey = match.captures.filter(c => c.name === 'nlsKey')[0]?.node.text; + + if (nlsKey) { + return { value, nlsKey }; + } else { + return value; + } + } +}; + +const StringArrayQ: QType<(string | NlsString)[]> = { + Q: `(array ${StringQ.Q})`, + + value(matches: Parser.QueryMatch[]): (string | NlsString)[] | undefined { + if (matches.length === 0) { + return undefined; + } + + return matches.map(match => { + return StringQ.value([match]) as string | NlsString; + }); + } +}; + +function getProperty(qtype: QType, node: Parser.SyntaxNode, key: string): T | undefined { + const query = new Parser.Query( + typescript, + `( + (pair + key: [(property_identifier)(string)] @key + value: ${qtype.Q} + ) + (#eq? @key ${key}) + )` + ); + + return qtype.value(query.matches(node)); +} + +function getIntProperty(node: Parser.SyntaxNode, key: string): number | undefined { + return getProperty(IntQ, node, key); +} + +function getStringProperty(node: Parser.SyntaxNode, key: string): string | NlsString | undefined { + return getProperty(StringQ, node, key); +} + +function getStringArrayProperty(node: Parser.SyntaxNode, key: string): (string | NlsString)[] | undefined { + return getProperty(StringArrayQ, node, key); +} + +// TODO: add more policy types +const PolicyTypes = [ + BooleanPolicy, + IntPolicy, + StringEnumPolicy, + StringPolicy, +]; + +function getPolicy( + moduleName: string, + configurationNode: Parser.SyntaxNode, + settingNode: Parser.SyntaxNode, + policyNode: Parser.SyntaxNode, + categories: Map +): Policy { + const name = getStringProperty(policyNode, 'name'); + + if (!name) { + throw new Error(`Missing required 'name' property.`); + } else if (isNlsString(name)) { + throw new Error(`Property 'name' should be a literal string.`); + } + + const categoryName = getStringProperty(configurationNode, 'title'); + + if (!categoryName) { + throw new Error(`Missing required 'title' property.`); + } else if (!isNlsString(categoryName)) { + throw new Error(`Property 'title' should be localized.`); + } + + const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; + let category = categories.get(categoryKey); + + if (!category) { + category = { moduleName, name: categoryName }; + categories.set(categoryKey, category); + } + + const minimumVersion = getStringProperty(policyNode, 'minimumVersion'); + + if (!minimumVersion) { + throw new Error(`Missing required 'minimumVersion' property.`); + } else if (isNlsString(minimumVersion)) { + throw new Error(`Property 'minimumVersion' should be a literal string.`); + } + + const description = getStringProperty(settingNode, 'description'); + + if (!description) { + throw new Error(`Missing required 'description' property.`); + } if (!isNlsString(description)) { + throw new Error(`Property 'description' should be localized.`); + } + + let result: Policy | undefined; + + for (const policyType of PolicyTypes) { + if (result = policyType.from(name, category, minimumVersion, description, moduleName, settingNode)) { + break; + } + } + + if (!result) { + throw new Error(`Failed to parse policy '${name}'.`); + } + + return result; +} + +function getPolicies(moduleName: string, node: Parser.SyntaxNode): Policy[] { + const query = new Parser.Query(typescript, ` + ( + (call_expression + function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration) + arguments: (arguments (object (pair + key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey properties) + value: (object (pair + key: [(property_identifier)(string)] + value: (object (pair + key: [(property_identifier)(string)] @policyKey (#eq? @policyKey policy) + value: (object) @policy + )) @setting + )) + )) @configuration) + ) + ) + `); + + const categories = new Map(); + + return query.matches(node).map(m => { + const configurationNode = m.captures.filter(c => c.name === 'configuration')[0].node; + const settingNode = m.captures.filter(c => c.name === 'setting')[0].node; + const policyNode = m.captures.filter(c => c.name === 'policy')[0].node; + return getPolicy(moduleName, configurationNode, settingNode, policyNode, categories); + }); +} + +async function getFiles(root: string): Promise { + return new Promise((c, e) => { + const result: string[] = []; + const rg = spawn(rgPath, ['-l', 'registerConfiguration\\(', '-g', 'src/**/*.ts', '-g', '!src/**/test/**', root]); + const stream = byline(rg.stdout.setEncoding('utf8')); + stream.on('data', path => result.push(path)); + stream.on('error', err => e(err)); + stream.on('end', () => c(result)); + }); +} + +function renderADMX(regKey: string, versions: string[], categories: Category[], policies: Policy[]) { + versions = versions.map(v => v.replace(/\./g, '_')); + + return ` + + + + + + + + ${versions.map(v => ``).join(`\n `)} + + + + + ${categories.map(c => ``).join(`\n `)} + + + ${policies.map(p => p.renderADMX(regKey)).flat().join(`\n `)} + + +`; +} + +function renderADML(appName: string, versions: string[], categories: Category[], policies: Policy[], translations?: LanguageTranslations) { + return ` + + + + + + ${appName} + ${versions.map(v => `${appName} >= ${v}`)} + ${categories.map(c => renderADMLString('Category', c.moduleName, c.name, translations))} + ${policies.map(p => p.renderADMLStrings(translations)).flat().join(`\n `)} + + + ${policies.map(p => p.renderADMLPresentation()).join(`\n `)} + + + +`; +} + +function renderGP(policies: Policy[], translations: Translations) { + const appName = product.nameLong; + const regKey = product.win32RegValueName; + + const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort(); + const categories = [...new Set(policies.map(p => p.category))]; + + return { + admx: renderADMX(regKey, versions, categories, policies), + adml: [ + { languageId: 'en-us', contents: renderADML(appName, versions, categories, policies) }, + ...translations.map(({ languageId, languageTranslations }) => + ({ languageId, contents: renderADML(appName, versions, categories, policies, languageTranslations) })) + ] + }; +} + +const Languages = { + 'fr': 'fr-fr', + 'it': 'it-it', + 'de': 'de-de', + 'es': 'es-es', + 'ru': 'ru-ru', + 'zh-hans': 'zh-cn', + 'zh-hant': 'zh-tw', + 'ja': 'ja-jp', + 'ko': 'ko-kr', + 'cs': 'cs-cz', + 'pt-br': 'pt-br', + 'tr': 'tr-tr', + 'pl': 'pl-pl', +}; + +type LanguageTranslations = { [moduleName: string]: { [nlsKey: string]: string } }; +type Translations = { languageId: string; languageTranslations: LanguageTranslations }[]; + +async function getLatestStableVersion(updateUrl: string) { + const res = await fetch(`${updateUrl}/api/update/darwin/stable/latest`); + const { name: version } = await res.json() as { name: string }; + return version; +} + +async function getSpecificNLS(resourceUrlTemplate: string, languageId: string, version: string) { + const resource = { + publisher: 'ms-ceintl', + name: `vscode-language-pack-${languageId}`, + version, + path: 'extension/translations/main.i18n.json' + }; + + const url = resourceUrlTemplate.replace(/\{([^}]+)\}/g, (_, key) => resource[key as keyof typeof resource]); + const res = await fetch(url); + + if (res.status !== 200) { + throw new Error(`[${res.status}] Error downloading language pack ${languageId}@${version}`); + } + + const { contents: result } = await res.json() as { contents: LanguageTranslations }; + return result; +} + +function previousVersion(version: string): string { + const [, major, minor, patch] = /^(\d+)\.(\d+)\.(\d+)$/.exec(version)!; + return `${major}.${parseInt(minor) - 1}.${patch}`; +} + +async function getNLS(resourceUrlTemplate: string, languageId: string, version: string) { + try { + return await getSpecificNLS(resourceUrlTemplate, languageId, version); + } catch (err) { + if (/\[404\]/.test(err.message)) { + console.warn(`Language pack ${languageId}@${version} is missing. Downloading previous version...`); + return await getSpecificNLS(resourceUrlTemplate, languageId, previousVersion(version)); + } else { + throw err; + } + } +} + +async function parsePolicies(): Promise { + const parser = new Parser(); + parser.setLanguage(typescript); + + const files = await getFiles(process.cwd()); + const base = path.join(process.cwd(), 'src'); + const policies = []; + + for (const file of files) { + const moduleName = path.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/'); + const contents = await fs.readFile(file, { encoding: 'utf8' }); + const tree = parser.parse(contents); + policies.push(...getPolicies(moduleName, tree.rootNode)); + } + + return policies; +} + +async function getTranslations(): Promise { + const updateUrl = product.updateUrl; + + if (!updateUrl) { + console.warn(`Skipping policy localization: No 'updateUrl' found in 'product.json'.`); + return []; + } + + const resourceUrlTemplate = product.extensionsGallery?.resourceUrlTemplate; + + if (!resourceUrlTemplate) { + console.warn(`Skipping policy localization: No 'resourceUrlTemplate' found in 'product.json'.`); + return []; + } + + const version = await getLatestStableVersion(updateUrl); + const languageIds = Object.keys(Languages); + + return await Promise.all(languageIds.map( + languageId => getNLS(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); + + const root = '.build/policies/win32'; + await fs.rm(root, { recursive: true, force: true }); + await fs.mkdir(root, { recursive: true }); + + await fs.writeFile(path.join(root, `${product.win32RegValueName}.admx`), admx.replace(/\r?\n/g, '\n')); + + for (const { languageId, contents } of adml) { + 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, `${product.win32RegValueName}.adml`), contents.replace(/\r?\n/g, '\n')); + } +} + +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/lib/preLaunch.js b/build/lib/preLaunch.js index 5cfce3e39d..558dda6655 100644 --- a/build/lib/preLaunch.js +++ b/build/lib/preLaunch.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); // @ts-check const path = require("path"); diff --git a/build/lib/preLaunch.ts b/build/lib/preLaunch.ts index 3498e57c70..0de0d5fc58 100644 --- a/build/lib/preLaunch.ts +++ b/build/lib/preLaunch.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - // @ts-check import * as path from 'path'; diff --git a/build/lib/reporter.js b/build/lib/reporter.js index 55feaf80ce..94df910908 100644 --- a/build/lib/reporter.js +++ b/build/lib/reporter.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.createReporter = void 0; const es = require("event-stream"); diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts index 2bf8c6a048..93e300e473 100644 --- a/build/lib/reporter.ts +++ b/build/lib/reporter.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as es from 'event-stream'; import * as _ from 'underscore'; import * as fancyLog from 'fancy-log'; diff --git a/build/lib/snapshotLoader.js b/build/lib/snapshotLoader.js index 822b9b2609..70c27a87bf 100644 --- a/build/lib/snapshotLoader.js +++ b/build/lib/snapshotLoader.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; var snaps; (function (snaps) { const fs = require('fs'); diff --git a/build/lib/snapshotLoader.ts b/build/lib/snapshotLoader.ts index 6826711822..d82217959a 100644 --- a/build/lib/snapshotLoader.ts +++ b/build/lib/snapshotLoader.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - namespace snaps { const fs = require('fs'); diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 027ee9ee63..f41b489ddb 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -10,7 +10,7 @@ const path = require("path"); const tss = require("./treeshaking"); const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); -let dirCache = {}; +const dirCache = {}; function writeFile(filePath, contents) { function ensureDirs(dirPath) { if (dirCache[dirPath]) { @@ -54,13 +54,13 @@ function extractEditor(options) { options.typings.push(`../node_modules/@types/${type}/index.d.ts`); }); } - let result = tss.shake(options); - for (let fileName in result) { + const result = tss.shake(options); + for (const fileName in result) { if (result.hasOwnProperty(fileName)) { writeFile(path.join(options.destRoot, fileName), result[fileName]); } } - let copied = {}; + const copied = {}; const copyFile = (fileName) => { if (copied[fileName]) { return; @@ -73,7 +73,7 @@ function extractEditor(options) { const writeOutputFile = (fileName, contents) => { writeFile(path.join(options.destRoot, fileName), contents); }; - for (let fileName in result) { + for (const fileName in result) { if (result.hasOwnProperty(fileName)) { const fileContents = result[fileName]; const info = ts.preProcessFile(fileContents); @@ -103,13 +103,12 @@ function extractEditor(options) { delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', + 'vs/css.build.ts', + 'vs/css.ts', 'vs/loader.js', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', + 'vs/loader.d.ts', + 'vs/nls.build.ts', + 'vs/nls.ts', 'vs/nls.mock.ts', ].forEach(copyFile); } @@ -120,7 +119,7 @@ function createESMSourcesAndResources2(options) { const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); const getDestAbsoluteFilePath = (file) => { - let dest = options.renames[file.replace(/\\/g, '/')] || file; + const dest = options.renames[file.replace(/\\/g, '/')] || file; if (dest === 'tsconfig.json') { return path.join(OUT_FOLDER, `tsconfig.json`); } @@ -194,7 +193,7 @@ function createESMSourcesAndResources2(options) { if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { dir += '/'; } - let result = []; + const result = []; _walkDirRecursive(dir, result, dir.length); return result; } @@ -216,7 +215,7 @@ function createESMSourcesAndResources2(options) { } writeFile(absoluteFilePath, contents); function toggleComments(fileContents) { - let lines = fileContents.split(/\r\n|\r|\n/); + const lines = fileContents.split(/\r\n|\r|\n/); let mode = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; @@ -279,14 +278,14 @@ function transportCSS(module, enqueue, write) { let DATA = ';base64,' + fileContents.toString('base64'); if (!forceBase64 && /\.svg$/.test(url)) { // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - let newText = fileContents.toString() + const newText = fileContents.toString() .replace(/"/g, '\'') .replace(//g, '%3E') .replace(/&/g, '%26') .replace(/#/g, '%23') .replace(/\s+/g, ' '); - let encodedData = ',' + newText; + const encodedData = ',' + newText; if (encodedData.length < DATA.length) { DATA = encodedData; } diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 16ab27516c..430785e828 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -10,7 +10,7 @@ import * as tss from './treeshaking'; const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); -let dirCache: { [dir: string]: boolean } = {}; +const dirCache: { [dir: string]: boolean } = {}; function writeFile(filePath: string, contents: Buffer | string): void { function ensureDirs(dirPath: string): void { @@ -63,13 +63,13 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str }); } - let result = tss.shake(options); - for (let fileName in result) { + const result = tss.shake(options); + for (const fileName in result) { if (result.hasOwnProperty(fileName)) { writeFile(path.join(options.destRoot, fileName), result[fileName]); } } - let copied: { [fileName: string]: boolean } = {}; + const copied: { [fileName: string]: boolean } = {}; const copyFile = (fileName: string) => { if (copied[fileName]) { return; @@ -82,7 +82,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str const writeOutputFile = (fileName: string, contents: string | Buffer) => { writeFile(path.join(options.destRoot, fileName), contents); }; - for (let fileName in result) { + for (const fileName in result) { if (result.hasOwnProperty(fileName)) { const fileContents = result[fileName]; const info = ts.preProcessFile(fileContents); @@ -115,13 +115,12 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', + 'vs/css.build.ts', + 'vs/css.ts', 'vs/loader.js', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', + 'vs/loader.d.ts', + 'vs/nls.build.ts', + 'vs/nls.ts', 'vs/nls.mock.ts', ].forEach(copyFile); } @@ -142,7 +141,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); const getDestAbsoluteFilePath = (file: string): string => { - let dest = options.renames[file.replace(/\\/g, '/')] || file; + const dest = options.renames[file.replace(/\\/g, '/')] || file; if (dest === 'tsconfig.json') { return path.join(OUT_FOLDER, `tsconfig.json`); } @@ -229,7 +228,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { dir += '/'; } - let result: string[] = []; + const result: string[] = []; _walkDirRecursive(dir, result, dir.length); return result; } @@ -253,7 +252,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { writeFile(absoluteFilePath, contents); function toggleComments(fileContents: string): string { - let lines = fileContents.split(/\r\n|\r|\n/); + const lines = fileContents.split(/\r\n|\r|\n/); let mode = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; @@ -325,14 +324,14 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: if (!forceBase64 && /\.svg$/.test(url)) { // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - let newText = fileContents.toString() + const newText = fileContents.toString() .replace(/"/g, '\'') .replace(//g, '%3E') .replace(/&/g, '%26') .replace(/#/g, '%23') .replace(/\s+/g, ' '); - let encodedData = ',' + newText; + const encodedData = ',' + newText; if (encodedData.length < DATA.length) { DATA = encodedData; } diff --git a/build/lib/task.js b/build/lib/task.js index f3a3fd8123..d844aeb273 100644 --- a/build/lib/task.js +++ b/build/lib/task.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.define = exports.parallel = exports.series = void 0; const fancyLog = require("fancy-log"); diff --git a/build/lib/task.ts b/build/lib/task.ts index 8b3d072386..954c2395c5 100644 --- a/build/lib/task.ts +++ b/build/lib/task.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index f20782dfc1..b56520bd11 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.shake = exports.toStringShakeLevel = exports.ShakeLevel = void 0; const fs = require("fs"); @@ -32,7 +32,7 @@ function printDiagnostics(options, diagnostics) { result += `${path.join(options.sourcesRoot, diag.file.fileName)}`; } if (diag.file && diag.start) { - let location = diag.file.getLineAndCharacterOfPosition(diag.start); + const location = diag.file.getLineAndCharacterOfPosition(diag.start); result += `:${location.line + 1}:${location.character}`; } result += ` - ` + JSON.stringify(diag.messageText); @@ -89,6 +89,8 @@ function discoverAndReadFiles(ts, options) { const in_queue = Object.create(null); const queue = []; const enqueue = (moduleId) => { + // To make the treeshaker work on windows... + moduleId = moduleId.replace(/\\/g, '/'); if (in_queue[moduleId]) { return; } @@ -150,7 +152,7 @@ function processLibFiles(ts, options) { result[key] = sourceText; // precess dependencies and "recurse" const info = ts.preProcessFile(sourceText); - for (let ref of info.libReferenceDirectives) { + for (const ref of info.libReferenceDirectives) { stack.push(ref.fileName); } } @@ -226,6 +228,12 @@ function getColor(node) { function setColor(node, color) { node.$$$color = color; } +function markNeededSourceFile(node) { + node.$$$neededSourceFile = true; +} +function isNeededSourceFile(node) { + return Boolean(node.$$$neededSourceFile); +} function nodeOrParentIsBlack(node) { while (node) { const color = getColor(node); @@ -351,6 +359,19 @@ function markNodes(ts, languageService, options) { } }); } + /** + * Return the parent of `node` which is an ImportDeclaration + */ + function findParentImportDeclaration(node) { + let _node = node; + do { + if (ts.isImportDeclaration(_node)) { + return _node; + } + _node = _node.parent; + } while (_node); + return null; + } function enqueue_gray(node) { if (nodeOrParentIsBlack(node) || getColor(node) === 1 /* Gray */) { return; @@ -418,6 +439,8 @@ function markNodes(ts, languageService, options) { console.warn(`Cannot find source file ${filename}`); return; } + // This source file should survive even if it is empty + markNeededSourceFile(sourceFile); enqueue_black(sourceFile); } function enqueueImport(node, importText) { @@ -469,7 +492,11 @@ function markNodes(ts, languageService, options) { const loop = (node) => { const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node); if (symbolImportNode) { - setColor(symbolImportNode, 2 /* Black */); + setColor(symbolImportNode, 2 /* NodeColor.Black */); + const importDeclarationNode = findParentImportDeclaration(symbolImportNode); + if (importDeclarationNode && ts.isStringLiteral(importDeclarationNode.moduleSpecifier)) { + enqueueImport(importDeclarationNode, importDeclarationNode.moduleSpecifier.text); + } } if (isSymbolWithDeclarations(symbol) && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { for (let i = 0, len = symbol.declarations.length; i < len; i++) { // {{SQL CARBON EDIT}} Compile fixes @@ -503,7 +530,7 @@ function markNodes(ts, languageService, options) { } // queue the heritage clauses if (declaration.heritageClauses) { - for (let heritageClause of declaration.heritageClauses) { + for (const heritageClause of declaration.heritageClauses) { enqueue_black(heritageClause); } } @@ -551,7 +578,7 @@ function generateResult(ts, languageService, shakeLevel) { if (!program) { throw new Error('Could not get program from language service'); } - let result = {}; + const result = {}; const writeFile = (filePath, contents) => { result[filePath] = contents; }; @@ -567,7 +594,7 @@ function generateResult(ts, languageService, shakeLevel) { } return; } - let text = sourceFile.text; + const text = sourceFile.text; let result = ''; function keep(node) { result += text.substring(node.pos, node.end); @@ -597,7 +624,7 @@ function generateResult(ts, languageService, shakeLevel) { } } else { - let survivingImports = []; + const survivingImports = []; for (const importNode of node.importClause.namedBindings.elements) { if (getColor(importNode) === 2 /* Black */) { survivingImports.push(importNode.getFullText(sourceFile)); @@ -626,7 +653,7 @@ function generateResult(ts, languageService, shakeLevel) { } if (ts.isExportDeclaration(node)) { if (node.exportClause && node.moduleSpecifier && ts.isNamedExports(node.exportClause)) { - let survivingExports = []; + const survivingExports = []; for (const exportSpecifier of node.exportClause.elements) { if (getColor(exportSpecifier) === 2 /* Black */) { survivingExports.push(exportSpecifier.getFullText(sourceFile)); @@ -647,8 +674,8 @@ function generateResult(ts, languageService, shakeLevel) { // keep method continue; } - let pos = member.pos - node.pos; - let end = member.end - node.pos; + const pos = member.pos - node.pos; + const end = member.end - node.pos; toWrite = toWrite.substring(0, pos) + toWrite.substring(end); } return write(toWrite); @@ -661,11 +688,23 @@ function generateResult(ts, languageService, shakeLevel) { } if (getColor(sourceFile) !== 2 /* Black */) { if (!nodeOrChildIsBlack(sourceFile)) { - // none of the elements are reachable => don't write this file at all! - return; + // none of the elements are reachable + if (isNeededSourceFile(sourceFile)) { + // this source file must be written, even if nothing is used from it + // because there is an import somewhere for it. + // However, TS complains with empty files with the error "x" is not a module, + // so we will export a dummy variable + result = 'export const __dummy = 0;'; + } + else { + // don't write this file at all! + return; + } + } + else { + sourceFile.forEachChild(writeMarkedNodes); + result += sourceFile.endOfFileToken.getFullText(sourceFile); } - sourceFile.forEachChild(writeMarkedNodes); - result += sourceFile.endOfFileToken.getFullText(sourceFile); } else { result = text; @@ -839,3 +878,4 @@ function getTokenAtPosition(ts, sourceFile, position, allowPositionInLeadingTriv return current; } } +//#endregion diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index e067fd2838..84f8aebbda 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as fs from 'fs'; import * as path from 'path'; import type * as ts from 'typescript'; @@ -73,7 +71,7 @@ function printDiagnostics(options: ITreeShakingOptions, diagnostics: ReadonlyArr result += `${path.join(options.sourcesRoot, diag.file.fileName)}`; } if (diag.file && diag.start) { - let location = diag.file.getLineAndCharacterOfPosition(diag.start); + const location = diag.file.getLineAndCharacterOfPosition(diag.start); result += `:${location.line + 1}:${location.character}`; } result += ` - ` + JSON.stringify(diag.messageText); @@ -144,6 +142,8 @@ function discoverAndReadFiles(ts: typeof import('typescript'), options: ITreeSha const queue: string[] = []; const enqueue = (moduleId: string) => { + // To make the treeshaker work on windows... + moduleId = moduleId.replace(/\\/g, '/'); if (in_queue[moduleId]) { return; } @@ -216,7 +216,7 @@ function processLibFiles(ts: typeof import('typescript'), options: ITreeShakingO // precess dependencies and "recurse" const info = ts.preProcessFile(sourceText); - for (let ref of info.libReferenceDirectives) { + for (const ref of info.libReferenceDirectives) { stack.push(ref.fileName); } } @@ -307,6 +307,12 @@ function getColor(node: ts.Node): NodeColor { function setColor(node: ts.Node, color: NodeColor): void { (node).$$$color = color; } +function markNeededSourceFile(node: ts.SourceFile): void { + (node).$$$neededSourceFile = true; +} +function isNeededSourceFile(node: ts.SourceFile): boolean { + return Boolean((node).$$$neededSourceFile); +} function nodeOrParentIsBlack(node: ts.Node): boolean { while (node) { const color = getColor(node); @@ -449,6 +455,20 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language }); } + /** + * Return the parent of `node` which is an ImportDeclaration + */ + function findParentImportDeclaration(node: ts.Declaration): ts.ImportDeclaration | null { + let _node: ts.Node = node; + do { + if (ts.isImportDeclaration(_node)) { + return _node; + } + _node = _node.parent; + } while (_node); + return null; + } + function enqueue_gray(node: ts.Node): void { if (nodeOrParentIsBlack(node) || getColor(node) === NodeColor.Gray) { return; @@ -531,6 +551,8 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language console.warn(`Cannot find source file ${filename}`); return; } + // This source file should survive even if it is empty + markNeededSourceFile(sourceFile); enqueue_black(sourceFile); } @@ -590,6 +612,10 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node); if (symbolImportNode) { setColor(symbolImportNode, NodeColor.Black); + const importDeclarationNode = findParentImportDeclaration(symbolImportNode); + if (importDeclarationNode && ts.isStringLiteral(importDeclarationNode.moduleSpecifier)) { + enqueueImport(importDeclarationNode, importDeclarationNode.moduleSpecifier.text); + } } if (isSymbolWithDeclarations(symbol) && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { @@ -629,7 +655,7 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language // queue the heritage clauses if (declaration.heritageClauses) { - for (let heritageClause of declaration.heritageClauses) { + for (const heritageClause of declaration.heritageClauses) { enqueue_black(heritageClause); } } @@ -682,7 +708,7 @@ function generateResult(ts: typeof import('typescript'), languageService: ts.Lan throw new Error('Could not get program from language service'); } - let result: ITreeShakingResult = {}; + const result: ITreeShakingResult = {}; const writeFile = (filePath: string, contents: string): void => { result[filePath] = contents; }; @@ -700,7 +726,7 @@ function generateResult(ts: typeof import('typescript'), languageService: ts.Lan return; } - let text = sourceFile.text; + const text = sourceFile.text; let result = ''; function keep(node: ts.Node): void { @@ -734,7 +760,7 @@ function generateResult(ts: typeof import('typescript'), languageService: ts.Lan return keep(node); } } else { - let survivingImports: string[] = []; + const survivingImports: string[] = []; for (const importNode of node.importClause.namedBindings.elements) { if (getColor(importNode) === NodeColor.Black) { survivingImports.push(importNode.getFullText(sourceFile)); @@ -762,7 +788,7 @@ function generateResult(ts: typeof import('typescript'), languageService: ts.Lan if (ts.isExportDeclaration(node)) { if (node.exportClause && node.moduleSpecifier && ts.isNamedExports(node.exportClause)) { - let survivingExports: string[] = []; + const survivingExports: string[] = []; for (const exportSpecifier of node.exportClause.elements) { if (getColor(exportSpecifier) === NodeColor.Black) { survivingExports.push(exportSpecifier.getFullText(sourceFile)); @@ -785,8 +811,8 @@ function generateResult(ts: typeof import('typescript'), languageService: ts.Lan continue; } - let pos = member.pos - node.pos; - let end = member.end - node.pos; + const pos = member.pos - node.pos; + const end = member.end - node.pos; toWrite = toWrite.substring(0, pos) + toWrite.substring(end); } return write(toWrite); @@ -802,11 +828,21 @@ function generateResult(ts: typeof import('typescript'), languageService: ts.Lan if (getColor(sourceFile) !== NodeColor.Black) { if (!nodeOrChildIsBlack(sourceFile)) { - // none of the elements are reachable => don't write this file at all! - return; + // none of the elements are reachable + if (isNeededSourceFile(sourceFile)) { + // this source file must be written, even if nothing is used from it + // because there is an import somewhere for it. + // However, TS complains with empty files with the error "x" is not a module, + // so we will export a dummy variable + result = 'export const __dummy = 0;'; + } else { + // don't write this file at all! + return; + } + } else { + sourceFile.forEachChild(writeMarkedNodes); + result += sourceFile.endOfFileToken.getFullText(sourceFile); } - sourceFile.forEachChild(writeMarkedNodes); - result += sourceFile.endOfFileToken.getFullText(sourceFile); } else { result = text; } diff --git a/build/lib/tsb/builder.js b/build/lib/tsb/builder.js new file mode 100644 index 0000000000..be74a30a17 --- /dev/null +++ b/build/lib/tsb/builder.js @@ -0,0 +1,491 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createTypeScriptBuilder = exports.CancellationToken = void 0; +const fs_1 = require("fs"); +const path = require("path"); +const crypto = require("crypto"); +const utils = require("./utils"); +const colors = require("ansi-colors"); +const ts = require("typescript"); +const Vinyl = require("vinyl"); +var CancellationToken; +(function (CancellationToken) { + CancellationToken.None = { + isCancellationRequested() { return false; } + }; +})(CancellationToken = exports.CancellationToken || (exports.CancellationToken = {})); +function normalize(path) { + return path.replace(/\\/g, '/'); +} +function createTypeScriptBuilder(config, projectFile, cmd) { + const _log = config.logFn; + const host = new LanguageServiceHost(cmd, projectFile, _log); + const service = ts.createLanguageService(host, ts.createDocumentRegistry()); + const lastBuildVersion = Object.create(null); + const lastDtsHash = Object.create(null); + const userWantsDeclarations = cmd.options.declaration; + let oldErrors = Object.create(null); + let headUsed = process.memoryUsage().heapUsed; + let emitSourceMapsInStream = true; + // always emit declaraction files + host.getCompilationSettings().declaration = true; + function file(file) { + // support gulp-sourcemaps + if (file.sourceMap) { + emitSourceMapsInStream = false; + } + if (!file.contents) { + host.removeScriptSnapshot(file.path); + } + else { + host.addScriptSnapshot(file.path, new VinylScriptSnapshot(file)); + } + } + function baseFor(snapshot) { + if (snapshot instanceof VinylScriptSnapshot) { + return cmd.options.outDir || snapshot.getBase(); + } + else { + return ''; + } + } + function isExternalModule(sourceFile) { + return sourceFile.externalModuleIndicator + || /declare\s+module\s+('|")(.+)\1/.test(sourceFile.getText()); + } + function build(out, onError, token = CancellationToken.None) { + function checkSyntaxSoon(fileName) { + return new Promise(resolve => { + process.nextTick(function () { + if (!host.getScriptSnapshot(fileName, false)) { + resolve([]); // no script, no problems + } + else { + resolve(service.getSyntacticDiagnostics(fileName)); + } + }); + }); + } + function checkSemanticsSoon(fileName) { + return new Promise(resolve => { + process.nextTick(function () { + if (!host.getScriptSnapshot(fileName, false)) { + resolve([]); // no script, no problems + } + else { + resolve(service.getSemanticDiagnostics(fileName)); + } + }); + }); + } + function emitSoon(fileName) { + return new Promise(resolve => { + process.nextTick(function () { + if (/\.d\.ts$/.test(fileName)) { + // if it's already a d.ts file just emit it signature + const snapshot = host.getScriptSnapshot(fileName); + const signature = crypto.createHash('md5') + .update(snapshot.getText(0, snapshot.getLength())) + .digest('base64'); + return resolve({ + fileName, + signature, + files: [] + }); + } + const output = service.getEmitOutput(fileName); + const files = []; + let signature; + for (const file of output.outputFiles) { + if (!emitSourceMapsInStream && /\.js\.map$/.test(file.name)) { + continue; + } + if (/\.d\.ts$/.test(file.name)) { + signature = crypto.createHash('md5') + .update(file.text) + .digest('base64'); + if (!userWantsDeclarations) { + // don't leak .d.ts files if users don't want them + continue; + } + } + const vinyl = new Vinyl({ + path: file.name, + contents: Buffer.from(file.text), + base: !config._emitWithoutBasePath && baseFor(host.getScriptSnapshot(fileName)) || undefined + }); + if (!emitSourceMapsInStream && /\.js$/.test(file.name)) { + const sourcemapFile = output.outputFiles.filter(f => /\.js\.map$/.test(f.name))[0]; + if (sourcemapFile) { + const extname = path.extname(vinyl.relative); + const basename = path.basename(vinyl.relative, extname); + const dirname = path.dirname(vinyl.relative); + const tsname = (dirname === '.' ? '' : dirname + '/') + basename + '.ts'; + const sourceMap = JSON.parse(sourcemapFile.text); + sourceMap.sources[0] = tsname.replace(/\\/g, '/'); + vinyl.sourceMap = sourceMap; + } + } + files.push(vinyl); + } + resolve({ + fileName, + signature, + files + }); + }); + }); + } + const newErrors = Object.create(null); + const t1 = Date.now(); + const toBeEmitted = []; + const toBeCheckedSyntactically = []; + const toBeCheckedSemantically = []; + const filesWithChangedSignature = []; + const dependentFiles = []; + const newLastBuildVersion = new Map(); + for (const fileName of host.getScriptFileNames()) { + if (lastBuildVersion[fileName] !== host.getScriptVersion(fileName)) { + toBeEmitted.push(fileName); + toBeCheckedSyntactically.push(fileName); + toBeCheckedSemantically.push(fileName); + } + } + return new Promise(resolve => { + const semanticCheckInfo = new Map(); + const seenAsDependentFile = new Set(); + function workOnNext() { + let promise; + // let fileName: string; + // someone told us to stop this + if (token.isCancellationRequested()) { + _log('[CANCEL]', '>>This compile run was cancelled<<'); + newLastBuildVersion.clear(); + resolve(); + return; + } + // (1st) emit code + else if (toBeEmitted.length) { + const fileName = toBeEmitted.pop(); + promise = emitSoon(fileName).then(value => { + for (const file of value.files) { + _log('[emit code]', file.path); + out(file); + } + // remember when this was build + newLastBuildVersion.set(fileName, host.getScriptVersion(fileName)); + // remeber the signature + if (value.signature && lastDtsHash[fileName] !== value.signature) { + lastDtsHash[fileName] = value.signature; + filesWithChangedSignature.push(fileName); + } + }).catch(e => { + // can't just skip this or make a result up.. + host.error(`ERROR emitting ${fileName}`); + host.error(e); + }); + } + // (2nd) check syntax + else if (toBeCheckedSyntactically.length) { + const fileName = toBeCheckedSyntactically.pop(); + _log('[check syntax]', fileName); + promise = checkSyntaxSoon(fileName).then(diagnostics => { + delete oldErrors[fileName]; + if (diagnostics.length > 0) { + diagnostics.forEach(d => onError(d)); + newErrors[fileName] = diagnostics; + // stop the world when there are syntax errors + toBeCheckedSyntactically.length = 0; + toBeCheckedSemantically.length = 0; + filesWithChangedSignature.length = 0; + } + }); + } + // (3rd) check semantics + else if (toBeCheckedSemantically.length) { + let fileName = toBeCheckedSemantically.pop(); + while (fileName && semanticCheckInfo.has(fileName)) { + fileName = toBeCheckedSemantically.pop(); + } + if (fileName) { + _log('[check semantics]', fileName); + promise = checkSemanticsSoon(fileName).then(diagnostics => { + delete oldErrors[fileName]; + semanticCheckInfo.set(fileName, diagnostics.length); + if (diagnostics.length > 0) { + diagnostics.forEach(d => onError(d)); + newErrors[fileName] = diagnostics; + } + }); + } + } + // (4th) check dependents + else if (filesWithChangedSignature.length) { + while (filesWithChangedSignature.length) { + const fileName = filesWithChangedSignature.pop(); + if (!isExternalModule(service.getProgram().getSourceFile(fileName))) { + _log('[check semantics*]', fileName + ' is an internal module and it has changed shape -> check whatever hasn\'t been checked yet'); + toBeCheckedSemantically.push(...host.getScriptFileNames()); + filesWithChangedSignature.length = 0; + dependentFiles.length = 0; + break; + } + host.collectDependents(fileName, dependentFiles); + } + } + // (5th) dependents contd + else if (dependentFiles.length) { + let fileName = dependentFiles.pop(); + while (fileName && seenAsDependentFile.has(fileName)) { + fileName = dependentFiles.pop(); + } + if (fileName) { + seenAsDependentFile.add(fileName); + const value = semanticCheckInfo.get(fileName); + if (value === 0) { + // already validated successfully -> look at dependents next + host.collectDependents(fileName, dependentFiles); + } + else if (typeof value === 'undefined') { + // first validate -> look at dependents next + dependentFiles.push(fileName); + toBeCheckedSemantically.push(fileName); + } + } + } + // (last) done + else { + resolve(); + return; + } + if (!promise) { + promise = Promise.resolve(); + } + promise.then(function () { + // change to change + process.nextTick(workOnNext); + }).catch(err => { + console.error(err); + }); + } + workOnNext(); + }).then(() => { + // store the build versions to not rebuilt the next time + newLastBuildVersion.forEach((value, key) => { + lastBuildVersion[key] = value; + }); + // print old errors and keep them + utils.collections.forEach(oldErrors, entry => { + entry.value.forEach(diag => onError(diag)); + newErrors[entry.key] = entry.value; + }); + oldErrors = newErrors; + // print stats + const headNow = process.memoryUsage().heapUsed; + const MB = 1024 * 1024; + _log('[tsb]', `time: ${colors.yellow((Date.now() - t1) + 'ms')} + \nmem: ${colors.cyan(Math.ceil(headNow / MB) + 'MB')} ${colors.bgCyan('delta: ' + Math.ceil((headNow - headUsed) / MB))}`); + headUsed = headNow; + }); + } + return { + file, + build, + languageService: service + }; +} +exports.createTypeScriptBuilder = createTypeScriptBuilder; +class ScriptSnapshot { + constructor(text, mtime) { + this._text = text; + this._mtime = mtime; + } + getVersion() { + return this._mtime.toUTCString(); + } + getText(start, end) { + return this._text.substring(start, end); + } + getLength() { + return this._text.length; + } + getChangeRange(_oldSnapshot) { + return undefined; + } +} +class VinylScriptSnapshot extends ScriptSnapshot { + constructor(file) { + super(file.contents.toString(), file.stat.mtime); + this._base = file.base; + } + getBase() { + return this._base; + } +} +class LanguageServiceHost { + constructor(_cmdLine, _projectPath, _log) { + this._cmdLine = _cmdLine; + this._projectPath = _projectPath; + this._log = _log; + this.directoryExists = ts.sys.directoryExists; + this.getDirectories = ts.sys.getDirectories; + this.fileExists = ts.sys.fileExists; + this.readFile = ts.sys.readFile; + this.readDirectory = ts.sys.readDirectory; + this._snapshots = Object.create(null); + this._filesInProject = new Set(_cmdLine.fileNames); + this._filesAdded = new Set(); + this._dependencies = new utils.graph.Graph(s => s); + this._dependenciesRecomputeList = []; + this._fileNameToDeclaredModule = Object.create(null); + this._projectVersion = 1; + } + log(_s) { + // console.log(s); + } + trace(_s) { + // console.log(s); + } + error(s) { + console.error(s); + } + getCompilationSettings() { + return this._cmdLine.options; + } + getProjectVersion() { + return String(this._projectVersion); + } + getScriptFileNames() { + const res = Object.keys(this._snapshots).filter(path => this._filesInProject.has(path) || this._filesAdded.has(path)); + return res; + } + getScriptVersion(filename) { + filename = normalize(filename); + const result = this._snapshots[filename]; + if (result) { + return result.getVersion(); + } + return 'UNKNWON_FILE_' + Math.random().toString(16).slice(2); + } + getScriptSnapshot(filename, resolve = true) { + filename = normalize(filename); + let result = this._snapshots[filename]; + if (!result && resolve) { + try { + result = new VinylScriptSnapshot(new Vinyl({ + path: filename, + contents: (0, fs_1.readFileSync)(filename), + base: this.getCompilationSettings().outDir, + stat: (0, fs_1.statSync)(filename) + })); + this.addScriptSnapshot(filename, result); + } + catch (e) { + // ignore + } + } + return result; + } + addScriptSnapshot(filename, snapshot) { + this._projectVersion++; + filename = normalize(filename); + const old = this._snapshots[filename]; + if (!old && !this._filesInProject.has(filename) && !filename.endsWith('.d.ts')) { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^ + // not very proper! + this._filesAdded.add(filename); + } + if (!old || old.getVersion() !== snapshot.getVersion()) { + this._dependenciesRecomputeList.push(filename); + const node = this._dependencies.lookup(filename); + if (node) { + node.outgoing = Object.create(null); + } + // (cheap) check for declare module + LanguageServiceHost._declareModule.lastIndex = 0; + let match; + while ((match = LanguageServiceHost._declareModule.exec(snapshot.getText(0, snapshot.getLength())))) { + let declaredModules = this._fileNameToDeclaredModule[filename]; + if (!declaredModules) { + this._fileNameToDeclaredModule[filename] = declaredModules = []; + } + declaredModules.push(match[2]); + } + } + this._snapshots[filename] = snapshot; + return old; + } + removeScriptSnapshot(filename) { + this._filesInProject.delete(filename); + this._filesAdded.delete(filename); + this._projectVersion++; + filename = normalize(filename); + delete this._fileNameToDeclaredModule[filename]; + return delete this._snapshots[filename]; + } + getCurrentDirectory() { + return path.dirname(this._projectPath); + } + getDefaultLibFileName(options) { + return ts.getDefaultLibFilePath(options); + } + // ---- dependency management + collectDependents(filename, target) { + while (this._dependenciesRecomputeList.length) { + this._processFile(this._dependenciesRecomputeList.pop()); + } + filename = normalize(filename); + const node = this._dependencies.lookup(filename); + if (node) { + utils.collections.forEach(node.incoming, entry => target.push(entry.key)); + } + } + _processFile(filename) { + if (filename.match(/.*\.d\.ts$/)) { + return; + } + filename = normalize(filename); + const snapshot = this.getScriptSnapshot(filename); + if (!snapshot) { + this._log('processFile', `Missing snapshot for: ${filename}`); + return; + } + const info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + // (1) ///-references + info.referencedFiles.forEach(ref => { + const resolvedPath = path.resolve(path.dirname(filename), ref.fileName); + const normalizedPath = normalize(resolvedPath); + this._dependencies.inertEdge(filename, normalizedPath); + }); + // (2) import-require statements + info.importedFiles.forEach(ref => { + const stopDirname = normalize(this.getCurrentDirectory()); + let dirname = filename; + let found = false; + while (!found && dirname.indexOf(stopDirname) === 0) { + dirname = path.dirname(dirname); + const resolvedPath = path.resolve(dirname, ref.fileName); + const normalizedPath = normalize(resolvedPath); + if (this.getScriptSnapshot(normalizedPath + '.ts')) { + this._dependencies.inertEdge(filename, normalizedPath + '.ts'); + found = true; + } + else if (this.getScriptSnapshot(normalizedPath + '.d.ts')) { + this._dependencies.inertEdge(filename, normalizedPath + '.d.ts'); + found = true; + } + } + if (!found) { + for (const key in this._fileNameToDeclaredModule) { + if (this._fileNameToDeclaredModule[key] && ~this._fileNameToDeclaredModule[key].indexOf(ref.fileName)) { + this._dependencies.inertEdge(filename, key); + } + } + } + }); + } +} +LanguageServiceHost._declareModule = /declare\s+module\s+('|")(.+)\1/g; diff --git a/build/lib/tsb/builder.ts b/build/lib/tsb/builder.ts new file mode 100644 index 0000000000..f36fd1195e --- /dev/null +++ b/build/lib/tsb/builder.ts @@ -0,0 +1,608 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { statSync, readFileSync } from 'fs'; +import * as path from 'path'; +import * as crypto from 'crypto'; +import * as utils from './utils'; +import * as colors from 'ansi-colors'; +import * as ts from 'typescript'; +import * as Vinyl from 'vinyl'; + +export interface IConfiguration { + logFn: (topic: string, message: string) => void; + _emitWithoutBasePath?: boolean; +} + +export interface CancellationToken { + isCancellationRequested(): boolean; +} + +export namespace CancellationToken { + export const None: CancellationToken = { + isCancellationRequested() { return false; } + }; +} + +export interface ITypeScriptBuilder { + build(out: (file: Vinyl) => void, onError: (err: ts.Diagnostic) => void, token?: CancellationToken): Promise; + file(file: Vinyl): void; + languageService: ts.LanguageService; +} + +function normalize(path: string): string { + return path.replace(/\\/g, '/'); +} + +export function createTypeScriptBuilder(config: IConfiguration, projectFile: string, cmd: ts.ParsedCommandLine): ITypeScriptBuilder { + + const _log = config.logFn; + + const host = new LanguageServiceHost(cmd, projectFile, _log); + const service = ts.createLanguageService(host, ts.createDocumentRegistry()); + const lastBuildVersion: { [path: string]: string } = Object.create(null); + const lastDtsHash: { [path: string]: string } = Object.create(null); + const userWantsDeclarations = cmd.options.declaration; + let oldErrors: { [path: string]: ts.Diagnostic[] } = Object.create(null); + let headUsed = process.memoryUsage().heapUsed; + let emitSourceMapsInStream = true; + + // always emit declaraction files + host.getCompilationSettings().declaration = true; + + function file(file: Vinyl): void { + // support gulp-sourcemaps + if ((file).sourceMap) { + emitSourceMapsInStream = false; + } + + if (!file.contents) { + host.removeScriptSnapshot(file.path); + } else { + host.addScriptSnapshot(file.path, new VinylScriptSnapshot(file)); + } + } + + function baseFor(snapshot: ScriptSnapshot): string { + if (snapshot instanceof VinylScriptSnapshot) { + return cmd.options.outDir || snapshot.getBase(); + } else { + return ''; + } + } + + function isExternalModule(sourceFile: ts.SourceFile): boolean { + return (sourceFile).externalModuleIndicator + || /declare\s+module\s+('|")(.+)\1/.test(sourceFile.getText()); + } + + function build(out: (file: Vinyl) => void, onError: (err: any) => void, token = CancellationToken.None): Promise { + + function checkSyntaxSoon(fileName: string): Promise { + return new Promise(resolve => { + process.nextTick(function () { + if (!host.getScriptSnapshot(fileName, false)) { + resolve([]); // no script, no problems + } else { + resolve(service.getSyntacticDiagnostics(fileName)); + } + }); + }); + } + + function checkSemanticsSoon(fileName: string): Promise { + return new Promise(resolve => { + process.nextTick(function () { + if (!host.getScriptSnapshot(fileName, false)) { + resolve([]); // no script, no problems + } else { + resolve(service.getSemanticDiagnostics(fileName)); + } + }); + }); + } + + function emitSoon(fileName: string): Promise<{ fileName: string; signature?: string; files: Vinyl[] }> { + + return new Promise(resolve => { + process.nextTick(function () { + + if (/\.d\.ts$/.test(fileName)) { + // if it's already a d.ts file just emit it signature + const snapshot = host.getScriptSnapshot(fileName); + const signature = crypto.createHash('md5') + .update(snapshot.getText(0, snapshot.getLength())) + .digest('base64'); + + return resolve({ + fileName, + signature, + files: [] + }); + } + + const output = service.getEmitOutput(fileName); + const files: Vinyl[] = []; + let signature: string | undefined; + + for (const file of output.outputFiles) { + if (!emitSourceMapsInStream && /\.js\.map$/.test(file.name)) { + continue; + } + + if (/\.d\.ts$/.test(file.name)) { + signature = crypto.createHash('md5') + .update(file.text) + .digest('base64'); + + if (!userWantsDeclarations) { + // don't leak .d.ts files if users don't want them + continue; + } + } + + const vinyl = new Vinyl({ + path: file.name, + contents: Buffer.from(file.text), + base: !config._emitWithoutBasePath && baseFor(host.getScriptSnapshot(fileName)) || undefined + }); + + if (!emitSourceMapsInStream && /\.js$/.test(file.name)) { + const sourcemapFile = output.outputFiles.filter(f => /\.js\.map$/.test(f.name))[0]; + + if (sourcemapFile) { + const extname = path.extname(vinyl.relative); + const basename = path.basename(vinyl.relative, extname); + const dirname = path.dirname(vinyl.relative); + const tsname = (dirname === '.' ? '' : dirname + '/') + basename + '.ts'; + + const sourceMap = JSON.parse(sourcemapFile.text); + sourceMap.sources[0] = tsname.replace(/\\/g, '/'); + (vinyl).sourceMap = sourceMap; + } + } + + files.push(vinyl); + } + + resolve({ + fileName, + signature, + files + }); + }); + }); + } + + const newErrors: { [path: string]: ts.Diagnostic[] } = Object.create(null); + const t1 = Date.now(); + + const toBeEmitted: string[] = []; + const toBeCheckedSyntactically: string[] = []; + const toBeCheckedSemantically: string[] = []; + const filesWithChangedSignature: string[] = []; + const dependentFiles: string[] = []; + const newLastBuildVersion = new Map(); + + for (const fileName of host.getScriptFileNames()) { + if (lastBuildVersion[fileName] !== host.getScriptVersion(fileName)) { + + toBeEmitted.push(fileName); + toBeCheckedSyntactically.push(fileName); + toBeCheckedSemantically.push(fileName); + } + } + + return new Promise(resolve => { + + const semanticCheckInfo = new Map(); + const seenAsDependentFile = new Set(); + + function workOnNext() { + + let promise: Promise | undefined; + // let fileName: string; + + // someone told us to stop this + if (token.isCancellationRequested()) { + _log('[CANCEL]', '>>This compile run was cancelled<<'); + newLastBuildVersion.clear(); + resolve(); + return; + } + + // (1st) emit code + else if (toBeEmitted.length) { + const fileName = toBeEmitted.pop()!; + promise = emitSoon(fileName).then(value => { + + for (const file of value.files) { + _log('[emit code]', file.path); + out(file); + } + + // remember when this was build + newLastBuildVersion.set(fileName, host.getScriptVersion(fileName)); + + // remeber the signature + if (value.signature && lastDtsHash[fileName] !== value.signature) { + lastDtsHash[fileName] = value.signature; + filesWithChangedSignature.push(fileName); + } + }).catch(e => { + // can't just skip this or make a result up.. + host.error(`ERROR emitting ${fileName}`); + host.error(e); + }); + } + + // (2nd) check syntax + else if (toBeCheckedSyntactically.length) { + const fileName = toBeCheckedSyntactically.pop()!; + _log('[check syntax]', fileName); + promise = checkSyntaxSoon(fileName).then(diagnostics => { + delete oldErrors[fileName]; + if (diagnostics.length > 0) { + diagnostics.forEach(d => onError(d)); + newErrors[fileName] = diagnostics; + + // stop the world when there are syntax errors + toBeCheckedSyntactically.length = 0; + toBeCheckedSemantically.length = 0; + filesWithChangedSignature.length = 0; + } + }); + } + + // (3rd) check semantics + else if (toBeCheckedSemantically.length) { + + let fileName = toBeCheckedSemantically.pop(); + while (fileName && semanticCheckInfo.has(fileName)) { + fileName = toBeCheckedSemantically.pop()!; + } + + if (fileName) { + _log('[check semantics]', fileName); + promise = checkSemanticsSoon(fileName).then(diagnostics => { + delete oldErrors[fileName!]; + semanticCheckInfo.set(fileName!, diagnostics.length); + if (diagnostics.length > 0) { + diagnostics.forEach(d => onError(d)); + newErrors[fileName!] = diagnostics; + } + }); + } + } + + // (4th) check dependents + else if (filesWithChangedSignature.length) { + while (filesWithChangedSignature.length) { + const fileName = filesWithChangedSignature.pop()!; + + if (!isExternalModule(service.getProgram()!.getSourceFile(fileName)!)) { + _log('[check semantics*]', fileName + ' is an internal module and it has changed shape -> check whatever hasn\'t been checked yet'); + toBeCheckedSemantically.push(...host.getScriptFileNames()); + filesWithChangedSignature.length = 0; + dependentFiles.length = 0; + break; + } + + host.collectDependents(fileName, dependentFiles); + } + } + + // (5th) dependents contd + else if (dependentFiles.length) { + let fileName = dependentFiles.pop(); + while (fileName && seenAsDependentFile.has(fileName)) { + fileName = dependentFiles.pop(); + } + if (fileName) { + seenAsDependentFile.add(fileName); + const value = semanticCheckInfo.get(fileName); + if (value === 0) { + // already validated successfully -> look at dependents next + host.collectDependents(fileName, dependentFiles); + + } else if (typeof value === 'undefined') { + // first validate -> look at dependents next + dependentFiles.push(fileName); + toBeCheckedSemantically.push(fileName); + } + } + } + + // (last) done + else { + resolve(); + return; + } + + if (!promise) { + promise = Promise.resolve(); + } + + promise.then(function () { + // change to change + process.nextTick(workOnNext); + }).catch(err => { + console.error(err); + }); + } + + workOnNext(); + + }).then(() => { + // store the build versions to not rebuilt the next time + newLastBuildVersion.forEach((value, key) => { + lastBuildVersion[key] = value; + }); + + // print old errors and keep them + utils.collections.forEach(oldErrors, entry => { + entry.value.forEach(diag => onError(diag)); + newErrors[entry.key] = entry.value; + }); + oldErrors = newErrors; + + // print stats + const headNow = process.memoryUsage().heapUsed; + const MB = 1024 * 1024; + _log( + '[tsb]', + `time: ${colors.yellow((Date.now() - t1) + 'ms')} + \nmem: ${colors.cyan(Math.ceil(headNow / MB) + 'MB')} ${colors.bgCyan('delta: ' + Math.ceil((headNow - headUsed) / MB))}` + ); + headUsed = headNow; + }); + } + + return { + file, + build, + languageService: service + }; +} + +class ScriptSnapshot implements ts.IScriptSnapshot { + + private readonly _text: string; + private readonly _mtime: Date; + + constructor(text: string, mtime: Date) { + this._text = text; + this._mtime = mtime; + } + + getVersion(): string { + return this._mtime.toUTCString(); + } + + getText(start: number, end: number): string { + return this._text.substring(start, end); + } + + getLength(): number { + return this._text.length; + } + + getChangeRange(_oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange | undefined { + return undefined; + } +} + +class VinylScriptSnapshot extends ScriptSnapshot { + + private readonly _base: string; + + constructor(file: Vinyl) { + super(file.contents!.toString(), file.stat!.mtime); + this._base = file.base; + } + + getBase(): string { + return this._base; + } +} + +class LanguageServiceHost implements ts.LanguageServiceHost { + + private readonly _snapshots: { [path: string]: ScriptSnapshot }; + private readonly _filesInProject: Set; + private readonly _filesAdded: Set; + private readonly _dependencies: utils.graph.Graph; + private readonly _dependenciesRecomputeList: string[]; + private readonly _fileNameToDeclaredModule: { [path: string]: string[] }; + + private _projectVersion: number; + + constructor( + private readonly _cmdLine: ts.ParsedCommandLine, + private readonly _projectPath: string, + private readonly _log: (topic: string, message: string) => void + ) { + this._snapshots = Object.create(null); + this._filesInProject = new Set(_cmdLine.fileNames); + this._filesAdded = new Set(); + this._dependencies = new utils.graph.Graph(s => s); + this._dependenciesRecomputeList = []; + this._fileNameToDeclaredModule = Object.create(null); + + this._projectVersion = 1; + } + + log(_s: string): void { + // console.log(s); + } + + trace(_s: string): void { + // console.log(s); + } + + error(s: string): void { + console.error(s); + } + + getCompilationSettings(): ts.CompilerOptions { + return this._cmdLine.options; + } + + getProjectVersion(): string { + return String(this._projectVersion); + } + + getScriptFileNames(): string[] { + const res = Object.keys(this._snapshots).filter(path => this._filesInProject.has(path) || this._filesAdded.has(path)); + return res; + } + + getScriptVersion(filename: string): string { + filename = normalize(filename); + const result = this._snapshots[filename]; + if (result) { + return result.getVersion(); + } + return 'UNKNWON_FILE_' + Math.random().toString(16).slice(2); + } + + getScriptSnapshot(filename: string, resolve: boolean = true): ScriptSnapshot { + filename = normalize(filename); + let result = this._snapshots[filename]; + if (!result && resolve) { + try { + result = new VinylScriptSnapshot(new Vinyl({ + path: filename, + contents: readFileSync(filename), + base: this.getCompilationSettings().outDir, + stat: statSync(filename) + })); + this.addScriptSnapshot(filename, result); + } catch (e) { + // ignore + } + } + return result; + } + + private static _declareModule = /declare\s+module\s+('|")(.+)\1/g; + + addScriptSnapshot(filename: string, snapshot: ScriptSnapshot): ScriptSnapshot { + this._projectVersion++; + filename = normalize(filename); + const old = this._snapshots[filename]; + if (!old && !this._filesInProject.has(filename) && !filename.endsWith('.d.ts')) { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^ + // not very proper! + this._filesAdded.add(filename); + } + if (!old || old.getVersion() !== snapshot.getVersion()) { + this._dependenciesRecomputeList.push(filename); + const node = this._dependencies.lookup(filename); + if (node) { + node.outgoing = Object.create(null); + } + + // (cheap) check for declare module + LanguageServiceHost._declareModule.lastIndex = 0; + let match: RegExpExecArray | null | undefined; + while ((match = LanguageServiceHost._declareModule.exec(snapshot.getText(0, snapshot.getLength())))) { + let declaredModules = this._fileNameToDeclaredModule[filename]; + if (!declaredModules) { + this._fileNameToDeclaredModule[filename] = declaredModules = []; + } + declaredModules.push(match[2]); + } + } + this._snapshots[filename] = snapshot; + return old; + } + + removeScriptSnapshot(filename: string): boolean { + this._filesInProject.delete(filename); + this._filesAdded.delete(filename); + this._projectVersion++; + filename = normalize(filename); + delete this._fileNameToDeclaredModule[filename]; + return delete this._snapshots[filename]; + } + + getCurrentDirectory(): string { + return path.dirname(this._projectPath); + } + + getDefaultLibFileName(options: ts.CompilerOptions): string { + return ts.getDefaultLibFilePath(options); + } + + readonly directoryExists = ts.sys.directoryExists; + readonly getDirectories = ts.sys.getDirectories; + readonly fileExists = ts.sys.fileExists; + readonly readFile = ts.sys.readFile; + readonly readDirectory = ts.sys.readDirectory; + + // ---- dependency management + + collectDependents(filename: string, target: string[]): void { + while (this._dependenciesRecomputeList.length) { + this._processFile(this._dependenciesRecomputeList.pop()!); + } + filename = normalize(filename); + const node = this._dependencies.lookup(filename); + if (node) { + utils.collections.forEach(node.incoming, entry => target.push(entry.key)); + } + } + + _processFile(filename: string): void { + if (filename.match(/.*\.d\.ts$/)) { + return; + } + filename = normalize(filename); + const snapshot = this.getScriptSnapshot(filename); + if (!snapshot) { + this._log('processFile', `Missing snapshot for: ${filename}`); + return; + } + const info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + + // (1) ///-references + info.referencedFiles.forEach(ref => { + const resolvedPath = path.resolve(path.dirname(filename), ref.fileName); + const normalizedPath = normalize(resolvedPath); + + this._dependencies.inertEdge(filename, normalizedPath); + }); + + // (2) import-require statements + info.importedFiles.forEach(ref => { + const stopDirname = normalize(this.getCurrentDirectory()); + let dirname = filename; + let found = false; + + while (!found && dirname.indexOf(stopDirname) === 0) { + dirname = path.dirname(dirname); + const resolvedPath = path.resolve(dirname, ref.fileName); + const normalizedPath = normalize(resolvedPath); + + if (this.getScriptSnapshot(normalizedPath + '.ts')) { + this._dependencies.inertEdge(filename, normalizedPath + '.ts'); + found = true; + + } else if (this.getScriptSnapshot(normalizedPath + '.d.ts')) { + this._dependencies.inertEdge(filename, normalizedPath + '.d.ts'); + found = true; + } + } + + if (!found) { + for (const key in this._fileNameToDeclaredModule) { + if (this._fileNameToDeclaredModule[key] && ~this._fileNameToDeclaredModule[key].indexOf(ref.fileName)) { + this._dependencies.inertEdge(filename, key); + } + } + } + }); + } +} diff --git a/build/lib/tsb/index.js b/build/lib/tsb/index.js new file mode 100644 index 0000000000..a0b6423bb6 --- /dev/null +++ b/build/lib/tsb/index.js @@ -0,0 +1,130 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.create = void 0; +const Vinyl = require("vinyl"); +const through = require("through"); +const builder = require("./builder"); +const ts = require("typescript"); +const stream_1 = require("stream"); +const path_1 = require("path"); +const utils_1 = require("./utils"); +const fs_1 = require("fs"); +const log = require("fancy-log"); +const colors = require("ansi-colors"); +const transpiler_1 = require("./transpiler"); +class EmptyDuplex extends stream_1.Duplex { + _write(_chunk, _encoding, callback) { callback(); } + _read() { this.push(null); } +} +function createNullCompiler() { + const result = function () { return new EmptyDuplex(); }; + result.src = () => new EmptyDuplex(); + return result; +} +const _defaultOnError = (err) => console.log(JSON.stringify(err, null, 4)); +function create(projectPath, existingOptions, config, onError = _defaultOnError) { + function printDiagnostic(diag) { + if (!diag.file || !diag.start) { + onError(ts.flattenDiagnosticMessageText(diag.messageText, '\n')); + } + else { + const lineAndCh = diag.file.getLineAndCharacterOfPosition(diag.start); + onError(utils_1.strings.format('{0}({1},{2}): {3}', diag.file.fileName, lineAndCh.line + 1, lineAndCh.character + 1, ts.flattenDiagnosticMessageText(diag.messageText, '\n'))); + } + } + const parsed = ts.readConfigFile(projectPath, ts.sys.readFile); + if (parsed.error) { + printDiagnostic(parsed.error); + return createNullCompiler(); + } + const cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, (0, path_1.dirname)(projectPath), existingOptions); + if (cmdLine.errors.length > 0) { + cmdLine.errors.forEach(printDiagnostic); + return createNullCompiler(); + } + function logFn(topic, message) { + if (config.verbose) { + log(colors.cyan(topic), message); + } + } + // FULL COMPILE stream doing transpile, syntax and semantic diagnostics + function createCompileStream(builder, token) { + return through(function (file) { + // give the file to the compiler + if (file.isStream()) { + this.emit('error', 'no support for streams'); + return; + } + builder.file(file); + }, function () { + // start the compilation process + builder.build(file => this.queue(file), printDiagnostic, token).catch(e => console.error(e)).then(() => this.queue(null)); + }); + } + // TRANSPILE ONLY stream doing just TS to JS conversion + function createTranspileStream(transpiler) { + return through(function (file) { + // give the file to the compiler + if (file.isStream()) { + this.emit('error', 'no support for streams'); + return; + } + if (!file.contents) { + return; + } + if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) { + return; + } + if (!transpiler.onOutfile) { + transpiler.onOutfile = file => this.queue(file); + } + transpiler.transpile(file); + }, function () { + transpiler.join().then(() => { + this.queue(null); + transpiler.onOutfile = undefined; + }); + }); + } + let result; + if (config.transpileOnly) { + const transpiler = new transpiler_1.Transpiler(logFn, printDiagnostic, projectPath, cmdLine); + result = (() => createTranspileStream(transpiler)); + } + else { + const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); + result = ((token) => createCompileStream(_builder, token)); + } + result.src = (opts) => { + let _pos = 0; + const _fileNames = cmdLine.fileNames.slice(0); + return new class extends stream_1.Readable { + constructor() { + super({ objectMode: true }); + } + _read() { + let more = true; + let path; + for (; more && _pos < _fileNames.length; _pos++) { + path = _fileNames[_pos]; + more = this.push(new Vinyl({ + path, + contents: (0, fs_1.readFileSync)(path), + stat: (0, fs_1.statSync)(path), + cwd: opts && opts.cwd, + base: opts && opts.base || (0, path_1.dirname)(projectPath) + })); + } + if (_pos >= _fileNames.length) { + this.push(null); + } + } + }; + }; + return result; +} +exports.create = create; diff --git a/build/lib/tsb/index.ts b/build/lib/tsb/index.ts new file mode 100644 index 0000000000..5c9175d8ab --- /dev/null +++ b/build/lib/tsb/index.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Vinyl from 'vinyl'; +import * as through from 'through'; +import * as builder from './builder'; +import * as ts from 'typescript'; +import { Readable, Writable, Duplex } from 'stream'; +import { dirname } from 'path'; +import { strings } from './utils'; +import { readFileSync, statSync } from 'fs'; +import * as log from 'fancy-log'; +import colors = require('ansi-colors'); +import { Transpiler } from './transpiler'; + +export interface IncrementalCompiler { + (token?: any): Readable & Writable; + src(opts?: { cwd?: string; base?: string }): Readable; +} + +class EmptyDuplex extends Duplex { + _write(_chunk: any, _encoding: string, callback: (err?: Error) => void): void { callback(); } + _read() { this.push(null); } +} + +function createNullCompiler(): IncrementalCompiler { + const result: IncrementalCompiler = function () { return new EmptyDuplex(); }; + result.src = () => new EmptyDuplex(); + return result; +} + +const _defaultOnError = (err: string) => console.log(JSON.stringify(err, null, 4)); + +export function create( + projectPath: string, + existingOptions: Partial, + config: { verbose?: boolean; transpileOnly?: boolean; transpileOnlyIncludesDts?: boolean }, + onError: (message: string) => void = _defaultOnError +): IncrementalCompiler { + + function printDiagnostic(diag: ts.Diagnostic): void { + + if (!diag.file || !diag.start) { + onError(ts.flattenDiagnosticMessageText(diag.messageText, '\n')); + } else { + const lineAndCh = diag.file.getLineAndCharacterOfPosition(diag.start); + onError(strings.format('{0}({1},{2}): {3}', + diag.file.fileName, + lineAndCh.line + 1, + lineAndCh.character + 1, + ts.flattenDiagnosticMessageText(diag.messageText, '\n')) + ); + } + } + + const parsed = ts.readConfigFile(projectPath, ts.sys.readFile); + if (parsed.error) { + printDiagnostic(parsed.error); + return createNullCompiler(); + } + + const cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname(projectPath), existingOptions); + if (cmdLine.errors.length > 0) { + cmdLine.errors.forEach(printDiagnostic); + return createNullCompiler(); + } + + function logFn(topic: string, message: string): void { + if (config.verbose) { + log(colors.cyan(topic), message); + } + } + + // FULL COMPILE stream doing transpile, syntax and semantic diagnostics + function createCompileStream(builder: builder.ITypeScriptBuilder, token?: builder.CancellationToken): Readable & Writable { + + return through(function (this: through.ThroughStream, file: Vinyl) { + // give the file to the compiler + if (file.isStream()) { + this.emit('error', 'no support for streams'); + return; + } + builder.file(file); + + }, function (this: { queue(a: any): void }) { + // start the compilation process + builder.build( + file => this.queue(file), + printDiagnostic, + token + ).catch(e => console.error(e)).then(() => this.queue(null)); + }); + } + + // TRANSPILE ONLY stream doing just TS to JS conversion + function createTranspileStream(transpiler: Transpiler): Readable & Writable { + return through(function (this: through.ThroughStream & { queue(a: any): void }, file: Vinyl) { + // give the file to the compiler + if (file.isStream()) { + this.emit('error', 'no support for streams'); + return; + } + if (!file.contents) { + return; + } + if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) { + return; + } + + if (!transpiler.onOutfile) { + transpiler.onOutfile = file => this.queue(file); + } + + transpiler.transpile(file); + + }, function (this: { queue(a: any): void }) { + transpiler.join().then(() => { + this.queue(null); + transpiler.onOutfile = undefined; + }); + }); + } + + + let result: IncrementalCompiler; + if (config.transpileOnly) { + const transpiler = new Transpiler(logFn, printDiagnostic, projectPath, cmdLine); + result = (() => createTranspileStream(transpiler)); + } else { + const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); + result = ((token: builder.CancellationToken) => createCompileStream(_builder, token)); + } + + result.src = (opts?: { cwd?: string; base?: string }) => { + let _pos = 0; + const _fileNames = cmdLine.fileNames.slice(0); + return new class extends Readable { + constructor() { + super({ objectMode: true }); + } + _read() { + let more: boolean = true; + let path: string; + for (; more && _pos < _fileNames.length; _pos++) { + path = _fileNames[_pos]; + more = this.push(new Vinyl({ + path, + contents: readFileSync(path), + stat: statSync(path), + cwd: opts && opts.cwd, + base: opts && opts.base || dirname(projectPath) + })); + } + if (_pos >= _fileNames.length) { + this.push(null); + } + } + }; + }; + + return result; +} diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js new file mode 100644 index 0000000000..38e6b5c00e --- /dev/null +++ b/build/lib/tsb/transpiler.js @@ -0,0 +1,220 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Transpiler = void 0; +const ts = require("typescript"); +const threads = require("node:worker_threads"); +const Vinyl = require("vinyl"); +const node_os_1 = require("node:os"); +function transpile(tsSrc, options) { + const isAmd = /\n(import|export)/m.test(tsSrc); + if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + // enforce NONE module-system for not-amd cases + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + } + const out = ts.transpileModule(tsSrc, options); + return { + jsSrc: out.outputText, + diag: out.diagnostics ?? [] + }; +} +if (!threads.isMainThread) { + // WORKER + threads.parentPort?.addListener('message', (req) => { + const res = { + jsSrcs: [], + diagnostics: [] + }; + for (const tsSrc of req.tsSrcs) { + const out = transpile(tsSrc, req.options); + res.jsSrcs.push(out.jsSrc); + res.diagnostics.push(out.diag); + } + threads.parentPort.postMessage(res); + }); +} +class TranspileWorker { + constructor(outFileFn) { + this.id = TranspileWorker.pool++; + this._worker = new threads.Worker(__filename); + this._durations = []; + this._worker.addListener('message', (res) => { + if (!this._pending) { + console.error('RECEIVING data WITHOUT request'); + return; + } + const [resolve, reject, files, options, t1] = this._pending; + const outFiles = []; + const diag = []; + for (let i = 0; i < res.jsSrcs.length; i++) { + // inputs and outputs are aligned across the arrays + const file = files[i]; + const jsSrc = res.jsSrcs[i]; + const diag = res.diagnostics[i]; + if (diag.length > 0) { + diag.push(...diag); + continue; + } + let SuffixTypes; + (function (SuffixTypes) { + SuffixTypes[SuffixTypes["Dts"] = 5] = "Dts"; + SuffixTypes[SuffixTypes["Ts"] = 3] = "Ts"; + SuffixTypes[SuffixTypes["Unknown"] = 0] = "Unknown"; + })(SuffixTypes || (SuffixTypes = {})); + const suffixLen = file.path.endsWith('.d.ts') ? 5 /* SuffixTypes.Dts */ + : file.path.endsWith('.ts') ? 3 /* SuffixTypes.Ts */ + : 0 /* SuffixTypes.Unknown */; + // check if output of a DTS-files isn't just "empty" and iff so + // skip this file + if (suffixLen === 5 /* SuffixTypes.Dts */ && _isDefaultEmpty(jsSrc)) { + continue; + } + const outBase = options.compilerOptions?.outDir ?? file.base; + const outPath = outFileFn(file.path); + outFiles.push(new Vinyl({ + path: outPath, + base: outBase, + contents: Buffer.from(jsSrc), + })); + } + this._pending = undefined; + this._durations.push(Date.now() - t1); + if (diag.length > 0) { + reject(diag); + } + else { + resolve(outFiles); + } + }); + } + terminate() { + // console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`); + this._worker.terminate(); + } + get isBusy() { + return this._pending !== undefined; + } + next(files, options) { + if (this._pending !== undefined) { + throw new Error('BUSY'); + } + return new Promise((resolve, reject) => { + this._pending = [resolve, reject, files, options, Date.now()]; + const req = { + options, + tsSrcs: files.map(file => String(file.contents)) + }; + this._worker.postMessage(req); + }); + } +} +TranspileWorker.pool = 1; +class Transpiler { + constructor(logFn, _onError, configFilePath, _cmdLine) { + this._onError = _onError; + this._cmdLine = _cmdLine; + this._workerPool = []; + this._queue = []; + this._allJobs = []; + logFn('Transpile', `will use ${Transpiler.P} transpile worker`); + this._getOutputFileName = (file) => { + try { + // windows: path-sep normalizing + file = ts.normalizePath(file); + if (!_cmdLine.options.configFilePath) { + // this is needed for the INTERNAL getOutputFileNames-call below... + _cmdLine.options.configFilePath = configFilePath; + } + const isDts = file.endsWith('.d.ts'); + if (isDts) { + file = file.slice(0, -5) + '.ts'; + _cmdLine.fileNames.push(file); + } + const outfile = ts.getOutputFileNames(_cmdLine, file, true)[0]; + if (isDts) { + _cmdLine.fileNames.pop(); + } + return outfile; + } + catch (err) { + console.error(file, _cmdLine.fileNames); + console.error(err); + throw new err; + } + }; + } + async join() { + // wait for all penindg jobs + this._consumeQueue(); + await Promise.allSettled(this._allJobs); + this._allJobs.length = 0; + // terminate all worker + this._workerPool.forEach(w => w.terminate()); + this._workerPool.length = 0; + } + transpile(file) { + if (this._cmdLine.options.noEmit) { + // not doing ANYTHING here + return; + } + const newLen = this._queue.push(file); + if (newLen > Transpiler.P ** 2) { + this._consumeQueue(); + } + } + _consumeQueue() { + if (this._queue.length === 0) { + // no work... + return; + } + // kinda LAZYily create workers + if (this._workerPool.length === 0) { + for (let i = 0; i < Transpiler.P; i++) { + this._workerPool.push(new TranspileWorker(file => this._getOutputFileName(file))); + } + } + const freeWorker = this._workerPool.filter(w => !w.isBusy); + if (freeWorker.length === 0) { + // OK, they will pick up work themselves + return; + } + for (const worker of freeWorker) { + if (this._queue.length === 0) { + break; + } + const job = new Promise(resolve => { + const consume = () => { + const files = this._queue.splice(0, Transpiler.P); + if (files.length === 0) { + // DONE + resolve(undefined); + return; + } + // work on the NEXT file + // const [inFile, outFn] = req; + worker.next(files, { compilerOptions: this._cmdLine.options }).then(outFiles => { + if (this.onOutfile) { + outFiles.map(this.onOutfile, this); + } + consume(); + }).catch(err => { + this._onError(err); + }); + }; + consume(); + }); + this._allJobs.push(job); + } + } +} +exports.Transpiler = Transpiler; +Transpiler.P = Math.floor((0, node_os_1.cpus)().length * .5); +function _isDefaultEmpty(src) { + return src + .replace('"use strict";', '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts new file mode 100644 index 0000000000..267fef97ae --- /dev/null +++ b/build/lib/tsb/transpiler.ts @@ -0,0 +1,285 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as threads from 'node:worker_threads'; +import * as Vinyl from 'vinyl'; +import { cpus } from 'node:os'; + +interface TranspileReq { + readonly tsSrcs: string[]; + readonly options: ts.TranspileOptions; +} + +interface TranspileRes { + readonly jsSrcs: string[]; + readonly diagnostics: ts.Diagnostic[][]; +} + +function transpile(tsSrc: string, options: ts.TranspileOptions): { jsSrc: string; diag: ts.Diagnostic[] } { + + const isAmd = /\n(import|export)/m.test(tsSrc); + if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + // enforce NONE module-system for not-amd cases + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + } + const out = ts.transpileModule(tsSrc, options); + return { + jsSrc: out.outputText, + diag: out.diagnostics ?? [] + }; +} + +if (!threads.isMainThread) { + // WORKER + threads.parentPort?.addListener('message', (req: TranspileReq) => { + const res: TranspileRes = { + jsSrcs: [], + diagnostics: [] + }; + for (const tsSrc of req.tsSrcs) { + const out = transpile(tsSrc, req.options); + res.jsSrcs.push(out.jsSrc); + res.diagnostics.push(out.diag); + } + threads.parentPort!.postMessage(res); + }); +} + +class TranspileWorker { + + private static pool = 1; + + readonly id = TranspileWorker.pool++; + + private _worker = new threads.Worker(__filename); + private _pending?: [resolve: Function, reject: Function, file: Vinyl[], options: ts.TranspileOptions, t1: number]; + private _durations: number[] = []; + + constructor(outFileFn: (fileName: string) => string) { + + this._worker.addListener('message', (res: TranspileRes) => { + if (!this._pending) { + console.error('RECEIVING data WITHOUT request'); + return; + } + + const [resolve, reject, files, options, t1] = this._pending; + + const outFiles: Vinyl[] = []; + const diag: ts.Diagnostic[] = []; + + for (let i = 0; i < res.jsSrcs.length; i++) { + // inputs and outputs are aligned across the arrays + const file = files[i]; + const jsSrc = res.jsSrcs[i]; + const diag = res.diagnostics[i]; + + if (diag.length > 0) { + diag.push(...diag); + continue; + } + const enum SuffixTypes { + Dts = 5, + Ts = 3, + Unknown = 0 + } + const suffixLen = file.path.endsWith('.d.ts') ? SuffixTypes.Dts + : file.path.endsWith('.ts') ? SuffixTypes.Ts + : SuffixTypes.Unknown; + + // check if output of a DTS-files isn't just "empty" and iff so + // skip this file + if (suffixLen === SuffixTypes.Dts && _isDefaultEmpty(jsSrc)) { + continue; + } + + const outBase = options.compilerOptions?.outDir ?? file.base; + const outPath = outFileFn(file.path); + + outFiles.push(new Vinyl({ + path: outPath, + base: outBase, + contents: Buffer.from(jsSrc), + })); + } + + this._pending = undefined; + this._durations.push(Date.now() - t1); + + if (diag.length > 0) { + reject(diag); + } else { + resolve(outFiles); + } + }); + } + + terminate() { + // console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`); + this._worker.terminate(); + } + + get isBusy() { + return this._pending !== undefined; + } + + next(files: Vinyl[], options: ts.TranspileOptions) { + if (this._pending !== undefined) { + throw new Error('BUSY'); + } + return new Promise((resolve, reject) => { + this._pending = [resolve, reject, files, options, Date.now()]; + const req: TranspileReq = { + options, + tsSrcs: files.map(file => String(file.contents)) + }; + this._worker.postMessage(req); + }); + } +} + + +export class Transpiler { + + static P = Math.floor(cpus().length * .5); + + private readonly _getOutputFileName: (name: string) => string; + + public onOutfile?: (file: Vinyl) => void; + + private _workerPool: TranspileWorker[] = []; + private _queue: Vinyl[] = []; + private _allJobs: Promise[] = []; + + constructor( + logFn: (topic: string, message: string) => void, + private readonly _onError: (err: any) => void, + configFilePath: string, + private readonly _cmdLine: ts.ParsedCommandLine + ) { + logFn('Transpile', `will use ${Transpiler.P} transpile worker`); + + + // very complicated logic to re-use TS internal functions to know the output path + // given a TS input path and its config + type InternalTsApi = typeof ts & { + normalizePath(path: string): string; + getOutputFileNames(commandLine: ts.ParsedCommandLine, inputFileName: string, ignoreCase: boolean): readonly string[]; + }; + this._getOutputFileName = (file) => { + try { + + // windows: path-sep normalizing + file = (ts).normalizePath(file); + + if (!_cmdLine.options.configFilePath) { + // this is needed for the INTERNAL getOutputFileNames-call below... + _cmdLine.options.configFilePath = configFilePath; + } + const isDts = file.endsWith('.d.ts'); + if (isDts) { + file = file.slice(0, -5) + '.ts'; + _cmdLine.fileNames.push(file); + } + const outfile = (ts).getOutputFileNames(_cmdLine, file, true)[0]; + if (isDts) { + _cmdLine.fileNames.pop(); + } + return outfile; + + } catch (err) { + console.error(file, _cmdLine.fileNames); + console.error(err); + throw new err; + } + }; + } + + async join() { + // wait for all penindg jobs + this._consumeQueue(); + await Promise.allSettled(this._allJobs); + this._allJobs.length = 0; + + // terminate all worker + this._workerPool.forEach(w => w.terminate()); + this._workerPool.length = 0; + } + + + transpile(file: Vinyl) { + + if (this._cmdLine.options.noEmit) { + // not doing ANYTHING here + return; + } + + const newLen = this._queue.push(file); + if (newLen > Transpiler.P ** 2) { + this._consumeQueue(); + } + } + + private _consumeQueue(): void { + + if (this._queue.length === 0) { + // no work... + return; + } + + // kinda LAZYily create workers + if (this._workerPool.length === 0) { + for (let i = 0; i < Transpiler.P; i++) { + this._workerPool.push(new TranspileWorker(file => this._getOutputFileName(file))); + } + } + + const freeWorker = this._workerPool.filter(w => !w.isBusy); + if (freeWorker.length === 0) { + // OK, they will pick up work themselves + return; + } + + for (const worker of freeWorker) { + if (this._queue.length === 0) { + break; + } + + const job = new Promise(resolve => { + + const consume = () => { + const files = this._queue.splice(0, Transpiler.P); + if (files.length === 0) { + // DONE + resolve(undefined); + return; + } + // work on the NEXT file + // const [inFile, outFn] = req; + worker.next(files, { compilerOptions: this._cmdLine.options }).then(outFiles => { + if (this.onOutfile) { + outFiles.map(this.onOutfile, this); + } + consume(); + }).catch(err => { + this._onError(err); + }); + }; + + consume(); + }); + + this._allJobs.push(job); + } + } +} + +function _isDefaultEmpty(src: string): boolean { + return src + .replace('"use strict";', '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} diff --git a/build/lib/tsb/utils.js b/build/lib/tsb/utils.js new file mode 100644 index 0000000000..cc8605758c --- /dev/null +++ b/build/lib/tsb/utils.js @@ -0,0 +1,124 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.graph = exports.strings = exports.collections = void 0; +var collections; +(function (collections) { + const hasOwnProperty = Object.prototype.hasOwnProperty; + function lookup(collection, key) { + if (hasOwnProperty.call(collection, key)) { + return collection[key]; + } + return null; + } + collections.lookup = lookup; + function insert(collection, key, value) { + collection[key] = value; + } + collections.insert = insert; + function lookupOrInsert(collection, key, value) { + if (hasOwnProperty.call(collection, key)) { + return collection[key]; + } + else { + collection[key] = value; + return value; + } + } + collections.lookupOrInsert = lookupOrInsert; + function forEach(collection, callback) { + for (const key in collection) { + if (hasOwnProperty.call(collection, key)) { + callback({ + key: key, + value: collection[key] + }); + } + } + } + collections.forEach = forEach; + function contains(collection, key) { + return hasOwnProperty.call(collection, key); + } + collections.contains = contains; +})(collections = exports.collections || (exports.collections = {})); +var strings; +(function (strings) { + /** + * The empty string. The one and only. + */ + strings.empty = ''; + strings.eolUnix = '\r\n'; + function format(value, ...rest) { + return value.replace(/({\d+})/g, function (match) { + const index = Number(match.substring(1, match.length - 1)); + return String(rest[index]) || match; + }); + } + strings.format = format; +})(strings = exports.strings || (exports.strings = {})); +var graph; +(function (graph) { + function newNode(data) { + return { + data: data, + incoming: {}, + outgoing: {} + }; + } + graph.newNode = newNode; + class Graph { + constructor(_hashFn) { + this._hashFn = _hashFn; + this._nodes = {}; + // empty + } + traverse(start, inwards, callback) { + const startNode = this.lookup(start); + if (!startNode) { + return; + } + this._traverse(startNode, inwards, {}, callback); + } + _traverse(node, inwards, seen, callback) { + const key = this._hashFn(node.data); + if (collections.contains(seen, key)) { + return; + } + seen[key] = true; + callback(node.data); + const nodes = inwards ? node.outgoing : node.incoming; + collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback)); + } + inertEdge(from, to) { + const fromNode = this.lookupOrInsertNode(from); + const toNode = this.lookupOrInsertNode(to); + fromNode.outgoing[this._hashFn(to)] = toNode; + toNode.incoming[this._hashFn(from)] = fromNode; + } + removeNode(data) { + const key = this._hashFn(data); + delete this._nodes[key]; + collections.forEach(this._nodes, (entry) => { + delete entry.value.outgoing[key]; + delete entry.value.incoming[key]; + }); + } + lookupOrInsertNode(data) { + const key = this._hashFn(data); + let node = collections.lookup(this._nodes, key); + if (!node) { + node = newNode(data); + this._nodes[key] = node; + } + return node; + } + lookup(data) { + return collections.lookup(this._nodes, this._hashFn(data)); + } + } + graph.Graph = Graph; +})(graph = exports.graph || (exports.graph = {})); diff --git a/build/lib/tsb/utils.ts b/build/lib/tsb/utils.ts new file mode 100644 index 0000000000..edeaa48cdf --- /dev/null +++ b/build/lib/tsb/utils.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export module collections { + + const hasOwnProperty = Object.prototype.hasOwnProperty; + + export function lookup(collection: { [keys: string]: T }, key: string): T | null { + if (hasOwnProperty.call(collection, key)) { + return collection[key]; + } + return null; + } + + export function insert(collection: { [keys: string]: T }, key: string, value: T): void { + collection[key] = value; + } + + export function lookupOrInsert(collection: { [keys: string]: T }, key: string, value: T): T { + if (hasOwnProperty.call(collection, key)) { + return collection[key]; + } else { + collection[key] = value; + return value; + } + } + + export function forEach(collection: { [keys: string]: T }, callback: (entry: { key: string; value: T }) => void): void { + for (const key in collection) { + if (hasOwnProperty.call(collection, key)) { + callback({ + key: key, + value: collection[key] + }); + } + } + } + + export function contains(collection: { [keys: string]: any }, key: string): boolean { + return hasOwnProperty.call(collection, key); + } +} + +export module strings { + + /** + * The empty string. The one and only. + */ + export const empty = ''; + + export const eolUnix = '\r\n'; + + export function format(value: string, ...rest: any[]): string { + return value.replace(/({\d+})/g, function (match) { + const index = Number(match.substring(1, match.length - 1)); + return String(rest[index]) || match; + }); + } +} + +export module graph { + + export interface Node { + data: T; + incoming: { [key: string]: Node }; + outgoing: { [key: string]: Node }; + } + + export function newNode(data: T): Node { + return { + data: data, + incoming: {}, + outgoing: {} + }; + } + + export class Graph { + + private _nodes: { [key: string]: Node } = {}; + + constructor(private _hashFn: (element: T) => string) { + // empty + } + + traverse(start: T, inwards: boolean, callback: (data: T) => void): void { + const startNode = this.lookup(start); + if (!startNode) { + return; + } + this._traverse(startNode, inwards, {}, callback); + } + + private _traverse(node: Node, inwards: boolean, seen: { [key: string]: boolean }, callback: (data: T) => void): void { + const key = this._hashFn(node.data); + if (collections.contains(seen, key)) { + return; + } + seen[key] = true; + callback(node.data); + const nodes = inwards ? node.outgoing : node.incoming; + collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback)); + } + + inertEdge(from: T, to: T): void { + const fromNode = this.lookupOrInsertNode(from); + const toNode = this.lookupOrInsertNode(to); + + fromNode.outgoing[this._hashFn(to)] = toNode; + toNode.incoming[this._hashFn(from)] = fromNode; + } + + removeNode(data: T): void { + const key = this._hashFn(data); + delete this._nodes[key]; + collections.forEach(this._nodes, (entry) => { + delete entry.value.outgoing[key]; + delete entry.value.incoming[key]; + }); + } + + lookupOrInsertNode(data: T): Node { + const key = this._hashFn(data); + let node = collections.lookup(this._nodes, key); + + if (!node) { + node = newNode(data); + this._nodes[key] = node; + } + + return node; + } + + lookup(data: T): Node | null { + return collections.lookup(this._nodes, this._hashFn(data)); + } + } + +} diff --git a/build/lib/typings/gulp-tsb.d.ts b/build/lib/typings/gulp-tsb.d.ts deleted file mode 100644 index df9bb34d3f..0000000000 --- a/build/lib/typings/gulp-tsb.d.ts +++ /dev/null @@ -1,18 +0,0 @@ - - -declare module "gulp-tsb" { - - export interface ICancellationToken { - isCancellationRequested(): boolean; - } - - export interface IncrementalCompiler { - (token?: ICancellationToken): NodeJS.ReadWriteStream; - src(opts?: { - cwd?: string; - base?: string; - }): NodeJS.ReadStream; - } - export function create(projectPath: string, existingOptions: any, verbose?: boolean, onError?: (message: any) => void): IncrementalCompiler; - -} diff --git a/build/lib/util.js b/build/lib/util.js index fa2a6b9561..d790efd354 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildWebNodePaths = exports.createExternalLoaderConfig = exports.acquireWebNodePaths = exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.rewriteSourceMappingURL = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.debounce = exports.incremental = void 0; const es = require("event-stream"); @@ -240,7 +240,7 @@ function _rreaddir(dirPath, prepend, result) { } } function rreddir(dirPath) { - let result = []; + const result = []; _rreaddir(dirPath, '', result); return result; } @@ -337,6 +337,13 @@ function acquireWebNodePaths() { } nodePaths[key] = entryPoint; } + // @TODO lramos15 can we make this dynamic like the rest of the node paths + // Add these paths as well for 1DS SDK dependencies. + // Not sure why given the 1DS entrypoint then requires these modules + // they are not fetched from the right location and instead are fetched from out/ + nodePaths['@microsoft/dynamicproto-js'] = 'lib/dist/umd/dynamicproto-js.min.js'; + nodePaths['@microsoft/applicationinsights-shims'] = 'dist/umd/applicationinsights-shims.min.js'; + nodePaths['@microsoft/applicationinsights-core-js'] = 'browser/applicationinsights-core-js.min.js'; return nodePaths; } exports.acquireWebNodePaths = acquireWebNodePaths; @@ -345,7 +352,7 @@ function createExternalLoaderConfig(webEndpoint, commit, quality) { return undefined; } webEndpoint = webEndpoint + `/${quality}/${commit}`; - let nodePaths = acquireWebNodePaths(); + const nodePaths = acquireWebNodePaths(); Object.keys(nodePaths).map(function (key, _) { nodePaths[key] = `${webEndpoint}/node_modules/${key}/${nodePaths[key]}`; }); diff --git a/build/lib/util.ts b/build/lib/util.ts index e5ce6c26f1..f0e9e0c634 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as es from 'event-stream'; import _debounce = require('debounce'); import * as _filter from 'gulp-filter'; @@ -306,7 +304,7 @@ function _rreaddir(dirPath: string, prepend: string, result: string[]): void { } export function rreddir(dirPath: string): string[] { - let result: string[] = []; + const result: string[] = []; _rreaddir(dirPath, '', result); return result; } @@ -414,6 +412,13 @@ export function acquireWebNodePaths() { nodePaths[key] = entryPoint; } + // @TODO lramos15 can we make this dynamic like the rest of the node paths + // Add these paths as well for 1DS SDK dependencies. + // Not sure why given the 1DS entrypoint then requires these modules + // they are not fetched from the right location and instead are fetched from out/ + nodePaths['@microsoft/dynamicproto-js'] = 'lib/dist/umd/dynamicproto-js.min.js'; + nodePaths['@microsoft/applicationinsights-shims'] = 'dist/umd/applicationinsights-shims.min.js'; + nodePaths['@microsoft/applicationinsights-core-js'] = 'browser/applicationinsights-core-js.min.js'; return nodePaths; } @@ -422,7 +427,7 @@ export function createExternalLoaderConfig(webEndpoint?: string, commit?: string return undefined; } webEndpoint = webEndpoint + `/${quality}/${commit}`; - let nodePaths = acquireWebNodePaths(); + const nodePaths = acquireWebNodePaths(); Object.keys(nodePaths).map(function (key, _) { nodePaths[key] = `${webEndpoint}/node_modules/${key}/${nodePaths[key]}`; }); diff --git a/build/lib/watch/.gitignore b/build/lib/watch/.gitignore deleted file mode 100644 index d777dcaa9d..0000000000 --- a/build/lib/watch/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.yarnrc \ No newline at end of file diff --git a/build/lib/watch/package.json b/build/lib/watch/package.json deleted file mode 100644 index e2e4f55202..0000000000 --- a/build/lib/watch/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "watch", - "version": "1.0.0", - "description": "", - "author": "Microsoft ", - "private": true, - "license": "MIT", - "devDependencies": {}, - "dependencies": { - "vscode-gulp-watch": "^5.0.3" - } -} diff --git a/build/lib/watch/yarn.lock b/build/lib/watch/yarn.lock deleted file mode 100644 index 258c0edada..0000000000 --- a/build/lib/watch/yarn.lock +++ /dev/null @@ -1,400 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - -anymatch@^3.1.1, anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -binary-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" - integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -fancy-log@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -first-chunk-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" - integrity sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA= - dependencies: - readable-stream "^2.0.2" - -fsevents@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.1.tgz#b209ab14c61012636c8863507edf7fb68cc54e9f" - integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw== - -glob-parent@^5.1.1, glob-parent@~5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -graceful-fs@^4.1.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -plugin-error@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -readable-stream@^2.0.2, readable-stream@^2.3.5: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-bom-buf@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz#1cb45aaf57530f4caf86c7f75179d2c9a51dd572" - integrity sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI= - dependencies: - is-utf8 "^0.2.1" - -strip-bom-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" - integrity sha1-+H217yYT9paKpUWr/h7HKLaoKco= - dependencies: - first-chunk-stream "^2.0.0" - strip-bom "^2.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -vinyl-file@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-3.0.0.tgz#b104d9e4409ffa325faadd520642d0a3b488b365" - integrity sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U= - dependencies: - graceful-fs "^4.1.2" - pify "^2.3.0" - strip-bom-buf "^1.0.0" - strip-bom-stream "^2.0.0" - vinyl "^2.0.1" - -vinyl@^2.0.1, vinyl@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -vscode-gulp-watch@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/vscode-gulp-watch/-/vscode-gulp-watch-5.0.3.tgz#1ca1c03581d43692ecb1fe0b9afd4256faeb701b" - integrity sha512-MTUp2yLE9CshhkNSNV58EQNxQSeF8lIj3mkXZX9a1vAk+EQNM2PAYdPUDSd/P/08W3PMHGznEiZyfK7JAjLosg== - dependencies: - ansi-colors "4.1.1" - anymatch "^3.1.1" - chokidar "3.5.1" - fancy-log "^1.3.3" - glob-parent "^5.1.1" - normalize-path "^3.0.0" - object-assign "^4.1.1" - plugin-error "1.0.1" - readable-stream "^3.6.0" - vinyl "^2.2.0" - vinyl-file "^3.0.0" diff --git a/build/linux/debian/dep-lists.js b/build/linux/debian/dep-lists.js new file mode 100644 index 0000000000..3db2ef1abf --- /dev/null +++ b/build/linux/debian/dep-lists.js @@ -0,0 +1,156 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.referenceGeneratedDepsByArch = exports.bundledDeps = exports.recommendedDeps = exports.additionalDeps = void 0; +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/additional_deps +// Additional dependencies not in the dpkg-shlibdeps output. +exports.additionalDeps = [ + 'ca-certificates', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnss3 (>= 3.26)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'xdg-utils (>= 1.0.2)' // OS integration +]; +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/manual_recommends +// Dependencies that we can only recommend +// for now since some of the older distros don't support them. +exports.recommendedDeps = [ + 'libvulkan1' // Move to additionalDeps once support for Trusty and Jessie are dropped. +]; +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/98.0.4758.109:chrome/installer/linux/BUILD.gn;l=64-80 +// and the Linux Archive build +// Shared library dependencies that we already bundle. +exports.bundledDeps = [ + 'libEGL.so', + 'libGLESv2.so', + 'libvulkan.so.1', + 'swiftshader_libEGL.so', + 'swiftshader_libGLESv2.so', + 'libvk_swiftshader.so', + 'libffmpeg.so' +]; +exports.referenceGeneratedDepsByArch = { + 'amd64': [ + 'ca-certificates', + 'libasound2 (>= 1.0.16)', + 'libatk-bridge2.0-0 (>= 2.5.3)', + 'libatk1.0-0 (>= 2.2.0)', + 'libatspi2.0-0 (>= 2.9.90)', + 'libc6 (>= 2.14)', + 'libc6 (>= 2.17)', + 'libc6 (>= 2.2.5)', + 'libcairo2 (>= 1.6.0)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'libdbus-1-3 (>= 1.5.12)', + 'libdrm2 (>= 2.4.38)', + 'libexpat1 (>= 2.0.1)', + 'libgbm1 (>= 8.1~0)', + 'libgcc1 (>= 1:3.0)', + 'libglib2.0-0 (>= 2.16.0)', + 'libglib2.0-0 (>= 2.39.4)', + 'libgtk-3-0 (>= 3.9.10)', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnspr4 (>= 2:4.9-2~)', + 'libnss3 (>= 2:3.22)', + 'libnss3 (>= 3.26)', + 'libpango-1.0-0 (>= 1.14.0)', + 'libsecret-1-0 (>= 0.18)', + 'libx11-6', + 'libx11-6 (>= 2:1.4.99.1)', + 'libxcb1 (>= 1.9.2)', + 'libxcomposite1 (>= 1:0.4.4-1)', + 'libxdamage1 (>= 1:1.1)', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0 (>= 0.4.1)', + 'libxkbfile1', + 'libxrandr2', + 'xdg-utils (>= 1.0.2)' + ], + 'armhf': [ + 'ca-certificates', + 'libasound2 (>= 1.0.16)', + 'libatk-bridge2.0-0 (>= 2.5.3)', + 'libatk1.0-0 (>= 2.2.0)', + 'libatspi2.0-0 (>= 2.9.90)', + 'libc6 (>= 2.17)', + 'libc6 (>= 2.4)', + 'libc6 (>= 2.9)', + 'libcairo2 (>= 1.6.0)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'libdbus-1-3 (>= 1.5.12)', + 'libdrm2 (>= 2.4.38)', + 'libexpat1 (>= 2.0.1)', + 'libgbm1 (>= 8.1~0)', + 'libgcc1 (>= 1:3.0)', + 'libgcc1 (>= 1:3.5)', + 'libglib2.0-0 (>= 2.16.0)', + 'libglib2.0-0 (>= 2.39.4)', + 'libgtk-3-0 (>= 3.9.10)', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnspr4 (>= 2:4.9-2~)', + 'libnss3 (>= 2:3.22)', + 'libnss3 (>= 3.26)', + 'libpango-1.0-0 (>= 1.14.0)', + 'libsecret-1-0 (>= 0.18)', + 'libstdc++6 (>= 4.1.1)', + 'libstdc++6 (>= 5)', + 'libstdc++6 (>= 5.2)', + 'libstdc++6 (>= 6)', + 'libx11-6', + 'libx11-6 (>= 2:1.4.99.1)', + 'libxcb1 (>= 1.9.2)', + 'libxcomposite1 (>= 1:0.4.4-1)', + 'libxdamage1 (>= 1:1.1)', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0 (>= 0.4.1)', + 'libxkbfile1', + 'libxrandr2', + 'xdg-utils (>= 1.0.2)' + ], + 'arm64': [ + 'ca-certificates', + 'libasound2 (>= 1.0.16)', + 'libatk-bridge2.0-0 (>= 2.5.3)', + 'libatk1.0-0 (>= 2.2.0)', + 'libatspi2.0-0 (>= 2.9.90)', + 'libc6 (>= 2.17)', + 'libcairo2 (>= 1.6.0)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'libdbus-1-3 (>= 1.0.2)', + 'libdrm2 (>= 2.4.38)', + 'libexpat1 (>= 2.0.1)', + 'libgbm1 (>= 8.1~0)', + 'libgcc1 (>= 1:3.0)', + 'libgcc1 (>= 1:4.2)', + 'libgcc1 (>= 1:4.5)', + 'libglib2.0-0 (>= 2.16.0)', + 'libglib2.0-0 (>= 2.39.4)', + 'libgtk-3-0 (>= 3.9.10)', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnspr4 (>= 2:4.9-2~)', + 'libnss3 (>= 2:3.22)', + 'libnss3 (>= 3.26)', + 'libpango-1.0-0 (>= 1.14.0)', + 'libsecret-1-0 (>= 0.18)', + 'libstdc++6 (>= 4.1.1)', + 'libstdc++6 (>= 5)', + 'libstdc++6 (>= 5.2)', + 'libstdc++6 (>= 6)', + 'libx11-6', + 'libx11-6 (>= 2:1.4.99.1)', + 'libxcb1 (>= 1.9.2)', + 'libxcomposite1 (>= 1:0.4.4-1)', + 'libxdamage1 (>= 1:1.1)', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0 (>= 0.4.1)', + 'libxkbfile1', + 'libxrandr2', + 'xdg-utils (>= 1.0.2)' + ] +}; diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts new file mode 100644 index 0000000000..b86b5609e2 --- /dev/null +++ b/build/linux/debian/dep-lists.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/additional_deps +// Additional dependencies not in the dpkg-shlibdeps output. +export const additionalDeps = [ + 'ca-certificates', // Make sure users have SSL certificates. + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnss3 (>= 3.26)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', // For Breakpad crash reports. + 'xdg-utils (>= 1.0.2)' // OS integration +]; + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/manual_recommends +// Dependencies that we can only recommend +// for now since some of the older distros don't support them. +export const recommendedDeps = [ + 'libvulkan1' // Move to additionalDeps once support for Trusty and Jessie are dropped. +]; + +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/98.0.4758.109:chrome/installer/linux/BUILD.gn;l=64-80 +// and the Linux Archive build +// Shared library dependencies that we already bundle. +export const bundledDeps = [ + 'libEGL.so', + 'libGLESv2.so', + 'libvulkan.so.1', + 'swiftshader_libEGL.so', + 'swiftshader_libGLESv2.so', + 'libvk_swiftshader.so', + 'libffmpeg.so' +]; + +export const referenceGeneratedDepsByArch = { + 'amd64': [ + 'ca-certificates', + 'libasound2 (>= 1.0.16)', + 'libatk-bridge2.0-0 (>= 2.5.3)', + 'libatk1.0-0 (>= 2.2.0)', + 'libatspi2.0-0 (>= 2.9.90)', + 'libc6 (>= 2.14)', + 'libc6 (>= 2.17)', + 'libc6 (>= 2.2.5)', + 'libcairo2 (>= 1.6.0)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'libdbus-1-3 (>= 1.5.12)', + 'libdrm2 (>= 2.4.38)', + 'libexpat1 (>= 2.0.1)', + 'libgbm1 (>= 8.1~0)', + 'libgcc1 (>= 1:3.0)', + 'libglib2.0-0 (>= 2.16.0)', + 'libglib2.0-0 (>= 2.39.4)', + 'libgtk-3-0 (>= 3.9.10)', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnspr4 (>= 2:4.9-2~)', + 'libnss3 (>= 2:3.22)', + 'libnss3 (>= 3.26)', + 'libpango-1.0-0 (>= 1.14.0)', + 'libsecret-1-0 (>= 0.18)', + 'libx11-6', + 'libx11-6 (>= 2:1.4.99.1)', + 'libxcb1 (>= 1.9.2)', + 'libxcomposite1 (>= 1:0.4.4-1)', + 'libxdamage1 (>= 1:1.1)', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0 (>= 0.4.1)', + 'libxkbfile1', + 'libxrandr2', + 'xdg-utils (>= 1.0.2)' + ], + 'armhf': [ + 'ca-certificates', + 'libasound2 (>= 1.0.16)', + 'libatk-bridge2.0-0 (>= 2.5.3)', + 'libatk1.0-0 (>= 2.2.0)', + 'libatspi2.0-0 (>= 2.9.90)', + 'libc6 (>= 2.17)', + 'libc6 (>= 2.4)', + 'libc6 (>= 2.9)', + 'libcairo2 (>= 1.6.0)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'libdbus-1-3 (>= 1.5.12)', + 'libdrm2 (>= 2.4.38)', + 'libexpat1 (>= 2.0.1)', + 'libgbm1 (>= 8.1~0)', + 'libgcc1 (>= 1:3.0)', + 'libgcc1 (>= 1:3.5)', + 'libglib2.0-0 (>= 2.16.0)', + 'libglib2.0-0 (>= 2.39.4)', + 'libgtk-3-0 (>= 3.9.10)', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnspr4 (>= 2:4.9-2~)', + 'libnss3 (>= 2:3.22)', + 'libnss3 (>= 3.26)', + 'libpango-1.0-0 (>= 1.14.0)', + 'libsecret-1-0 (>= 0.18)', + 'libstdc++6 (>= 4.1.1)', + 'libstdc++6 (>= 5)', + 'libstdc++6 (>= 5.2)', + 'libstdc++6 (>= 6)', + 'libx11-6', + 'libx11-6 (>= 2:1.4.99.1)', + 'libxcb1 (>= 1.9.2)', + 'libxcomposite1 (>= 1:0.4.4-1)', + 'libxdamage1 (>= 1:1.1)', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0 (>= 0.4.1)', + 'libxkbfile1', + 'libxrandr2', + 'xdg-utils (>= 1.0.2)' + ], + 'arm64': [ + 'ca-certificates', + 'libasound2 (>= 1.0.16)', + 'libatk-bridge2.0-0 (>= 2.5.3)', + 'libatk1.0-0 (>= 2.2.0)', + 'libatspi2.0-0 (>= 2.9.90)', + 'libc6 (>= 2.17)', + 'libcairo2 (>= 1.6.0)', + 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', + 'libdbus-1-3 (>= 1.0.2)', + 'libdrm2 (>= 2.4.38)', + 'libexpat1 (>= 2.0.1)', + 'libgbm1 (>= 8.1~0)', + 'libgcc1 (>= 1:3.0)', + 'libgcc1 (>= 1:4.2)', + 'libgcc1 (>= 1:4.5)', + 'libglib2.0-0 (>= 2.16.0)', + 'libglib2.0-0 (>= 2.39.4)', + 'libgtk-3-0 (>= 3.9.10)', + 'libgtk-3-0 (>= 3.9.10) | libgtk-4-1', + 'libnspr4 (>= 2:4.9-2~)', + 'libnss3 (>= 2:3.22)', + 'libnss3 (>= 3.26)', + 'libpango-1.0-0 (>= 1.14.0)', + 'libsecret-1-0 (>= 0.18)', + 'libstdc++6 (>= 4.1.1)', + 'libstdc++6 (>= 5)', + 'libstdc++6 (>= 5.2)', + 'libstdc++6 (>= 6)', + 'libx11-6', + 'libx11-6 (>= 2:1.4.99.1)', + 'libxcb1 (>= 1.9.2)', + 'libxcomposite1 (>= 1:0.4.4-1)', + 'libxdamage1 (>= 1:1.1)', + 'libxext6', + 'libxfixes3', + 'libxkbcommon0 (>= 0.4.1)', + 'libxkbfile1', + 'libxrandr2', + 'xdg-utils (>= 1.0.2)' + ] +}; diff --git a/build/linux/debian/dependencies-generator.js b/build/linux/debian/dependencies-generator.js new file mode 100644 index 0000000000..235ca26853 --- /dev/null +++ b/build/linux/debian/dependencies-generator.js @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getDependencies = void 0; +const child_process_1 = require("child_process"); +const fs_1 = require("fs"); +const os_1 = require("os"); +const path = require("path"); +const dep_lists_1 = require("./dep-lists"); +// A flag that can easily be toggled. +// Make sure to compile the build directory after toggling the value. +// If false, we warn about new dependencies if they show up +// while running the Debian prepare package task for a release. +// If true, we fail the build if there are new dependencies found during that task. +// 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; +function getDependencies(buildDir, applicationName, arch, sysroot) { + // Get the files for which we want to find dependencies. + const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked'); + const findResult = (0, child_process_1.spawnSync)('find', [nativeModulesPath, '-name', '*.node']); + if (findResult.status) { + console.error('Error finding files:'); + console.error(findResult.stderr.toString()); + return []; + } + const files = findResult.stdout.toString().trimEnd().split('\n'); + const appPath = path.join(buildDir, applicationName); + files.push(appPath); + // Add chrome sandbox and crashpad handler. + files.push(path.join(buildDir, 'chrome-sandbox')); + files.push(path.join(buildDir, 'chrome_crashpad_handler')); + // Generate the dependencies. + const dependencies = files.map((file) => calculatePackageDeps(file, arch, sysroot)); + // Add additional dependencies. + const additionalDepsSet = new Set(dep_lists_1.additionalDeps); + dependencies.push(additionalDepsSet); + // Merge all the dependencies. + const mergedDependencies = mergePackageDeps(dependencies); + let sortedDependencies = []; + for (const dependency of mergedDependencies) { + sortedDependencies.push(dependency); + } + sortedDependencies.sort(); + // Exclude bundled dependencies + sortedDependencies = sortedDependencies.filter(dependency => { + return !dep_lists_1.bundledDeps.some(bundledDep => dependency.startsWith(bundledDep)); + }); + const referenceGeneratedDeps = dep_lists_1.referenceGeneratedDepsByArch[arch]; + if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) { + const failMessage = 'The dependencies list has changed.' + + '\nOld:\n' + referenceGeneratedDeps.join('\n') + + '\nNew:\n' + sortedDependencies.join('\n'); + if (FAIL_BUILD_FOR_NEW_DEPENDENCIES) { + throw new Error(failMessage); + } + else { + console.warn(failMessage); + } + } + return sortedDependencies; +} +exports.getDependencies = getDependencies; +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/calculate_package_deps.py. +function calculatePackageDeps(binaryPath, arch, sysroot) { + try { + if (!((0, fs_1.statSync)(binaryPath).mode & fs_1.constants.S_IXUSR)) { + throw new Error(`Binary ${binaryPath} needs to have an executable bit set.`); + } + } + catch (e) { + // The package might not exist. Don't re-throw the error here. + console.error('Tried to stat ' + binaryPath + ' but failed.'); + } + // Get the Chromium dpkg-shlibdeps file. + const dpkgShlibdepsUrl = 'https://raw.githubusercontent.com/chromium/chromium/100.0.4896.160/third_party/dpkg-shlibdeps/dpkg-shlibdeps.pl'; + const dpkgShlibdepsScriptLocation = `${(0, os_1.tmpdir)()}/dpkg-shlibdeps.pl`; + const result = (0, child_process_1.spawnSync)('curl', [dpkgShlibdepsUrl, '-o', dpkgShlibdepsScriptLocation]); + if (result.status !== 0) { + throw new Error('Cannot retrieve dpkg-shlibdeps. Stderr:\n' + result.stderr); + } + const cmd = [dpkgShlibdepsScriptLocation, '--ignore-weak-undefined']; + switch (arch) { + case 'amd64': + cmd.push(`-l${sysroot}/usr/lib/x86_64-linux-gnu`, `-l${sysroot}/lib/x86_64-linux-gnu`); + break; + case 'armhf': + cmd.push(`-l${sysroot}/usr/lib/arm-linux-gnueabihf`, `-l${sysroot}/lib/arm-linux-gnueabihf`); + break; + case 'arm64': + cmd.push(`-l${sysroot}/usr/lib/aarch64-linux-gnu`, `-l${sysroot}/lib/aarch64-linux-gnu`); + break; + default: + throw new Error('Unsupported architecture ' + arch); + } + cmd.push(`-l${sysroot}/usr/lib`); + cmd.push('-O', '-e', path.resolve(binaryPath)); + const dpkgShlibdepsResult = (0, child_process_1.spawnSync)('perl', cmd, { cwd: sysroot }); + if (dpkgShlibdepsResult.status !== 0) { + throw new Error(`dpkg-shlibdeps failed with exit code ${dpkgShlibdepsResult.status}. stderr:\n${dpkgShlibdepsResult.stderr} `); + } + const shlibsDependsPrefix = 'shlibs:Depends='; + const requiresList = dpkgShlibdepsResult.stdout.toString('utf-8').trimEnd().split('\n'); + let depsStr = ''; + for (const line of requiresList) { + if (line.startsWith(shlibsDependsPrefix)) { + depsStr = line.substring(shlibsDependsPrefix.length); + } + } + const requires = new Set(depsStr.split(', ').sort()); + return requires; +} +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py. +function mergePackageDeps(inputDeps) { + // For now, see if directly appending the dependencies helps. + const requires = new Set(); + for (const depSet of inputDeps) { + for (const dep of depSet) { + const trimmedDependency = dep.trim(); + if (trimmedDependency.length && !trimmedDependency.startsWith('#')) { + requires.add(trimmedDependency); + } + } + } + return requires; +} diff --git a/build/linux/debian/dependencies-generator.ts b/build/linux/debian/dependencies-generator.ts new file mode 100644 index 0000000000..ec23cb7229 --- /dev/null +++ b/build/linux/debian/dependencies-generator.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { spawnSync } from 'child_process'; +import { constants, statSync } from 'fs'; +import { tmpdir } from 'os'; +import path = require('path'); +import { additionalDeps, bundledDeps, referenceGeneratedDepsByArch } from './dep-lists'; +import { ArchString } from './types'; + +// A flag that can easily be toggled. +// Make sure to compile the build directory after toggling the value. +// If false, we warn about new dependencies if they show up +// while running the Debian prepare package task for a release. +// If true, we fail the build if there are new dependencies found during that task. +// 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: boolean = true; + +export function getDependencies(buildDir: string, applicationName: string, arch: ArchString, sysroot: string): string[] { + // Get the files for which we want to find dependencies. + const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked'); + const findResult = spawnSync('find', [nativeModulesPath, '-name', '*.node']); + if (findResult.status) { + console.error('Error finding files:'); + console.error(findResult.stderr.toString()); + return []; + } + + const files = findResult.stdout.toString().trimEnd().split('\n'); + + const appPath = path.join(buildDir, applicationName); + files.push(appPath); + + // Add chrome sandbox and crashpad handler. + files.push(path.join(buildDir, 'chrome-sandbox')); + files.push(path.join(buildDir, 'chrome_crashpad_handler')); + + // Generate the dependencies. + const dependencies: Set[] = files.map((file) => calculatePackageDeps(file, arch, sysroot)); + // Add additional dependencies. + const additionalDepsSet = new Set(additionalDeps); + dependencies.push(additionalDepsSet); + + // Merge all the dependencies. + const mergedDependencies = mergePackageDeps(dependencies); + let sortedDependencies: string[] = []; + for (const dependency of mergedDependencies) { + sortedDependencies.push(dependency); + } + sortedDependencies.sort(); + + // Exclude bundled dependencies + sortedDependencies = sortedDependencies.filter(dependency => { + return !bundledDeps.some(bundledDep => dependency.startsWith(bundledDep)); + }); + + const referenceGeneratedDeps = referenceGeneratedDepsByArch[arch]; + if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) { + const failMessage = 'The dependencies list has changed.' + + '\nOld:\n' + referenceGeneratedDeps.join('\n') + + '\nNew:\n' + sortedDependencies.join('\n'); + if (FAIL_BUILD_FOR_NEW_DEPENDENCIES) { + throw new Error(failMessage); + } else { + console.warn(failMessage); + } + } + + return sortedDependencies; +} + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/calculate_package_deps.py. +function calculatePackageDeps(binaryPath: string, arch: ArchString, sysroot: string): Set { + try { + if (!(statSync(binaryPath).mode & constants.S_IXUSR)) { + throw new Error(`Binary ${binaryPath} needs to have an executable bit set.`); + } + } catch (e) { + // The package might not exist. Don't re-throw the error here. + console.error('Tried to stat ' + binaryPath + ' but failed.'); + } + + // Get the Chromium dpkg-shlibdeps file. + const dpkgShlibdepsUrl = 'https://raw.githubusercontent.com/chromium/chromium/100.0.4896.160/third_party/dpkg-shlibdeps/dpkg-shlibdeps.pl'; + const dpkgShlibdepsScriptLocation = `${tmpdir()}/dpkg-shlibdeps.pl`; + const result = spawnSync('curl', [dpkgShlibdepsUrl, '-o', dpkgShlibdepsScriptLocation]); + if (result.status !== 0) { + throw new Error('Cannot retrieve dpkg-shlibdeps. Stderr:\n' + result.stderr); + } + const cmd = [dpkgShlibdepsScriptLocation, '--ignore-weak-undefined']; + switch (arch) { + case 'amd64': + cmd.push(`-l${sysroot}/usr/lib/x86_64-linux-gnu`, + `-l${sysroot}/lib/x86_64-linux-gnu`); + break; + case 'armhf': + cmd.push(`-l${sysroot}/usr/lib/arm-linux-gnueabihf`, + `-l${sysroot}/lib/arm-linux-gnueabihf`); + break; + case 'arm64': + cmd.push(`-l${sysroot}/usr/lib/aarch64-linux-gnu`, + `-l${sysroot}/lib/aarch64-linux-gnu`); + break; + default: + throw new Error('Unsupported architecture ' + arch); + } + cmd.push(`-l${sysroot}/usr/lib`); + cmd.push('-O', '-e', path.resolve(binaryPath)); + + const dpkgShlibdepsResult = spawnSync('perl', cmd, { cwd: sysroot }); + if (dpkgShlibdepsResult.status !== 0) { + throw new Error(`dpkg-shlibdeps failed with exit code ${dpkgShlibdepsResult.status}. stderr:\n${dpkgShlibdepsResult.stderr} `); + } + + const shlibsDependsPrefix = 'shlibs:Depends='; + const requiresList = dpkgShlibdepsResult.stdout.toString('utf-8').trimEnd().split('\n'); + let depsStr = ''; + for (const line of requiresList) { + if (line.startsWith(shlibsDependsPrefix)) { + depsStr = line.substring(shlibsDependsPrefix.length); + } + } + const requires = new Set(depsStr.split(', ').sort()); + return requires; +} + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py. +function mergePackageDeps(inputDeps: Set[]): Set { + // For now, see if directly appending the dependencies helps. + const requires = new Set(); + for (const depSet of inputDeps) { + for (const dep of depSet) { + const trimmedDependency = dep.trim(); + if (trimmedDependency.length && !trimmedDependency.startsWith('#')) { + requires.add(trimmedDependency); + } + } + } + return requires; +} diff --git a/build/linux/debian/install-sysroot.js b/build/linux/debian/install-sysroot.js new file mode 100644 index 0000000000..c6b57a7b8a --- /dev/null +++ b/build/linux/debian/install-sysroot.js @@ -0,0 +1,81 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getSysroot = void 0; +const child_process_1 = require("child_process"); +const crypto_1 = require("crypto"); +const os_1 = require("os"); +const fs = require("fs"); +const https = require("https"); +const path = require("path"); +const sysroots_1 = require("./sysroots"); +// Based on https://source.chromium.org/chromium/chromium/src/+/main:build/linux/sysroot_scripts/install-sysroot.py. +const URL_PREFIX = 'https://msftelectron.blob.core.windows.net'; +const URL_PATH = 'sysroots/toolchain'; +function getSha(filename) { + const hash = (0, crypto_1.createHash)('sha1'); + // Read file 1 MB at a time + const fd = fs.openSync(filename, 'r'); + const buffer = Buffer.alloc(1024 * 1024); + let position = 0; + let bytesRead = 0; + while ((bytesRead = fs.readSync(fd, buffer, 0, buffer.length, position)) === buffer.length) { + hash.update(buffer); + position += bytesRead; + } + hash.update(buffer.slice(0, bytesRead)); + return hash.digest('hex'); +} +async function getSysroot(arch) { + const sysrootDict = sysroots_1.sysrootInfo[arch]; + const tarballFilename = sysrootDict['Tarball']; + const tarballSha = sysrootDict['Sha1Sum']; + const sysroot = path.join((0, os_1.tmpdir)(), sysrootDict['SysrootDir']); + const url = [URL_PREFIX, URL_PATH, tarballSha, tarballFilename].join('/'); + const stamp = path.join(sysroot, '.stamp'); + if (fs.existsSync(stamp) && fs.readFileSync(stamp).toString() === url) { + return sysroot; + } + console.log(`Installing Debian ${arch} root image: ${sysroot}`); + fs.rmSync(sysroot, { recursive: true, force: true }); + fs.mkdirSync(sysroot); + const tarball = path.join(sysroot, tarballFilename); + console.log(`Downloading ${url}`); + let downloadSuccess = false; + for (let i = 0; i < 3 && !downloadSuccess; i++) { + await new Promise((c) => { + https.get(url, (res) => { + const chunks = []; + res.on('data', (chunk) => { + chunks.push(chunk); + }); + res.on('end', () => { + fs.writeFileSync(tarball, Buffer.concat(chunks)); + downloadSuccess = true; + c(); + }); + }).on('error', (err) => { + console.error('Encountered an error during the download attempt: ' + err.message); + c(); + }); + }); + } + if (!downloadSuccess) { + throw new Error('Failed to download ' + url); + } + const sha = getSha(tarball); + if (sha !== tarballSha) { + throw new Error(`Tarball sha1sum is wrong. Expected ${tarballSha}, actual ${sha}`); + } + const proc = (0, child_process_1.spawnSync)('tar', ['xf', tarball, '-C', sysroot]); + if (proc.status) { + throw new Error('Tarball extraction failed with code ' + proc.status); + } + fs.rmSync(tarball); + fs.writeFileSync(stamp, url); + return sysroot; +} +exports.getSysroot = getSysroot; diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install-sysroot.ts new file mode 100644 index 0000000000..0f170fc2be --- /dev/null +++ b/build/linux/debian/install-sysroot.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { spawnSync } from 'child_process'; +import { createHash } from 'crypto'; +import { tmpdir } from 'os'; +import * as fs from 'fs'; +import * as https from 'https'; +import * as path from 'path'; +import { sysrootInfo } from './sysroots'; +import { ArchString } from './types'; + +// Based on https://source.chromium.org/chromium/chromium/src/+/main:build/linux/sysroot_scripts/install-sysroot.py. +const URL_PREFIX = 'https://msftelectron.blob.core.windows.net'; +const URL_PATH = 'sysroots/toolchain'; + +function getSha(filename: fs.PathLike): string { + const hash = createHash('sha1'); + // Read file 1 MB at a time + const fd = fs.openSync(filename, 'r'); + const buffer = Buffer.alloc(1024 * 1024); + let position = 0; + let bytesRead = 0; + while ((bytesRead = fs.readSync(fd, buffer, 0, buffer.length, position)) === buffer.length) { + hash.update(buffer); + position += bytesRead; + } + hash.update(buffer.slice(0, bytesRead)); + return hash.digest('hex'); +} + +type SysrootDictEntry = { + Sha1Sum: string; + SysrootDir: string; + Tarball: string; +}; + +export async function getSysroot(arch: ArchString): Promise { + const sysrootDict: SysrootDictEntry = sysrootInfo[arch]; + const tarballFilename = sysrootDict['Tarball']; + const tarballSha = sysrootDict['Sha1Sum']; + const sysroot = path.join(tmpdir(), sysrootDict['SysrootDir']); + const url = [URL_PREFIX, URL_PATH, tarballSha, tarballFilename].join('/'); + const stamp = path.join(sysroot, '.stamp'); + if (fs.existsSync(stamp) && fs.readFileSync(stamp).toString() === url) { + return sysroot; + } + + console.log(`Installing Debian ${arch} root image: ${sysroot}`); + fs.rmSync(sysroot, { recursive: true, force: true }); + fs.mkdirSync(sysroot); + const tarball = path.join(sysroot, tarballFilename); + console.log(`Downloading ${url}`); + let downloadSuccess = false; + for (let i = 0; i < 3 && !downloadSuccess; i++) { + await new Promise((c) => { + https.get(url, (res) => { + const chunks: Uint8Array[] = []; + res.on('data', (chunk) => { + chunks.push(chunk); + }); + res.on('end', () => { + fs.writeFileSync(tarball, Buffer.concat(chunks)); + downloadSuccess = true; + c(); + }); + }).on('error', (err) => { + console.error('Encountered an error during the download attempt: ' + err.message); + c(); + }); + }); + } + if (!downloadSuccess) { + throw new Error('Failed to download ' + url); + } + const sha = getSha(tarball); + if (sha !== tarballSha) { + throw new Error(`Tarball sha1sum is wrong. Expected ${tarballSha}, actual ${sha}`); + } + + const proc = spawnSync('tar', ['xf', tarball, '-C', sysroot]); + if (proc.status) { + throw new Error('Tarball extraction failed with code ' + proc.status); + } + fs.rmSync(tarball); + fs.writeFileSync(stamp, url); + return sysroot; +} diff --git a/build/linux/debian/sysroots.js b/build/linux/debian/sysroots.js new file mode 100644 index 0000000000..3825de4402 --- /dev/null +++ b/build/linux/debian/sysroots.js @@ -0,0 +1,26 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.sysrootInfo = void 0; +// Based on https://github.com/electron/electron/blob/main/script/sysroots.json, +// which itself is based on https://source.chromium.org/chromium/chromium/src/+/main:build/linux/sysroot_scripts/sysroots.json. +exports.sysrootInfo = { + 'amd64': { + 'Sha1Sum': '7e008cea9eae822d80d55c67fbb5ef4204678e74', + 'SysrootDir': 'debian_sid_amd64-sysroot', + 'Tarball': 'debian_sid_amd64_sysroot.tar.xz' + }, + 'armhf': { + 'Sha1Sum': 'b6f4bb07817bea91b06514a9c1e3832df5a90dbf', + 'SysrootDir': 'debian_sid_arm-sysroot', + 'Tarball': 'debian_sid_arm_sysroot.tar.xz' + }, + 'arm64': { + 'Sha1Sum': '5a56c1ef714154ea5003bcafb16f21b0f8dde023', + 'SysrootDir': 'debian_sid_arm64-sysroot', + 'Tarball': 'debian_sid_arm64_sysroot.tar.xz' + } +}; diff --git a/build/linux/debian/sysroots.ts b/build/linux/debian/sysroots.ts new file mode 100644 index 0000000000..8b21431f7e --- /dev/null +++ b/build/linux/debian/sysroots.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Based on https://github.com/electron/electron/blob/main/script/sysroots.json, +// which itself is based on https://source.chromium.org/chromium/chromium/src/+/main:build/linux/sysroot_scripts/sysroots.json. +export const sysrootInfo = { + 'amd64': { + 'Sha1Sum': '7e008cea9eae822d80d55c67fbb5ef4204678e74', + 'SysrootDir': 'debian_sid_amd64-sysroot', + 'Tarball': 'debian_sid_amd64_sysroot.tar.xz' + }, + 'armhf': { + 'Sha1Sum': 'b6f4bb07817bea91b06514a9c1e3832df5a90dbf', + 'SysrootDir': 'debian_sid_arm-sysroot', + 'Tarball': 'debian_sid_arm_sysroot.tar.xz' + }, + 'arm64': { + 'Sha1Sum': '5a56c1ef714154ea5003bcafb16f21b0f8dde023', + 'SysrootDir': 'debian_sid_arm64-sysroot', + 'Tarball': 'debian_sid_arm64_sysroot.tar.xz' + } +}; diff --git a/build/linux/debian/types.js b/build/linux/debian/types.js new file mode 100644 index 0000000000..56d4e6c56c --- /dev/null +++ b/build/linux/debian/types.js @@ -0,0 +1,6 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/src/vs/css.d.ts b/build/linux/debian/types.ts similarity index 86% rename from src/vs/css.d.ts rename to build/linux/debian/types.ts index e99b7c43cc..6797a56111 100644 --- a/src/vs/css.d.ts +++ b/build/linux/debian/types.ts @@ -3,3 +3,4 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +export type ArchString = 'amd64' | 'armhf' | 'arm64'; diff --git a/build/linux/libcxx-fetcher.js b/build/linux/libcxx-fetcher.js index b2a3e6556b..7877f82679 100644 --- a/build/linux/libcxx-fetcher.js +++ b/build/linux/libcxx-fetcher.js @@ -1,11 +1,11 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// Can be removed once https://github.com/electron/electron-rebuild/pull/703 is available. -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.downloadLibcxxObjects = exports.downloadLibcxxHeaders = void 0; +// Can be removed once https://github.com/electron/electron-rebuild/pull/703 is available. const debug = require("debug"); const extract = require("extract-zip"); const fs = require("fs-extra"); diff --git a/build/linux/libcxx-fetcher.ts b/build/linux/libcxx-fetcher.ts index 677c88c5cc..82be5a4727 100644 --- a/build/linux/libcxx-fetcher.ts +++ b/build/linux/libcxx-fetcher.ts @@ -5,8 +5,6 @@ // Can be removed once https://github.com/electron/electron-rebuild/pull/703 is available. -'use strict'; - import * as debug from 'debug'; import * as extract from 'extract-zip'; import * as fs from 'fs-extra'; diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index bc77d0936d..b912865c2c 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -75,10 +75,8 @@ exports.referenceGeneratedDepsByArch = { 'libgbm.so.1()(64bit)', 'libgcc_s.so.1()(64bit)', 'libgcc_s.so.1(GCC_3.0)(64bit)', - 'libgdk_pixbuf-2.0.so.0()(64bit)', 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', - 'libgmodule-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', 'libgtk-3.so.0()(64bit)', 'libm.so.6()(64bit)', @@ -160,10 +158,8 @@ exports.referenceGeneratedDepsByArch = { 'libgcc_s.so.1(GCC_3.0)', 'libgcc_s.so.1(GCC_3.4)', 'libgcc_s.so.1(GCC_3.5)', - 'libgdk_pixbuf-2.0.so.0', 'libgio-2.0.so.0', 'libglib-2.0.so.0', - 'libgmodule-2.0.so.0', 'libgobject-2.0.so.0', 'libgtk-3.so.0', 'libgtk-3.so.0()(64bit)', @@ -252,10 +248,8 @@ exports.referenceGeneratedDepsByArch = { 'libgcc_s.so.1(GCC_3.0)(64bit)', 'libgcc_s.so.1(GCC_4.2.0)(64bit)', 'libgcc_s.so.1(GCC_4.5.0)(64bit)', - 'libgdk_pixbuf-2.0.so.0()(64bit)', 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', - 'libgmodule-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', 'libgtk-3.so.0()(64bit)', 'libm.so.6()(64bit)', diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 07747a2723..0edd4758c0 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -75,10 +75,8 @@ export const referenceGeneratedDepsByArch = { 'libgbm.so.1()(64bit)', 'libgcc_s.so.1()(64bit)', 'libgcc_s.so.1(GCC_3.0)(64bit)', - 'libgdk_pixbuf-2.0.so.0()(64bit)', 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', - 'libgmodule-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', 'libgtk-3.so.0()(64bit)', 'libm.so.6()(64bit)', @@ -160,10 +158,8 @@ export const referenceGeneratedDepsByArch = { 'libgcc_s.so.1(GCC_3.0)', 'libgcc_s.so.1(GCC_3.4)', 'libgcc_s.so.1(GCC_3.5)', - 'libgdk_pixbuf-2.0.so.0', 'libgio-2.0.so.0', 'libglib-2.0.so.0', - 'libgmodule-2.0.so.0', 'libgobject-2.0.so.0', 'libgtk-3.so.0', 'libgtk-3.so.0()(64bit)', @@ -252,10 +248,8 @@ export const referenceGeneratedDepsByArch = { 'libgcc_s.so.1(GCC_3.0)(64bit)', 'libgcc_s.so.1(GCC_4.2.0)(64bit)', 'libgcc_s.so.1(GCC_4.5.0)(64bit)', - 'libgdk_pixbuf-2.0.so.0()(64bit)', 'libgio-2.0.so.0()(64bit)', 'libglib-2.0.so.0()(64bit)', - 'libgmodule-2.0.so.0()(64bit)', 'libgobject-2.0.so.0()(64bit)', 'libgtk-3.so.0()(64bit)', 'libm.so.6()(64bit)', diff --git a/build/linux/rpm/dependencies-generator.js b/build/linux/rpm/dependencies-generator.js index 1d39b14287..6b63258373 100644 --- a/build/linux/rpm/dependencies-generator.js +++ b/build/linux/rpm/dependencies-generator.js @@ -1,8 +1,8 @@ +"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDependencies = void 0; const child_process_1 = require("child_process"); @@ -81,7 +81,7 @@ function calculatePackageDeps(binaryPath) { const requires = new Set(findRequiresResult.stdout.toString('utf-8').trimEnd().split('\n')); return requires; } -// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py. function mergePackageDeps(inputDeps) { const requires = new Set(); for (const depSet of inputDeps) { diff --git a/build/linux/rpm/dependencies-generator.ts b/build/linux/rpm/dependencies-generator.ts index de41da6259..bb7db403be 100644 --- a/build/linux/rpm/dependencies-generator.ts +++ b/build/linux/rpm/dependencies-generator.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { spawnSync } from 'child_process'; import { constants, statSync } from 'fs'; import path = require('path'); @@ -94,7 +92,7 @@ function calculatePackageDeps(binaryPath: string): Set { return requires; } -// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py. function mergePackageDeps(inputDeps: Set[]): Set { const requires = new Set(); for (const depSet of inputDeps) { diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index f1ef829459..c2524ec0a2 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -63,7 +63,7 @@ declare namespace monaco.editor { export interface ICommandHandler { (...args: any[]): void; } -#include(vs/platform/contextkey/common/contextkey): IContextKey +#include(vs/platform/contextkey/common/contextkey): IContextKey, ContextKeyValue #include(vs/editor/standalone/browser/standaloneServices): IEditorOverrideServices #include(vs/platform/markers/common/markers): IMarker, IMarkerData, IRelatedInformation #include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions diff --git a/build/npm/dirs.js b/build/npm/dirs.js index 65fd2e1ea4..a45c19be8e 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -7,7 +7,6 @@ exports.dirs = [ '', 'build', - 'build/lib/watch', 'extensions', // {{SQL CARBON EDIT}} Add ADS extensions and remove VSCode ones 'extensions/admin-tool-ext-win', @@ -34,6 +33,7 @@ exports.dirs = [ 'extensions/json-language-features/server', 'extensions/kusto', 'extensions/machine-learning', + 'extensions/markdown-language-features/server', 'extensions/markdown-language-features', 'extensions/markdown-math', 'extensions/merge-conflict', diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 21882f74ac..82b77bf5b5 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -2,10 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + const cp = require('child_process'); -const path = require('path'); -const fs = require('fs'); const { dirs } = require('./dirs'); +const { setupBuildYarnrc } = require('./setupBuildYarnrc'); const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; /** @@ -47,9 +47,9 @@ for (let dir of dirs) { continue; } - if (dir === 'build/lib/watch') { - // node modules for watching, specific to host node version, not electron - yarnInstallBuildDependencies(); + if (dir === 'build') { + setupBuildYarnrc(); + yarnInstall('build'); continue; } @@ -73,23 +73,5 @@ for (let dir of dirs) { yarnInstall(dir, opts); } -function yarnInstallBuildDependencies() { - // make sure we install the deps of build/lib/watch for the system installed - // node, since that is the driver of gulp - const watchPath = path.join(path.dirname(__dirname), 'lib', 'watch'); - const yarnrcPath = path.join(watchPath, '.yarnrc'); - - const disturl = 'https://nodejs.org/download/release'; - const target = process.versions.node; - const runtime = 'node'; - - const yarnrc = `disturl "${disturl}" -target "${target}" -runtime "${runtime}"`; - - fs.writeFileSync(yarnrcPath, yarnrc, 'utf8'); - yarnInstall(watchPath); -} - cp.execSync('git config pull.rebase merges'); cp.execSync('git config blame.ignoreRevsFile .git-blame-ignore'); diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index 3a151261d4..880f02d362 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -9,10 +9,13 @@ const majorNodeVersion = parseInt(nodeVersion[1]); const minorNodeVersion = parseInt(nodeVersion[2]); const patchNodeVersion = parseInt(nodeVersion[3]); -if (majorNodeVersion < 14 || (majorNodeVersion === 14 && minorNodeVersion < 17) || (majorNodeVersion === 14 && minorNodeVersion === 17 && patchNodeVersion < 4) || majorNodeVersion >= 17) { - console.error('\033[1;31m*** Please use node.js versions >=14.17.4 and <17.\033[0;0m'); +if (majorNodeVersion < 16 || (majorNodeVersion === 16 && minorNodeVersion < 14)) { + console.error('\033[1;31m*** Please use node.js versions >=16.14.x and <17.\033[0;0m'); err = true; } +if (majorNodeVersion >= 17) { + console.warn('\033[1;31m*** Warning: Versions of node.js >= 17 have not been tested.\033[0;0m') +} const path = require('path'); const fs = require('fs'); diff --git a/build/npm/setupBuildYarnrc.js b/build/npm/setupBuildYarnrc.js new file mode 100644 index 0000000000..4e35564e7c --- /dev/null +++ b/build/npm/setupBuildYarnrc.js @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const fs = require('fs'); + +// make sure we install the deps of build for the system installed +// node, since that is the driver of gulp +function setupBuildYarnrc() { + const yarnrcPath = path.join(path.dirname(__dirname), '.yarnrc'); + const yarnrc = `disturl "https://nodejs.org/download/release" +target "${process.versions.node}" +runtime "node" +arch "${process.arch}"`; + + fs.writeFileSync(yarnrcPath, yarnrc, 'utf8'); +} + +exports.setupBuildYarnrc = setupBuildYarnrc; + +if (require.main === module) { + setupBuildYarnrc(); +} diff --git a/build/package-lock.json b/build/package-lock.json new file mode 100644 index 0000000000..6177bbf591 --- /dev/null +++ b/build/package-lock.json @@ -0,0 +1,7877 @@ +{ + "name": "azuredatastudio-oss-dev-build", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "azuredatastudio-oss-dev-build", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@azure/cosmos": "^3.14.1", + "@azure/identity": "^2.1.0", + "@azure/storage-blob": "^12.8.0", + "@electron/get": "^1.12.4", + "@types/ansi-colors": "^3.2.0", + "@types/azure": "0.9.19", + "@types/byline": "^4.2.32", + "@types/cssnano": "^4.0.0", + "@types/debounce": "^1.0.0", + "@types/debug": "4.1.5", + "@types/documentdb": "^1.10.5", + "@types/eslint": "4.16.1", + "@types/eslint-visitor-keys": "^1.0.0", + "@types/fancy-log": "^1.3.1", + "@types/fs-extra": "^9.0.12", + "@types/glob": "^7.1.1", + "@types/gulp": "^4.0.10", + "@types/gulp-concat": "^0.0.32", + "@types/gulp-filter": "^3.0.35", + "@types/gulp-gzip": "^0.0.31", + "@types/gulp-json-editor": "^2.2.31", + "@types/gulp-postcss": "^8.0.0", + "@types/gulp-rename": "^0.0.33", + "@types/gulp-sourcemaps": "^0.0.32", + "@types/mime": "0.0.29", + "@types/minimatch": "^3.0.3", + "@types/minimist": "^1.2.1", + "@types/mkdirp": "^1.0.1", + "@types/mocha": "^9.1.1", + "@types/node": "16.x", + "@types/p-limit": "^2.2.0", + "@types/plist": "^3.0.2", + "@types/pump": "^1.0.1", + "@types/request": "^2.47.0", + "@types/rimraf": "^2.0.4", + "@types/through": "^0.0.29", + "@types/through2": "^2.0.34", + "@types/tmp": "^0.2.1", + "@types/underscore": "^1.8.9", + "@types/webpack": "^4.41.25", + "@types/xml2js": "0.0.33", + "@typescript-eslint/experimental-utils": "~2.13.0", + "@typescript-eslint/parser": "^5.10.0", + "@vscode/iconv-lite-umd": "0.7.0", + "applicationinsights": "1.0.8", + "azure-storage": "^2.1.0", + "byline": "^5.0.0", + "colors": "^1.4.0", + "commander": "^7.0.0", + "debug": "^4.3.2", + "documentdb": "1.13.0", + "electron-osx-sign": "^0.4.16", + "esbuild": "^0.12.6", + "extract-zip": "^2.0.1", + "fs-extra": "^9.1.0", + "got": "11.8.5", + "gulp-merge-json": "^2.1.1", + "iconv-lite-umd": "0.6.8", + "jsonc-parser": "^2.3.0", + "mime": "^1.4.1", + "mkdirp": "^1.0.4", + "node-fetch": "2", + "p-limit": "^3.1.0", + "plist": "^3.0.5", + "rollup": "^1.20.3", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-node-resolve": "^5.2.0", + "source-map": "0.6.1", + "tmp": "^0.2.1", + "typescript": "^4.5.0-dev.20210817", + "vsce": "2.8.0", + "vscode-universal-bundler": "^0.0.2" + }, + "optionalDependencies": { + "tree-sitter": "https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz", + "tree-sitter-typescript": "^0.20.1", + "vscode-gulp-watch": "^5.0.3" + } + }, + "node_modules/@azure/abort-controller": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.2.tgz", + "integrity": "sha512-XUyTo+bcyxHEf+jlN2MXA7YU9nxVehaubngHV1MIZZaqYmZqykkoeAz/JMMEeR7t3TcyDwbFa3Zw8BZywmIx4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@azure/abort-controller/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@azure/core-asynciterator-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", + "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.6.1.tgz", + "integrity": "sha512-mZ1MSKhZBYoV8GAWceA+PEJFWV2VpdNSpxxcj1wjIAOi00ykRuIQChT99xlQGZWLY3/NApWhSImlFwsmCEs4vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-http": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-2.2.7.tgz", + "integrity": "sha512-TyGMeDm90mkRS8XzSQbSMD+TqnWL1XKGCh0x0QVGMD8COH2yU0q5SaHm/IBEBkzcq0u73NhS/p57T3KVSgUFqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.0", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tough-cookie": "^4.0.0", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.4.19" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-http/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.3.1.tgz", + "integrity": "sha512-nQ+Xnm9g1EWcmbqgxJGmkNHfOHRUmrbYIlRT4KjluzhHQooaGO55m/h6wCX0ho3Jte2ZNBzZPJRmi6yBWeb3yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz", + "integrity": "sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/core-asynciterator-polyfill": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.9.2.tgz", + "integrity": "sha512-8rXI6ircjenaLp+PkOFpo37tQ1PQfztZkfVj97BIF3RPxHAsoVSgkJtu3IK/bUEWcb7HzXSoyBe06M7ODRkRyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.0.tgz", + "integrity": "sha512-+i93lNJNA3Pl3KSuC6xKP2jTL4YFeDfO6VNOaYdk0cppZcLCxt811gS878VsqsCisaltdhl9lhMzK5kbxCiF4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/cosmos": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.17.1.tgz", + "integrity": "sha512-3pgPwNwAiTgiH/OgcntDLzrANy+roaaDFYoLOhC4bxoDC94nPCjpLYRRwueIpisZAdopPVrxQloNs9fEjVlL0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure/core-rest-pipeline": "^1.2.0", + "@azure/core-tracing": "^1.0.0", + "debug": "^4.1.1", + "fast-json-stable-stringify": "^2.1.0", + "jsbi": "^3.1.3", + "node-abort-controller": "^3.0.0", + "priorityqueuejs": "^1.0.0", + "semaphore": "^1.0.5", + "tslib": "^2.2.0", + "universal-user-agent": "^6.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/cosmos/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@azure/cosmos/node_modules/semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/identity": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-2.1.0.tgz", + "integrity": "sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^2.26.0", + "@azure/msal-common": "^7.0.0", + "@azure/msal-node": "^1.10.0", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.1.tgz", + "integrity": "sha512-QYQeaJ+A5x6aMNu8BG5qdsVBnYBop9UMwgUvGihSjf1PdZZXB+c/oMdM2ajKwzobLBh9e9QuMQkN9iL+IxLBLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@azure/logger/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@azure/msal-browser": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.28.3.tgz", + "integrity": "sha512-2SdyH2el3s8BzPURf9RK17BvvXvaMEGpLc3D9WilZcmjJqP4nStVH7Ogwr/SNTuGV48FUhqEkP0RxDvzuFJSIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "^7.4.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.4.1.tgz", + "integrity": "sha512-zxcxg9pRdgGTS5mrRJeQvwA8aIjD8qSGzaAiz5SeTVkyhtjB0AeFnAcvBOKHv/TkswWNfYKpERxsXOAKXkXk0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.14.0.tgz", + "integrity": "sha512-3XB7FuHLhmGBjw7bxuz1LCHOXQgmNIO3J56tlbOjuJcyJtd4aBCgnYIXNKLed3uRcQNHEO0mlg24I4iGxAV/UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "^7.4.1", + "jsonwebtoken": "^8.5.1", + "uuid": "^8.3.0" + }, + "engines": { + "node": "10 || 12 || 14 || 16 || 18" + } + }, + "node_modules/@azure/msal-node/node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/@azure/msal-node/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@azure/msal-node/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.11.0.tgz", + "integrity": "sha512-na+FisoARuaOWaHWpmdtk3FeuTWf2VWamdJ9/TJJzj5ZdXPLC3juoDgFs6XVuJIoK30yuBpyFBEDXVRK4pB7Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^2.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/storage-blob/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/get": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz", + "integrity": "sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^9.6.0", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=8.6" + }, + "optionalDependencies": { + "global-agent": "^2.0.2", + "global-tunnel-ng": "^2.7.1" + } + }, + "node_modules/@electron/get/node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@electron/get/node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@electron/get/node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/get/node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/get/node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/get/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@electron/get/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@electron/get/node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@electron/get/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@electron/get/node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@electron/get/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/get/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/get/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/@electron/get/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@electron/get/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/get/node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@electron/get/node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true, + "peer": true + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", + "integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/ansi-colors": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.0.tgz", + "integrity": "sha512-0caWAhXht9N2lOdMzJLXybsSkYCx1QOdxx6pae48tswI9QV3DFX26AoOpy0JxwhCb+zISTqmd6H8t9Zby9BoZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/azure": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@types/azure/-/azure-0.9.19.tgz", + "integrity": "sha1-Gmqb2Fa0N93s8/n8hAemg8hpugI=", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/azure/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/byline": { + "version": "4.2.32", + "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.32.tgz", + "integrity": "sha512-qtlm/J6XOO9p+Ep/ZB5+mCFEDhzWDDHWU4a1eReN7lkPZXW9rkloq2jcAhvKKmlO5tL2GSvKROb+PTsNVhBiyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/byline/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/cacheable-request/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/caseless": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz", + "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cssnano": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/cssnano/-/cssnano-4.0.1.tgz", + "integrity": "sha512-hGOroxRTBkYl5gSBRJOffhV4+io+Y2bFX1VP7LgKEVHJt/LPPJaWUIuDAz74Vlp7l7hCDZfaDi7iPxwNwuVA4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "5 - 7" + } + }, + "node_modules/@types/debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.0.0.tgz", + "integrity": "sha1-QXVgIAMx4buE1y2oU5EQLC/NYbc=", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/documentdb": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@types/documentdb/-/documentdb-1.10.8.tgz", + "integrity": "sha512-GkOXovVMlMVTYkPomq9rOI79DmVOMZ0TDziL3H3TSlhUSm1/txi5qA49H/qZRDFsExagjnf5Cd/4xF8mXVxubw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/documentdb/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-4.16.1.tgz", + "integrity": "sha512-lRUXQAULl5geixTiP2K0iYvMUbCkEnuOwvLGjwff12I4ECxoW5QaWML5UUOZ1CvpQLILkddBdMPMZz4ByQizsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.41.tgz", + "integrity": "sha512-rIAmXyJlqw4KEBO7+u9gxZZSQHaCNnIzYrnNmYVpgfJhxTqO0brCX0SYpqUTkVI5mwwUwzmtspLBGBKroMeynA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fancy-log": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.1.tgz", + "integrity": "sha512-31Dt9JaGfHretvwVxCBrCFL5iC9MQ3zOXpu+8C4qzW0cxc5rJJVGxB5c/vZ+wmeTk/JjPz/D0gv8BZ+Ip6iCqQ==", + "dev": true + }, + "node_modules/@types/form-data": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", + "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/fs-extra": { + "version": "9.0.12", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz", + "integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/fs-extra/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-RHv6ZQjcTncXo3thYZrsbAVwoy4vSKosSWhuhuQxLOTv74OJuFQxXkmUuZCr3q9uNBEVCvIzmZL/FeRNbHZGUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "node_modules/@types/glob-stream/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/glob/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.10.tgz", + "integrity": "sha512-spgZHJFqiEJGwqGlf7T/k4nkBpBcLgP7T0EfN6G2vvnhUfvd4uO1h8RwpXOE8x/54DVYUs1XCAtBHkX/R3axAQ==", + "dev": true, + "dependencies": { + "@types/undertaker": ">=1.2.6", + "@types/vinyl-fs": "*", + "chokidar": "^3.3.1" + } + }, + "node_modules/@types/gulp-concat": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/gulp-concat/-/gulp-concat-0.0.32.tgz", + "integrity": "sha512-CUCFADlITzzBfBa2bdGzhKtvBr4eFh+evb+4igVbvPoO5RyPfHifmyQlZl6lM7q19+OKncRlFXDU7B4X9Ayo2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/gulp-concat/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp-filter": { + "version": "3.0.35", + "resolved": "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.35.tgz", + "integrity": "sha512-xBdmHVMnPvAdwyLDFD2Yi8lYXXhYlMWnsWxvMPiinj8b7LJmzNKloI4TQyod+WXEfbiUsHULp0q0qdCIXBVRDQ==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "node_modules/@types/gulp-gzip": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@types/gulp-gzip/-/gulp-gzip-0.0.31.tgz", + "integrity": "sha512-KQjHz1FTqLse8/EiktfhN/vo33vamX4gVAQhMTp55STDBA7UToW5CJqYyP7iRorkHK9/aJ2nL2hLkNZkY4M8+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/gulp-gzip/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp-json-editor": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/@types/gulp-json-editor/-/gulp-json-editor-2.2.31.tgz", + "integrity": "sha512-piis0ImYAy0dt18R4EtTbAY+RV8jwTq5VisnUV6OfP8kD4743aHGkAdAd08No4NY3rFa5mD6ytIu8L0YU7nL9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/js-beautify": "*", + "@types/node": "*" + } + }, + "node_modules/@types/gulp-json-editor/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp-postcss": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/gulp-postcss/-/gulp-postcss-8.0.0.tgz", + "integrity": "sha512-AVgjA03bpkYONKZpzuJviB9PzaNbDzrovYPbenj8/XxivUc35C/dIzJanyaQv7CFqfLLPLsqSalmtP3GLq6iag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "node_modules/@types/gulp-postcss/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp-rename": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/gulp-rename/-/gulp-rename-0.0.33.tgz", + "integrity": "sha512-FIZQvbZJj6V1gHPTzO+g/BCWpDur7fJrroae4gwV3LaoHBQ+MrR9sB+2HssK8fHv4WdY6hVNxkcft9bYatuPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/gulp-rename/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp-sourcemaps": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.32.tgz", + "integrity": "sha512-+7BAmptW2bxyJnJcCEuie7vLoop3FwWgCdBMzyv7MYXED/HeNMeQuX7uPCkp4vfU1TTu4CYFH0IckNPvo0VePA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/gulp-sourcemaps/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/js-beautify": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/js-beautify/-/js-beautify-1.8.0.tgz", + "integrity": "sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/keyv/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-0.0.29.tgz", + "integrity": "sha1-+8/TMFc7kS71nu7hRgK/rOYwdUs=", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mkdirp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", + "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mkdirp/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "16.11.62", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.62.tgz", + "integrity": "sha512-K/ggecSdwAAy2NUW4WKmF4Rc03GKbsfP+k326UWgckoS+Rzd2PaWbjk76dSmqdLQvLTJAO9axiTUJ6488mFsYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node-fetch": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", + "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-fGFbybl1r0oE9mqgfc2EHHUin9ZL5rbQIexWI6jYRU1ADVn4I3LHzT+g/kpPpZsfp8PB94CQ655pfAjNF8LP6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "*" + } + }, + "node_modules/@types/plist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", + "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/plist/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/plist/node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@types/pump": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.0.1.tgz", + "integrity": "sha1-roFXzv7wTRpNJMHMkdQDwvXaXNA=", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pump/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/request": { + "version": "2.47.0", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz", + "integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/caseless": "*", + "@types/form-data": "*", + "@types/node": "*", + "@types/tough-cookie": "*" + } + }, + "node_modules/@types/request/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/resolve/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/responselike/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/rimraf": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.4.tgz", + "integrity": "sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "node_modules/@types/rimraf/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tapable": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz", + "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/through": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz", + "integrity": "sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/through/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/through2": { + "version": "2.0.34", + "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.34.tgz", + "integrity": "sha512-nhRG8+RuG/L+0fAZBQYaRflXKjTrHOKH8MFTChnf+dNVMxA3wHYYrfj0tztK0W51ABXjGfRCDc0vRkecCOrsow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/through2/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-7cTXwKP/HLOPVgjg+YhBdQ7bMiobGMuoBmrGmqwIWJv8elC6t1DfVc/mn4fD9UE1IjhwmhaQ5pGVXkmXbH0rhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tunnel/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/@types/underscore": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.8.9.tgz", + "integrity": "sha512-vfzZGgZKRFy7KEWcBGfIFk+h6B+thDCLfkD1exMBMRlUsx2icA+J6y4kAbZs/TjSTeY1duw89QUU133TSzr60Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/undertaker": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.8.tgz", + "integrity": "sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/undertaker-registry": "*", + "async-done": "~1.3.2" + } + }, + "node_modules/@types/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", + "dev": true + }, + "node_modules/@types/vinyl": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz", + "integrity": "sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/vinyl-fs": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.9.tgz", + "integrity": "sha512-Q0EXd6c1fORjiOuK4ZaKdfFcMyFzJlTi56dqktwaWVLIDAzE49wUs3bKnYbZwzyMWoH+NcMWnRuR73S9A0jnRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/events": "*", + "@types/glob-stream": "*", + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "node_modules/@types/vinyl-fs/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vinyl/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/webpack": { + "version": "4.41.30", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.30.tgz", + "integrity": "sha512-GUHyY+pfuQ6haAfzu4S14F+R5iGRwN6b2FRNJY7U0NilmFAqbsOfK6j1HwuLBAqwRIT+pVdNDJGJ6e8rpp0KHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tapable": "^1", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "anymatch": "^3.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/webpack-sources": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.1.tgz", + "integrity": "sha512-MjM1R6iuw8XaVbtkCBz0N349cyqBjJHCbQiOeppe3VBeFvxqs74RKHAVt9LkxTnUWc7YLZOEsUfPUnmK6SBPKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "node_modules/@types/webpack-sources/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/webpack-sources/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/webpack/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/webpack/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/xml2js": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.0.33.tgz", + "integrity": "sha1-IMXdZGAkUoTWSlVpABW5XkCft94=", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", + "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz", + "integrity": "sha512-+Hss3clwa6aNiC8ZjA45wEm4FutDV5HsVXPl/rDug1THq6gEtOYRGLqS3JlTk7mSnL5TbJz0LpEbzbPnKvY6sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.13.0", + "eslint-scope": "^5.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.1.tgz", + "integrity": "sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.38.1", + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/typescript-estree": "5.38.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz", + "integrity": "sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/visitor-keys": "5.38.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@typescript-eslint/parser/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz", + "integrity": "sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/visitor-keys": "5.38.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.1.tgz", + "integrity": "sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz", + "integrity": "sha512-t21Mg5cc8T3ADEUGwDisHLIubgXKjuNRbkpzDMLb7/JMmgCe/gHM9FaaujokLey+gwTuLF5ndSQ7/EfQqrQx4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash.unescape": "4.0.1", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz", + "integrity": "sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.38.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.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==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/applicationinsights": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.8.tgz", + "integrity": "sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "diagnostic-channel": "0.2.0", + "diagnostic-channel-publishers": "0.2.1", + "zone.js": "0.7.6" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asar": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/asar/-/asar-3.0.3.tgz", + "integrity": "sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chromium-pickle-js": "^0.2.0", + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + }, + "optionalDependencies": { + "@types/glob": "^7.1.1" + } + }, + "node_modules/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/azure-devops-node-api": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.1.1.tgz", + "integrity": "sha512-XDG91XzLZ15reP12s3jFkKS8oiagSICjnLwxEYieme4+4h3ZveFOFRA4iYIG40RyHXsiI0mefFYYMFIJbMpWcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/azure-storage": { + "version": "2.10.7", + "resolved": "https://registry.npmjs.org/azure-storage/-/azure-storage-2.10.7.tgz", + "integrity": "sha512-4oeFGtn3Ziw/fGs/zkoIpKKtygnCVIcZwzJ7UQzKTxhkGQqVCByOFbYqMGYR3L+wOsunX9lNfD0jc51SQuKSSA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "browserify-mime": "^1.2.9", + "extend": "^3.0.2", + "json-edm-parser": "~0.1.2", + "json-schema": "~0.4.0", + "md5.js": "^1.3.4", + "readable-stream": "^2.0.0", + "request": "^2.86.0", + "underscore": "^1.12.1", + "uuid": "^3.0.0", + "validator": "^13.7.0", + "xml2js": "~0.2.8", + "xmlbuilder": "^9.0.7" + }, + "engines": { + "node": ">= 0.8.26" + } + }, + "node_modules/azure-storage/node_modules/sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true, + "license": "BSD" + }, + "node_modules/azure-storage/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/azure-storage/node_modules/xml2js": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", + "integrity": "sha512-ZHZBIAO55GHCn2jBYByVPHvHS+o3j8/a/qmpEe6kxO3cTnTCWC3Htq9RYJ5G4XMwMMClD2QkXA9SNdPadLyn3Q==", + "dev": true, + "dependencies": { + "sax": "0.5.x" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/binary-search-bounds": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.3.tgz", + "integrity": "sha1-X/hhbW3SylOIvIWy1iZuK52lAtw=", + "dev": true, + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true, + "license": "ISC" + }, + "node_modules/boolean": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz", + "integrity": "sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserify-mime": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/browserify-mime/-/browserify-mime-1.2.9.tgz", + "integrity": "sha512-uz+ItyJXBLb6wgon1ELEiVowJBEsy03PUWGRQU7cxxx9S+DW2hujPp+DaMYEOClRPzsn7NB99NtJ6pGnt8y+CQ==", + "dev": true + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.11", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.11.tgz", + "integrity": "sha512-bQwNaDIBKID5ts/DsdhxrjqFXYfLw4ste+wMKqWA8DyKcS4qwsPP4Bk8ZNaTJjvpiX/qW3BT4sU7d6Bh5i+dag==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=", + "dev": true, + "license": "MIT" + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "devOptional": true, + "license": "ISC" + }, + "node_modules/core-js": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz", + "integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "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, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/diagnostic-channel": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz", + "integrity": "sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.3.0" + } + }, + "node_modules/diagnostic-channel-publishers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz", + "integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM=", + "dev": true, + "license": "MIT", + "peerDependencies": { + "diagnostic-channel": "*" + } + }, + "node_modules/dir-compare": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz", + "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "1.0.0", + "colors": "1.0.3", + "commander": "2.9.0", + "minimatch": "3.0.4" + }, + "bin": { + "dircompare": "src/cli/dircompare.js" + } + }, + "node_modules/dir-compare/node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/dir-compare/node_modules/commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/documentdb": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/documentdb/-/documentdb-1.13.0.tgz", + "integrity": "sha1-u6bwMVCy9CSYzsQmG8Q52DSjP4s=", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-search-bounds": "2.0.3", + "priorityqueuejs": "1.0.0", + "semaphore": "1.0.5", + "underscore": "1.8.3" + } + }, + "node_modules/documentdb/node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "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==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-osx-sign": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.17.tgz", + "integrity": "sha512-wUJPmZJQCs1zgdlQgeIpRcvrf7M5/COQaOV68Va1J/SgmWx5KL2otgg+fAae7luw6qz9R8Gvu/Qpe9tAOu/3xQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^3.0.1" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/electron-osx-sign/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/electron-osx-sign/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz", + "integrity": "sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.12.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.6.tgz", + "integrity": "sha512-RDvVLvAjsq/kIZJoneMiUOH7EE7t2QaW7T3Q7EdQij14+bZbDq5sndb0tTanmHIFSqZVMBMMyqzVHkS3dJobeA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz", + "integrity": "sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esquery": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", + "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "devOptional": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true, + "peer": true + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "devOptional": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-agent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", + "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "core-js": "^3.6.5", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true, + "license": "ISC" + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true, + "license": "MIT" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true, + "peer": true + }, + "node_modules/gulp-merge-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/gulp-merge-json/-/gulp-merge-json-2.1.2.tgz", + "integrity": "sha512-FysBAdHdnQvZzigVJJzlrt6TEosHxVb0mR2h/8eSnd+eJyBvb1LQF1EIrovrOCfj4HGE5p/95wGEjXsJk9qomw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.1", + "lodash.mergewith": "^4.6.1", + "plugin-error": "^1.0.1", + "through": "^2.3.8", + "vinyl": "^2.2.1" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "devOptional": true, + "license": "ISC" + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/iconv-lite-umd": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz", + "integrity": "sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "devOptional": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "license": "MIT" + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "license": "MIT", + "optional": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-alloc": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true, + "license": "MIT" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbi": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", + "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-edm-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/json-edm-parser/-/json-edm-parser-0.1.2.tgz", + "integrity": "sha512-J1U9mk6lf8dPULcaMwALXB6yel3cJyyhk9Z8FQ4sMwiazNwjaUhegIcpZyZFNMvLRtnXwh+TkCjX9uYUObBBYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "~1.2.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/jsonparse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", + "integrity": "sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keytar/node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/keytar/node_modules/node-abi": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/keytar/node_modules/prebuild-install": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.0.tgz", + "integrity": "sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/keytar/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/keytar/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" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, + "node_modules/lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/matcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.46.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "license": "MIT", + "optional": 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, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "peer": true + }, + "node_modules/node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/node-abort-controller": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz", + "integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-conf/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "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==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz", + "integrity": "sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "devOptional": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.3.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1", + "xmlbuilder": "^9.0.7" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/plugin-error/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/priorityqueuejs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz", + "integrity": "sha1-LuTyPCVgkT4IwHzlzN1t498sWvg=", + "dev": true, + "license": "MIT" + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "devOptional": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rollup": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz", + "integrity": "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/node": "*", + "acorn": "^7.1.0" + }, + "bin": { + "rollup": "dist/bin/rollup" + } + }, + "node_modules/rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + }, + "peerDependencies": { + "rollup": ">=1.12.0" + } + }, + "node_modules/rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + }, + "peerDependencies": { + "rollup": ">=1.11.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup/node_modules/@types/node": { + "version": "8.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz", + "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "license": "ISC" + }, + "node_modules/semaphore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.0.5.tgz", + "integrity": "sha1-tJJXbmavGT25XWXiXsU/Xxl5jWA=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "devOptional": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "devOptional": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", + "license": "MIT", + "optional": true, + "dependencies": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "peer": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true, + "license": "MIT" + }, + "node_modules/tree-sitter": { + "version": "0.20.0", + "resolved": "https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz", + "integrity": "sha512-FfyATFV6xANETqV0izjt/iC76yxFM8oGe5IfeyGfqXxRTsKKs2IOSGx1LTa6guOL3M2SagIQShAkHkJ4sK5/eA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "nan": "^2.14.0", + "prebuild-install": "^6.0.1" + } + }, + "node_modules/tree-sitter-typescript": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.20.1.tgz", + "integrity": "sha512-wqpnhdVYX26ATNXeZtprib4+mF2GlYQB1cjRPibYGxDRiugx5OfjWwLE4qPPxEGdp2ZLSmZVesGUjLWzfKo6rA==", + "license": "MIT", + "optional": true, + "dependencies": { + "nan": "^2.14.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz", + "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz", + "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/extsprintf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz", + "integrity": "sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg==", + "license": "MIT", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "strip-bom-buf": "^1.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vinyl-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "license": "ISC", + "optional": true + }, + "node_modules/vsce": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.8.0.tgz", + "integrity": "sha512-p6BTbUVp33Ed0OWRRhRQT55yrmgLEca2fTmqxZJW44T1eP4yVWEsdaNIDsjFIeuCrjG/CYvwi1QLG4ql0s7bDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "keytar": "^7.7.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.4.23", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/vsce/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/vsce/node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/vscode-gulp-watch": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vscode-gulp-watch/-/vscode-gulp-watch-5.0.3.tgz", + "integrity": "sha512-MTUp2yLE9CshhkNSNV58EQNxQSeF8lIj3mkXZX9a1vAk+EQNM2PAYdPUDSd/P/08W3PMHGznEiZyfK7JAjLosg==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-colors": "4.1.1", + "anymatch": "^3.1.1", + "chokidar": "3.5.1", + "fancy-log": "^1.3.3", + "glob-parent": "^5.1.1", + "normalize-path": "^3.0.0", + "object-assign": "^4.1.1", + "plugin-error": "1.0.1", + "readable-stream": "^3.6.0", + "vinyl": "^2.2.0", + "vinyl-file": "^3.0.0" + } + }, + "node_modules/vscode-gulp-watch/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/vscode-gulp-watch/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/vscode-universal-bundler": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/vscode-universal-bundler/-/vscode-universal-bundler-0.0.2.tgz", + "integrity": "sha512-FPJcvKnQGBqFzy6M6Nm2yvAczNLUeXsfYM6GwCex/pUOkvIM2icIHmiSvtMJINlLW1iG+oEwE3/LVbABmcjEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^1.1.0", + "asar": "^3.0.3", + "debug": "^4.3.1", + "dir-compare": "^2.4.0", + "fs-extra": "^9.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/vscode-universal-bundler/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "devOptional": true, + "license": "ISC" + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zone.js": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz", + "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=", + "dev": true, + "license": "MIT" + } + } +} diff --git a/build/package.json b/build/package.json index 5ecfc0001e..92a0e46d09 100644 --- a/build/package.json +++ b/build/package.json @@ -17,12 +17,12 @@ "@types/documentdb": "^1.10.5", "@types/eslint": "4.16.1", "@types/eslint-visitor-keys": "^1.0.0", - "@types/fancy-log": "^1.3.0", + "@types/fancy-log": "^1.3.1", "@types/fs-extra": "^9.0.12", "@types/glob": "^7.1.1", - "@types/gulp": "^4.0.5", + "@types/gulp": "^4.0.10", "@types/gulp-concat": "^0.0.32", - "@types/gulp-filter": "^3.0.32", + "@types/gulp-filter": "^3.0.35", "@types/gulp-gzip": "^0.0.31", "@types/gulp-json-editor": "^2.2.31", "@types/gulp-postcss": "^8.0.0", @@ -47,22 +47,24 @@ "@types/xml2js": "0.4.11", "@typescript-eslint/experimental-utils": "~2.13.0", "@typescript-eslint/parser": "^5.10.0", + "@vscode/iconv-lite-umd": "0.7.0", "applicationinsights": "1.0.8", "byline": "^5.0.0", "colors": "^1.4.0", "commander": "^7.0.0", "debug": "^4.3.2", + "documentdb": "1.13.0", "electron-osx-sign": "^0.4.16", "esbuild": "^0.12.6", "extract-zip": "^2.0.1", "fs-extra": "^9.1.0", - "documentdb": "1.13.0", "got": "11.8.5", "gulp-merge-json": "^2.1.1", "iconv-lite-umd": "0.6.8", "jsonc-parser": "^2.3.0", "mime": "^1.4.1", "mkdirp": "^1.0.4", + "node-fetch": "2", "p-limit": "^3.1.0", "plist": "^3.0.5", "rollup": "^1.20.3", @@ -70,7 +72,8 @@ "rollup-plugin-node-resolve": "^5.2.0", "source-map": "0.6.1", "tmp": "^0.2.1", - "typescript": "^4.5.0-dev.20210817", + "typescript": "^4.8.0-dev.20220518", + "vsce": "2.8.0", "vscode-universal-bundler": "^0.0.2" }, "scripts": { @@ -78,7 +81,11 @@ "watch": "tsc -p tsconfig.build.json --watch", "npmCheckJs": "tsc --noEmit" }, - "dependencies": {}, + "optionalDependencies": { + "tree-sitter": "https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz", + "tree-sitter-typescript": "^0.20.1", + "vscode-gulp-watch": "^5.0.3" + }, "resolutions": { "json-schema": "0.4.0", "jsonwebtoken": "9.0.0" diff --git a/build/tsconfig.json b/build/tsconfig.json index 290dc2792c..17f030c386 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "es2017", "module": "commonjs", + "alwaysStrict": true, "removeComments": false, "preserveConstEnums": true, "sourceMap": false, diff --git a/build/win32/code.iss b/build/win32/code.iss index 204a0902b5..facbc7beb8 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -346,7 +346,7 @@ begin Permissions := '/grant:r "*S-1-5-18:(OI)(CI)F" /grant:r "*S-1-5-32-544:(OI)(CI)F" /grant:r "*S-1-5-11:(OI)(CI)RX" /grant:r "*S-1-5-32-545:(OI)(CI)RX"'; #if "user" == InstallTarget - Permissions := Permissions + ' /grant:r "*S-1-3-0:(OI)(CI)F"'; + Permissions := Permissions + Format(' /grant:r "*S-1-3-0:(OI)(CI)F" /grant:r "%s:(OI)(CI)F"', [GetUserNameString()]); #endif Exec(ExpandConstant('{sys}\icacls.exe'), ExpandConstant('"{app}" /inheritancelevel:r ') + Permissions, '', SW_HIDE, ewWaitUntilTerminated, ResultCode); diff --git a/build/yarn.lock b/build/yarn.lock index b93c1954cd..086cadee94 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -4,19 +4,19 @@ "@azure/abort-controller@^1.0.0": version "1.0.2" - resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.2.tgz#822405c966b2aec16fb62c1b19d37eaccf231995" + resolved "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.2.tgz" integrity sha512-XUyTo+bcyxHEf+jlN2MXA7YU9nxVehaubngHV1MIZZaqYmZqykkoeAz/JMMEeR7t3TcyDwbFa3Zw8BZywmIx4g== dependencies: tslib "^2.0.0" "@azure/core-asynciterator-polyfill@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" + resolved "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz" integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== "@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.4.0.tgz#6fa9661c1705857820dbc216df5ba5665ac36a9e" + resolved "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz" integrity sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ== dependencies: "@azure/abort-controller" "^1.0.0" @@ -24,7 +24,7 @@ "@azure/core-client@^1.4.0": version "1.6.1" - resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.6.1.tgz#a1aad3f7c69b6e5d9dddb39fabaeba013eac9313" + resolved "https://registry.npmjs.org/@azure/core-client/-/core-client-1.6.1.tgz" integrity sha512-mZ1MSKhZBYoV8GAWceA+PEJFWV2VpdNSpxxcj1wjIAOi00ykRuIQChT99xlQGZWLY3/NApWhSImlFwsmCEs4vA== dependencies: "@azure/abort-controller" "^1.0.0" @@ -57,7 +57,7 @@ "@azure/core-lro@^2.2.0": version "2.3.1" - resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.3.1.tgz#c8270b2785ea98c793af28ed106a470650859049" + resolved "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.3.1.tgz" integrity sha512-nQ+Xnm9g1EWcmbqgxJGmkNHfOHRUmrbYIlRT4KjluzhHQooaGO55m/h6wCX0ho3Jte2ZNBzZPJRmi6yBWeb3yA== dependencies: "@azure/abort-controller" "^1.0.0" @@ -66,14 +66,14 @@ "@azure/core-paging@^1.1.1": version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.1.3.tgz#3587c9898a0530cacb64bab216d7318468aa5efc" + resolved "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz" integrity sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A== dependencies: "@azure/core-asynciterator-polyfill" "^1.0.0" "@azure/core-rest-pipeline@^1.1.0", "@azure/core-rest-pipeline@^1.2.0", "@azure/core-rest-pipeline@^1.9.1": version "1.9.2" - resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.9.2.tgz#47ee72ca96e2b82e3d1362c29fd7688dc7464527" + resolved "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.9.2.tgz" integrity sha512-8rXI6ircjenaLp+PkOFpo37tQ1PQfztZkfVj97BIF3RPxHAsoVSgkJtu3IK/bUEWcb7HzXSoyBe06M7ODRkRyw== dependencies: "@azure/abort-controller" "^1.0.0" @@ -89,7 +89,7 @@ "@azure/core-tracing@1.0.0-preview.13": version "1.0.0-preview.13" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz#55883d40ae2042f6f1e12b17dd0c0d34c536d644" + resolved "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz" integrity sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ== dependencies: "@opentelemetry/api" "^1.0.1" @@ -97,14 +97,14 @@ "@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" + resolved "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" "@azure/core-util@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.0.tgz#36736b274e9abee4b6cc6e4d162683b4e1e3db52" + resolved "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.0.tgz" integrity sha512-+i93lNJNA3Pl3KSuC6xKP2jTL4YFeDfO6VNOaYdk0cppZcLCxt811gS878VsqsCisaltdhl9lhMzK5kbxCiF4w== dependencies: tslib "^2.2.0" @@ -119,7 +119,7 @@ "@azure/cosmos@^3.14.1": version "3.17.1" - resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.17.1.tgz#10a654f59720681adad670b49c1f3a3ccf3e13d4" + resolved "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.17.1.tgz" integrity sha512-3pgPwNwAiTgiH/OgcntDLzrANy+roaaDFYoLOhC4bxoDC94nPCjpLYRRwueIpisZAdopPVrxQloNs9fEjVlL0A== dependencies: "@azure/core-auth" "^1.3.0" @@ -137,7 +137,7 @@ "@azure/identity@^2.1.0": version "2.1.0" - resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-2.1.0.tgz#89f0bfc1d1264dfd3d0cb19837c33a9c6706d548" + resolved "https://registry.npmjs.org/@azure/identity/-/identity-2.1.0.tgz" integrity sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw== dependencies: "@azure/abort-controller" "^1.0.0" @@ -159,26 +159,26 @@ "@azure/logger@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.1.tgz#19b333203d1b2931353d8879e814b64a7274837a" + resolved "https://registry.npmjs.org/@azure/logger/-/logger-1.0.1.tgz" integrity sha512-QYQeaJ+A5x6aMNu8BG5qdsVBnYBop9UMwgUvGihSjf1PdZZXB+c/oMdM2ajKwzobLBh9e9QuMQkN9iL+IxLBLA== dependencies: tslib "^2.0.0" "@azure/msal-browser@^2.26.0": version "2.28.3" - resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-2.28.3.tgz#7cd35e632ea74a2ef5f9939fdce8757ffb93487f" + resolved "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.28.3.tgz" integrity sha512-2SdyH2el3s8BzPURf9RK17BvvXvaMEGpLc3D9WilZcmjJqP4nStVH7Ogwr/SNTuGV48FUhqEkP0RxDvzuFJSIw== dependencies: "@azure/msal-common" "^7.4.1" "@azure/msal-common@^7.0.0", "@azure/msal-common@^7.4.1": version "7.4.1" - resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-7.4.1.tgz#204c32d247336d7e334984e599bfd63156554f83" + resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.4.1.tgz" integrity sha512-zxcxg9pRdgGTS5mrRJeQvwA8aIjD8qSGzaAiz5SeTVkyhtjB0AeFnAcvBOKHv/TkswWNfYKpERxsXOAKXkXk0w== "@azure/msal-node@^1.10.0": version "1.14.0" - resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-1.14.0.tgz#b1b6018e52e06c3789b434f636f5b632aa1d2ec7" + resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.14.0.tgz" integrity sha512-3XB7FuHLhmGBjw7bxuz1LCHOXQgmNIO3J56tlbOjuJcyJtd4aBCgnYIXNKLed3uRcQNHEO0mlg24I4iGxAV/UA== dependencies: "@azure/msal-common" "^7.4.1" @@ -186,9 +186,9 @@ uuid "^8.3.0" "@azure/storage-blob@^12.13.0": - version "12.13.0" - resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.13.0.tgz#9209cbb5c2cd463fb967a0f2ae144ace20879160" - integrity sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA== + version "12.14.0" + resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.14.0.tgz#32d3e5fa3bb2a12d5d44b186aed11c8e78f00178" + integrity sha512-g8GNUDpMisGXzBeD+sKphhH5yLwesB4JkHr1U6be/X3F+cAMcyGLPD1P89g2M7wbEtUJWoikry1rlr83nNRBzg== dependencies: "@azure/abort-controller" "^1.0.0" "@azure/core-http" "^3.0.0" @@ -201,7 +201,7 @@ "@electron/get@^1.12.4": version "1.12.4" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab" + resolved "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz" integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg== dependencies: debug "^4.1.1" @@ -217,14 +217,14 @@ "@malept/cross-spawn-promise@^1.1.0": version "1.1.1" - resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" + resolved "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz" integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== dependencies: cross-spawn "^7.0.1" "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -232,12 +232,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -245,60 +245,60 @@ "@opentelemetry/api@^1.0.1": version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" + resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz" integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== "@sindresorhus/is@^0.14.0": version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== "@sindresorhus/is@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz" integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== "@szmarczak/http-timer@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== dependencies: defer-to-connect "^1.0.1" "@szmarczak/http-timer@^4.0.5": version "4.0.5" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz" integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== dependencies: defer-to-connect "^2.0.0" "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@types/ansi-colors@^3.2.0": version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/ansi-colors/-/ansi-colors-3.2.0.tgz#3e4fe85d9131ce1c6994f3040bd0b25306c16a6e" + resolved "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.0.tgz" integrity sha512-0caWAhXht9N2lOdMzJLXybsSkYCx1QOdxx6pae48tswI9QV3DFX26AoOpy0JxwhCb+zISTqmd6H8t9Zby9BoZg== "@types/azure@0.9.19": version "0.9.19" - resolved "https://registry.yarnpkg.com/@types/azure/-/azure-0.9.19.tgz#1a6a9bd856b437ddecf3f9fc8407a683c869ba02" + resolved "https://registry.npmjs.org/@types/azure/-/azure-0.9.19.tgz" integrity sha1-Gmqb2Fa0N93s8/n8hAemg8hpugI= dependencies: "@types/node" "*" "@types/byline@^4.2.32": version "4.2.32" - resolved "https://registry.yarnpkg.com/@types/byline/-/byline-4.2.32.tgz#9d35ec15968056118548412ee24c2c3026c997dc" + resolved "https://registry.npmjs.org/@types/byline/-/byline-4.2.32.tgz" integrity sha512-qtlm/J6XOO9p+Ep/ZB5+mCFEDhzWDDHWU4a1eReN7lkPZXW9rkloq2jcAhvKKmlO5tL2GSvKROb+PTsNVhBiyQ== dependencies: "@types/node" "*" "@types/cacheable-request@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz" integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== dependencies: "@types/http-cache-semantics" "*" @@ -308,49 +308,41 @@ "@types/caseless@*": version "0.12.1" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.1.tgz#9794c69c8385d0192acc471a540d1f8e0d16218a" + resolved "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz" integrity sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A== -"@types/chokidar@*": - version "1.7.5" - resolved "https://registry.yarnpkg.com/@types/chokidar/-/chokidar-1.7.5.tgz#1fa78c8803e035bed6d98e6949e514b133b0c9b6" - integrity sha512-PDkSRY7KltW3M60hSBlerxI8SFPXsO3AL/aRVsO4Kh9IHRW74Ih75gUuTd/aE4LSSFqypb10UIX3QzOJwBQMGQ== - dependencies: - "@types/events" "*" - "@types/node" "*" - "@types/cssnano@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/cssnano/-/cssnano-4.0.1.tgz#67fa912753d80973a016e7684a47fedf338aacff" + resolved "https://registry.npmjs.org/@types/cssnano/-/cssnano-4.0.1.tgz" integrity sha512-hGOroxRTBkYl5gSBRJOffhV4+io+Y2bFX1VP7LgKEVHJt/LPPJaWUIuDAz74Vlp7l7hCDZfaDi7iPxwNwuVA4Q== dependencies: postcss "5 - 7" "@types/debounce@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/debounce/-/debounce-1.0.0.tgz#417560200331e1bb84d72da85391102c2fcd61b7" + resolved "https://registry.npmjs.org/@types/debounce/-/debounce-1.0.0.tgz" integrity sha1-QXVgIAMx4buE1y2oU5EQLC/NYbc= "@types/debug@4.1.5": version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" + resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== "@types/documentdb@^1.10.5": version "1.10.8" - resolved "https://registry.yarnpkg.com/@types/documentdb/-/documentdb-1.10.8.tgz#cf2008f8a4944abbd971f2ac1dbb81abf2d92f92" + resolved "https://registry.npmjs.org/@types/documentdb/-/documentdb-1.10.8.tgz" integrity sha512-GkOXovVMlMVTYkPomq9rOI79DmVOMZ0TDziL3H3TSlhUSm1/txi5qA49H/qZRDFsExagjnf5Cd/4xF8mXVxubw== dependencies: "@types/node" "*" "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + resolved "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== "@types/eslint@4.16.1": version "4.16.1" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-4.16.1.tgz#19730c9fcb66b6e44742d12b27a603fabfeb2f49" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-4.16.1.tgz" integrity sha512-lRUXQAULl5geixTiP2K0iYvMUbCkEnuOwvLGjwff12I4ECxoW5QaWML5UUOZ1CvpQLILkddBdMPMZz4ByQizsg== dependencies: "@types/estree" "*" @@ -358,36 +350,36 @@ "@types/estree@*": version "0.0.41" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.41.tgz#fd90754150b57432b72bf560530500597ff04421" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.41.tgz" integrity sha512-rIAmXyJlqw4KEBO7+u9gxZZSQHaCNnIzYrnNmYVpgfJhxTqO0brCX0SYpqUTkVI5mwwUwzmtspLBGBKroMeynA== "@types/events@*": version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" + resolved "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz" integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== -"@types/fancy-log@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.0.tgz#a61ab476e5e628cd07a846330df53b85e05c8ce0" - integrity sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw== +"@types/fancy-log@^1.3.1": + version "1.3.1" + resolved "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.1.tgz" + integrity sha512-31Dt9JaGfHretvwVxCBrCFL5iC9MQ3zOXpu+8C4qzW0cxc5rJJVGxB5c/vZ+wmeTk/JjPz/D0gv8BZ+Ip6iCqQ== "@types/form-data@*": version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" + resolved "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz" integrity sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ== dependencies: "@types/node" "*" "@types/fs-extra@^9.0.12": version "9.0.12" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.12.tgz#9b8f27973df8a7a3920e8461517ebf8a7d4fdfaf" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz" integrity sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw== dependencies: "@types/node" "*" "@types/glob-stream@*": version "6.1.0" - resolved "https://registry.yarnpkg.com/@types/glob-stream/-/glob-stream-6.1.0.tgz#7ede8a33e59140534f8d8adfb8ac9edfb31897bc" + resolved "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-6.1.0.tgz" integrity sha512-RHv6ZQjcTncXo3thYZrsbAVwoy4vSKosSWhuhuQxLOTv74OJuFQxXkmUuZCr3q9uNBEVCvIzmZL/FeRNbHZGUg== dependencies: "@types/glob" "*" @@ -395,7 +387,7 @@ "@types/glob@*", "@types/glob@^7.1.1": version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz" integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== dependencies: "@types/events" "*" @@ -404,15 +396,15 @@ "@types/gulp-concat@^0.0.32": version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/gulp-concat/-/gulp-concat-0.0.32.tgz#72486028b1cf5faa94c8c1cf34c626531cecacd6" + resolved "https://registry.npmjs.org/@types/gulp-concat/-/gulp-concat-0.0.32.tgz" integrity sha512-CUCFADlITzzBfBa2bdGzhKtvBr4eFh+evb+4igVbvPoO5RyPfHifmyQlZl6lM7q19+OKncRlFXDU7B4X9Ayo2g== dependencies: "@types/node" "*" -"@types/gulp-filter@^3.0.32": - version "3.0.32" - resolved "https://registry.yarnpkg.com/@types/gulp-filter/-/gulp-filter-3.0.32.tgz#eeff3e9dbc092268fed01f2421ab00f6c8cb4848" - integrity sha512-JvY4qTxXehoK2yCUxYVxTMvckVbDM5TWHWeUoYJyX31gwFqw4YDD6JNzhuTxI3yHPUTY4BBRTqgm6puQEZVCNg== +"@types/gulp-filter@^3.0.35": + version "3.0.35" + resolved "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.35.tgz" + integrity sha512-xBdmHVMnPvAdwyLDFD2Yi8lYXXhYlMWnsWxvMPiinj8b7LJmzNKloI4TQyod+WXEfbiUsHULp0q0qdCIXBVRDQ== dependencies: "@types/minimatch" "*" "@types/node" "*" @@ -420,14 +412,14 @@ "@types/gulp-gzip@^0.0.31": version "0.0.31" - resolved "https://registry.yarnpkg.com/@types/gulp-gzip/-/gulp-gzip-0.0.31.tgz#9358def25540442138fd61a7227f40d4c1e26760" + resolved "https://registry.npmjs.org/@types/gulp-gzip/-/gulp-gzip-0.0.31.tgz" integrity sha512-KQjHz1FTqLse8/EiktfhN/vo33vamX4gVAQhMTp55STDBA7UToW5CJqYyP7iRorkHK9/aJ2nL2hLkNZkY4M8+w== dependencies: "@types/node" "*" "@types/gulp-json-editor@^2.2.31": version "2.2.31" - resolved "https://registry.yarnpkg.com/@types/gulp-json-editor/-/gulp-json-editor-2.2.31.tgz#3c1a8950556c109a0e2d0ab11d5f2a2443665ed2" + resolved "https://registry.npmjs.org/@types/gulp-json-editor/-/gulp-json-editor-2.2.31.tgz" integrity sha512-piis0ImYAy0dt18R4EtTbAY+RV8jwTq5VisnUV6OfP8kD4743aHGkAdAd08No4NY3rFa5mD6ytIu8L0YU7nL9w== dependencies: "@types/js-beautify" "*" @@ -435,7 +427,7 @@ "@types/gulp-postcss@^8.0.0": version "8.0.0" - resolved "https://registry.yarnpkg.com/@types/gulp-postcss/-/gulp-postcss-8.0.0.tgz#f7e86d45e4999fd43e6d8c55b00504c88a67ad61" + resolved "https://registry.npmjs.org/@types/gulp-postcss/-/gulp-postcss-8.0.0.tgz" integrity sha512-AVgjA03bpkYONKZpzuJviB9PzaNbDzrovYPbenj8/XxivUc35C/dIzJanyaQv7CFqfLLPLsqSalmtP3GLq6iag== dependencies: "@types/node" "*" @@ -443,109 +435,104 @@ "@types/gulp-rename@^0.0.33": version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/gulp-rename/-/gulp-rename-0.0.33.tgz#38d146e97786569f74f5391a1b1f9b5198674b6c" + resolved "https://registry.npmjs.org/@types/gulp-rename/-/gulp-rename-0.0.33.tgz" integrity sha512-FIZQvbZJj6V1gHPTzO+g/BCWpDur7fJrroae4gwV3LaoHBQ+MrR9sB+2HssK8fHv4WdY6hVNxkcft9bYatuPIA== dependencies: "@types/node" "*" "@types/gulp-sourcemaps@^0.0.32": version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.32.tgz#e79ee617e0cb15729874be4533fe59c07793a175" + resolved "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.32.tgz" integrity sha512-+7BAmptW2bxyJnJcCEuie7vLoop3FwWgCdBMzyv7MYXED/HeNMeQuX7uPCkp4vfU1TTu4CYFH0IckNPvo0VePA== dependencies: "@types/node" "*" -"@types/gulp@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/gulp/-/gulp-4.0.5.tgz#f5f498d5bf9538364792de22490a12c0e6bc5eb4" - integrity sha512-nx1QjPTiRpvLfYsZ7MBu7bT6Cm7tAXyLbY0xbdx2IEMxCK2v2urIhJMQZHW0iV1TskM71Xl6p2uRRuWDbk+/7g== +"@types/gulp@^4.0.10": + version "4.0.10" + resolved "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.10.tgz" + integrity sha512-spgZHJFqiEJGwqGlf7T/k4nkBpBcLgP7T0EfN6G2vvnhUfvd4uO1h8RwpXOE8x/54DVYUs1XCAtBHkX/R3axAQ== dependencies: - "@types/chokidar" "*" - "@types/undertaker" "*" + "@types/undertaker" ">=1.2.6" "@types/vinyl-fs" "*" + chokidar "^3.3.1" "@types/http-cache-semantics@*": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== "@types/js-beautify@*": version "1.8.0" - resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.8.0.tgz#0369d3d0e1f35a6aec07cb4da2ee2bcda111367c" + resolved "https://registry.npmjs.org/@types/js-beautify/-/js-beautify-1.8.0.tgz" integrity sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA== "@types/json-schema@*": version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== "@types/json-schema@^7.0.3": version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== "@types/keyv@*": version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz" integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== dependencies: "@types/node" "*" "@types/mime@0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" + resolved "https://registry.npmjs.org/@types/mime/-/mime-0.0.29.tgz" integrity sha1-+8/TMFc7kS71nu7hRgK/rOYwdUs= "@types/minimatch@*", "@types/minimatch@^3.0.3": version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/minimist@^1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz" integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== "@types/mkdirp@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.1.tgz#0930b948914a78587de35458b86c907b6e98bbf6" + resolved "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz" integrity sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q== dependencies: "@types/node" "*" "@types/mocha@^9.1.1": version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + resolved "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz" integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== "@types/node-fetch@^2.5.0": version "2.5.8" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz" integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== dependencies: "@types/node" "*" form-data "^3.0.0" -"@types/node@*": - version "8.0.51" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" - integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== - -"@types/node@16.x": +"@types/node@*", "@types/node@16.x": version "16.11.62" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.62.tgz#bab2e6208531321d147eda20c38e389548cd5ffc" + resolved "https://registry.npmjs.org/@types/node/-/node-16.11.62.tgz" integrity sha512-K/ggecSdwAAy2NUW4WKmF4Rc03GKbsfP+k326UWgckoS+Rzd2PaWbjk76dSmqdLQvLTJAO9axiTUJ6488mFsYQ== "@types/p-limit@^2.2.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@types/p-limit/-/p-limit-2.2.0.tgz#94a608e9b258a6c6156a13d1a14fd720dba70b97" + resolved "https://registry.npmjs.org/@types/p-limit/-/p-limit-2.2.0.tgz" integrity sha512-fGFbybl1r0oE9mqgfc2EHHUin9ZL5rbQIexWI6jYRU1ADVn4I3LHzT+g/kpPpZsfp8PB94CQ655pfAjNF8LP6A== dependencies: p-limit "*" "@types/plist@^3.0.2": version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01" + resolved "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz" integrity sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw== dependencies: "@types/node" "*" @@ -553,14 +540,14 @@ "@types/pump@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/pump/-/pump-1.0.1.tgz#ae8157cefef04d1a4d24c1cc91d403c2f5da5cd0" + resolved "https://registry.npmjs.org/@types/pump/-/pump-1.0.1.tgz" integrity sha1-roFXzv7wTRpNJMHMkdQDwvXaXNA= dependencies: "@types/node" "*" "@types/request@^2.47.0": version "2.47.0" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.47.0.tgz#76a666cee4cb85dcffea6cd4645227926d9e114e" + resolved "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz" integrity sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q== dependencies: "@types/caseless" "*" @@ -570,21 +557,21 @@ "@types/resolve@0.0.8": version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + resolved "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz" integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== dependencies: "@types/node" "*" "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz" integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== dependencies: "@types/node" "*" "@types/rimraf@^2.0.4": version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.4.tgz#403887b0b53c6100a6c35d2ab24f6ccc042fec46" + resolved "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.4.tgz" integrity sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q== dependencies: "@types/glob" "*" @@ -592,73 +579,74 @@ "@types/source-list-map@*": version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + resolved "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== "@types/tapable@^1": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" + resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== "@types/through2@^2.0.34": version "2.0.34" - resolved "https://registry.yarnpkg.com/@types/through2/-/through2-2.0.34.tgz#9c2a259a238dace2a05a2f8e94b786961bc27ac4" + resolved "https://registry.npmjs.org/@types/through2/-/through2-2.0.34.tgz" integrity sha512-nhRG8+RuG/L+0fAZBQYaRflXKjTrHOKH8MFTChnf+dNVMxA3wHYYrfj0tztK0W51ABXjGfRCDc0vRkecCOrsow== dependencies: "@types/node" "*" "@types/through@^0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93" + resolved "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz" integrity sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w== dependencies: "@types/node" "*" "@types/tmp@^0.2.1": version "0.2.1" - resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.1.tgz#83ecf4ec22a8c218c71db25f316619fe5b986011" + resolved "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.1.tgz" integrity sha512-7cTXwKP/HLOPVgjg+YhBdQ7bMiobGMuoBmrGmqwIWJv8elC6t1DfVc/mn4fD9UE1IjhwmhaQ5pGVXkmXbH0rhg== "@types/tough-cookie@*": version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.2.tgz#e0d481d8bb282ad8a8c9e100ceb72c995fb5e709" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz" integrity sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA== "@types/tunnel@^0.0.3": version "0.0.3" - resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" + resolved "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz" integrity sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA== dependencies: "@types/node" "*" "@types/uglify-js@*": version "3.13.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea" + resolved "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz" integrity sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ== dependencies: source-map "^0.6.1" "@types/underscore@^1.8.9": version "1.8.9" - resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.8.9.tgz#fef41f800cd23db1b4f262ddefe49cd952d82323" + resolved "https://registry.npmjs.org/@types/underscore/-/underscore-1.8.9.tgz" integrity sha512-vfzZGgZKRFy7KEWcBGfIFk+h6B+thDCLfkD1exMBMRlUsx2icA+J6y4kAbZs/TjSTeY1duw89QUU133TSzr60Q== "@types/undertaker-registry@*": version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz#4306d4a03d7acedb974b66530832b90729e1d1da" + resolved "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz" integrity sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ== -"@types/undertaker@*": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/undertaker/-/undertaker-1.2.0.tgz#d39a81074b4f274eb656870fc904a70737e00f8e" - integrity sha512-bx/5nZCGkasXs6qaA3B6SVDjBZqdyk04UO12e0uEPSzjt5H8jEJw0DKe7O7IM0hM2bVHRh70pmOH7PEHqXwzOw== +"@types/undertaker@>=1.2.6": + version "1.2.8" + resolved "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.8.tgz" + integrity sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg== dependencies: - "@types/events" "*" + "@types/node" "*" "@types/undertaker-registry" "*" + async-done "~1.3.2" "@types/vinyl-fs@*": version "2.4.9" - resolved "https://registry.yarnpkg.com/@types/vinyl-fs/-/vinyl-fs-2.4.9.tgz#d312c24b5ba8d2db456d23ee4a66f9d016af82ea" + resolved "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.9.tgz" integrity sha512-Q0EXd6c1fORjiOuK4ZaKdfFcMyFzJlTi56dqktwaWVLIDAzE49wUs3bKnYbZwzyMWoH+NcMWnRuR73S9A0jnRA== dependencies: "@types/events" "*" @@ -668,14 +656,14 @@ "@types/vinyl@*": version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.2.tgz#4f3b8dae8f5828d3800ef709b0cff488ee852de3" + resolved "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz" integrity sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw== dependencies: "@types/node" "*" "@types/webpack-sources@*": version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.1.1.tgz#6af17e3a3ded71eec2b98008d7c12f498a0a4506" + resolved "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.1.tgz" integrity sha512-MjM1R6iuw8XaVbtkCBz0N349cyqBjJHCbQiOeppe3VBeFvxqs74RKHAVt9LkxTnUWc7YLZOEsUfPUnmK6SBPKQ== dependencies: "@types/node" "*" @@ -684,7 +672,7 @@ "@types/webpack@^4.41.25": version "4.41.30" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.30.tgz#fd3db6d0d41e145a8eeeafcd3c4a7ccde9068ddc" + resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.30.tgz" integrity sha512-GUHyY+pfuQ6haAfzu4S14F+R5iGRwN6b2FRNJY7U0NilmFAqbsOfK6j1HwuLBAqwRIT+pVdNDJGJ6e8rpp0KHA== dependencies: "@types/node" "*" @@ -703,14 +691,14 @@ "@types/yauzl@^2.9.1": version "2.9.2" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz" integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== dependencies: "@types/node" "*" "@typescript-eslint/experimental-utils@~2.13.0": version "2.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz#958614faa6f77599ee2b241740e0ea402482533d" + resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz" integrity sha512-+Hss3clwa6aNiC8ZjA45wEm4FutDV5HsVXPl/rDug1THq6gEtOYRGLqS3JlTk7mSnL5TbJz0LpEbzbPnKvY6sw== dependencies: "@types/json-schema" "^7.0.3" @@ -719,7 +707,7 @@ "@typescript-eslint/parser@^5.10.0": version "5.38.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.38.1.tgz#c577f429f2c32071b92dff4af4f5fbbbd2414bd0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.1.tgz" integrity sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw== dependencies: "@typescript-eslint/scope-manager" "5.38.1" @@ -729,7 +717,7 @@ "@typescript-eslint/scope-manager@5.38.1": version "5.38.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz#f87b289ef8819b47189351814ad183e8801d5764" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz" integrity sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ== dependencies: "@typescript-eslint/types" "5.38.1" @@ -737,12 +725,12 @@ "@typescript-eslint/types@5.38.1": version "5.38.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.1.tgz#74f9d6dcb8dc7c58c51e9fbc6653ded39e2e225c" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.1.tgz" integrity sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg== "@typescript-eslint/typescript-estree@2.13.0": version "2.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz#a2e746867da772c857c13853219fced10d2566bc" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz" integrity sha512-t21Mg5cc8T3ADEUGwDisHLIubgXKjuNRbkpzDMLb7/JMmgCe/gHM9FaaujokLey+gwTuLF5ndSQ7/EfQqrQx4g== dependencies: debug "^4.1.1" @@ -755,7 +743,7 @@ "@typescript-eslint/typescript-estree@5.38.1": version "5.38.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz#657d858d5d6087f96b638ee383ee1cff52605a1e" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz" integrity sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g== dependencies: "@typescript-eslint/types" "5.38.1" @@ -768,12 +756,17 @@ "@typescript-eslint/visitor-keys@5.38.1": version "5.38.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz#508071bfc6b96d194c0afe6a65ad47029059edbc" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz" integrity sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA== dependencies: "@typescript-eslint/types" "5.38.1" eslint-visitor-keys "^3.3.0" +"@vscode/iconv-lite-umd@0.7.0": + 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== + "@vscode/vsce@2.16.0": version "2.16.0" resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.16.0.tgz#a3ddcf7e84914576f35d891e236bc496c568776f" @@ -803,56 +796,76 @@ acorn@^7.1.0: version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== agent-base@6: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-colors@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz" integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== dependencies: ansi-wrap "^0.1.0" +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz" + integrity sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw== + dependencies: + ansi-wrap "0.1.0" + ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansi-wrap@^0.1.0: +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + resolved "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz" integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw== anymatch@^3.0.0: version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" +anymatch@^3.1.1, anymatch@~3.1.1: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + applicationinsights@1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + resolved "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.8.tgz" integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" @@ -861,12 +874,12 @@ applicationinsights@1.0.8: aproba@^1.0.3: version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== are-we-there-yet@~1.1.2: version "1.1.7" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz" integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== dependencies: delegates "^1.0.0" @@ -874,27 +887,27 @@ are-we-there-yet@~1.1.2: argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== arr-diff@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== arr-union@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== asar@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b" + resolved "https://registry.npmjs.org/asar/-/asar-3.0.3.tgz" integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw== dependencies: chromium-pickle-js "^0.2.0" @@ -906,22 +919,32 @@ asar@^3.0.3: assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== +async-done@~1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz" + integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.2" + process-nextick-args "^2.0.0" + stream-exhaust "^1.0.1" + asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= at-least-node@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== azure-devops-node-api@^11.0.1: version "11.1.1" - resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-11.1.1.tgz#dd1356031fa4e334e016732594e8fee0f68268c4" + resolved "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.1.1.tgz" integrity sha512-XDG91XzLZ15reP12s3jFkKS8oiagSICjnLwxEYieme4+4h3ZveFOFRA4iYIG40RyHXsiI0mefFYYMFIJbMpWcg== dependencies: tunnel "0.0.6" @@ -929,22 +952,27 @@ azure-devops-node-api@^11.0.1: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + binary-search-bounds@2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/binary-search-bounds/-/binary-search-bounds-2.0.3.tgz#5ff8616d6dd2ca5388bc85b2d6266e2b9da502dc" + resolved "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.3.tgz" integrity sha1-X/hhbW3SylOIvIWy1iZuK52lAtw= bl@^4.0.3: version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" @@ -953,42 +981,42 @@ bl@^4.0.3: bluebird@^3.5.0: version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== boolbase@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= boolean@^3.0.1: version "3.1.2" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.1.2.tgz#e30f210a26b02458482a8cc353ab06f262a780c2" + resolved "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz" integrity sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw== brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" buffer-alloc-unsafe@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz" integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== buffer-alloc@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz" integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== dependencies: buffer-alloc-unsafe "^1.1.0" @@ -996,27 +1024,27 @@ buffer-alloc@^1.2.0: buffer-crc32@~0.2.3: version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-equal-constant-time@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer-equal@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" + resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz" integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= buffer-fill@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz" integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= buffer@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1024,22 +1052,22 @@ buffer@^5.5.0: builtin-modules@^3.1.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz" integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== byline@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= cacheable-lookup@^5.0.3: version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== cacheable-request@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== dependencies: clone-response "^1.0.2" @@ -1052,7 +1080,7 @@ cacheable-request@^6.0.0: cacheable-request@^7.0.2: version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz" integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== dependencies: clone-response "^1.0.2" @@ -1065,7 +1093,7 @@ cacheable-request@^7.0.2: call-bind@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -1073,7 +1101,7 @@ call-bind@^1.0.0: chalk@^2.4.2: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -1082,7 +1110,7 @@ chalk@^2.4.2: cheerio-select@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz" integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== dependencies: boolbase "^1.0.0" @@ -1094,7 +1122,7 @@ cheerio-select@^2.1.0: cheerio@^1.0.0-rc.9: version "1.0.0-rc.11" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.11.tgz#1be84be1a126958366bcc57a11648cd9b30a60c2" + resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.11.tgz" integrity sha512-bQwNaDIBKID5ts/DsdhxrjqFXYfLw4ste+wMKqWA8DyKcS4qwsPP4Bk8ZNaTJjvpiX/qW3BT4sU7d6Bh5i+dag== dependencies: cheerio-select "^2.1.0" @@ -1106,41 +1134,56 @@ cheerio@^1.0.0-rc.9: parse5-htmlparser2-tree-adapter "^7.0.0" tslib "^2.4.0" +chokidar@3.5.1, chokidar@^3.3.1: + version "3.5.1" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chownr@^1.1.1: version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chromium-pickle-js@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" + resolved "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz" integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= clone-buffer@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz" integrity sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g== clone-response@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= dependencies: mimic-response "^1.0.0" clone-stats@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + resolved "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz" integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag== clone@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== cloneable-readable@^1.0.0: version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" + resolved "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz" integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== dependencies: inherits "^2.0.1" @@ -1149,73 +1192,78 @@ cloneable-readable@^1.0.0: code-point-at@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + colors@1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= colors@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + resolved "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= dependencies: graceful-readlink ">= 1.0.0" commander@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== commander@^6.1.0: version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== commander@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== compare-version@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" + resolved "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz" integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA= concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= config-chain@^1.1.11: version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== dependencies: ini "^1.3.4" @@ -1223,22 +1271,22 @@ config-chain@^1.1.11: console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= core-js@^3.6.5: version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz" integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q== core-util-is@~1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cross-spawn@^7.0.1: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -1247,7 +1295,7 @@ cross-spawn@^7.0.1: css-select@^5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + resolved "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz" integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== dependencies: boolbase "^1.0.0" @@ -1258,113 +1306,125 @@ css-select@^5.1.0: css-what@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== debug@4, debug@^4.3.4: version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" debug@^2.6.8: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^4.1.0, debug@^4.3.2: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" -debug@^4.1.1, debug@^4.3.1: +debug@^4.3.1: version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" decompress-response@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + decompress-response@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: mimic-response "^3.1.0" deep-extend@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== defer-to-connect@^1.0.1: version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== defer-to-connect@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz" integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== define-lazy-prop@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== define-properties@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + detect-libc@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz" integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== detect-node@^2.0.4: version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== diagnostic-channel-publishers@0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + resolved "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz" integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= diagnostic-channel@0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + resolved "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz" integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= dependencies: semver "^5.3.0" dir-compare@^2.4.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-2.4.0.tgz#785c41dc5f645b34343a4eafc50b79bac7f11631" + resolved "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz" integrity sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA== dependencies: buffer-equal "1.0.0" @@ -1374,14 +1434,14 @@ dir-compare@^2.4.0: dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" documentdb@1.13.0: version "1.13.0" - resolved "https://registry.yarnpkg.com/documentdb/-/documentdb-1.13.0.tgz#bba6f03150b2f42498cec4261bc439d834a33f8b" + resolved "https://registry.npmjs.org/documentdb/-/documentdb-1.13.0.tgz" integrity sha1-u6bwMVCy9CSYzsQmG8Q52DSjP4s= dependencies: binary-search-bounds "2.0.3" @@ -1391,7 +1451,7 @@ documentdb@1.13.0: dom-serializer@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== dependencies: domelementtype "^2.3.0" @@ -1400,19 +1460,19 @@ dom-serializer@^2.0.0: domelementtype@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== dependencies: domelementtype "^2.3.0" domutils@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + resolved "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz" integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== dependencies: dom-serializer "^2.0.0" @@ -1421,19 +1481,19 @@ domutils@^3.0.1: duplexer3@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= ecdsa-sig-formatter@1.0.11: version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" electron-osx-sign@^0.4.16: version "0.4.17" - resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.17.tgz#2727ca0c79e1e4e5ccd3861fb3da9c3c913b006c" + resolved "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.17.tgz" integrity sha512-wUJPmZJQCs1zgdlQgeIpRcvrf7M5/COQaOV68Va1J/SgmWx5KL2otgg+fAae7luw6qz9R8Gvu/Qpe9tAOu/3xQ== dependencies: bluebird "^3.5.0" @@ -1445,59 +1505,59 @@ electron-osx-sign@^0.4.16: emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encodeurl@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" entities@^4.2.0, entities@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.0.tgz#62915f08d67353bb4eb67e3d62641a4059aec656" + resolved "https://registry.npmjs.org/entities/-/entities-4.3.0.tgz" integrity sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg== entities@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + resolved "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz" integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== es6-error@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + resolved "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== esbuild@^0.12.6: version "0.12.6" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.6.tgz#85bc755c7cf3005d4f34b4f10f98049ce0ee67ce" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.6.tgz" integrity sha512-RDvVLvAjsq/kIZJoneMiUOH7EE7t2QaW7T3Q7EdQij14+bZbDq5sndb0tTanmHIFSqZVMBMMyqzVHkS3dJobeA== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-scope@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -1505,49 +1565,49 @@ eslint-scope@^5.0.0: eslint-visitor-keys@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== estree-walker@^0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz" integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== events@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + resolved "https://registry.npmjs.org/events/-/events-3.2.0.tgz" integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== expand-template@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + resolved "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== extend-shallow@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== dependencies: assign-symbols "^1.0.0" @@ -1555,7 +1615,7 @@ extend-shallow@^3.0.2: extract-zip@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: debug "^4.1.1" @@ -1564,9 +1624,19 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" +fancy-log@^1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + fast-glob@^3.2.9: version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -1577,33 +1647,40 @@ fast-glob@^3.2.9: fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fastq@^1.6.0: version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== dependencies: reusify "^1.0.4" fd-slicer@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= dependencies: pend "~1.2.0" fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" +first-chunk-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz" + integrity sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg== + dependencies: + readable-stream "^2.0.2" + form-data@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" @@ -1612,7 +1689,7 @@ form-data@^3.0.0: form-data@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" @@ -1621,12 +1698,12 @@ form-data@^4.0.0: fs-constants@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: graceful-fs "^4.2.0" @@ -1635,7 +1712,7 @@ fs-extra@^8.1.0: fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" @@ -1645,17 +1722,22 @@ fs-extra@^9.0.1, fs-extra@^9.1.0: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== gauge@~2.7.3: version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz" integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" @@ -1669,7 +1751,7 @@ gauge@~2.7.3: get-intrinsic@^1.0.2: version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" @@ -1678,33 +1760,33 @@ get-intrinsic@^1.0.2: get-stream@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" github-from-package@0.0.0: version "0.0.0" - resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + resolved "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob-parent@^5.1.2: +glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob@^7.0.6: version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" @@ -1716,7 +1798,7 @@ glob@^7.0.6: glob@^7.1.3, glob@^7.1.6: version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -1728,7 +1810,7 @@ glob@^7.1.3, glob@^7.1.6: global-agent@^2.0.2: version "2.2.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc" + resolved "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz" integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg== dependencies: boolean "^3.0.1" @@ -1741,7 +1823,7 @@ global-agent@^2.0.2: global-tunnel-ng@^2.7.1: version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" + resolved "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz" integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== dependencies: encodeurl "^1.0.2" @@ -1751,14 +1833,14 @@ global-tunnel-ng@^2.7.1: globalthis@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz" integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== dependencies: define-properties "^1.1.3" globby@^11.1.0: version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -1770,7 +1852,7 @@ globby@^11.1.0: got@11.8.5: version "11.8.5" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" + resolved "https://registry.npmjs.org/got/-/got-11.8.5.tgz" integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== dependencies: "@sindresorhus/is" "^4.0.0" @@ -1787,7 +1869,7 @@ got@11.8.5: got@^9.6.0: version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: "@sindresorhus/is" "^0.14.0" @@ -1802,19 +1884,24 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" +graceful-fs@^4.1.2: + version "4.2.10" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== "graceful-readlink@>= 1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= gulp-merge-json@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/gulp-merge-json/-/gulp-merge-json-2.1.2.tgz#7810dbf8f1f1a8638c18d46bad165bd44d18ea79" + resolved "https://registry.npmjs.org/gulp-merge-json/-/gulp-merge-json-2.1.2.tgz" integrity sha512-FysBAdHdnQvZzigVJJzlrt6TEosHxVb0mR2h/8eSnd+eJyBvb1LQF1EIrovrOCfj4HGE5p/95wGEjXsJk9qomw== dependencies: json5 "^2.2.1" @@ -1825,36 +1912,36 @@ gulp-merge-json@^2.1.1: has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-symbols@^1.0.1: version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-unicode@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" hosted-git-info@^4.0.2: version "4.1.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== dependencies: lru-cache "^6.0.0" htmlparser2@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz" integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA== dependencies: domelementtype "^2.3.0" @@ -1864,12 +1951,12 @@ htmlparser2@^8.0.1: http-cache-semantics@^4.0.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" @@ -1878,7 +1965,7 @@ http-proxy-agent@^5.0.0: http2-wrapper@^1.0.0-beta.5.2: version "1.0.0-beta.5.2" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz" integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== dependencies: quick-lru "^5.1.1" @@ -1886,7 +1973,7 @@ http2-wrapper@^1.0.0-beta.5.2: https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -1894,22 +1981,22 @@ https-proxy-agent@^5.0.0: iconv-lite-umd@0.6.8: version "0.6.8" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" + resolved "https://registry.npmjs.org/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz" integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== ieee754@^1.1.13: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" @@ -1917,162 +2004,167 @@ inflight@^1.0.4: inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.4, ini@~1.3.0: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-core-module@^2.2.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz" integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== dependencies: has "^1.0.3" is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extendable@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-glob@^4.0.3: +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-module@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-reference@^1.1.2: version "1.2.1" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + resolved "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== dependencies: "@types/estree" "*" +is-utf8@^0.2.0, is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isbinaryfile@^3.0.2: version "3.0.3" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" + resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz" integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw== dependencies: buffer-alloc "^1.2.0" isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== jsbi@^3.1.3: version "3.1.4" - resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0" + resolved "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz" integrity sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg== json-buffer@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-schema@0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stringify-safe@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^2.2.1: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonc-parser@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== jsonfile@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -2091,7 +2183,7 @@ jsonwebtoken@9.0.0, jsonwebtoken@^8.5.1: jwa@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== dependencies: buffer-equal-constant-time "1.0.1" @@ -2100,7 +2192,7 @@ jwa@^1.4.1: jwa@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz" integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== dependencies: buffer-equal-constant-time "1.0.1" @@ -2109,7 +2201,7 @@ jwa@^2.0.0: jws@^3.2.2: version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: jwa "^1.4.1" @@ -2117,7 +2209,7 @@ jws@^3.2.2: jws@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== dependencies: jwa "^2.0.0" @@ -2125,7 +2217,7 @@ jws@^4.0.0: keytar@^7.7.0: version "7.9.0" - resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" + resolved "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz" integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ== dependencies: node-addon-api "^4.3.0" @@ -2133,38 +2225,38 @@ keytar@^7.7.0: keyv@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== dependencies: json-buffer "3.0.0" keyv@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz" integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== dependencies: json-buffer "3.0.1" leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== linkify-it@^3.0.1: version "3.0.3" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" + resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz" integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== dependencies: uc.micro "^1.0.1" lodash.mergewith@^4.6.1: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== lodash.unescape@4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + resolved "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= lodash@^4.17.10, lodash@^4.17.21: @@ -2174,31 +2266,31 @@ lodash@^4.17.10, lodash@^4.17.21: lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lowercase-keys@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" magic-string@^0.25.2: version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== dependencies: sourcemap-codec "^1.4.4" markdown-it@^12.3.2: version "12.3.2" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz" integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== dependencies: argparse "^2.0.1" @@ -2209,24 +2301,24 @@ markdown-it@^12.3.2: matcher@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + resolved "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz" integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== dependencies: escape-string-regexp "^4.0.0" mdurl@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.4: version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" @@ -2234,120 +2326,132 @@ micromatch@^4.0.4: mime-db@1.46.0: version "1.46.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz" integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== mime-types@^2.1.12: version "2.1.29" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz" integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== dependencies: mime-db "1.46.0" mime@^1.3.4, mime@^1.4.1: version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + mimic-response@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.3: version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.2: +ms@2.1.2, ms@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - mute-stream@~0.0.4: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nan@^2.14.0: + version "2.17.0" + resolved "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + napi-build-utils@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + resolved "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== +node-abi@^2.21.0: + version "2.30.1" + resolved "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz" + integrity sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w== + dependencies: + semver "^5.4.1" + node-abi@^3.3.0: version "3.22.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.22.0.tgz#00b8250e86a0816576258227edbce7bbe0039362" + resolved "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz" integrity sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w== dependencies: semver "^7.3.5" node-abort-controller@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.0.1.tgz#f91fa50b1dee3f909afabb7e261b1e1d6b0cb74e" + resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz" integrity sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw== node-addon-api@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== -node-fetch@^2.6.7: +node-fetch@2, node-fetch@^2.6.7: version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^4.1.0: version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== normalize-url@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== npm-conf@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" + resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz" integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== dependencies: config-chain "^1.1.11" @@ -2355,7 +2459,7 @@ npm-conf@^1.1.3: npmlog@^4.0.1: version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" @@ -2365,41 +2469,41 @@ npmlog@^4.0.1: nth-check@^2.0.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" number-is-nan@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -object-assign@^4.1.0: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-inspect@^1.9.0: version "1.12.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.1.tgz#28a661153bad7e470e4b01479ef1cb91ce511191" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.1.tgz" integrity sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA== object-keys@^1.0.12: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" open@^8.0.0: version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz" integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== dependencies: define-lazy-prop "^2.0.0" @@ -2408,31 +2512,36 @@ open@^8.0.0: p-cancelable@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== p-cancelable@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz" integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== p-limit@*, p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse-semver@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8" + resolved "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz" integrity sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg= dependencies: semver "^5.1.0" parse5-htmlparser2-tree-adapter@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" + resolved "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz" integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== dependencies: domhandler "^5.0.2" @@ -2440,62 +2549,67 @@ parse5-htmlparser2-tree-adapter@^7.0.0: parse5@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.0.0.tgz#51f74a5257f5fcc536389e8c2d0b3802e1bfa91a" + resolved "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz" integrity sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g== dependencies: entities "^4.3.0" path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pend@~1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= picomatch@^2.0.4: version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -picomatch@^2.3.1: +picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + pify@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= plist@^3.0.1, plist@^3.0.5: version "3.0.5" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" + resolved "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz" integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== dependencies: base64-js "^1.5.1" xmlbuilder "^9.0.7" -plugin-error@^1.0.1: +plugin-error@1.0.1, plugin-error@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" + resolved "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz" integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== dependencies: ansi-colors "^1.0.1" @@ -2505,16 +2619,35 @@ plugin-error@^1.0.1: "postcss@5 - 7": version "7.0.36" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz" integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw== dependencies: chalk "^2.4.2" source-map "^0.6.1" supports-color "^6.1.0" +prebuild-install@^6.0.1: + version "6.1.4" + resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz" + integrity sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^2.21.0" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prebuild-install@^7.0.1: version "7.1.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.0.tgz#991b6ac16c81591ba40a6d5de93fb33673ac1370" + resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.0.tgz" integrity sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA== dependencies: detect-libc "^2.0.0" @@ -2533,37 +2666,37 @@ prebuild-install@^7.0.1: prepend-http@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= priorityqueuejs@1.0.0, priorityqueuejs@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz#2ee4f23c2560913e08c07ce5ccdd6de3df2c5af8" + resolved "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz" integrity sha1-LuTyPCVgkT4IwHzlzN1t498sWvg= process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@^0.11.10: version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= progress@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== proto-list@~1.2.1: version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= pump@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -2571,24 +2704,24 @@ pump@^3.0.0: qs@^6.9.1: version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== rc@^1.2.7: version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" @@ -2598,14 +2731,14 @@ rc@^1.2.7: read@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= dependencies: mute-stream "~0.0.4" -readable-stream@^2.0.6, readable-stream@^2.3.5: +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.5: version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -2618,31 +2751,47 @@ readable-stream@^2.0.6, readable-stream@^2.3.5: readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== replace-ext@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz" integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== resolve-alpn@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c" + resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz" integrity sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA== resolve@^1.11.0, resolve@^1.11.1: version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: is-core-module "^2.2.0" @@ -2650,33 +2799,33 @@ resolve@^1.11.0, resolve@^1.11.1: responselike@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" responselike@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz" integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== dependencies: lowercase-keys "^2.0.0" reusify@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" roarr@^2.15.3: version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + resolved "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz" integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== dependencies: boolean "^3.0.1" @@ -2688,7 +2837,7 @@ roarr@^2.15.3: rollup-plugin-commonjs@^10.1.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz#417af3b54503878e084d127adf4d1caf8beb86fb" + resolved "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz" integrity sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q== dependencies: estree-walker "^0.6.1" @@ -2699,7 +2848,7 @@ rollup-plugin-commonjs@^10.1.0: rollup-plugin-node-resolve@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" + resolved "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz" integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== dependencies: "@types/resolve" "0.0.8" @@ -2710,14 +2859,14 @@ rollup-plugin-node-resolve@^5.2.0: rollup-pluginutils@^2.8.1: version "2.8.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + resolved "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz" integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== dependencies: estree-walker "^0.6.1" rollup@^1.20.3: version "1.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4" + resolved "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz" integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A== dependencies: "@types/estree" "*" @@ -2726,61 +2875,61 @@ rollup@^1.20.3: run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== sax@>=0.6.0: version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== semaphore@1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.0.5.tgz#b492576e66af193db95d65e25ec53f5f19798d60" + resolved "https://registry.npmjs.org/semaphore/-/semaphore-1.0.5.tgz" integrity sha1-tJJXbmavGT25XWXiXsU/Xxl5jWA= semaphore@^1.0.5: version "1.1.0" - resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + resolved "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz" integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== semver-compare@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -semver@^5.1.0, semver@^5.3.0: +semver@^5.1.0, semver@^5.3.0, semver@^5.4.1: version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^6.2.0, semver@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.2: version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz" integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== dependencies: lru-cache "^6.0.0" semver@^7.3.5, semver@^7.3.7: version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" @@ -2794,31 +2943,31 @@ semver@^7.3.8: serialize-error@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz" integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== dependencies: type-fest "^0.13.1" set-blocking@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -2827,17 +2976,26 @@ side-channel@^1.0.4: signal-exit@^3.0.0: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-concat@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz" integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== +simple-get@^3.0.3: + version "3.1.1" + resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz" + integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-get@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + resolved "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz" integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== dependencies: decompress-response "^6.0.0" @@ -2846,37 +3004,42 @@ simple-get@^4.0.0: slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== sourcemap-codec@^1.4.4: version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== sprintf-js@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz" integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== stoppable@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + resolved "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz" integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== +stream-exhaust@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz" + integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== + string-width@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" @@ -2885,7 +3048,7 @@ string-width@^1.0.1: "string-width@^1.0.2 || 2 || 3 || 4": version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -2894,61 +3057,83 @@ string-width@^1.0.1: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-bom-buf@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz" + integrity sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ== + dependencies: + is-utf8 "^0.2.1" + +strip-bom-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz" + integrity sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w== + dependencies: + first-chunk-stream "^2.0.0" + strip-bom "^2.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== + dependencies: + is-utf8 "^0.2.0" + strip-json-comments@~2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= sumchecker@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" + resolved "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz" integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== dependencies: debug "^4.1.0" supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz" integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== dependencies: has-flag "^3.0.0" tar-fs@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== dependencies: chownr "^1.1.1" @@ -2958,7 +3143,7 @@ tar-fs@^2.0.0: tar-stream@^2.1.4: version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: bl "^4.0.3" @@ -2969,148 +3154,179 @@ tar-stream@^2.1.4: through@^2.3.8: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz" + integrity sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw== + tmp@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== dependencies: rimraf "^3.0.0" to-readable-stream@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tree-sitter-typescript@^0.20.1: + version "0.20.1" + resolved "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.20.1.tgz" + integrity sha512-wqpnhdVYX26ATNXeZtprib4+mF2GlYQB1cjRPibYGxDRiugx5OfjWwLE4qPPxEGdp2ZLSmZVesGUjLWzfKo6rA== + dependencies: + nan "^2.14.0" + +"tree-sitter@https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz": + version "0.20.0" + resolved "https://github.com/joaomoreno/node-tree-sitter/releases/download/v0.20.0/tree-sitter-0.20.0.tgz" + integrity sha512-FfyATFV6xANETqV0izjt/iC76yxFM8oGe5IfeyGfqXxRTsKKs2IOSGx1LTa6guOL3M2SagIQShAkHkJ4sK5/eA== + dependencies: + nan "^2.14.0" + prebuild-install "^6.0.1" + tslib@^1.8.1: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz" integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== tslib@^2.2.0, tslib@^2.4.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== tsutils@^3.17.1: version "3.20.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz" integrity sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg== dependencies: tslib "^1.8.1" tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tunnel@0.0.6, tunnel@^0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + resolved "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== type-fest@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== typed-rest-client@^1.8.4: version "1.8.9" - resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.8.9.tgz#e560226bcadfe71b0fb5c416b587f8da3b8f92d8" + resolved "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz" integrity sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g== dependencies: qs "^6.9.1" tunnel "0.0.6" underscore "^1.12.1" -typescript@^4.5.0-dev.20210817: - version "4.5.0-dev.20210817" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.0-dev.20210817.tgz#6b0d0ce68c2381cc85fd0d609817cb3576eb9480" - integrity sha512-G427tdOZrQKSEUcLF+dq57gK7D6CzxhbZggpEwqZP1HDuBhIk2bu+br9QvR5uoubR2P6lHhWhUZaCDmkIpnnDQ== +typescript@^4.8.0-dev.20220518: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== underscore@1.8.3: version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= underscore@^1.12.1: version "1.13.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.3.tgz#54bc95f7648c5557897e5e968d0f76bc062c34ee" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz" integrity sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA== universal-user-agent@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== universalify@^0.1.0: version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== url-join@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== url-parse-lax@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= dependencies: prepend-http "^2.0.0" util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= uuid@^8.3.0: version "8.3.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz" integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== -vinyl@^2.2.1: +vinyl-file@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz" + integrity sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg== + dependencies: + graceful-fs "^4.1.2" + pify "^2.3.0" + strip-bom-buf "^1.0.0" + strip-bom-stream "^2.0.0" + vinyl "^2.0.1" + +vinyl@^2.0.1, vinyl@^2.2.0, vinyl@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" + resolved "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz" integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== dependencies: clone "^2.1.1" @@ -3120,9 +3336,52 @@ vinyl@^2.2.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" +vsce@2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/vsce/-/vsce-2.8.0.tgz" + integrity sha512-p6BTbUVp33Ed0OWRRhRQT55yrmgLEca2fTmqxZJW44T1eP4yVWEsdaNIDsjFIeuCrjG/CYvwi1QLG4ql0s7bDA== + dependencies: + azure-devops-node-api "^11.0.1" + chalk "^2.4.2" + cheerio "^1.0.0-rc.9" + commander "^6.1.0" + glob "^7.0.6" + hosted-git-info "^4.0.2" + keytar "^7.7.0" + leven "^3.1.0" + markdown-it "^12.3.2" + mime "^1.3.4" + minimatch "^3.0.3" + parse-semver "^1.1.1" + read "^1.0.7" + semver "^5.1.0" + tmp "^0.2.1" + typed-rest-client "^1.8.4" + url-join "^4.0.1" + xml2js "^0.4.23" + yauzl "^2.3.1" + yazl "^2.2.2" + +vscode-gulp-watch@^5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/vscode-gulp-watch/-/vscode-gulp-watch-5.0.3.tgz" + integrity sha512-MTUp2yLE9CshhkNSNV58EQNxQSeF8lIj3mkXZX9a1vAk+EQNM2PAYdPUDSd/P/08W3PMHGznEiZyfK7JAjLosg== + dependencies: + ansi-colors "4.1.1" + anymatch "^3.1.1" + chokidar "3.5.1" + fancy-log "^1.3.3" + glob-parent "^5.1.1" + normalize-path "^3.0.0" + object-assign "^4.1.1" + plugin-error "1.0.1" + readable-stream "^3.6.0" + vinyl "^2.2.0" + vinyl-file "^3.0.0" + vscode-universal-bundler@^0.0.2: version "0.0.2" - resolved "https://registry.yarnpkg.com/vscode-universal-bundler/-/vscode-universal-bundler-0.0.2.tgz#2c988dac681d3ffe6baec6defac0995cb833c55a" + resolved "https://registry.npmjs.org/vscode-universal-bundler/-/vscode-universal-bundler-0.0.2.tgz" integrity sha512-FPJcvKnQGBqFzy6M6Nm2yvAczNLUeXsfYM6GwCex/pUOkvIM2icIHmiSvtMJINlLW1iG+oEwE3/LVbABmcjEmQ== dependencies: "@malept/cross-spawn-promise" "^1.1.0" @@ -3133,12 +3392,12 @@ vscode-universal-bundler@^0.0.2: webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: tr46 "~0.0.3" @@ -3146,26 +3405,26 @@ whatwg-url@^5.0.0: which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== dependencies: string-width "^1.0.2 || 2 || 3 || 4" wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= xml2js@^0.4.23: version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz" integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== dependencies: sax ">=0.6.0" @@ -3181,27 +3440,27 @@ xml2js@^0.5.0: xmlbuilder@>=11.0.1: version "15.1.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz" integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== xmlbuilder@^9.0.7: version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= xmlbuilder@~11.0.0: version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yauzl@^2.10.0, yauzl@^2.3.1: version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= dependencies: buffer-crc32 "~0.2.3" @@ -3209,17 +3468,17 @@ yauzl@^2.10.0, yauzl@^2.3.1: yazl@^2.2.2: version "2.5.1" - resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" + resolved "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz" integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== dependencies: buffer-crc32 "~0.2.3" yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== zone.js@0.7.6: version "0.7.6" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + resolved "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz" integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/cglicenses.json b/cglicenses.json index e969eb826e..5612be6269 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -147,5 +147,58 @@ "", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] + }, + { + "name": "@vscode/win32-app-container-tokens", + "fullLicenseText": [ + "MIT License", + "", + "Copyright (c) Microsoft Corporation.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE" + ] + }, + { + // Reason: Waiting for https://github.com/microsoft/vscode-markdown-languageservice/pull/9 + "name": "vscode-markdown-languageservice", + "fullLicenseText": [ + "MIT License", + "", + "Copyright (c) Microsoft Corporation.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE" + ] } ] diff --git a/cgmanifest.json b/cgmanifest.json index f4cd487d91..290816d4d2 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "16e28102fdf876ce6d136674ba66343ede07441f" + "commitHash": "c53c15c92c076f8d7593518ba99a9f8a6fc5ead6" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "100.0.4894.0" + "version": "102.0.5005.148" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "acb71eab779fb56bf70e8a9e0cb2e82a089a87de" + "commitHash": "442e84a358d75152556b5d087e4dd6a51615330d" } }, "isOnlyProductionDependency": true, - "version": "16.13.2" + "version": "16.14.2" }, { "component": { @@ -539,6 +539,40 @@ "isOnlyProductionDependency": true, "license": "MIT", "version": "0.10.0" + }, + { + "name": "@vscode/win32-app-container-tokens", + "component": { + "type": "git", + "git": { + "name": "vscode-win32-app-container-tokens", + "repositoryUrl": "https://github.com/microsoft/vscode-win32-app-container-tokens", + "commitHash": "5b871f95fd9cb8efa8ee9a80600510d5e5339137" + } + }, + "licenseDetail":[ + "MIT License", + "", + "Copyright (c) Microsoft Corporation.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE" + ] } ], "version": 1 diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index f2ebd926da..fdd714a55c 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -52,7 +52,8 @@ ".devcontainer.json" ], "filenamePatterns": [ - "**/User/snippets/*.json" + "**/User/snippets/*.json", + "**/User/profiles/*/snippets/*.json" ] }, { "id": "json", @@ -78,6 +79,10 @@ "fileMatch": "%APP_SETTINGS_HOME%/settings.json", "url": "vscode://schemas/settings/user" }, + { + "fileMatch": "%APP_SETTINGS_HOME%/profiles/*/settings.json", + "url": "vscode://schemas/settings/profile" + }, { "fileMatch": "%MACHINE_SETTINGS_HOME%/settings.json", "url": "vscode://schemas/settings/machine" diff --git a/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json b/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json new file mode 100644 index 0000000000..d224f91910 --- /dev/null +++ b/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json @@ -0,0 +1,189 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "customizations": { + "type": "object", + "properties": { + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*\/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + } + } + } + } + } + }, + "codespaces": { + "type": "object", + "additionalProperties": true, + "description": "Codespaces-specific configuration.", + "deprecated": true, + "deprecationMessage": "Use 'customizations/codespaces' instead" + } + } +} diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json index d47f119240..4ae5033f3f 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.generated.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -406,6 +406,7 @@ }, "customizations": { "type": "object", + "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.", "properties": { "vscode": { "type": "object", @@ -429,13 +430,186 @@ } }, "additionalProperties": false + }, + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, - "additionalProperties": { - "type": "object", - "additionalProperties": true - }, - "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations." + "additionalProperties": false + }, + "additionalProperties": { + "type": "object", + "additionalProperties": true } }, "required": [ @@ -842,6 +1016,7 @@ }, "customizations": { "type": "object", + "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.", "properties": { "vscode": { "type": "object", @@ -865,13 +1040,186 @@ } }, "additionalProperties": false + }, + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, - "additionalProperties": { - "type": "object", - "additionalProperties": true - }, - "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations." + "additionalProperties": false + }, + "additionalProperties": { + "type": "object", + "additionalProperties": true } }, "required": [ @@ -1244,6 +1592,7 @@ }, "customizations": { "type": "object", + "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.", "properties": { "vscode": { "type": "object", @@ -1267,13 +1616,186 @@ } }, "additionalProperties": false + }, + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, - "additionalProperties": { - "type": "object", - "additionalProperties": true - }, - "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations." + "additionalProperties": false + }, + "additionalProperties": { + "type": "object", + "additionalProperties": true } }, "required": [ @@ -1620,6 +2142,7 @@ }, "customizations": { "type": "object", + "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.", "properties": { "vscode": { "type": "object", @@ -1643,13 +2166,186 @@ } }, "additionalProperties": false + }, + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, - "additionalProperties": { - "type": "object", - "additionalProperties": true - }, - "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations." + "additionalProperties": false + }, + "additionalProperties": { + "type": "object", + "additionalProperties": true } }, "required": [ @@ -1961,6 +2657,7 @@ }, "customizations": { "type": "object", + "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.", "properties": { "vscode": { "type": "object", @@ -1984,13 +2681,186 @@ } }, "additionalProperties": false + }, + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, - "additionalProperties": { - "type": "object", - "additionalProperties": true - }, - "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations." + "additionalProperties": false + }, + "additionalProperties": { + "type": "object", + "additionalProperties": true } }, "additionalProperties": false diff --git a/extensions/configuration-editing/schemas/devContainer.schema.src.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json index b0e5b5c6f4..73456101bb 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.src.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -309,6 +309,7 @@ }, "customizations": { "type": "object", + "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations.", "properties": { "vscode": { "type": "object", @@ -331,13 +332,183 @@ "description": "The port VS Code can use to connect to its backend." } } + }, + "codespaces": { + "type": "object", + "description": "Customizations specific to GitHub Codespaces", + "properties": { + "repositories": { + "type": "object", + "description": "Configuration relative to the given repositories, following the format 'owner/repo'.\n A wildcard (*) is permitted for the repo name (eg: 'microsoft/*')", + "patternProperties": { + "^[a-zA-Z0-9-_.]+[.]*\/[a-zA-Z0-9-_*]+[.]*$": { + "type": "object", + "additionalProperties": true, + "oneOf": [ + { + "properties": { + "permissions": { + "type": "object", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "additionalProperties": true, + "anyOf": [ + { + "properties": { + "actions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "checks": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "contents": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "deployments": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "discussions": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "issues": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "packages": { + "type": "string", + "enum": [ + "read" + ] + } + } + }, + { + "properties": { + "pages": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "pull_requests": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "repository_projects": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "statuses": { + "type": "string", + "enum": [ + "read", + "write" + ] + } + } + }, + { + "properties": { + "workflows": { + "type": "string", + "enum": [ + "write" + ] + } + } + } + ] + } + } + }, + { + "properties": { + "permissions": { + "type": "string", + "description": "Additional repository permissions.\n See https://aka.ms/ghcs/multi-repo-auth for more info.", + "enum": [ + "read-all", + "write-all" + ] + } + } + } + ] + } + } + } + } } - }, - "additionalProperties": { - "type": "object", - "additionalProperties": true - }, - "description": "Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations." + } + }, + "additionalProperties": { + "type": "object", + "additionalProperties": true } } }, diff --git a/extensions/configuration-editing/schemas/devContainer.vscode.schema.json b/extensions/configuration-editing/schemas/devContainer.vscode.schema.json new file mode 100644 index 0000000000..e0f2a8aa68 --- /dev/null +++ b/extensions/configuration-editing/schemas/devContainer.vscode.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "customizations": { + "type": "object", + "properties": { + "vscode": { + "type": "object", + "properties": { + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)(@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again." + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + } + } + } + } + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string", + "pattern": "^([a-z0-9A-Z][a-z0-9A-Z-]*)\\.([a-z0-9A-Z][a-z0-9A-Z-]*)((@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)|@prerelease)?$", + "errorMessage": "Expected format: '${publisher}.${name}' or '${publisher}.${name}@${version}'. Example: 'ms-dotnettools.csharp'." + }, + "deprecated": true, + "deprecationMessage": "Use 'customizations/vscode/extensions' instead" + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again.", + "deprecated": true, + "deprecationMessage": "Use 'customizations/vscode/settings' instead" + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend.", + "deprecated": true, + "deprecationMessage": "Use 'customizations/vscode/devPort' instead" + } + } +} diff --git a/extensions/configuration-editing/src/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts index 37bac75c41..2a08fc7791 100644 --- a/extensions/configuration-editing/src/configurationEditingMain.ts +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getLocation, JSONPath, parse, visit } from 'jsonc-parser'; +import { getLocation, JSONPath, parse, visit, Location } from 'jsonc-parser'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { SettingsDocument } from './settingsDocumentHelper'; @@ -39,9 +39,11 @@ function registerVariableCompletions(pattern: string): vscode.Disposable { return vscode.languages.registerCompletionItemProvider({ language: 'jsonc', pattern }, { provideCompletionItems(document, position, _token) { const location = getLocation(document.getText(), document.offsetAt(position)); - if (!location.isAtPropertyKey && location.previousNode && location.previousNode.type === 'string') { - const indexOf$ = document.lineAt(position.line).text.lastIndexOf('$', position.character); - const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position; + if (isCompletingInsidePropertyStringValue(document, location, position)) { + let range = document.getWordRangeAtPosition(position, /\$\{[^"\}]*\}?/); + if (!range || range.start.isEqual(position) || range.end.isEqual(position) && document.getText(range).endsWith('}')) { + range = new vscode.Range(position, position); + } return [ { label: 'workspaceFolder', detail: localize('workspaceFolder', "The path of the folder opened in VS Code") }, @@ -61,7 +63,7 @@ function registerVariableCompletions(pattern: string): vscode.Disposable { { label: 'extensionInstallFolder', detail: localize('extensionInstallFolder', "The path where an an extension is installed."), param: 'publisher.extension' }, ].map(variable => ({ label: `\${${variable.label}}`, - range: new vscode.Range(startPosition, position), + range, insertText: variable.param ? new vscode.SnippetString(`\${${variable.label}:`).appendPlaceholder(variable.param).appendText('}') : (`\${${variable.label}}`), detail: variable.detail })); @@ -72,6 +74,18 @@ function registerVariableCompletions(pattern: string): vscode.Disposable { }); } +function isCompletingInsidePropertyStringValue(document: vscode.TextDocument, location: Location, pos: vscode.Position) { + if (location.isAtPropertyKey) { + return false; + } + const previousNode = location.previousNode; + if (previousNode && previousNode.type === 'string') { + const offset = document.offsetAt(pos); + return offset > previousNode.offset && offset < previousNode.offset + previousNode.length; + } + return false; +} + interface IExtensionsContent { recommendations: string[]; } @@ -84,8 +98,8 @@ function registerExtensionsCompletionsInExtensionsDocument(): vscode.Disposable return vscode.languages.registerCompletionItemProvider({ pattern: '**/extensions.json' }, { provideCompletionItems(document, position, _token) { const location = getLocation(document.getText(), document.offsetAt(position)); - const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); if (location.path[0] === 'recommendations') { + const range = getReplaceRange(document, location, position); const extensionsContent = parse(document.getText()); return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], '', range, false); } @@ -98,8 +112,8 @@ function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode return vscode.languages.registerCompletionItemProvider({ pattern: '**/*.code-workspace' }, { provideCompletionItems(document, position, _token) { const location = getLocation(document.getText(), document.offsetAt(position)); - const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); if (location.path[0] === 'extensions' && location.path[1] === 'recommendations') { + const range = getReplaceRange(document, location, position); const extensionsContent = parse(document.getText())['extensions']; return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], '', range, false); } @@ -108,6 +122,17 @@ function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode }); } +function getReplaceRange(document: vscode.TextDocument, location: Location, position: vscode.Position) { + const node = location.previousNode; + if (node) { + const nodeStart = document.positionAt(node.offset), nodeEnd = document.positionAt(node.offset + node.length); + if (nodeStart.isBeforeOrEqual(position) && nodeEnd.isAfterOrEqual(position)) { + return new vscode.Range(nodeStart, nodeEnd); + } + } + return new vscode.Range(position, position); +} + vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, { provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult { const result: vscode.SymbolInformation[] = []; @@ -180,28 +205,11 @@ function registerContextKeyCompletions(): vscode.Disposable { } } - if (!isValidLocation) { + if (!isValidLocation || !isCompletingInsidePropertyStringValue(document, location, position)) { return; } - // for JSON everything with quotes is a word - const jsonWord = document.getWordRangeAtPosition(position); - if (!jsonWord || jsonWord.start.isEqual(position) || jsonWord.end.isEqual(position)) { - // we aren't inside a "JSON word" or on its quotes - return; - } - - let replacing: vscode.Range | undefined; - if (jsonWord.end.character - jsonWord.start.character === 2 || document.getWordRangeAtPosition(position, /\s+/)) { - // empty json word or on whitespace - replacing = new vscode.Range(position, position); - } else { - replacing = document.getWordRangeAtPosition(position, /[a-zA-Z.]+/); - } - - if (!replacing) { - return; - } + const replacing = document.getWordRangeAtPosition(position, /[a-zA-Z.]+/) || new vscode.Range(position, position); const inserting = replacing.with(undefined, position); const data = await vscode.commands.executeCommand('getContextKeyInfo'); diff --git a/extensions/configuration-editing/src/extensionsProposals.ts b/extensions/configuration-editing/src/extensionsProposals.ts index 14f9226e85..419a23e879 100644 --- a/extensions/configuration-editing/src/extensionsProposals.ts +++ b/extensions/configuration-editing/src/extensionsProposals.ts @@ -8,7 +8,7 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -export function provideInstalledExtensionProposals(existing: string[], additionalText: string, range: vscode.Range, includeBuiltinExtensions: boolean): vscode.ProviderResult { +export async function provideInstalledExtensionProposals(existing: string[], additionalText: string, range: vscode.Range, includeBuiltinExtensions: boolean): Promise { if (Array.isArray(existing)) { const extensions = includeBuiltinExtensions ? vscode.extensions.all : vscode.extensions.all.filter(e => !(e.id.startsWith('vscode.') || e.id === 'Microsoft.vscode-markdown')); const knownExtensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1); @@ -30,10 +30,10 @@ export function provideInstalledExtensionProposals(existing: string[], additiona return [example]; } } - return undefined; + return []; } -export function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): vscode.ProviderResult { +export async function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): Promise { if (Array.isArray(existing)) { const extensions = vscode.extensions.all.filter(e => e.packageJSON.main); const extensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1); @@ -56,5 +56,5 @@ export function provideWorkspaceTrustExtensionProposals(existing: string[], rang } } - return undefined; + return []; } diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index f685a820ae..2a001cd382 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -15,32 +15,27 @@ export class SettingsDocument { constructor(private document: vscode.TextDocument) { } - public provideCompletionItems(position: vscode.Position, _token: vscode.CancellationToken): vscode.ProviderResult { + public async provideCompletionItems(position: vscode.Position, _token: vscode.CancellationToken): Promise { const location = getLocation(this.document.getText(), this.document.offsetAt(position)); - const range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position); // window.title if (location.path[0] === 'window.title') { - return this.provideWindowTitleCompletionItems(location, range); + return this.provideWindowTitleCompletionItems(location, position); } // files.association if (location.path[0] === 'files.associations') { - return this.provideFilesAssociationsCompletionItems(location, range); + return this.provideFilesAssociationsCompletionItems(location, position); } // files.exclude, search.exclude if (location.path[0] === 'files.exclude' || location.path[0] === 'search.exclude') { - return this.provideExcludeCompletionItems(location, range); + return this.provideExcludeCompletionItems(location, position); } // files.defaultLanguage if (location.path[0] === 'files.defaultLanguage') { - return this.provideLanguageCompletionItems(location, range).then(items => { - - // Add special item '${activeEditorLanguage}' - return [this.newSimpleCompletionItem(JSON.stringify('${activeEditorLanguage}'), range, localize('activeEditor', "Use the language of the currently active text editor if any")), ...items]; - }); + return this.provideLanguageCompletionItems(location, position); } // settingsSync.ignoredExtensions @@ -49,6 +44,7 @@ export class SettingsDocument { try { ignoredExtensions = parse(this.document.getText())['settingsSync.ignoredExtensions']; } catch (e) {/* ignore error */ } + const range = this.getReplaceRange(location, position); return provideInstalledExtensionProposals(ignoredExtensions, '', range, true); } @@ -58,44 +54,85 @@ export class SettingsDocument { try { alreadyConfigured = Object.keys(parse(this.document.getText())['remote.extensionKind']); } catch (e) {/* ignore error */ } - return provideInstalledExtensionProposals(alreadyConfigured, `: [\n\t"ui"\n]`, range, true); + const range = this.getReplaceRange(location, position); + return provideInstalledExtensionProposals(alreadyConfigured, location.previousNode ? '' : `: [\n\t"ui"\n]`, range, true); } // remote.portsAttributes if (location.path[0] === 'remote.portsAttributes' && location.path.length === 2 && location.isAtPropertyKey) { - return this.providePortsAttributesCompletionItem(range); + return this.providePortsAttributesCompletionItem(this.getReplaceRange(location, position)); } return this.provideLanguageOverridesCompletionItems(location, position); } - private provideWindowTitleCompletionItems(_location: Location, range: vscode.Range): vscode.ProviderResult { - const completions: vscode.CompletionItem[] = []; - - completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "the file name (e.g. myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeFolderShort}', range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)"))); - completions.push(this.newSimpleCompletionItem('${activeFolderMedium}', range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)"))); - completions.push(this.newSimpleCompletionItem('${activeFolderLong}', range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)"))); - completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)"))); - completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)"))); - completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)"))); - completions.push(this.newSimpleCompletionItem('${folderPath}', range, localize('folderPath', "file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)"))); - completions.push(this.newSimpleCompletionItem('${appName}', range, localize('appName', "e.g. VS Code"))); - completions.push(this.newSimpleCompletionItem('${remoteName}', range, localize('remoteName', "e.g. SSH"))); - completions.push(this.newSimpleCompletionItem('${dirty}', range, localize('dirty', "an indicator for when the active editor has unsaved changes"))); - completions.push(this.newSimpleCompletionItem('${separator}', range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values"))); - - return Promise.resolve(completions); + private getReplaceRange(location: Location, position: vscode.Position) { + const node = location.previousNode; + if (node) { + const nodeStart = this.document.positionAt(node.offset), nodeEnd = this.document.positionAt(node.offset + node.length); + if (nodeStart.isBeforeOrEqual(position) && nodeEnd.isAfterOrEqual(position)) { + return new vscode.Range(nodeStart, nodeEnd); + } + } + return new vscode.Range(position, position); } - private provideFilesAssociationsCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult { + private isCompletingPropertyValue(location: Location, pos: vscode.Position) { + if (location.isAtPropertyKey) { + return false; + } + const previousNode = location.previousNode; + if (previousNode) { + const offset = this.document.offsetAt(pos); + return offset >= previousNode.offset && offset <= previousNode.offset + previousNode.length; + } + return true; + } + + private async provideWindowTitleCompletionItems(location: Location, pos: vscode.Position): Promise { + const completions: vscode.CompletionItem[] = []; + + if (!this.isCompletingPropertyValue(location, pos)) { + return completions; + } + + let range = this.document.getWordRangeAtPosition(pos, /\$\{[^"\}]*\}?/); + if (!range || range.start.isEqual(pos) || range.end.isEqual(pos) && this.document.getText(range).endsWith('}')) { + range = new vscode.Range(pos, pos); + } + + const getText = (variable: string) => { + const text = '${' + variable + '}'; + return location.previousNode ? text : JSON.stringify(text); + }; + + + completions.push(this.newSimpleCompletionItem(getText('activeEditorShort'), range, localize('activeEditorShort', "the file name (e.g. myFile.txt)"))); + completions.push(this.newSimpleCompletionItem(getText('activeEditorMedium'), range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem(getText('activeEditorLong'), range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem(getText('activeFolderShort'), range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)"))); + completions.push(this.newSimpleCompletionItem(getText('activeFolderMedium'), range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)"))); + completions.push(this.newSimpleCompletionItem(getText('activeFolderLong'), range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)"))); + completions.push(this.newSimpleCompletionItem(getText('rootName'), range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)"))); + completions.push(this.newSimpleCompletionItem(getText('rootPath'), range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)"))); + completions.push(this.newSimpleCompletionItem(getText('folderName'), range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)"))); + completions.push(this.newSimpleCompletionItem(getText('folderPath'), range, localize('folderPath', "file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)"))); + completions.push(this.newSimpleCompletionItem(getText('appName'), range, localize('appName', "e.g. VS Code"))); + completions.push(this.newSimpleCompletionItem(getText('remoteName'), range, localize('remoteName', "e.g. SSH"))); + completions.push(this.newSimpleCompletionItem(getText('dirty'), range, localize('dirty', "an indicator for when the active editor has unsaved changes"))); + completions.push(this.newSimpleCompletionItem(getText('separator'), range, localize('separator', "a conditional separator (' - ') that only shows when surrounded by variables with values"))); + + return completions; + } + + private async provideFilesAssociationsCompletionItems(location: Location, position: vscode.Position): Promise { const completions: vscode.CompletionItem[] = []; if (location.path.length === 2) { // Key - if (!location.isAtPropertyKey || location.path[1] === '') { + if (location.path[1] === '') { + const range = this.getReplaceRange(location, position); + completions.push(this.newSnippetCompletionItem({ label: localize('assocLabelFile', "Files with Extension"), documentation: localize('assocDescriptionFile', "Map all files matching the glob pattern in their filename to the language with the given identifier."), @@ -109,68 +146,68 @@ export class SettingsDocument { snippet: location.isAtPropertyKey ? '"/${1:path to file}/*.${2:extension}": "${3:language}"' : '{ "/${1:path to file}/*.${2:extension}": "${3:language}" }', range })); - } else { + } else if (this.isCompletingPropertyValue(location, position)) { // Value - return this.provideLanguageCompletionItemsForLanguageOverrides(location, range); + return this.provideLanguageCompletionItemsForLanguageOverrides(this.getReplaceRange(location, position)); } } - return Promise.resolve(completions); + return completions; } - private provideExcludeCompletionItems(location: Location, range: vscode.Range): vscode.ProviderResult { + private async provideExcludeCompletionItems(location: Location, position: vscode.Position): Promise { const completions: vscode.CompletionItem[] = []; // Key - if (location.path.length === 1) { + if (location.path.length === 1 || (location.path.length === 2 && location.path[1] === '')) { + const range = this.getReplaceRange(location, position); + completions.push(this.newSnippetCompletionItem({ label: localize('fileLabel', "Files by Extension"), documentation: localize('fileDescription', "Match all files of a specific file extension."), - snippet: location.isAtPropertyKey ? '"**/*.${1:extension}": true' : '{ "**/*.${1:extension}": true }', + snippet: location.path.length === 2 ? '"**/*.${1:extension}": true' : '{ "**/*.${1:extension}": true }', range })); completions.push(this.newSnippetCompletionItem({ label: localize('filesLabel', "Files with Multiple Extensions"), documentation: localize('filesDescription', "Match all files with any of the file extensions."), - snippet: location.isAtPropertyKey ? '"**/*.{ext1,ext2,ext3}": true' : '{ "**/*.{ext1,ext2,ext3}": true }', + snippet: location.path.length === 2 ? '"**/*.{ext1,ext2,ext3}": true' : '{ "**/*.{ext1,ext2,ext3}": true }', range })); completions.push(this.newSnippetCompletionItem({ label: localize('derivedLabel', "Files with Siblings by Name"), documentation: localize('derivedDescription', "Match files that have siblings with the same name but a different extension."), - snippet: location.isAtPropertyKey ? '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }' : '{ "**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" } }', + snippet: location.path.length === 2 ? '"**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" }' : '{ "**/*.${1:source-extension}": { "when": "$(basename).${2:target-extension}" } }', range })); completions.push(this.newSnippetCompletionItem({ label: localize('topFolderLabel', "Folder by Name (Top Level)"), documentation: localize('topFolderDescription', "Match a top level folder with a specific name."), - snippet: location.isAtPropertyKey ? '"${1:name}": true' : '{ "${1:name}": true }', + snippet: location.path.length === 2 ? '"${1:name}": true' : '{ "${1:name}": true }', range })); completions.push(this.newSnippetCompletionItem({ label: localize('topFoldersLabel', "Folders with Multiple Names (Top Level)"), documentation: localize('topFoldersDescription', "Match multiple top level folders."), - snippet: location.isAtPropertyKey ? '"{folder1,folder2,folder3}": true' : '{ "{folder1,folder2,folder3}": true }', + snippet: location.path.length === 2 ? '"{folder1,folder2,folder3}": true' : '{ "{folder1,folder2,folder3}": true }', range })); completions.push(this.newSnippetCompletionItem({ label: localize('folderLabel', "Folder by Name (Any Location)"), documentation: localize('folderDescription', "Match a folder with a specific name in any location."), - snippet: location.isAtPropertyKey ? '"**/${1:name}": true' : '{ "**/${1:name}": true }', + snippet: location.path.length === 2 ? '"**/${1:name}": true' : '{ "**/${1:name}": true }', range })); } // Value - else { - completions.push(this.newSimpleCompletionItem('false', range, localize('falseDescription', "Disable the pattern."))); - completions.push(this.newSimpleCompletionItem('true', range, localize('trueDescription', "Enable the pattern."))); - + else if (location.path.length === 2 && this.isCompletingPropertyValue(location, position)) { + const range = this.getReplaceRange(location, position); completions.push(this.newSnippetCompletionItem({ label: localize('derivedLabel', "Files with Siblings by Name"), documentation: localize('siblingsDescription', "Match files that have siblings with the same name but a different extension."), @@ -179,15 +216,22 @@ export class SettingsDocument { })); } - return Promise.resolve(completions); + return completions; } - private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable { - return vscode.languages.getLanguages() - .then(languages => languages.map(l => this.newSimpleCompletionItem(formatFunc(l), range))); + private async provideLanguageCompletionItems(location: Location, position: vscode.Position): Promise { + if (location.path.length === 1 && this.isCompletingPropertyValue(location, position)) { + const range = this.getReplaceRange(location, position); + const languages = await vscode.languages.getLanguages(); + return [ + this.newSimpleCompletionItem(JSON.stringify('${activeEditorLanguage}'), range, localize('activeEditor', "Use the language of the currently active text editor if any")), + ...languages.map(l => this.newSimpleCompletionItem(JSON.stringify(l), range)) + ]; + } + return []; } - private async provideLanguageCompletionItemsForLanguageOverrides(_location: Location, range: vscode.Range): Promise { + private async provideLanguageCompletionItemsForLanguageOverrides(range: vscode.Range): Promise { const languages = await vscode.languages.getLanguages(); const completionItems = []; for (const language of languages) { @@ -200,7 +244,7 @@ export class SettingsDocument { } private async provideLanguageOverridesCompletionItems(location: Location, position: vscode.Position): Promise { - if (location.path.length === 1 && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) { + if (location.path.length === 1 && location.isAtPropertyKey && location.previousNode && typeof location.previousNode.value === 'string' && location.previousNode.value.startsWith('[')) { const startPosition = this.document.positionAt(location.previousNode.offset + 1); const endPosition = startPosition.translate(undefined, location.previousNode.value.length); const donotSuggestLanguages: string[] = []; diff --git a/extensions/configuration-editing/src/test/completion.test.ts b/extensions/configuration-editing/src/test/completion.test.ts new file mode 100644 index 0000000000..f353f317ac --- /dev/null +++ b/extensions/configuration-editing/src/test/completion.test.ts @@ -0,0 +1,594 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as assert from 'assert'; +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import 'mocha'; + + +const testFolder = fs.mkdtemp(path.join(os.tmpdir(), 'conf-editing-')); + +suite('Completions in settings.json', () => { + const testFile = 'settings.json'; + + test('window.title', async () => { + { // inserting after text + const content = [ + '{', + ' "window.title": "custom|"', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "window.title": "custom${activeEditorShort}"', + '}', + ].join('\n'); + const expected = { label: '${activeEditorShort}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { // inserting before a variable + const content = [ + '{', + ' "window.title": "|${activeEditorShort}"', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "window.title": "${folderPath}${activeEditorShort}"', + '}', + ].join('\n'); + const expected = { label: '${folderPath}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { // inserting after a variable + const content = [ + '{', + ' "window.title": "${activeEditorShort}|"', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "window.title": "${activeEditorShort}${folderPath}"', + '}', + ].join('\n'); + const expected = { label: '${folderPath}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { // replacing an variable + const content = [ + '{', + ' "window.title": "${a|ctiveEditorShort}"', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "window.title": "${activeEditorMedium}"', + '}', + ].join('\n'); + const expected = { label: '${activeEditorMedium}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { // replacing a partial variable + const content = [ + '{', + ' "window.title": "${a|"', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "window.title": "${dirty}"', + '}', + ].join('\n'); + const expected = { label: '${dirty}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { // inserting a literal + const content = [ + '{', + ' "window.title": |', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "window.title": "${activeEditorMedium}"', + '}', + ].join('\n'); + const expected = { label: '"${activeEditorMedium}"', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { // no proposals after literal + const content = [ + '{', + ' "window.title": "${activeEditorShort}" |', + '}', + ].join('\n'); + const expected = { label: '${activeEditorMedium}', notAvailable: true }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + + test('files.associations', async () => { + { + const content = [ + '{', + ' "files.associations": {', + ' |', + ' }', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.associations": {', + ' "*.${1:extension}": "${2:language}"', + ' }', + '}', + ].join('\n'); + const expected = { label: 'Files with Extension', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "files.associations": {', + ' |', + ' }', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.associations": {', + ' "/${1:path to file}/*.${2:extension}": "${3:language}"', + ' }', + '}', + ].join('\n'); + const expected = { label: 'Files with Path', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "files.associations": {', + ' "*.extension": "|bat"', + ' }', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.associations": {', + ' "*.extension": "json"', + ' }', + '}', + ].join('\n'); + const expected = { label: '"json"', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "files.associations": {', + ' "*.extension": "bat"|', + ' }', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.associations": {', + ' "*.extension": "json"', + ' }', + '}', + ].join('\n'); + const expected = { label: '"json"', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "files.associations": {', + ' "*.extension": "bat" |', + ' }', + '}', + ].join('\n'); + const expected = { label: '"json"', notAvailable: true }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + test('files.exclude', async () => { + { + const content = [ + '{', + ' "files.exclude": {', + ' |', + ' }', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.exclude": {', + ' "**/*.${1:extension}": true', + ' }', + '}', + ].join('\n'); + const expected = { label: 'Files by Extension', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "files.exclude": {', + ' "**/*.extension": |true', + ' }', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.exclude": {', + ' "**/*.extension": { "when": "$(basename).${1:extension}" }', + ' }', + '}', + ].join('\n'); + const expected = { label: 'Files with Siblings by Name', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + test('files.defaultLanguage', async () => { + { + const content = [ + '{', + ' "files.defaultLanguage": "json|"', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.defaultLanguage": "jsonc"', + '}', + ].join('\n'); + const expected = { label: '"jsonc"', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "files.defaultLanguage": |', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "files.defaultLanguage": "jsonc"', + '}', + ].join('\n'); + const expected = { label: '"jsonc"', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + test('remote.extensionKind', async () => { + { + const content = [ + '{', + '\t"remote.extensionKind": {', + '\t\t|', + '\t}', + '}', + ].join('\n'); + const expected = { label: 'vscode.npm' }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + test('remote.portsAttributes', async () => { + { + const content = [ + '{', + ' "remote.portsAttributes": {', + ' |', + ' }', + '}', + ].join('\n'); + const expected = { label: '"3000"' }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); +}); + +suite('Completions in extensions.json', () => { + const testFile = 'extensions.json'; + test('change recommendation', async () => { + { + const content = [ + '{', + ' "recommendations": [', + ' "|a.b"', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "recommendations": [', + ' "ms-vscode.js-debug"', + ' ]', + '}', + ].join('\n'); + const expected = { label: 'ms-vscode.js-debug', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + test('add recommendation', async () => { + { + const content = [ + '{', + ' "recommendations": [', + ' |', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "recommendations": [', + ' "ms-vscode.js-debug"', + ' ]', + '}', + ].join('\n'); + const expected = { label: 'ms-vscode.js-debug', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); +}); + +suite('Completions in launch.json', () => { + const testFile = 'launch.json'; + test('variable completions', async () => { + { + const content = [ + '{', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Run Extension",', + ' "type": "extensionHost",', + ' "preLaunchTask": "${|defaultBuildTask}"', + ' }', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Run Extension",', + ' "type": "extensionHost",', + ' "preLaunchTask": "${cwd}"', + ' }', + ' ]', + '}', + ].join('\n'); + const expected = { label: '${cwd}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Run Extension",', + ' "type": "extensionHost",', + ' "preLaunchTask": "|${defaultBuildTask}"', + ' }', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Run Extension",', + ' "type": "extensionHost",', + ' "preLaunchTask": "${cwd}${defaultBuildTask}"', + ' }', + ' ]', + '}', + ].join('\n'); + const expected = { label: '${cwd}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Do It",', + ' "program": "${workspace|"', + ' }', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "version": "0.2.0",', + ' "configurations": [', + ' {', + ' "name": "Do It",', + ' "program": "${cwd}"', + ' }', + ' ]', + '}', + ].join('\n'); + const expected = { label: '${cwd}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); +}); + +suite('Completions in tasks.json', () => { + const testFile = 'tasks.json'; + test('variable completions', async () => { + { + const content = [ + '{', + ' "version": "0.2.0",', + ' "tasks": [', + ' {', + ' "type": "shell",', + ' "command": "${|defaultBuildTask}"', + ' }', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "version": "0.2.0",', + ' "tasks": [', + ' {', + ' "type": "shell",', + ' "command": "${cwd}"', + ' }', + ' ]', + '}', + ].join('\n'); + const expected = { label: '${cwd}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + { + const content = [ + '{', + ' "version": "0.2.0",', + ' "tasks": [', + ' {', + ' "type": "shell",', + ' "command": "${defaultBuildTask}|"', + ' }', + ' ]', + '}', + ].join('\n'); + const resultText = [ + '{', + ' "version": "0.2.0",', + ' "tasks": [', + ' {', + ' "type": "shell",', + ' "command": "${defaultBuildTask}${cwd}"', + ' }', + ' ]', + '}', + ].join('\n'); + const expected = { label: '${cwd}', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); +}); + +suite('Completions in keybindings.json', () => { + const testFile = 'keybindings.json'; + test('context key insertion', async () => { + { + const content = [ + '[', + ' {', + ' "key": "ctrl+k ctrl+,",', + ' "command": "editor.jumpToNextFold",', + ' "when": "|"', + ' }', + ']', + ].join('\n'); + const resultText = [ + '[', + ' {', + ' "key": "ctrl+k ctrl+,",', + ' "command": "editor.jumpToNextFold",', + ' "when": "resourcePath"', + ' }', + ']', + ].join('\n'); + const expected = { label: 'resourcePath', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); + + test('context key replace', async () => { + { + const content = [ + '[', + ' {', + ' "key": "ctrl+k ctrl+,",', + ' "command": "editor.jumpToNextFold",', + ' "when": "resou|rcePath"', + ' }', + ']', + ].join('\n'); + const resultText = [ + '[', + ' {', + ' "key": "ctrl+k ctrl+,",', + ' "command": "editor.jumpToNextFold",', + ' "when": "resource"', + ' }', + ']', + ].join('\n'); + const expected = { label: 'resource', resultText }; + await testCompletion(testFile, 'jsonc', content, expected); + } + }); +}); + +interface ItemDescription { + label: string; + resultText?: string; + notAvailable?: boolean; +} + +async function testCompletion(testFileName: string, languageId: string, content: string, expected: ItemDescription) { + + const offset = content.indexOf('|'); + content = content.substring(0, offset) + content.substring(offset + 1); + + const docUri = vscode.Uri.file(path.join(await testFolder, testFileName)); + await fs.writeFile(docUri.fsPath, content); + + const editor = await setTestContent(docUri, languageId, content); + const position = editor.document.positionAt(offset); + + // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion + const actualCompletions = (await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', docUri, position)) as vscode.CompletionList; + + const matches = actualCompletions.items.filter(completion => { + return completion.label === expected.label; + }); + if (expected.notAvailable) { + assert.strictEqual(matches.length, 0, `${expected.label} should not existing is results`); + } else { + assert.strictEqual(matches.length, 1, `${expected.label} should only existing once: Actual: ${actualCompletions.items.map(c => c.label).join(', ')}`); + + if (expected.resultText) { + const match = matches[0]; + if (match.range && match.insertText) { + const range = match.range instanceof vscode.Range ? match.range : match.range.replacing; + const text = typeof match.insertText === 'string' ? match.insertText : match.insertText.value; + + await editor.edit(eb => eb.replace(range, text)); + assert.strictEqual(editor.document.getText(), expected.resultText); + } else { + assert.fail(`Range or insertText missing`); + } + } + } +} + +async function setTestContent(docUri: vscode.Uri, languageId: string, content: string): Promise { + const ext = vscode.extensions.getExtension('vscode.configuration-editing')!; + await ext.activate(); + + const doc = await vscode.workspace.openTextDocument(docUri); + await vscode.languages.setTextDocumentLanguage(doc, languageId); + const editor = await vscode.window.showTextDocument(doc); + + const fullRange = new vscode.Range(new vscode.Position(0, 0), doc.positionAt(doc.getText().length)); + await editor.edit(eb => eb.replace(fullRange, content)); + return editor; + +} diff --git a/extensions/css-language-features/server/src/utils/validation.ts b/extensions/css-language-features/server/src/utils/validation.ts new file mode 100644 index 0000000000..07d40d2ff3 --- /dev/null +++ b/extensions/css-language-features/server/src/utils/validation.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-css-languageservice'; +import { formatError, runSafeAsync } from './runner'; +import { RuntimeEnvironment } from '../cssServer'; + +export type Validator = (textDocument: TextDocument) => Promise; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; + +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + const pendingValidationRequests: { [uri: string]: Disposable } = {}; + const validationDelayMs = 500; + + const disposables: Disposable[] = []; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }, undefined, disposables); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }, undefined, disposables); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + request.dispose(); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { + if (request === pendingValidationRequests[textDocument.uri]) { + try { + const diagnostics = await validate(textDocument); + if (request === pendingValidationRequests[textDocument.uri]) { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + } + delete pendingValidationRequests[textDocument.uri]; + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + }, validationDelayMs); + } + + return { + requestRefresh: () => { + documents.all().forEach(triggerValidation); + }, + dispose: () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + const keys = Object.keys(pendingValidationRequests); + for (const key of keys) { + pendingValidationRequests[key].dispose(); + delete pendingValidationRequests[key]; + } + } + }; +} + +export function registerDiagnosticsPullSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafeAsync(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + registration.dispose(); + } + }; + +} diff --git a/extensions/datavirtualization/src/typings/markdown-it-named-headers.d.ts b/extensions/datavirtualization/src/typings/markdown-it-named-headers.d.ts index de9b23d0df..c80a3af89d 100644 --- a/extensions/datavirtualization/src/typings/markdown-it-named-headers.d.ts +++ b/extensions/datavirtualization/src/typings/markdown-it-named-headers.d.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module 'markdown-it-named-headers' { } \ No newline at end of file +declare module 'markdown-it-named-headers' { } diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index c01c28355a..6841f8c880 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "8825a76681cdc14801b5d9490372ff67ec6b9711" + "commitHash": "e177bd7f9d3402f70d2f1fb42c74057ed1ccf6fa" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index f6cbec6c36..81c033d714 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/8825a76681cdc14801b5d9490372ff67ec6b9711", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/e177bd7f9d3402f70d2f1fb42c74057ed1ccf6fa", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -958,6 +958,26 @@ } ] }, + { + "name": "binding.fsharp", + "begin": "\\b(use|use\\!|and|and!)\\s*(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", + "end": "\\s*(=)", + "beginCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "patterns": [ + { + "include": "#common_binding_definition" + } + ] + }, { "name": "binding.fsharp", "begin": "(?<=with|and)\\s*\\b((get|set)\\s*(?=\\())(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", @@ -1275,11 +1295,11 @@ }, { "name": "constant.character.string.escape.fsharp", - "match": "\\\\([\\\\''ntbr]|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8})" + "match": "\\\\(['\"\\\\abfnrtv]|([01][0-9][0-9]|2[0-4][0-9]|25[0-5])|(x[0-9a-fA-F]{2})|(u[0-9a-fA-F]{4})|(U00(0[0-9a-fA-F]|10)[0-9a-fA-F]{4}))" }, { - "name": "invalid.illeagal.character.string.fsharp", - "match": "\\\\(?![\\\\''ntbr]|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|u[a-fA-F0-9]{8})." + "name": "invalid.illegal.character.string.fsharp", + "match": "\\\\(([0-9]{1,3})|(x[^\\s]{0,2})|(u[^\\s]{0,4})|(U[^\\s]{0,8})|[^\\s])" }, { "include": "#string_formatter" diff --git a/extensions/git-base/src/remoteSource.ts b/extensions/git-base/src/remoteSource.ts index b4a9e51470..9fd4bd61a5 100644 --- a/extensions/git-base/src/remoteSource.ts +++ b/extensions/git-base/src/remoteSource.ts @@ -49,11 +49,12 @@ class RemoteSourceProviderQuickPick { @throttle private async query(): Promise { try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick?.value) || []; - this.ensureQuickPick(); + this.quickpick!.busy = true; this.quickpick!.show(); + const remoteSources = await this.provider.getRemoteSources(this.quickpick?.value) || []; + if (remoteSources.length === 0) { this.quickpick!.items = [{ label: localize('none found', "No remote repositories found."), @@ -69,7 +70,7 @@ class RemoteSourceProviderQuickPick { })); } } catch (err) { - this.quickpick!.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; + this.quickpick!.items = [{ label: localize('error', "{0} Error: {1}", '$(error)', err.message), alwaysShow: true }]; console.error(err); } finally { this.quickpick!.busy = false; diff --git a/extensions/git/extension.webpack.config.js b/extensions/git/extension.webpack.config.js index 7c3b1a7408..de93775894 100644 --- a/extensions/git/extension.webpack.config.js +++ b/extensions/git/extension.webpack.config.js @@ -13,6 +13,7 @@ module.exports = withDefaults({ context: __dirname, entry: { main: './src/main.ts', - ['askpass-main']: './src/askpass-main.ts' + ['askpass-main']: './src/askpass-main.ts', + ['git-editor-main']: './src/git-editor-main.ts' } }); diff --git a/extensions/git/package.json b/extensions/git/package.json index ff804584ab..4933625182 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -8,13 +8,15 @@ "engines": { "vscode": "^1.5.0" }, - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "enabledApiProposals": [ "diffCommand", + "contribMergeEditorToolbar", "contribViewsWelcome", "scmActionButton", "scmSelectedProvider", "scmValidation", + "tabInputTextMerge", "timeline" ], "categories": [ @@ -22,7 +24,8 @@ ], "activationEvents": [ "*", - "onFileSystem:git" + "onFileSystem:git", + "onFileSystem:git-show" ], "extensionDependencies": [ "vscode.git-base" @@ -211,82 +214,110 @@ "command": "git.commit", "title": "%command.commit%", "category": "Git", - "icon": "$(check)" + "icon": "$(check)", + "enablement": "!commitInProgress" }, { "command": "git.commitStaged", "title": "%command.commitStaged%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitEmpty", "title": "%command.commitEmpty%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedSigned", "title": "%command.commitStagedSigned%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedAmend", "title": "%command.commitStagedAmend%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAll", "title": "%command.commitAll%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllSigned", "title": "%command.commitAllSigned%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllAmend", "title": "%command.commitAllAmend%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitNoVerify", "title": "%command.commitNoVerify%", "category": "Git", - "icon": "$(check)" + "icon": "$(check)", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedNoVerify", "title": "%command.commitStagedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitEmptyNoVerify", "title": "%command.commitEmptyNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedSignedNoVerify", "title": "%command.commitStagedSignedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedAmendNoVerify", "title": "%command.commitStagedAmendNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllNoVerify", "title": "%command.commitAllNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllSignedNoVerify", "title": "%command.commitAllSignedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllAmendNoVerify", "title": "%command.commitAllAmendNoVerify%", + "category": "Git", + "enablement": "!commitInProgress" + }, + { + "command": "git.commitMessageAccept", + "title": "%command.commitMessageAccept%", + "icon": "$(check)", + "category": "Git" + }, + { + "command": "git.commitMessageDiscard", + "title": "%command.commitMessageDiscard%", + "icon": "$(discard)", "category": "Git" }, { @@ -297,7 +328,8 @@ { "command": "git.undoCommit", "title": "%command.undoCommit%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.checkout", @@ -459,6 +491,21 @@ "title": "%command.revealInExplorer%", "category": "Git" }, + { + "command": "git.revealFileInOS.linux", + "title": "%command.revealFileInOS.linux%", + "category": "Git" + }, + { + "command": "git.revealFileInOS.mac", + "title": "%command.revealFileInOS.mac%", + "category": "Git" + }, + { + "command": "git.revealFileInOS.windows", + "title": "%command.revealFileInOS.windows%", + "category": "Git" + }, { "command": "git.stashIncludeUntracked", "title": "%command.stashIncludeUntracked%", @@ -549,6 +596,12 @@ "command": "git.api.getRemoteSources", "title": "%command.api.getRemoteSources%", "category": "Git API" + }, + { + "command": "git.acceptMerge", + "title": "%command.git.acceptMerge%", + "category": "Git", + "enablement": "isMergeEditor && mergeEditorResultUri in git.mergeChanges" } ], "keybindings": [ @@ -757,10 +810,30 @@ "command": "git.restoreCommitTemplate", "when": "false" }, + { + "command": "git.commitMessageAccept", + "when": "false" + }, + { + "command": "git.commitMessageDiscard", + "when": "false" + }, { "command": "git.revealInExplorer", "when": "false" }, + { + "command": "git.revealFileInOS.linux", + "when": "false" + }, + { + "command": "git.revealFileInOS.mac", + "when": "false" + }, + { + "command": "git.revealFileInOS.windows", + "when": "false" + }, { "command": "git.undoCommit", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -1211,7 +1284,22 @@ { "command": "git.revealInExplorer", "when": "scmProvider == git && scmResourceGroup == merge", - "group": "2_view" + "group": "2_view@1" + }, + { + "command": "git.revealFileInOS.linux", + "when": "scmProvider == git && scmResourceGroup == merge && remoteName == '' && isLinux", + "group": "2_view@2" + }, + { + "command": "git.revealFileInOS.mac", + "when": "scmProvider == git && scmResourceGroup == merge && remoteName == '' && isMac", + "group": "2_view@2" + }, + { + "command": "git.revealFileInOS.windows", + "when": "scmProvider == git && scmResourceGroup == merge && remoteName == '' && isWindows", + "group": "2_view@2" }, { "command": "git.openFile2", @@ -1251,7 +1339,22 @@ { "command": "git.revealInExplorer", "when": "scmProvider == git && scmResourceGroup == index", - "group": "2_view" + "group": "2_view@1" + }, + { + "command": "git.revealFileInOS.linux", + "when": "scmProvider == git && scmResourceGroup == index && remoteName == '' && isLinux", + "group": "2_view@2" + }, + { + "command": "git.revealFileInOS.mac", + "when": "scmProvider == git && scmResourceGroup == index && remoteName == '' && isMac", + "group": "2_view@2" + }, + { + "command": "git.revealFileInOS.windows", + "when": "scmProvider == git && scmResourceGroup == index && remoteName == '' && isWindows", + "group": "2_view@2" }, { "command": "git.openFile2", @@ -1316,7 +1419,22 @@ { "command": "git.revealInExplorer", "when": "scmProvider == git && scmResourceGroup == workingTree", - "group": "2_view" + "group": "2_view@1" + }, + { + "command": "git.revealFileInOS.linux", + "when": "scmProvider == git && scmResourceGroup == workingTree && remoteName == '' && isLinux", + "group": "2_view@2" + }, + { + "command": "git.revealFileInOS.mac", + "when": "scmProvider == git && scmResourceGroup == workingTree && remoteName == '' && isMac", + "group": "2_view@2" + }, + { + "command": "git.revealFileInOS.windows", + "when": "scmProvider == git && scmResourceGroup == workingTree && remoteName == '' && isWindows", + "group": "2_view@2" }, { "command": "git.openChange", @@ -1383,7 +1501,17 @@ { "command": "git.openChange", "group": "navigation", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file && scmActiveResourceHasChanges" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && !isMergeEditor && resourceScheme == file && scmActiveResourceHasChanges" + }, + { + "command": "git.commitMessageAccept", + "group": "navigation", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && editorLangId == git-commit && commitInProgress" + }, + { + "command": "git.commitMessageDiscard", + "group": "navigation", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && editorLangId == git-commit && commitInProgress" }, { "command": "git.stageSelectedRanges", @@ -1418,6 +1546,12 @@ "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], + "merge/toolbar": [ + { + "command": "git.acceptMerge", + "when": "isMergeEditor && mergeEditorBaseUri =~ /^(git|file):/ && mergeEditorResultUri in git.mergeChanges" + } + ], "scm/change/title": [ { "command": "git.stageChange", @@ -1777,6 +1911,37 @@ "markdownDescription": "%config.autofetchPeriod%", "default": 180 }, + "git.branchPrefix": { + "type": "string", + "description": "%config.branchPrefix%", + "default": "", + "scope": "resource" + }, + "git.branchProtection": { + "type": "array", + "markdownDescription": "%config.branchProtection%", + "items": { + "type": "string" + }, + "default": [], + "scope": "resource" + }, + "git.branchProtectionPrompt": { + "type": "string", + "description": "%config.branchProtectionPrompt%", + "enum": [ + "alwaysCommit", + "alwaysCommitToNewBranch", + "alwaysPrompt" + ], + "enumDescriptions": [ + "%config.branchProtectionPrompt.alwaysCommit%", + "%config.branchProtectionPrompt.alwaysCommitToNewBranch%", + "%config.branchProtectionPrompt.alwaysPrompt%" + ], + "default": "alwaysPrompt", + "scope": "resource" + }, "git.branchValidationRegex": { "type": "string", "description": "%config.branchValidationRegex%", @@ -1787,6 +1952,38 @@ "description": "%config.branchWhitespaceChar%", "default": "-" }, + "git.branchRandomName.enable": { + "type": "boolean", + "description": "%config.branchRandomNameEnable%", + "default": false, + "scope": "resource" + }, + "git.branchRandomName.dictionary": { + "type": "array", + "markdownDescription": "%config.branchRandomNameDictionary%", + "items": { + "type": "string", + "enum": [ + "adjectives", + "animals", + "colors", + "numbers" + ], + "enumDescriptions": [ + "%config.branchRandomNameDictionary.adjectives%", + "%config.branchRandomNameDictionary.animals%", + "%config.branchRandomNameDictionary.colors%", + "%config.branchRandomNameDictionary.numbers%" + ] + }, + "minItems": 1, + "maxItems": 5, + "default": [ + "adjectives", + "animals" + ], + "scope": "resource" + }, "git.confirmSync": { "type": "boolean", "description": "%config.confirmSync%", @@ -1865,6 +2062,17 @@ "scope": "machine", "description": "%config.defaultCloneDirectory%" }, + "git.useEditorAsCommitInput": { + "type": "boolean", + "description": "%config.useEditorAsCommitInput%", + "default": true + }, + "git.verboseCommit": { + "type": "boolean", + "scope": "resource", + "markdownDescription": "%config.verboseCommit%", + "default": false + }, "git.enableSmartCommit": { "type": "boolean", "scope": "resource", @@ -2173,7 +2381,8 @@ "git.requireGitUserConfig": { "type": "boolean", "description": "%config.requireGitUserConfig%", - "default": true + "default": true, + "scope": "resource" }, "git.showCommitInput": { "type": "boolean", @@ -2183,10 +2392,14 @@ }, "git.terminalAuthentication": { "type": "boolean", - "scope": "resource", "default": true, "description": "%config.terminalAuthentication%" }, + "git.terminalGitEditor": { + "type": "boolean", + "default": false, + "description": "%config.terminalGitEditor%" + }, "git.useCommitInputAsStashMessage": { "type": "boolean", "scope": "resource", @@ -2227,20 +2440,29 @@ "description": "%config.timeline.showUncommitted%", "scope": "window" }, - "git.showUnpublishedCommitsButton": { - "type": "string", - "enum": [ - "always", - "whenEmpty", - "never" - ], - "enumDescriptions": [ - "%config.showUnpublishedCommitsButton.always%", - "%config.showUnpublishedCommitsButton.whenEmpty%", - "%config.showUnpublishedCommitsButton.never%" - ], - "default": "whenEmpty", - "description": "%config.showUnpublishedCommitsButton%", + "git.showActionButton": { + "type": "object", + "additionalProperties": false, + "description": "%config.showActionButton%", + "properties": { + "commit": { + "type": "boolean", + "description": "%config.showActionButton.commit%" + }, + "publish": { + "type": "boolean", + "description": "%config.showActionButton.publish%" + }, + "sync": { + "type": "boolean", + "description": "%config.showActionButton.sync%" + } + }, + "default": { + "commit": true, + "publish": true, + "sync": true + }, "scope": "resource" }, "git.statusLimit": { @@ -2249,19 +2471,6 @@ "default": 10000, "description": "%config.statusLimit%" }, - "git.experimental.installGuide": { - "type": "string", - "enum": [ - "default", - "download" - ], - "tags": [ - "experimental" - ], - "scope": "machine", - "description": "%config.experimental.installGuide%", - "default": "default" - }, "git.repositoryScanIgnoredFolders": { "type": "array", "items": { @@ -2285,8 +2494,37 @@ "type": "string" }, "default": [], - "scope": "resource", "markdownDescription": "%config.commandsToLog%" + }, + "git.logLevel": { + "type": "string", + "default": "Info", + "enum": [ + "Trace", + "Debug", + "Info", + "Warning", + "Error", + "Critical", + "Off" + ], + "enumDescriptions": [ + "%config.logLevel.trace%", + "%config.logLevel.debug%", + "%config.logLevel.info%", + "%config.logLevel.warn%", + "%config.logLevel.error%", + "%config.logLevel.critical%", + "%config.logLevel.off%" + ], + "markdownDescription": "%config.logLevel%", + "scope": "window" + }, + "git.mergeEditor": { + "type": "boolean", + "default": true, + "markdownDescription": "%config.mergeEditor%", + "scope": "window" } } }, @@ -2297,7 +2535,7 @@ "defaults": { "light": "#587c0c", "dark": "#81b88b", - "highContrast": "#1b5225", + "highContrast": "#a1e3ad", "highContrastLight": "#374e06" } }, @@ -2409,56 +2647,51 @@ "contents": "%view.workbench.scm.disabled%", "when": "!config.git.enabled" }, - { - "view": "scm", - "contents": "%view.workbench.scm.missing.guide%", - "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download" - }, - { - "view": "scm", - "contents": "%view.workbench.scm.missing.guide.mac%", - "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isMac" - }, - { - "view": "scm", - "contents": "%view.workbench.scm.missing.guide.windows%", - "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isWindows" - }, - { - "view": "scm", - "contents": "%view.workbench.scm.missing.guide.linux%", - "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isLinux" - }, { "view": "scm", "contents": "%view.workbench.scm.missing%", - "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == default" + "when": "config.git.enabled && git.missing" + }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.mac%", + "when": "config.git.enabled && git.missing && isMac" + }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.windows%", + "when": "config.git.enabled && git.missing && isWindows" + }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.linux%", + "when": "config.git.enabled && git.missing && isLinux" }, { "view": "scm", "contents": "%view.workbench.scm.empty%", - "when": "config.git.enabled && workbenchState == empty", + "when": "config.git.enabled && !git.missing && workbenchState == empty", "enablement": "git.state == initialized", "group": "2_open@1" }, { "view": "scm", "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && workbenchState == folder", + "when": "config.git.enabled && !git.missing && workbenchState == folder", "enablement": "git.state == initialized", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount != 0", + "when": "config.git.enabled && !git.missing && workbenchState == workspace && workspaceFolderCount != 0", "enablement": "git.state == initialized", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.emptyWorkspace%", - "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount == 0", + "when": "config.git.enabled && !git.missing && workbenchState == workspace && workspaceFolderCount == 0", "enablement": "git.state == initialized", "group": "2_open@1" }, @@ -2479,11 +2712,13 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "0.4.10", + "@joaomoreno/unique-names-generator": "5.0.0", + "@vscode/extension-telemetry": "0.6.2", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", "jschardet": "3.0.0", + "picomatch": "2.3.1", "vscode-nls": "^4.0.0", "vscode-uri": "^2.0.0", "which": "^1.3.0" @@ -2492,6 +2727,7 @@ "@types/byline": "4.2.31", "@types/mocha": "^9.1.1", "@types/node": "16.x", + "@types/picomatch": "2.3.0", "@types/which": "^1.0.28" }, "repository": { diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 0cfc6c6c34..6710c283c4 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -46,6 +46,8 @@ "command.commitAllNoVerify": "Commit All (No Verify)", "command.commitAllSignedNoVerify": "Commit All (Signed Off, No Verify)", "command.commitAllAmendNoVerify": "Commit All (Amend, No Verify)", + "command.commitMessageAccept": "Accept Commit Message", + "command.commitMessageDiscard": "Discard Commit Message", "command.restoreCommitTemplate": "Restore Commit Template", "command.undoCommit": "Undo Last Commit", "command.checkout": "Checkout to...", @@ -80,6 +82,9 @@ "command.showOutput": "Show Git Output", "command.ignore": "Add to .gitignore", "command.revealInExplorer": "Reveal in Explorer View", + "command.revealFileInOS.linux": "Open Containing Folder", + "command.revealFileInOS.mac": "Reveal in Finder", + "command.revealFileInOS.windows": "Reveal in File Explorer", "command.rebaseAbort": "Abort Rebase", "command.stashIncludeUntracked": "Stash (Include Untracked)", "command.stash": "Stash", @@ -97,6 +102,7 @@ "command.api.getRepositories": "Get Repositories", "command.api.getRepositoryState": "Get Repository State", "command.api.getRemoteSources": "Get Remote Sources", + "command.git.acceptMerge": "Accept Merge", "config.enabled": "Whether git is enabled.", "config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows). This can also be an array of string values containing multiple paths to look up.", "config.autoRepositoryDetection": "Configures when repositories should be automatically detected.", @@ -116,14 +122,28 @@ "config.checkoutType.local": "Local branches", "config.checkoutType.tags": "Tags", "config.checkoutType.remote": "Remote branches", + "config.branchPrefix": "Prefix used when creating a new branch.", + "config.branchProtection": "List of protected branches. By default, a prompt is shown before changes are committed to a protected branch. The prompt can be controlled using the `#git.branchProtectionPrompt#` setting.", + "config.branchProtectionPrompt": "Controls whether a prompt is being before changes are committed to a protected branch.", + "config.branchProtectionPrompt.alwaysCommit": "Always commit changes to the protected branch.", + "config.branchProtectionPrompt.alwaysCommitToNewBranch": "Always commit changes to a new branch.", + "config.branchProtectionPrompt.alwaysPrompt": "Always prompt before changes are committed to a protected branch.", + "config.branchRandomNameDictionary": "List of dictionaries used for the randomly generated branch name. Each value represents the dictionary used to generate the segment of the branch name. Supported dictionaries: `adjectives`, `animals`, `colors` and `numbers`.", + "config.branchRandomNameDictionary.adjectives": "A random adjective", + "config.branchRandomNameDictionary.animals": "A random animal name", + "config.branchRandomNameDictionary.colors": "A random color name", + "config.branchRandomNameDictionary.numbers": "A random number between 100 and 999", + "config.branchRandomNameEnable": "Controls whether a random name is generated when creating a new branch.", "config.branchValidationRegex": "A regular expression to validate new branch names.", - "config.branchWhitespaceChar": "The character to replace whitespace in new branch names.", + "config.branchWhitespaceChar": "The character to replace whitespace in new branch names, and to separate segments of a randomly generated branch name.", "config.ignoreLegacyWarning": "Ignores the legacy Git warning.", "config.ignoreMissingGitWarning": "Ignores the warning when Git is missing.", "config.ignoreWindowsGit27Warning": "Ignores the warning when Git 2.25 - 2.26 is installed on Windows.", "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.", "config.ignoreRebaseWarning": "Ignores the warning when it looks like the branch might have been rebased when pulling.", "config.defaultCloneDirectory": "The default location to clone a git repository.", + "config.useEditorAsCommitInput": "Controls whether a full text editor will be used to author commit messages, whenever no message is provided in the commit input box.", + "config.verboseCommit": "Enable verbose output when `#git.useEditorAsCommitInput#` is enabled.", "config.enableSmartCommit": "Commit all changes when there are no staged changes.", "config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.", "config.smartCommitChanges.all": "Automatically stage all changes.", @@ -193,22 +213,39 @@ "config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.", "config.requireGitUserConfig": "Controls whether to require explicit Git user configuration or allow Git to guess if missing.", "config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.", - "config.terminalAuthentication": "Controls whether to enable Azure Data Studio to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", + "config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", + "config.terminalGitEditor": "Controls whether to enable VS Code to be git editor for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", "config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view.", "config.timeline.showUncommitted": "Controls whether to show uncommitted changes in the Timeline view.", "config.timeline.date": "Controls which date to use for items in the Timeline view.", "config.timeline.date.committed": "Use the committed date", "config.timeline.date.authored": "Use the authored date", "config.useCommitInputAsStashMessage": "Controls whether to use the message from the commit input box as the default stash message.", - "config.showUnpublishedCommitsButton": "Controls whether to show an action button to sync or publish, if there are unpublished commits.", - "config.showUnpublishedCommitsButton.always": "Always shows the action button, if there are unpublished commits.", - "config.showUnpublishedCommitsButton.whenEmpty": "Only shows the action button if there are no other changes and there are unpublished commits.", - "config.showUnpublishedCommitsButton.never": "Never shows the action button.", + "config.showActionButton": "Controls whether an action button is shown in the Source Control view.", + "config.showActionButton.commit": "Show an action button to commit changes when the local branch has modified files ready to be committed.", + "config.showActionButton.publish": "Show an action button to publish the local branch when it does not have a tracking remote branch.", + "config.showActionButton.sync": "Show an action button to synchronize changes when the local branch is either ahead or behind the remote branch.", "config.statusLimit": "Controls how to limit the number of changes that can be parsed from Git status command. Can be set to 0 for no limit.", "config.experimental.installGuide": "Experimental improvements for the git setup flow.", "config.repositoryScanIgnoredFolders": "List of folders that are ignored while scanning for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`.", "config.repositoryScanMaxDepth": "Controls the depth used when scanning workspace folders for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`. Can be set to `-1` for no limit.", "config.useIntegratedAskPass": "Controls whether GIT_ASKPASS should be overwritten to use the integrated version.", + "config.logLevel": { + "message": "Specifies how much information (if any) to log to the [git output](command:git.showOutput).", + "comment": [ + "{Locked='](command:git.showOutput'}", + "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", + "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" + ] + }, + "config.logLevel.trace": "Log all information", + "config.logLevel.debug": "Log only debug, information, warning, error, and critical information", + "config.logLevel.info": "Log only information, warning, error, and critical information", + "config.logLevel.warn": "Log only warning, error, and critical information", + "config.logLevel.error": "Log only error, and critical information", + "config.logLevel.critical": "Log only critical information", + "config.logLevel.off": "Log nothing", + "config.mergeEditor": "Open the merge editor for files that are currently under conflict.", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", @@ -229,15 +266,7 @@ "colors.ignored": "Color for ignored resources.", "colors.conflict": "Color for resources with conflicts.", "colors.submodule": "Color for submodule resources.", - "view.workbench.scm.missing": { - "message": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use git and source control in Azure Data Studio in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.", - "comment": [ - "{Locked='](command:git.showOutput'}", - "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio", - "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" - ] - }, - "view.workbench.scm.missing.guide.windows": { + "view.workbench.scm.missing.windows": { "message": "[Download Git for Windows](https://git-scm.com/download/win)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", @@ -245,7 +274,7 @@ "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] }, - "view.workbench.scm.missing.guide.mac": { + "view.workbench.scm.missing.mac": { "message": "[Download Git for macOS](https://git-scm.com/download/mac)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", @@ -253,7 +282,7 @@ "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] }, - "view.workbench.scm.missing.guide.linux": { + "view.workbench.scm.missing.linux": { "message": "Source control depends on Git being installed.\n[Download Git for Linux](https://git-scm.com/download/linux)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", @@ -261,7 +290,7 @@ "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] }, - "view.workbench.scm.missing.guide": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).", + "view.workbench.scm.missing": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).", "view.workbench.scm.disabled": { "message": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "comment": [ diff --git a/extensions/git/src/actionButton.ts b/extensions/git/src/actionButton.ts index 4516b11ef6..30b6427232 100644 --- a/extensions/git/src/actionButton.ts +++ b/extensions/git/src/actionButton.ts @@ -3,18 +3,23 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace } from 'vscode'; import * as nls from 'vscode-nls'; +import { Command, Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace } from 'vscode'; +import { ApiRepository } from './api/api1'; +import { Branch, Status } from './api/git'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; import { Repository, Operation } from './repository'; import { dispose } from './util'; -import { Branch } from './api/git'; const localize = nls.loadMessageBundle(); interface ActionButtonState { readonly HEAD: Branch | undefined; - readonly isSyncRunning: boolean; - readonly repositoryHasNoChanges: boolean; + readonly isCommitInProgress: boolean; + readonly isMergeInProgress: boolean; + readonly isRebaseInProgress: boolean; + readonly isSyncInProgress: boolean; + readonly repositoryHasChangesToCommit: boolean; } export class ActionButtonCommand { @@ -32,81 +37,263 @@ export class ActionButtonCommand { private disposables: Disposable[] = []; - constructor(readonly repository: Repository) { - this._state = { HEAD: undefined, isSyncRunning: false, repositoryHasNoChanges: false }; + constructor( + readonly repository: Repository, + readonly postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry) { + this._state = { + HEAD: undefined, + isCommitInProgress: false, + isMergeInProgress: false, + isRebaseInProgress: false, + isSyncInProgress: false, + repositoryHasChangesToCommit: false + }; repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables); repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables); + + this.disposables.push(postCommitCommandsProviderRegistry.onDidChangePostCommitCommandsProviders(() => this._onDidChange.fire())); + + const root = Uri.file(repository.root); + this.disposables.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('git.enableSmartCommit', root) || + e.affectsConfiguration('git.smartCommitChanges', root) || + e.affectsConfiguration('git.suggestSmartCommit', root)) { + this.onDidChangeSmartCommitSettings(); + } + + if (e.affectsConfiguration('git.branchProtection', root) || + e.affectsConfiguration('git.branchProtectionPrompt', root) || + e.affectsConfiguration('git.postCommitCommand', root) || + e.affectsConfiguration('git.showActionButton', root)) { + this._onDidChange.fire(); + } + })); } get button(): SourceControlActionButton | undefined { - if (!this.state.HEAD || !this.state.HEAD.name || !this.state.HEAD.commit) { return undefined; } - - const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); - const showActionButton = config.get('showUnpublishedCommitsButton', 'whenEmpty'); - const postCommitCommand = config.get('postCommitCommand'); - const noPostCommitCommand = postCommitCommand !== 'sync' && postCommitCommand !== 'push'; + if (!this.state.HEAD) { return undefined; } let actionButton: SourceControlActionButton | undefined; - if (showActionButton === 'always' || (showActionButton === 'whenEmpty' && this.state.repositoryHasNoChanges && noPostCommitCommand)) { - if (this.state.HEAD.upstream) { - if (this.state.HEAD.ahead) { - const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); - const rebaseWhenSync = config.get('rebaseWhenSync'); - const ahead = `${this.state.HEAD.ahead}$(arrow-up)`; - const behind = this.state.HEAD.behind ? `${this.state.HEAD.behind}$(arrow-down) ` : ''; - const icon = this.state.isSyncRunning ? '$(sync~spin)' : '$(sync)'; + if (this.state.repositoryHasChangesToCommit) { + // Commit Changes (enabled) + actionButton = this.getCommitActionButton(); + } - actionButton = { - command: { - command: this.state.isSyncRunning ? '' : rebaseWhenSync ? 'git.syncRebase' : 'git.sync', - title: localize('scm button sync title', "{0} {1}{2}", icon, behind, ahead), - tooltip: this.state.isSyncRunning ? - localize('syncing changes', "Synchronizing Changes...") - : this.repository.syncTooltip, - arguments: [this.repository.sourceControl], - }, - description: localize('scm button sync description', "{0} Sync Changes {1}{2}", icon, behind, ahead) - }; + // Commit Changes (enabled) -> Publish Branch -> Sync Changes -> Commit Changes (disabled) + return actionButton ?? this.getPublishBranchActionButton() ?? this.getSyncChangesActionButton() ?? this.getCommitActionButton(); + } + + private getCommitActionButton(): SourceControlActionButton | undefined { + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const showActionButton = config.get<{ commit: boolean }>('showActionButton', { commit: true }); + + // The button is disabled + if (!showActionButton.commit) { return undefined; } + + return { + command: this.getCommitActionButtonPrimaryCommand(), + secondaryCommands: this.getCommitActionButtonSecondaryCommands(), + enabled: (this.state.repositoryHasChangesToCommit || this.state.isRebaseInProgress) && !this.state.isCommitInProgress && !this.state.isMergeInProgress + }; + } + + private getCommitActionButtonPrimaryCommand(): Command { + // Rebase Continue + if (this.state.isRebaseInProgress) { + return { + command: 'git.commit', + title: localize('scm button continue title', "{0} Continue", '$(check)'), + tooltip: this.state.isCommitInProgress ? localize('scm button continuing tooltip', "Continuing Rebase...") : localize('scm button continue tooltip', "Continue Rebase"), + arguments: [this.repository.sourceControl, ''] + }; + } + + // Commit + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const postCommitCommand = config.get('postCommitCommand'); + + // Branch protection + const isBranchProtected = this.repository.isBranchProtected(); + const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; + const alwaysPrompt = isBranchProtected && branchProtectionPrompt === 'alwaysPrompt'; + const alwaysCommitToNewBranch = isBranchProtected && branchProtectionPrompt === 'alwaysCommitToNewBranch'; + + // Icon + const icon = alwaysPrompt ? '$(lock)' : alwaysCommitToNewBranch ? '$(git-branch)' : undefined; + + let commandArg = ''; + let title = localize('scm button commit title', "{0} Commit", icon ?? '$(check)'); + let tooltip = this.state.isCommitInProgress ? localize('scm button committing tooltip', "Committing Changes...") : localize('scm button commit tooltip', "Commit Changes"); + + // Title, tooltip + switch (postCommitCommand) { + case 'push': { + commandArg = 'git.push'; + title = localize('scm button commit and push title', "{0} Commit & Push", icon ?? '$(arrow-up)'); + if (alwaysCommitToNewBranch) { + tooltip = this.state.isCommitInProgress ? + localize('scm button committing to new branch and pushing tooltip', "Committing to New Branch & Pushing Changes...") : + localize('scm button commit to new branch and push tooltip', "Commit to New Branch & Push Changes"); + } else { + tooltip = this.state.isCommitInProgress ? + localize('scm button committing and pushing tooltip', "Committing & Pushing Changes...") : + localize('scm button commit and push tooltip', "Commit & Push Changes"); } - } else { - actionButton = { - command: { - command: this.state.isSyncRunning ? '' : 'git.publish', - title: localize('scm button publish title', "$(cloud-upload) Publish Branch"), - tooltip: this.state.isSyncRunning ? - localize('scm button publish branch running', "Publishing Branch...") : - localize('scm button publish branch', "Publish Branch"), - arguments: [this.repository.sourceControl], - } - }; + break; + } + case 'sync': { + commandArg = 'git.sync'; + title = localize('scm button commit and sync title', "{0} Commit & Sync", icon ?? '$(sync)'); + if (alwaysCommitToNewBranch) { + tooltip = this.state.isCommitInProgress ? + localize('scm button committing to new branch and synching tooltip', "Committing to New Branch & Synching Changes...") : + localize('scm button commit to new branch and sync tooltip', "Commit to New Branch & Sync Changes"); + } else { + tooltip = this.state.isCommitInProgress ? + localize('scm button committing and synching tooltip', "Committing & Synching Changes...") : + localize('scm button commit and sync tooltip', "Commit & Sync Changes"); + } + break; + } + default: { + if (alwaysCommitToNewBranch) { + tooltip = this.state.isCommitInProgress ? + localize('scm button committing to new branch tooltip', "Committing Changes to New Branch...") : + localize('scm button commit to new branch tooltip', "Commit Changes to New Branch"); + } + break; } } - return actionButton; + return { command: 'git.commit', title, tooltip, arguments: [this.repository.sourceControl, commandArg] }; + } + + private getCommitActionButtonSecondaryCommands(): Command[][] { + const commandGroups: Command[][] = []; + + if (!this.state.isRebaseInProgress) { + for (const provider of this.postCommitCommandsProviderRegistry.getPostCommitCommandsProviders()) { + const commands = provider.getCommands(new ApiRepository(this.repository)); + commandGroups.push((commands ?? []).map(c => { + return { + command: 'git.commit', + title: c.title, + arguments: [this.repository.sourceControl, c.command] + }; + })); + } + + if (commandGroups.length > 0) { + commandGroups[0].splice(0, 0, { command: 'git.commit', title: localize('scm secondary button commit', "Commit") }); + } + } + + return commandGroups; + } + + private getPublishBranchActionButton(): SourceControlActionButton | undefined { + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const showActionButton = config.get<{ publish: boolean }>('showActionButton', { publish: true }); + + // Branch does have an upstream, commit/merge/rebase is in progress, or the button is disabled + if (this.state.HEAD?.upstream || this.state.isCommitInProgress || this.state.isMergeInProgress || this.state.isRebaseInProgress || !showActionButton.publish) { return undefined; } + + return { + command: { + command: 'git.publish', + title: localize({ key: 'scm publish branch action button title', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }, "{0} Publish Branch", '$(cloud-upload)'), + tooltip: this.state.isSyncInProgress ? + localize({ key: 'scm button publish branch running', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }, "Publishing Branch...") : + localize({ key: 'scm button publish branch', comment: ['{Locked="Branch"}', 'Do not translate "Branch" as it is a git term'] }, "Publish Branch"), + arguments: [this.repository.sourceControl], + }, + enabled: !this.state.isSyncInProgress + }; + } + + private getSyncChangesActionButton(): SourceControlActionButton | undefined { + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const showActionButton = config.get<{ sync: boolean }>('showActionButton', { sync: true }); + const branchIsAheadOrBehind = (this.state.HEAD?.behind ?? 0) > 0 || (this.state.HEAD?.ahead ?? 0) > 0; + + // Branch does not have an upstream, branch is not ahead/behind the remote branch, commit/merge/rebase is in progress, or the button is disabled + if (!this.state.HEAD?.upstream || !branchIsAheadOrBehind || this.state.isCommitInProgress || this.state.isMergeInProgress || this.state.isRebaseInProgress || !showActionButton.sync) { return undefined; } + + const ahead = this.state.HEAD.ahead ? ` ${this.state.HEAD.ahead}$(arrow-up)` : ''; + const behind = this.state.HEAD.behind ? ` ${this.state.HEAD.behind}$(arrow-down)` : ''; + const icon = this.state.isSyncInProgress ? '$(sync~spin)' : '$(sync)'; + + return { + command: { + command: 'git.sync', + title: `${icon}${behind}${ahead}`, + tooltip: this.state.isSyncInProgress ? + localize('syncing changes', "Synchronizing Changes...") + : this.repository.syncTooltip, + arguments: [this.repository.sourceControl], + }, + description: localize('scm button sync description', "{0} Sync Changes{1}{2}", icon, behind, ahead), + enabled: !this.state.isSyncInProgress + }; } private onDidChangeOperations(): void { - const isSyncRunning = this.repository.operations.isRunning(Operation.Sync) || + const isCommitInProgress = + this.repository.operations.isRunning(Operation.Commit) || + this.repository.operations.isRunning(Operation.RebaseContinue); + + const isSyncInProgress = + this.repository.operations.isRunning(Operation.Sync) || this.repository.operations.isRunning(Operation.Push) || this.repository.operations.isRunning(Operation.Pull); - this.state = { ...this.state, isSyncRunning }; + this.state = { ...this.state, isCommitInProgress, isSyncInProgress }; + } + + private onDidChangeSmartCommitSettings(): void { + this.state = { + ...this.state, + repositoryHasChangesToCommit: this.repositoryHasChangesToCommit() + }; } private onDidRunGitStatus(): void { this.state = { ...this.state, HEAD: this.repository.HEAD, - repositoryHasNoChanges: - this.repository.indexGroup.resourceStates.length === 0 && - this.repository.mergeGroup.resourceStates.length === 0 && - this.repository.untrackedGroup.resourceStates.length === 0 && - this.repository.workingTreeGroup.resourceStates.length === 0 + isMergeInProgress: this.repository.mergeGroup.resourceStates.length !== 0, + isRebaseInProgress: !!this.repository.rebaseCommit, + repositoryHasChangesToCommit: this.repositoryHasChangesToCommit() }; } + private repositoryHasChangesToCommit(): boolean { + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const enableSmartCommit = config.get('enableSmartCommit') === true; + const suggestSmartCommit = config.get('suggestSmartCommit') === true; + const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges', 'all'); + + const resources = [...this.repository.indexGroup.resourceStates]; + + if ( + // Smart commit enabled (all) + (enableSmartCommit && smartCommitChanges === 'all') || + // Smart commit disabled, smart suggestion enabled + (!enableSmartCommit && suggestSmartCommit) + ) { + resources.push(...this.repository.workingTreeGroup.resourceStates); + } + + // Smart commit enabled (tracked only) + if (enableSmartCommit && smartCommitChanges === 'tracked') { + resources.push(...this.repository.workingTreeGroup.resourceStates.filter(r => r.type !== Status.UNTRACKED)); + } + + return resources.length !== 0; + } + dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 029a19933b..e159be1b63 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,8 +5,8 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, ICloneOptions } from './git'; // {{SQL CARBON EDIT}} add ICloneOptions -import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, ICloneOptions, PostCommitCommandsProvider } from './git'; // {{SQL CARBON EDIT}} add ICloneOptions +import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode'; // {{SQL CARBON EDIT}} Add cancellationToken import { combinedDisposable, mapEvent } from '../util'; import { toGitUri } from '../uri'; import { GitExtensionImpl } from './extension'; @@ -57,157 +57,157 @@ export class ApiRepositoryUIState implements RepositoryUIState { export class ApiRepository implements Repository { - readonly rootUri: Uri = Uri.file(this._repository.root); - readonly inputBox: InputBox = new ApiInputBox(this._repository.inputBox); - readonly state: RepositoryState = new ApiRepositoryState(this._repository); - readonly ui: RepositoryUIState = new ApiRepositoryUIState(this._repository.sourceControl); + readonly rootUri: Uri = Uri.file(this.repository.root); + readonly inputBox: InputBox = new ApiInputBox(this.repository.inputBox); + readonly state: RepositoryState = new ApiRepositoryState(this.repository); + readonly ui: RepositoryUIState = new ApiRepositoryUIState(this.repository.sourceControl); - constructor(private _repository: BaseRepository) { } + constructor(readonly repository: BaseRepository) { } apply(patch: string, reverse?: boolean): Promise { - return this._repository.apply(patch, reverse); + return this.repository.apply(patch, reverse); } getConfigs(): Promise<{ key: string; value: string }[]> { - return this._repository.getConfigs(); + return this.repository.getConfigs(); } getConfig(key: string): Promise { - return this._repository.getConfig(key); + return this.repository.getConfig(key); } setConfig(key: string, value: string): Promise { - return this._repository.setConfig(key, value); + return this.repository.setConfig(key, value); } getGlobalConfig(key: string): Promise { - return this._repository.getGlobalConfig(key); + return this.repository.getGlobalConfig(key); } getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> { - return this._repository.getObjectDetails(treeish, path); + return this.repository.getObjectDetails(treeish, path); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { - return this._repository.detectObjectType(object); + return this.repository.detectObjectType(object); } buffer(ref: string, filePath: string): Promise { - return this._repository.buffer(ref, filePath); + return this.repository.buffer(ref, filePath); } show(ref: string, path: string): Promise { - return this._repository.show(ref, path); + return this.repository.show(ref, path); } getCommit(ref: string): Promise { - return this._repository.getCommit(ref); + return this.repository.getCommit(ref); } add(paths: string[]) { - return this._repository.add(paths.map(p => Uri.file(p))); + return this.repository.add(paths.map(p => Uri.file(p))); } revert(paths: string[]) { - return this._repository.revert(paths.map(p => Uri.file(p))); + return this.repository.revert(paths.map(p => Uri.file(p))); } clean(paths: string[]) { - return this._repository.clean(paths.map(p => Uri.file(p))); + return this.repository.clean(paths.map(p => Uri.file(p))); } diff(cached?: boolean) { - return this._repository.diff(cached); + return this.repository.diff(cached); } diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; diffWithHEAD(path?: string): Promise { - return this._repository.diffWithHEAD(path); + return this.repository.diffWithHEAD(path); } diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; diffWith(ref: string, path?: string): Promise { - return this._repository.diffWith(ref, path); + return this.repository.diffWith(ref, path); } diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; diffIndexWithHEAD(path?: string): Promise { - return this._repository.diffIndexWithHEAD(path); + return this.repository.diffIndexWithHEAD(path); } diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffIndexWith(ref: string, path?: string): Promise { - return this._repository.diffIndexWith(ref, path); + return this.repository.diffIndexWith(ref, path); } diffBlobs(object1: string, object2: string): Promise { - return this._repository.diffBlobs(object1, object2); + return this.repository.diffBlobs(object1, object2); } diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; diffBetween(ref1: string, ref2: string, path?: string): Promise { - return this._repository.diffBetween(ref1, ref2, path); + return this.repository.diffBetween(ref1, ref2, path); } hashObject(data: string): Promise { - return this._repository.hashObject(data); + return this.repository.hashObject(data); } createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise { - return this._repository.branch(name, checkout, ref); + return this.repository.branch(name, checkout, ref); } deleteBranch(name: string, force?: boolean): Promise { - return this._repository.deleteBranch(name, force); + return this.repository.deleteBranch(name, force); } getBranch(name: string): Promise { - return this._repository.getBranch(name); + return this.repository.getBranch(name); } getBranches(query: BranchQuery): Promise { - return this._repository.getBranches(query); + return this.repository.getBranches(query); } setBranchUpstream(name: string, upstream: string): Promise { - return this._repository.setBranchUpstream(name, upstream); + return this.repository.setBranchUpstream(name, upstream); } getMergeBase(ref1: string, ref2: string): Promise { - return this._repository.getMergeBase(ref1, ref2); + return this.repository.getMergeBase(ref1, ref2); } tag(name: string, upstream: string): Promise { - return this._repository.tag(name, upstream); + return this.repository.tag(name, upstream); } deleteTag(name: string): Promise { - return this._repository.deleteTag(name); + return this.repository.deleteTag(name); } status(): Promise { - return this._repository.status(); + return this.repository.status(); } checkout(treeish: string): Promise { - return this._repository.checkout(treeish); + return this.repository.checkout(treeish); } addRemote(name: string, url: string): Promise { - return this._repository.addRemote(name, url); + return this.repository.addRemote(name, url); } removeRemote(name: string): Promise { - return this._repository.removeRemote(name); + return this.repository.removeRemote(name); } renameRemote(name: string, newName: string): Promise { - return this._repository.renameRemote(name, newName); + return this.repository.renameRemote(name, newName); } fetch(arg0?: FetchOptions | string | undefined, @@ -216,30 +216,30 @@ export class ApiRepository implements Repository { prune?: boolean | undefined ): Promise { if (arg0 !== undefined && typeof arg0 !== 'string') { - return this._repository.fetch(arg0); + return this.repository.fetch(arg0); } - return this._repository.fetch({ remote: arg0, ref, depth, prune }); + return this.repository.fetch({ remote: arg0, ref, depth, prune }); } pull(unshallow?: boolean): Promise { - return this._repository.pull(undefined, unshallow); + return this.repository.pull(undefined, unshallow); } push(remoteName?: string, branchName?: string, setUpstream: boolean = false, force?: ForcePushMode): Promise { - return this._repository.pushTo(remoteName, branchName, setUpstream, force); + return this.repository.pushTo(remoteName, branchName, setUpstream, force); } blame(path: string): Promise { - return this._repository.blame(path); + return this.repository.blame(path); } log(options?: LogOptions): Promise { - return this._repository.log(options); + return this.repository.log(options); } commit(message: string, opts?: CommitOptions): Promise { - return this._repository.commit(message, opts); + return this.repository.commit(message, opts); } } @@ -323,6 +323,10 @@ export class ApiImpl implements API { return this._model.registerCredentialsProvider(provider); } + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable { + return this._model.registerPostCommitCommandsProvider(provider); + } + registerPushErrorHandler(handler: PushErrorHandler): Disposable { return this._model.registerPushErrorHandler(handler); } diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index f7bea962a3..d339f8a4c2 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, Event, Disposable, ProviderResult, CancellationToken, Progress } from 'vscode'; // {{SQL CARBON EDIT}} add CancellationToken +import { Uri, Event, Disposable, ProviderResult, CancellationToken, Progress, Command } from 'vscode'; // {{SQL CARBON EDIT}} add CancellationToken export { ProviderResult } from 'vscode'; export interface Git { @@ -137,6 +137,9 @@ export interface CommitOptions { empty?: boolean; noVerify?: boolean; requireUserConfig?: boolean; + useEditor?: boolean; + verbose?: boolean; + postCommitCommand?: string; } export interface FetchOptions { @@ -251,6 +254,10 @@ export interface CredentialsProvider { getCredentials(host: Uri): ProviderResult; } +export interface PostCommitCommandsProvider { + getCommands(repository: Repository): Command[]; +} + export interface PushErrorHandler { handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise; } @@ -287,6 +294,7 @@ export interface API { registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable; registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; registerCredentialsProvider(provider: CredentialsProvider): Disposable; + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; registerPushErrorHandler(handler: PushErrorHandler): Disposable; } @@ -298,7 +306,7 @@ export interface GitExtension { /** * Returns a specific API version. * - * Throws error if git extension is disabled. You can listed to the + * Throws error if git extension is disabled. You can listen to the * [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event * to know when the extension becomes enabled/disabled. * @@ -344,6 +352,7 @@ export const enum GitErrorCodes { PatchDoesNotApply = 'PatchDoesNotApply', NoPathFound = 'NoPathFound', UnknownPath = 'UnknownPath', + EmptyCommitMessage = 'EmptyCommitMessage' } // {{SQL CARBON EDIT}} move ICloneOptions from git.ts to here since it's used in clone() diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 454ee27b32..87e6a21a8a 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -3,31 +3,31 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, InputBoxOptions, Uri, OutputChannel, Disposable, workspace } from 'vscode'; -import { IDisposable, EmptyDisposable, toDisposable, logTimestamp } from './util'; +import { window, InputBoxOptions, Uri, Disposable, workspace } from 'vscode'; +import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; -import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer'; +import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; +import { ITerminalEnvironmentProvider } from './terminal'; -export class Askpass implements IIPCHandler { +export class Askpass implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; private cache = new Map(); private credentialsProviders = new Set(); - static async create(outputChannel: OutputChannel, context?: string): Promise { - try { - return new Askpass(await createIPCServer(context)); - } catch (err) { - outputChannel.appendLine(`${logTimestamp()} [error] Failed to create git askpass IPC: ${err}`); - return new Askpass(); - } - } - - private constructor(private ipc?: IIPCServer) { + constructor(private ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); } + + this.env = { + GIT_ASKPASS: path.join(__dirname, this.ipc ? 'askpass.sh' : 'askpass-empty.sh'), + VSCODE_GIT_ASKPASS_NODE: process.execPath, + VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), + }; } async handle({ request, host }: { request: string; host: string }): Promise { @@ -73,25 +73,13 @@ export class Askpass implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') - }; - } - - let env: { [key: string]: string } = { - ...this.ipc.getEnv(), - VSCODE_GIT_ASKPASS_NODE: process.execPath, - VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useIntegratedAskPass')) { - env.GIT_ASKPASS = path.join(__dirname, 'askpass.sh'); - } + return config.get('useIntegratedAskPass') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useIntegratedAskPass') && config.get('terminalAuthentication') ? this.env : {}; } registerCredentialsProvider(provider: CredentialsProvider): Disposable { diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a171a64aa5..5ac9475237 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,17 +5,18 @@ import * as os from 'os'; import * as path from 'path'; -import { Command, commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; +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 } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; +import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git'; import { Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; -import { fromGitUri, toGitUri, isGitUri } from './uri'; -import { grep, isDescendant, logTimestamp, pathEquals, relativePath } from './util'; -import { Log, LogLevel } from './log'; +import { fromGitUri, toGitUri, isGitUri, toMergeUris } from './uri'; +import { grep, isDescendant, pathEquals, relativePath } from './util'; +import { LogLevel, OutputChannelLogger } from './log'; import { GitTimelineItem } from './timelineProvider'; import { ApiRepository } from './api/api1'; import { pickRemoteSource } from './remoteSource'; @@ -25,24 +26,26 @@ const localize = nls.loadMessageBundle(); class CheckoutItem implements QuickPickItem { protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } - get label(): string { return this.ref.name || this.shortCommit; } + get label(): string { return `${this.repository.isBranchProtected(this.ref.name ?? '') ? '$(lock)' : '$(git-branch)'} ${this.ref.name || this.shortCommit}`; } get description(): string { return this.shortCommit; } + get refName(): string | undefined { return this.ref.name; } - constructor(protected ref: Ref) { } + constructor(protected repository: Repository, protected ref: Ref) { } - async run(repository: Repository, opts?: { detached?: boolean }): Promise { + async run(opts?: { detached?: boolean }): Promise { const ref = this.ref.name; if (!ref) { return; } - await repository.checkout(ref, opts); + await this.repository.checkout(ref, opts); } } class CheckoutTagItem extends CheckoutItem { + override get label(): string { return `$(tag) ${this.ref.name || this.shortCommit}`; } override get description(): string { return localize('tag at', "Tag at {0}", this.shortCommit); } @@ -50,21 +53,22 @@ class CheckoutTagItem extends CheckoutItem { class CheckoutRemoteHeadItem extends CheckoutItem { + override get label(): string { return `$(cloud) ${this.ref.name || this.shortCommit}`; } override get description(): string { return localize('remote branch at', "Remote branch at {0}", this.shortCommit); } - override async run(repository: Repository, opts?: { detached?: boolean }): Promise { + override async run(opts?: { detached?: boolean }): Promise { if (!this.ref.name) { return; } - const branches = await repository.findTrackingBranches(this.ref.name); + const branches = await this.repository.findTrackingBranches(this.ref.name); if (branches.length > 0) { - await repository.checkout(branches[0].name!, opts); + await this.repository.checkout(branches[0].name!, opts); } else { - await repository.checkoutTracking(this.ref.name, opts); + await this.repository.checkoutTracking(this.ref.name, opts); } } } @@ -137,6 +141,7 @@ class HEADItem implements QuickPickItem { get label(): string { return 'HEAD'; } get description(): string { return (this.repository.HEAD && this.repository.HEAD.commit || '').substr(0, 8); } get alwaysShow(): boolean { return true; } + get refName(): string { return 'HEAD'; } } class AddRemoteItem implements QuickPickItem { @@ -217,7 +222,7 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { checkoutTypes = checkoutTypeConfig; } - const processors = checkoutTypes.map(getCheckoutProcessor) + const processors = checkoutTypes.map(type => getCheckoutProcessor(repository, type)) .filter(p => !!p) as CheckoutProcessor[]; for (const ref of repository.refs) { @@ -232,8 +237,8 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { class CheckoutProcessor { private refs: Ref[] = []; - get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); } - constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { } + get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(this.repository, r)); } + constructor(private repository: Repository, private type: RefType, private ctor: { new(repository: Repository, ref: Ref): CheckoutItem }) { } onRef(ref: Ref): void { if (ref.type === this.type) { @@ -242,14 +247,14 @@ class CheckoutProcessor { } } -function getCheckoutProcessor(type: string): CheckoutProcessor | undefined { +function getCheckoutProcessor(repository: Repository, type: string): CheckoutProcessor | undefined { switch (type) { case 'local': - return new CheckoutProcessor(RefType.Head, CheckoutItem); + return new CheckoutProcessor(repository, RefType.Head, CheckoutItem); case 'remote': - return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem); + return new CheckoutProcessor(repository, RefType.RemoteHead, CheckoutRemoteHeadItem); case 'tags': - return new CheckoutProcessor(RefType.Tag, CheckoutTagItem); + return new CheckoutProcessor(repository, RefType.Tag, CheckoutTagItem); } return undefined; @@ -310,7 +315,7 @@ export class CommandCenter { constructor( private git: Git, private model: Model, - private outputChannel: OutputChannel, + private outputChannelLogger: OutputChannelLogger, private telemetryReporter: TelemetryReporter ) { this.disposables = Commands.map(({ commandId, key, method, options }) => { @@ -328,11 +333,25 @@ export class CommandCenter { @command('git.setLogLevel') async setLogLevel(): Promise { - const createItem = (logLevel: LogLevel) => ({ - label: LogLevel[logLevel], - logLevel, - description: Log.logLevel === logLevel ? localize('current', "Current") : undefined - }); + const createItem = (logLevel: LogLevel) => { + let description: string | undefined; + const defaultDescription = localize('default', "Default"); + const currentDescription = localize('current', "Current"); + + if (logLevel === this.outputChannelLogger.defaultLogLevel && logLevel === this.outputChannelLogger.currentLogLevel) { + description = `${defaultDescription} & ${currentDescription} `; + } else if (logLevel === this.outputChannelLogger.defaultLogLevel) { + description = defaultDescription; + } else if (logLevel === this.outputChannelLogger.currentLogLevel) { + description = currentDescription; + } + + return { + label: LogLevel[logLevel], + logLevel, + description + }; + }; const items = [ createItem(LogLevel.Trace), @@ -352,8 +371,7 @@ export class CommandCenter { return; } - Log.logLevel = choice.logLevel; - this.outputChannel.appendLine(localize('changed', "{0} Log level changed to: {1}", logTimestamp(), LogLevel[Log.logLevel])); + this.outputChannelLogger.currentLogLevel = choice.logLevel; } @command('git.refresh', { repository: true }) @@ -390,6 +408,55 @@ export class CommandCenter { } } + @command('_git.openMergeEditor') + async openMergeEditor(uri: unknown) { + if (!(uri instanceof Uri)) { + return; + } + const repo = this.model.getRepository(uri); + if (!repo) { + return; + } + + const isRebasing = Boolean(repo.rebaseCommit); + + type InputData = { uri: Uri; title?: string; detail?: string; description?: string }; + const mergeUris = toMergeUris(uri); + const ours: InputData = { uri: mergeUris.ours, title: localize('Yours', 'Yours') }; + const theirs: InputData = { uri: mergeUris.theirs, title: localize('Theirs', 'Theirs') }; + + try { + const [head, rebaseOrMergeHead] = await Promise.all([ + repo.getCommit('HEAD'), + isRebasing ? repo.getCommit('REBASE_HEAD') : repo.getCommit('MERGE_HEAD') + ]); + // ours (current branch and commit) + ours.detail = head.refNames.map(s => s.replace(/^HEAD ->/, '')).join(', '); + ours.description = '$(git-commit) ' + head.hash.substring(0, 7); + + // theirs + theirs.detail = rebaseOrMergeHead.refNames.join(', '); + theirs.description = '$(git-commit) ' + rebaseOrMergeHead.hash.substring(0, 7); + + } catch (error) { + // not so bad, can continue with just uris + console.error('FAILED to read HEAD, MERGE_HEAD commits'); + console.error(error); + } + + const options = { + base: mergeUris.base, + input1: isRebasing ? ours : theirs, + input2: isRebasing ? theirs : ours, + output: uri + }; + + await commands.executeCommand( + '_open.mergeEditor', + options + ); + } + async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise { if (!url || typeof url !== 'string') { url = await pickRemoteSource({ @@ -401,7 +468,8 @@ export class CommandCenter { if (!url) { /* __GDPR__ "clone" : { - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "owner": "lszomoru", + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" } } */ this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_URL' }); @@ -426,7 +494,8 @@ export class CommandCenter { if (!uris || uris.length === 0) { /* __GDPR__ "clone" : { - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "owner": "lszomoru", + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" } } */ this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'no_directory' }); @@ -484,8 +553,9 @@ export class CommandCenter { /* __GDPR__ "clone" : { - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + "owner": "lszomoru", + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" }, + "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "Indicates whether the folder is opened following the clone operation" } } */ this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'success' }, { openFolder: action === PostCloneAction.Open || action === PostCloneAction.OpenNewWindow ? 1 : 0 }); @@ -503,7 +573,8 @@ export class CommandCenter { if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { /* __GDPR__ "clone" : { - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "owner": "lszomoru", + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" } } */ this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'directory_not_empty' }); @@ -512,7 +583,8 @@ export class CommandCenter { } else { /* __GDPR__ "clone" : { - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "owner": "lszomoru", + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The outcome of the git operation" } } */ this.telemetryReporter.sendTelemetryEvent('clone', { outcome: 'error' }); @@ -820,14 +892,14 @@ export class CommandCenter { @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { - this.outputChannel.appendLine(`${logTimestamp()} git.stage ${resourceStates.length}`); + this.outputChannelLogger.logDebug(`git.stage ${resourceStates.length} `); resourceStates = resourceStates.filter(s => !!s); if (resourceStates.length === 0 || (resourceStates[0] && !(resourceStates[0].resourceUri instanceof Uri))) { const resource = this.getSCMResource(); - this.outputChannel.appendLine(`${logTimestamp()} git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null}`); + this.outputChannelLogger.logDebug(`git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null} `); if (!resource) { return; @@ -870,7 +942,7 @@ export class CommandCenter { const untracked = selection.filter(s => s.resourceGroupType === ResourceGroupType.Untracked); const scmResources = [...workingTree, ...untracked, ...resolved, ...unresolved]; - this.outputChannel.appendLine(`${logTimestamp()} git.stage.scmResources ${scmResources.length}`); + this.outputChannelLogger.logDebug(`git.stage.scmResources ${scmResources.length} `); if (!scmResources.length) { return; } @@ -1020,6 +1092,47 @@ export class CommandCenter { await this._stageChanges(textEditor, selectedChanges); } + @command('git.acceptMerge') + async acceptMerge(uri: Uri | unknown): Promise { + if (!(uri instanceof Uri)) { + return; + } + const repository = this.model.getRepository(uri); + if (!repository) { + console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't belong to any repository`); + return; + } + + const { activeTab } = window.tabGroups.activeTabGroup; + if (!activeTab) { + return; + } + + // make sure to save the merged document + const doc = workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); + if (!doc) { + console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't match a document`); + return; + } + if (doc.isDirty) { + await doc.save(); + } + + // find the merge editor tabs for the resource in question and close them all + let didCloseTab = false; + const mergeEditorTabs = window.tabGroups.all.map(group => group.tabs.filter(tab => tab.input instanceof TabInputTextMerge && tab.input.result.toString() === uri.toString())).flat(); + if (mergeEditorTabs.includes(activeTab)) { + didCloseTab = await window.tabGroups.close(mergeEditorTabs, true); + } + + // Only stage if the merge editor has been successfully closed. That means all conflicts have been + // handled or unhandled conflicts are OK by the user. + if (didCloseTab) { + await repository.add([uri]); + await commands.executeCommand('workbench.view.scm'); + } + } + private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise { const modifiedDocument = textEditor.document; const modifiedUri = modifiedDocument.uri; @@ -1339,7 +1452,7 @@ export class CommandCenter { private async smartCommit( repository: Repository, getCommitMessage: () => Promise, - opts?: CommitOptions + opts: CommitOptions ): Promise { const config = workspace.getConfiguration('git', Uri.file(repository.root)); let promptToSaveFilesBeforeCommit = config.get<'always' | 'staged' | 'never'>('promptToSaveFilesBeforeCommit'); @@ -1385,14 +1498,8 @@ export class CommandCenter { } } - if (!opts) { - opts = { all: noStagedChanges }; - } else if (!opts.all && noStagedChanges && !opts.empty) { - opts = { ...opts, all: true }; - } - // no changes, and the user has not configured to commit all in this case - if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.empty) { + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.empty && !opts.all) { const suggestSmartCommit = config.get('suggestSmartCommit') === true; if (!suggestSmartCommit) { @@ -1416,6 +1523,12 @@ export class CommandCenter { } } + if (opts.all === undefined) { + opts = { ...opts, all: noStagedChanges }; + } else if (!opts.all && noStagedChanges && !opts.empty) { + opts = { ...opts, all: true }; + } + // enable signing of commits if configured opts.signCommit = enableCommitSigning; @@ -1423,6 +1536,14 @@ export class CommandCenter { opts.signoff = true; } + if (config.get('useEditorAsCommitInput')) { + opts.useEditor = true; + + if (config.get('verboseCommit')) { + opts.verbose = true; + } + } + const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges'); if ( @@ -1437,6 +1558,8 @@ export class CommandCenter { // amend allows changing only the commit message && !opts.amend && !opts.empty + // rebase not in progress + && repository.rebaseCommit === undefined ) { const commitAnyway = localize('commit anyway', "Create Empty Commit"); const answer = await window.showInformationMessage(localize('no changes', "There are no changes to commit."), commitAnyway); @@ -1468,9 +1591,9 @@ export class CommandCenter { } } - let message = await getCommitMessage(); + const message = await getCommitMessage(); - if (!message && !opts.amend) { + if (!message && !opts.amend && !opts.useEditor) { return false; } @@ -1482,29 +1605,55 @@ export class CommandCenter { opts.all = 'tracked'; } + // Branch protection + const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; + if (repository.isBranchProtected() && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) { + const commitToNewBranch = localize('commit to branch', "Commit to a New Branch"); + + let pick: string | undefined = commitToNewBranch; + + if (branchProtectionPrompt === 'alwaysPrompt') { + const message = localize('confirm branch protection commit', "You are trying to commit to a protected branch and you might not have permission to push your commits to the remote.\n\nHow would you like to proceed?"); + const commit = localize('commit changes', "Commit Anyway"); + + pick = await window.showWarningMessage(message, { modal: true }, commitToNewBranch, commit); + } + + if (!pick) { + return false; + } else if (pick === commitToNewBranch) { + const branchName = await this.promptForBranchName(repository); + + if (!branchName) { + return false; + } + + await repository.branch(branchName, true); + } + } + await repository.commit(message, opts); - const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand'); - - switch (postCommitCommand) { - case 'push': - await this._push(repository, { pushType: PushType.Push, silent: true }); - break; - case 'sync': - await this.sync(repository); - break; + // Execute post commit command + if (opts.postCommitCommand?.length) { + await commands.executeCommand( + opts.postCommitCommand, + new ApiRepository(repository)); } return true; } - private async commitWithAnyInput(repository: Repository, opts?: CommitOptions): Promise { + private async commitWithAnyInput(repository: Repository, opts: CommitOptions): Promise { const message = repository.inputBox.value; + const root = Uri.file(repository.root); + const config = workspace.getConfiguration('git', root); + const getCommitMessage = async () => { let _message: string | undefined = message; - if (!_message) { - let value: string | undefined = undefined; + if (!_message && !config.get('useEditorAsCommitInput')) { + const value: string | undefined = undefined; if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { return undefined; @@ -1538,8 +1687,8 @@ export class CommandCenter { } @command('git.commit', { repository: true }) - async commit(repository: Repository): Promise { - await this.commitWithAnyInput(repository); + async commit(repository: Repository, postCommitCommand?: string): Promise { + await this.commitWithAnyInput(repository, { postCommitCommand }); } @command('git.commitStaged', { repository: true }) @@ -1572,13 +1721,58 @@ export class CommandCenter { await this.commitWithAnyInput(repository, { all: true, amend: true }); } + @command('git.commitMessageAccept') + async commitMessageAccept(arg?: Uri): Promise { + if (!arg) { return; } + + // Close the tab + this._closeEditorTab(arg); + } + + @command('git.commitMessageDiscard') + async commitMessageDiscard(arg?: Uri): Promise { + if (!arg) { return; } + + // Clear the contents of the editor + const editors = window.visibleTextEditors + .filter(e => e.document.languageId === 'git-commit' && e.document.uri.toString() === arg.toString()); + + if (editors.length !== 1) { return; } + + const commitMsgEditor = editors[0]; + const commitMsgDocument = commitMsgEditor.document; + + const editResult = await commitMsgEditor.edit(builder => { + const firstLine = commitMsgDocument.lineAt(0); + const lastLine = commitMsgDocument.lineAt(commitMsgDocument.lineCount - 1); + + builder.delete(new Range(firstLine.range.start, lastLine.range.end)); + }); + + if (!editResult) { return; } + + // Save the document + const saveResult = await commitMsgDocument.save(); + if (!saveResult) { return; } + + // Close the tab + this._closeEditorTab(arg); + } + + private _closeEditorTab(uri: Uri): void { + const tabToClose = window.tabGroups.all.map(g => g.tabs).flat() + .filter(t => t.input instanceof TabInputText && t.input.uri.toString() === uri.toString()); + + window.tabGroups.close(tabToClose); + } + private async _commitEmpty(repository: Repository, noVerify?: boolean): Promise { const root = Uri.file(repository.root); const config = workspace.getConfiguration('git', root); const shouldPrompt = config.get('confirmEmptyCommits') === true; if (shouldPrompt) { - const message = localize('confirm emtpy commit', "Are you sure you want to create an empty commit?"); + const message = localize('confirm empty commit', "Are you sure you want to create an empty commit?"); const yes = localize('yes', "Yes"); const neverAgain = localize('yes never again', "Yes, Don't Show Again"); const pick = await window.showWarningMessage(message, { modal: true }, yes, neverAgain); @@ -1725,7 +1919,7 @@ export class CommandCenter { const item = choice as CheckoutItem; try { - await item.run(repository, opts); + await item.run(opts); } catch (err) { if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) { throw err; @@ -1737,10 +1931,10 @@ export class CommandCenter { if (choice === force) { await this.cleanAll(repository); - await item.run(repository, opts); + await item.run(opts); } else if (choice === stash) { await this.stash(repository); - await item.run(repository, opts); + await item.run(opts); await this.stashPopLatest(repository); } } @@ -1759,34 +1953,100 @@ export class CommandCenter { await this._branch(repository, undefined, true); } - private async promptForBranchName(defaultName?: string, initialValue?: string): Promise { + private generateRandomBranchName(repository: Repository, separator: string): string { const config = workspace.getConfiguration('git'); + const branchRandomNameDictionary = config.get('branchRandomName.dictionary')!; + + const dictionaries: string[][] = []; + for (const dictionary of branchRandomNameDictionary) { + if (dictionary.toLowerCase() === 'adjectives') { + dictionaries.push(adjectives); + } + if (dictionary.toLowerCase() === 'animals') { + dictionaries.push(animals); + } + if (dictionary.toLowerCase() === 'colors') { + dictionaries.push(colors); + } + if (dictionary.toLowerCase() === 'numbers') { + dictionaries.push(NumberDictionary.generate({ length: 3 })); + } + } + + if (dictionaries.length === 0) { + return ''; + } + + // 5 attempts to generate a random branch name + for (let index = 0; index < 5; index++) { + const randomName = uniqueNamesGenerator({ + dictionaries, + length: dictionaries.length, + separator + }); + + // Check for local ref conflict + if (!repository.refs.find(r => r.type === RefType.Head && r.name === randomName)) { + return randomName; + } + } + + return ''; + } + + private async promptForBranchName(repository: Repository, defaultName?: string, initialValue?: string): Promise { + const config = workspace.getConfiguration('git'); + const branchPrefix = config.get('branchPrefix')!; const branchWhitespaceChar = config.get('branchWhitespaceChar')!; const branchValidationRegex = config.get('branchValidationRegex')!; const sanitize = (name: string) => name ? name.trim().replace(/^-+/, '').replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar) : name; - const rawBranchName = defaultName || await window.showInputBox({ - placeHolder: localize('branch name', "Branch name"), - prompt: localize('provide branch name', "Please provide a new branch name"), - value: initialValue, - ignoreFocusOut: true, - validateInput: (name: string) => { - const validateName = new RegExp(branchValidationRegex); - if (validateName.test(sanitize(name))) { - return null; - } + let rawBranchName = defaultName; - return localize('branch name format invalid', "Branch name needs to match regex: {0}", branchValidationRegex); + if (!rawBranchName) { + // Branch name + if (!initialValue) { + const branchRandomNameEnabled = config.get('branchRandomName.enable', false); + initialValue = `${branchPrefix}${branchRandomNameEnabled ? this.generateRandomBranchName(repository, branchWhitespaceChar) : ''}`; } - }); + + // Branch name selection + const initialValueSelection: [number, number] | undefined = + initialValue.startsWith(branchPrefix) ? [branchPrefix.length, initialValue.length] : undefined; + + rawBranchName = await window.showInputBox({ + placeHolder: localize('branch name', "Branch name"), + prompt: localize('provide branch name', "Please provide a new branch name"), + value: initialValue, + valueSelection: initialValueSelection, + ignoreFocusOut: true, + validateInput: (name: string) => { + const validateName = new RegExp(branchValidationRegex); + const sanitizedName = sanitize(name); + if (validateName.test(sanitizedName)) { + // If the sanitized name that we will use is different than what is + // in the input box, show an info message to the user informing them + // the branch name that will be used. + return name === sanitizedName + ? null + : { + message: localize('branch name does not match sanitized', "The new branch will be '{0}'", sanitizedName), + severity: InputBoxValidationSeverity.Info + }; + } + + return localize('branch name format invalid', "Branch name needs to match regex: {0}", branchValidationRegex); + } + }); + } return sanitize(rawBranchName || ''); } private async _branch(repository: Repository, defaultName?: string, from = false): Promise { - const branchName = await this.promptForBranchName(defaultName); + const branchName = await this.promptForBranchName(repository, defaultName); if (!branchName) { return; @@ -1803,7 +2063,9 @@ export class CommandCenter { return; } - target = choice.label; + if (choice.refName) { + target = choice.refName; + } } await repository.branch(branchName, true, target); @@ -1849,7 +2111,7 @@ export class CommandCenter { @command('git.renameBranch', { repository: true }) async renameBranch(repository: Repository): Promise { const currentBranchName = repository.HEAD && repository.HEAD.name; - const branchName = await this.promptForBranchName(undefined, currentBranchName); + const branchName = await this.promptForBranchName(repository, undefined, currentBranchName); if (!branchName) { return; @@ -2315,17 +2577,16 @@ export class CommandCenter { } } - if (rebase) { - await repository.syncRebase(HEAD); - } else { - await repository.sync(HEAD); - } + await repository.sync(HEAD, rebase); } @command('git.sync', { repository: true }) async sync(repository: Repository): Promise { + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const rebase = config.get('rebaseWhenSync', false) === true; + try { - await this._sync(repository, false); + await this._sync(repository, rebase); } catch (err) { if (/Cancelled/i.test(err && (err.message || err.stderr || ''))) { return; @@ -2338,13 +2599,16 @@ export class CommandCenter { @command('git._syncAll') async syncAll(): Promise { await Promise.all(this.model.repositories.map(async repository => { + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const rebase = config.get('rebaseWhenSync', false) === true; + const HEAD = repository.HEAD; if (!HEAD || !HEAD.upstream) { return; } - await repository.sync(HEAD); + await repository.sync(HEAD, rebase); })); } @@ -2466,6 +2730,21 @@ export class CommandCenter { await commands.executeCommand('revealInExplorer', resourceState.resourceUri); } + @command('git.revealFileInOS.linux') + @command('git.revealFileInOS.mac') + @command('git.revealFileInOS.windows') + async revealFileInOS(resourceState: SourceControlResourceState): Promise { + if (!resourceState) { + return; + } + + if (!(resourceState.resourceUri instanceof Uri)) { + return; + } + + await commands.executeCommand('revealFileInOS', resourceState.resourceUri); + } + private async _stash(repository: Repository, includeUntracked = false): Promise { const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0 && (!includeUntracked || repository.untrackedGroup.resourceStates.length === 0); @@ -2755,13 +3034,7 @@ export class CommandCenter { @command('git.closeAllDiffEditors', { repository: true }) closeDiffEditors(repository: Repository): void { - const resources = [ - ...repository.indexGroup.resourceStates.map(r => r.resourceUri.fsPath), - ...repository.workingTreeGroup.resourceStates.map(r => r.resourceUri.fsPath), - ...repository.untrackedGroup.resourceStates.map(r => r.resourceUri.fsPath) - ]; - - repository.closeDiffEditors(resources, resources, true); + repository.closeDiffEditors(undefined, undefined, true); } private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any { @@ -2794,7 +3067,8 @@ export class CommandCenter { /* __GDPR__ "git.command" : { - "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "owner": "lszomoru", + "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command id of the command being executed" } } */ this.telemetryReporter.sendTelemetryEvent('git.command', { command: id }); @@ -2805,12 +3079,12 @@ export class CommandCenter { }; let message: string; - let type: 'error' | 'warning' = 'error'; + let type: 'error' | 'warning' | 'information' = 'error'; const choices = new Map void>(); const openOutputChannelChoice = localize('open git log', "Open Git Log"); - const outputChannel = this.outputChannel as OutputChannel; - choices.set(openOutputChannelChoice, () => outputChannel.show()); + const outputChannelLogger = this.outputChannelLogger; + choices.set(openOutputChannelChoice, () => outputChannelLogger.showOutputChannel()); const showCommandOutputChoice = localize('show command output', "Show Command Output"); if (err.stderr) { @@ -2868,6 +3142,12 @@ export class CommandCenter { message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git."); choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git'))); break; + case GitErrorCodes.EmptyCommitMessage: + message = localize('empty commit', "Commit operation was cancelled due to empty commit message."); + choices.clear(); + type = 'information'; + options.modal = false; + break; default: { const hint = (err.stderr || err.message || String(err)) .replace(/^error: /mi, '') @@ -2889,17 +3169,25 @@ export class CommandCenter { return; } + let result: string | undefined; const allChoices = Array.from(choices.keys()); - const result = type === 'error' - ? await window.showErrorMessage(message, options, ...allChoices) - : await window.showWarningMessage(message, options, ...allChoices); + + switch (type) { + case 'error': + result = await window.showErrorMessage(message, options, ...allChoices); + break; + case 'warning': + result = await window.showWarningMessage(message, options, ...allChoices); + break; + case 'information': + result = await window.showInformationMessage(message, options, ...allChoices); + break; + } if (result) { const resultFn = choices.get(result); - if (resultFn) { - resultFn(); - } + resultFn?.(); } }); }; @@ -2913,10 +3201,10 @@ export class CommandCenter { private getSCMResource(uri?: Uri): Resource | undefined { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); - this.outputChannel.appendLine(`${logTimestamp()} git.getSCMResource.uri ${uri && uri.toString()}`); + this.outputChannelLogger.logDebug(`git.getSCMResource.uri ${uri && uri.toString()}`); for (const r of this.model.repositories.map(r => r.root)) { - this.outputChannel.appendLine(`${logTimestamp()} repo root ${r}`); + this.outputChannelLogger.logDebug(`repo root ${r}`); } if (!uri) { diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 7b9f4b17f6..eacb45fdc5 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -106,7 +106,7 @@ class GitDecorationProvider implements FileDecorationProvider { } private onDidRunGitStatus(): void { - let newDecorations = new Map(); + const newDecorations = new Map(); this.collectSubmoduleDecorationData(newDecorations); this.collectDecorationData(this.repository.indexGroup, newDecorations); diff --git a/extensions/git/src/encoding.ts b/extensions/git/src/encoding.ts index 424f5312bb..a74c9ec5ec 100644 --- a/extensions/git/src/encoding.ts +++ b/extensions/git/src/encoding.ts @@ -50,7 +50,7 @@ const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { }; export function detectEncoding(buffer: Buffer): string | null { - let result = detectEncodingByBOM(buffer); + const result = detectEncodingByBOM(buffer); if (result) { return result; diff --git a/extensions/git/src/git-editor-empty.sh b/extensions/git/src/git-editor-empty.sh new file mode 100755 index 0000000000..1a2485251c --- /dev/null +++ b/extensions/git/src/git-editor-empty.sh @@ -0,0 +1 @@ +#!/bin/sh diff --git a/extensions/git/src/git-editor-main.ts b/extensions/git/src/git-editor-main.ts new file mode 100644 index 0000000000..978ac6b610 --- /dev/null +++ b/extensions/git/src/git-editor-main.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IPCClient } from './ipc/ipcClient'; + +function fatal(err: any): void { + console.error(err); + process.exit(1); +} + +function main(argv: string[]): void { + const ipcClient = new IPCClient('git-editor'); + const commitMessagePath = argv[argv.length - 1]; + + ipcClient.call({ commitMessagePath }).then(() => { + setTimeout(() => process.exit(0), 0); + }).catch(err => fatal(err)); +} + +main(process.argv); diff --git a/extensions/git/src/git-editor.sh b/extensions/git/src/git-editor.sh new file mode 100755 index 0000000000..d7e0d2deec --- /dev/null +++ b/extensions/git/src/git-editor.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +ELECTRON_RUN_AS_NODE="1" \ +"$VSCODE_GIT_EDITOR_NODE" "$VSCODE_GIT_EDITOR_MAIN" $VSCODE_GIT_EDITOR_EXTRA_ARGS "$@" diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 3233a06f07..28008d24ca 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -354,7 +354,7 @@ function sanitizePath(path: string): string { return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); } -const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%B'; +const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; /*export interface ICloneOptions { {{SQL CARBON EDIT}} moved to git.d.ts readonly parentPath: string; @@ -406,7 +406,7 @@ export class Git { } async clone(url: string, options: ICloneOptions, cancellationToken?: CancellationToken): Promise { - let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*[\/\\]/, '').replace(/\.git$/, '') || 'repository'; + const baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*[\/\\]/, '').replace(/\.git$/, '') || 'repository'; let folderName = baseFolderName; let folderPath = path.join(options.parentPath, folderName); let count = 1; @@ -447,7 +447,7 @@ export class Git { }; try { - let command = ['clone', url.includes(' ') ? encodeURI(url) : url, folderPath, '--progress']; + const command = ['clone', url.includes(' ') ? encodeURI(url) : url, folderPath, '--progress']; if (options.recursive) { command.push('--recursive'); } @@ -475,13 +475,14 @@ export class Git { const repoPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); if (isWindows) { - // On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped drive path back, you get the UNC path for the mapped drive. - // So we will try to normalize it back to the mapped drive path, if possible + // On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped + // drive path back, you get the UNC path for the mapped drive. So we will try to normalize it back to the + // mapped drive path, if possible const repoUri = Uri.file(repoPath); const pathUri = Uri.file(repositoryPath); if (repoUri.authority.length !== 0 && pathUri.authority.length === 0) { // eslint-disable-next-line code-no-look-behind-regex - let match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path); + const match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path); if (match !== null) { const [, letter] = match; @@ -504,6 +505,13 @@ export class Git { return path.normalize(pathUri.fsPath); } + + // On Windows, there are cases in which the normalized path for a mapped folder contains a trailing `\` + // character (ex: \\server\folder\) due to the implementation of `path.normalize()`. This behaviour is + // by design as documented in https://github.com/nodejs/node/issues/1765. + if (repoUri.authority.length !== 0) { + return repoPath.replace(/\\$/, ''); + } } return repoPath; @@ -556,9 +564,7 @@ export class Git { private async _exec(args: string[], options: SpawnOptions = {}): Promise> { const child = this.spawn(args, options); - if (options.onSpawn) { - options.onSpawn(child); - } + options.onSpawn?.(child); if (options.input) { child.stdin!.end(options.input, 'utf8'); @@ -660,6 +666,7 @@ export interface Commit { authorName?: string; authorEmail?: string; commitDate?: Date; + refNames: string[]; } export class GitStatusParser { @@ -790,10 +797,10 @@ export function parseGitmodules(raw: string): Submodule[] { return result; } -const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; +const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; export function parseGitCommits(data: string): Commit[] { - let commits: Commit[] = []; + const commits: Commit[] = []; let ref; let authorName; @@ -801,6 +808,7 @@ export function parseGitCommits(data: string): Commit[] { let authorDate; let commitDate; let parents; + let refNames; let message; let match; @@ -810,7 +818,7 @@ export function parseGitCommits(data: string): Commit[] { break; } - [, ref, authorName, authorEmail, authorDate, commitDate, parents, message] = match; + [, ref, authorName, authorEmail, authorDate, commitDate, parents, refNames, message] = match; if (message[message.length - 1] === '\n') { message = message.substr(0, message.length - 1); @@ -825,6 +833,7 @@ export function parseGitCommits(data: string): Commit[] { authorName: ` ${authorName}`.substr(1), authorEmail: ` ${authorEmail}`.substr(1), commitDate: new Date(Number(commitDate) * 1000), + refNames: refNames.split(',').map(s => s.trim()) }); } while (true); @@ -1397,20 +1406,37 @@ export class Repository { } async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { - const args = ['commit', '--quiet', '--allow-empty-message']; + const args = ['commit', '--quiet']; + const options: SpawnOptions = {}; + + if (message) { + options.input = message; + args.push('--allow-empty-message', '--file', '-'); + } + + if (opts.verbose) { + args.push('--verbose'); + } if (opts.all) { args.push('--all'); } - if (opts.amend && message) { + if (opts.amend) { args.push('--amend'); } - if (opts.amend && !message) { - args.push('--amend', '--no-edit'); - } else { - args.push('--file', '-'); + if (!opts.useEditor) { + if (!message) { + if (opts.amend) { + args.push('--no-edit'); + } else { + options.input = ''; + args.push('--file', '-'); + } + } + + args.push('--allow-empty-message'); } if (opts.signoff) { @@ -1435,7 +1461,7 @@ export class Repository { } try { - await this.exec(args, !opts.amend || message ? { input: message || '' } : {}); + await this.exec(args, options); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1449,7 +1475,7 @@ export class Repository { const args = ['rebase', '--continue']; try { - await this.exec(args); + await this.exec(args, { env: { GIT_EDITOR: 'true' } }); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1459,6 +1485,9 @@ export class Repository { if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) { commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges; throw commitErr; + } else if (/Aborting commit due to empty commit message/.test(commitErr.stderr || '')) { + commitErr.gitErrorCode = GitErrorCodes.EmptyCommitMessage; + throw commitErr; } try { @@ -1540,7 +1569,7 @@ export class Repository { } async deleteTag(name: string): Promise { - let args = ['tag', '-d', name]; + const args = ['tag', '-d', name]; await this.exec(args); } @@ -1764,12 +1793,12 @@ export class Repository { } catch (err) { if (/^error: failed to push some refs to\b/m.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.PushRejected; + } else if (/Permission.*denied/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.PermissionDenied; } else if (/Could not read from remote repository/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.RemoteConnectionError; } else if (/^fatal: The current branch .* has no upstream branch/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.NoUpstreamBranch; - } else if (/Permission.*denied/.test(err.stderr || '')) { - err.gitErrorCode = GitErrorCodes.PermissionDenied; } throw err; diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts new file mode 100644 index 0000000000..6e401bdb14 --- /dev/null +++ b/extensions/git/src/gitEditor.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import { TabInputText, Uri, window, workspace } from 'vscode'; +import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; +import { ITerminalEnvironmentProvider } from './terminal'; +import { EmptyDisposable, IDisposable } from './util'; + +interface GitEditorRequest { + commitMessagePath?: string; +} + +export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { + + private env: { [key: string]: string }; + private disposable: IDisposable = EmptyDisposable; + + constructor(ipc?: IIPCServer) { + if (ipc) { + this.disposable = ipc.registerHandler('git-editor', this); + } + + this.env = { + GIT_EDITOR: `"${path.join(__dirname, ipc ? 'git-editor.sh' : 'git-editor-empty.sh')}"`, + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') + }; + } + + async handle({ commitMessagePath }: GitEditorRequest): Promise { + if (commitMessagePath) { + const uri = Uri.file(commitMessagePath); + const doc = await workspace.openTextDocument(uri); + await window.showTextDocument(doc, { preview: false }); + + return new Promise((c) => { + const onDidClose = window.tabGroups.onDidChangeTabs(async (tabs) => { + if (tabs.closed.some(t => t.input instanceof TabInputText && t.input.uri.toString() === uri.toString())) { + onDidClose.dispose(); + return c(true); + } + }); + }); + } + } + + getEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useEditorAsCommitInput') ? this.env : {}; + } + + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useEditorAsCommitInput') && config.get('terminalGitEditor') ? this.env : {}; + } + + dispose(): void { + this.disposable.dispose(); + } +} diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index 1b30098166..977b31eb78 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vscode'; +import { ITerminalEnvironmentProvider } from '../terminal'; import { toDisposable } from '../util'; import * as path from 'path'; import * as http from 'http'; @@ -27,7 +28,7 @@ export interface IIPCHandler { handle(request: any): Promise; } -export async function createIPCServer(context?: string): Promise { +export async function createIPCServer(context?: string): Promise { const server = http.createServer(); const hash = crypto.createHash('sha1'); @@ -65,7 +66,7 @@ export interface IIPCServer extends Disposable { registerHandler(name: string, handler: IIPCHandler): Disposable; } -class IPCServer implements IIPCServer, Disposable { +export class IPCServer implements IIPCServer, ITerminalEnvironmentProvider, Disposable { private handlers = new Map(); get ipcHandlePath(): string { return this._ipcHandlePath; } @@ -110,6 +111,10 @@ class IPCServer implements IIPCServer, Disposable { return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; } + getTerminalEnv(): { [key: string]: string } { + return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; + } + dispose(): void { this.handlers.clear(); this.server.close(); diff --git a/extensions/git/src/log.ts b/extensions/git/src/log.ts index 7f18054bef..17e3eefc95 100644 --- a/extensions/git/src/log.ts +++ b/extensions/git/src/log.ts @@ -3,7 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, EventEmitter } from 'vscode'; +import * as nls from 'vscode-nls'; +const localize = nls.loadMessageBundle(); + +import { commands, Disposable, Event, EventEmitter, OutputChannel, window, workspace } from 'vscode'; +import { dispose } from './util'; /** * The severity level of a log message @@ -18,33 +22,94 @@ export enum LogLevel { Off = 7 } -let _logLevel: LogLevel = LogLevel.Info; -const _onDidChangeLogLevel = new EventEmitter(); +/** + * Output channel logger + */ +export class OutputChannelLogger { -export const Log = { - /** - * Current logging level. - */ - get logLevel(): LogLevel { - return _logLevel; - }, + private _onDidChangeLogLevel = new EventEmitter(); + readonly onDidChangeLogLevel: Event = this._onDidChangeLogLevel.event; - /** - * Current logging level. - */ - set logLevel(logLevel: LogLevel) { - if (_logLevel === logLevel) { + private _currentLogLevel!: LogLevel; + get currentLogLevel(): LogLevel { + return this._currentLogLevel; + } + set currentLogLevel(value: LogLevel) { + if (this._currentLogLevel === value) { return; } - _logLevel = logLevel; - _onDidChangeLogLevel.fire(logLevel); - }, + this._currentLogLevel = value; + this._onDidChangeLogLevel.fire(value); - /** - * An [event](#Event) that fires when the log level has changed. - */ - get onDidChangeLogLevel(): Event { - return _onDidChangeLogLevel.event; + this.log(localize('gitLogLevel', "Log level: {0}", LogLevel[value])); } -}; + + private _defaultLogLevel!: LogLevel; + get defaultLogLevel(): LogLevel { + return this._defaultLogLevel; + } + + private _outputChannel: OutputChannel; + private _disposables: Disposable[] = []; + + constructor() { + // Output channel + this._outputChannel = window.createOutputChannel('Git'); + commands.registerCommand('git.showOutput', () => this.showOutputChannel()); + this._disposables.push(this._outputChannel); + + this._disposables.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('git.logLevel')) { + this.onLogLevelChange(); + } + })); + this.onLogLevelChange(); + } + + private onLogLevelChange(): void { + const config = workspace.getConfiguration('git'); + const logLevel: keyof typeof LogLevel = config.get('logLevel', 'Info'); + this.currentLogLevel = this._defaultLogLevel = LogLevel[logLevel] ?? LogLevel.Info; + } + + log(message: string, logLevel?: LogLevel): void { + if (logLevel && logLevel < this._currentLogLevel) { + return; + } + + this._outputChannel.appendLine(`[${new Date().toISOString()}]${logLevel ? ` [${LogLevel[logLevel].toLowerCase()}]` : ''} ${message}`); + } + + logCritical(message: string): void { + this.log(message, LogLevel.Critical); + } + + logDebug(message: string): void { + this.log(message, LogLevel.Debug); + } + + logError(message: string): void { + this.log(message, LogLevel.Error); + } + + logInfo(message: string): void { + this.log(message, LogLevel.Info); + } + + logTrace(message: string): void { + this.log(message, LogLevel.Trace); + } + + logWarning(message: string): void { + this.log(message, LogLevel.Warning); + } + + showOutputChannel(): void { + this._outputChannel.show(); + } + + dispose(): void { + this._disposables = dispose(this._disposables); + } +} diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index ecef7fcdfd..33c102ebc3 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -6,14 +6,14 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { ExtensionContext, workspace, window, Disposable, commands, OutputChannel } from 'vscode'; +import { ExtensionContext, workspace, Disposable, commands } from 'vscode'; import { findGit, Git } from './git'; import { Model } from './model'; import { CommandCenter } from './commands'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; -import { toDisposable, filterEvent, eventToPromise, logTimestamp } from './util'; +import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from '@vscode/extension-telemetry'; import { GitExtension } from './api/git'; import { GitProtocolHandler } from './protocolHandler'; @@ -24,6 +24,9 @@ import * as os from 'os'; import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; +import { OutputChannelLogger } from './log'; +import { createIPCServer, IPCServer } from './ipc/ipcServer'; +import { GitEditor } from './gitEditor'; const deactivateTasks: { (): Promise }[] = []; @@ -33,7 +36,7 @@ export async function deactivate(): Promise { } } -async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { +async function createModel(context: ExtensionContext, outputChannelLogger: OutputChannelLogger, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { const pathValue = workspace.getConfiguration('git').get('path'); let pathHints = Array.isArray(pathValue) ? pathValue : pathValue ? [pathValue] : []; @@ -46,7 +49,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann } const info = await findGit(pathHints, gitPath => { - outputChannel.appendLine(localize('validating', "{0} Validating found git in: {1}", logTimestamp(), gitPath)); + outputChannelLogger.logInfo(localize('validating', "Validating found git in: {0}", gitPath)); if (excludes.length === 0) { return true; } @@ -54,18 +57,30 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann const normalized = path.normalize(gitPath).replace(/[\r\n]+$/, ''); const skip = excludes.some(e => normalized.startsWith(e)); if (skip) { - outputChannel.appendLine(localize('skipped', "{0} Skipped found git in: {1}", logTimestamp(), gitPath)); + outputChannelLogger.logInfo(localize('skipped', "Skipped found git in: {0}", gitPath)); } return !skip; }); - const askpass = await Askpass.create(outputChannel, context.storagePath); + let ipcServer: IPCServer | undefined = undefined; + + try { + ipcServer = await createIPCServer(context.storagePath); + } catch (err) { + outputChannelLogger.logError(`Failed to create git IPC: ${err}`); + } + + const askpass = new Askpass(ipcServer); disposables.push(askpass); - const environment = askpass.getEnv(); - const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment); + const gitEditor = new GitEditor(ipcServer); + disposables.push(gitEditor); + + const environment = { ...askpass.getEnv(), ...gitEditor.getEnv(), ...ipcServer?.getEnv() }; + const terminalEnvironmentManager = new TerminalEnvironmentManager(context, [askpass, gitEditor, ipcServer]); disposables.push(terminalEnvironmentManager); + outputChannelLogger.logInfo(localize('using git', "Using git {0} from {1}", info.version, info.path)); const git = new Git({ gitPath: info.path, @@ -73,7 +88,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann version: info.version, env: environment, }); - const model = new Model(git, askpass, context.globalState, outputChannel, telemetryReporter); + const model = new Model(git, askpass, context.globalState, outputChannelLogger, telemetryReporter); disposables.push(model); const onRepository = () => commands.executeCommand('setContext', 'gitOpenRepositoryCount', `${model.repositories.length}`); @@ -81,8 +96,6 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann model.onDidCloseRepository(onRepository, null, disposables); onRepository(); - outputChannel.appendLine(localize('using git', "{0} Using git {1} from {2}", logTimestamp(), info.version, info.path)); - const onOutput = (str: string) => { const lines = str.split(/\r?\n/mg); @@ -90,12 +103,12 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann lines.pop(); } - outputChannel.appendLine(`${logTimestamp()} ${lines.join('\n')}`); + outputChannelLogger.log(lines.join('\n')); }; git.onOutput.addListener('log', onOutput); disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput))); - const cc = new CommandCenter(git, model, outputChannel, telemetryReporter); + const cc = new CommandCenter(git, model, outputChannelLogger, telemetryReporter); disposables.push( cc, new GitFileSystemProvider(model), @@ -104,6 +117,9 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann new GitTimelineProvider(model, cc) ); + // const postCommitCommandsProvider = new GitPostCommitCommandsProvider(); {{SQL CARBON TODO}} lewissanchez - Do we need this? + // model.registerPostCommitCommandsProvider(postCommitCommandsProvider); {{SQL CARBON TODO}} lewissanchez - Do we need this? + // checkGitVersion(info); {{SQL CARBON EDIT}} Don't check git version return model; @@ -162,9 +178,8 @@ export async function _activate(context: ExtensionContext): Promise Disposable.from(...disposables).dispose())); - const outputChannel = window.createOutputChannel('Git'); - commands.registerCommand('git.showOutput', () => outputChannel.show()); - disposables.push(outputChannel); + const outputChannelLogger = new OutputChannelLogger(); + disposables.push(outputChannelLogger); const { name, version, aiKey } = require('../package.json') as { name: string; version: string; aiKey: string }; const telemetryReporter = new TelemetryReporter(name, version, aiKey); @@ -178,12 +193,12 @@ export async function _activate(context: ExtensionContext): Promise workspace.getConfiguration('git', null).get('enabled') === true); const result = new GitExtensionImpl(); - eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannel, telemetryReporter, disposables)); + eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannelLogger, telemetryReporter, disposables)); return result; } try { - const model = await createModel(context, outputChannel, telemetryReporter, disposables); + const model = await createModel(context, outputChannelLogger, telemetryReporter, disposables); return new GitExtensionImpl(model); } catch (err) { if (!/Git installation not found/.test(err.message || '')) { @@ -194,7 +209,9 @@ export async function _activate(context: ExtensionContext): Promise(); readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; @@ -105,12 +106,17 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR private _onDidRemoveRemoteSourcePublisher = new EventEmitter(); readonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event; + private postCommitCommandsProviders = new Set(); + + private _onDidChangePostCommitCommandsProviders = new EventEmitter(); + readonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event; + private showRepoOnHomeDriveRootWarning = true; private pushErrorHandlers = new Set(); private disposables: Disposable[] = []; - constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel, private telemetryReporter: TelemetryReporter) { + constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannelLogger: OutputChannelLogger, private telemetryReporter: TelemetryReporter) { workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); @@ -143,11 +149,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR private async scanWorkspaceFolders(): Promise { const config = workspace.getConfiguration('git'); const autoRepositoryDetection = config.get('autoRepositoryDetection'); - - // Log repository scan settings - if (Log.logLevel <= LogLevel.Trace) { - this.outputChannel.appendLine(`${logTimestamp()} Trace: autoRepositoryDetection="${autoRepositoryDetection}"`); - } + this.outputChannelLogger.logTrace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`); if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { return; @@ -155,6 +157,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR await Promise.all((workspace.workspaceFolders || []).map(async folder => { const root = folder.uri.fsPath; + this.outputChannelLogger.logTrace(`[swsf] Workspace folder: ${root}`); // Workspace folder children const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); @@ -164,19 +167,25 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR // Repository scan folders const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; + this.outputChannelLogger.logTrace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); + for (const scanPath of scanPaths) { if (scanPath === '.git') { + this.outputChannelLogger.logTrace('[swsf] \'.git\' not supported in \'git.scanRepositories\' setting.'); continue; } if (path.isAbsolute(scanPath)) { - console.warn(localize('not supported', "Absolute paths not supported in 'git.scanRepositories' setting.")); + const notSupportedMessage = localize('not supported', "Absolute paths not supported in 'git.scanRepositories' setting."); + this.outputChannelLogger.logWarning(notSupportedMessage); + console.warn(notSupportedMessage); continue; } subfolders.add(path.join(root, scanPath)); } + this.outputChannelLogger.logTrace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); await Promise.all([...subfolders].map(f => this.openRepository(f))); })); } @@ -247,6 +256,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR .filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; openRepositoriesToDispose.forEach(r => r.dispose()); + this.outputChannelLogger.logTrace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath))); } @@ -260,17 +270,20 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR .filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true) .map(({ repository }) => repository); + this.outputChannelLogger.logTrace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath)); openRepositoriesToDispose.forEach(r => r.dispose()); } private async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise { if (!workspace.isTrusted) { + this.outputChannelLogger.logTrace('[svte] Workspace is not trusted.'); return; } const config = workspace.getConfiguration('git'); const autoRepositoryDetection = config.get('autoRepositoryDetection'); + this.outputChannelLogger.logTrace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`); if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') { return; @@ -286,16 +299,20 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR const repository = this.getRepository(uri); if (repository) { + this.outputChannelLogger.logTrace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); return; } + this.outputChannelLogger.logTrace(`[svte] Open repository for editor resource ${uri.fsPath}`); await this.openRepository(path.dirname(uri.fsPath)); })); } @sequentialize async openRepository(repoPath: string): Promise { + this.outputChannelLogger.logTrace(`Opening repository: ${repoPath}`); if (this.getRepository(repoPath)) { + this.outputChannelLogger.logTrace(`Repository for path ${repoPath} already exists`); return; } @@ -303,6 +320,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR const enabled = config.get('enabled') === true; if (!enabled) { + this.outputChannelLogger.logTrace('Git is not enabled'); return; } @@ -312,6 +330,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR fs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK); const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']); if (result.stderr.trim() === '' && result.stdout.trim() === '') { + this.outputChannelLogger.logTrace(`Bare repository: ${repoPath}`); return; } } catch { @@ -326,12 +345,15 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR // case insensitive file systems // https://github.com/microsoft/vscode/issues/33498 const repositoryRoot = Uri.file(rawRoot).fsPath; + this.outputChannelLogger.logTrace(`Repository root: ${repositoryRoot}`); if (this.getRepository(repositoryRoot)) { + this.outputChannelLogger.logTrace(`Repository for path ${repositoryRoot} already exists`); return; } if (this.shouldRepositoryBeIgnored(rawRoot)) { + this.outputChannelLogger.logTrace(`Repository for path ${repositoryRoot} is ignored`); return; } @@ -347,20 +369,19 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR this.showRepoOnHomeDriveRootWarning = false; } + this.outputChannelLogger.logTrace(`Repository for path ${repositoryRoot} is on the root of the HOMEDRIVE`); return; } } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); this.open(repository); repository.status(); // do not await this, we want SCM to know about the repo asap } catch (ex) { // noop - if (Log.logLevel <= LogLevel.Trace) { - this.outputChannel.appendLine(`${logTimestamp()} Trace: Opening repository for path='${repoPath}' failed; ex=${ex}`); - } + this.outputChannelLogger.logTrace(`Opening repository for path='${repoPath}' failed; ex=${ex}`); } } @@ -386,7 +407,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } private open(repository: Repository): void { - this.outputChannel.appendLine(`${logTimestamp()} Open repository: ${repository.root}`); + this.outputChannelLogger.logInfo(`Open repository: ${repository.root}`); const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); const disappearListener = onDidDisappearRepository(() => dispose()); @@ -443,7 +464,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return; } - this.outputChannel.appendLine(`${logTimestamp()} Close repository: ${repository.root}`); + this.outputChannelLogger.logInfo(`Close repository: ${repository.root}`); openRepository.dispose(); } @@ -491,6 +512,10 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return this.openRepositories.filter(r => r.repository === hint)[0]; } + if (hint instanceof ApiRepository) { + return this.openRepositories.filter(r => r.repository === hint.repository)[0]; + } + if (typeof hint === 'string') { hint = Uri.file(hint); } @@ -567,6 +592,20 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return [...this.remoteSourcePublishers.values()]; } + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable { + this.postCommitCommandsProviders.add(provider); + this._onDidChangePostCommitCommandsProviders.fire(); + + return toDisposable(() => { + this.postCommitCommandsProviders.delete(provider); + this._onDidChangePostCommitCommandsProviders.fire(); + }); + } + + getPostCommitCommandsProviders(): PostCommitCommandsProvider[] { + return [...this.postCommitCommandsProviders.values()]; + } + registerCredentialsProvider(provider: CredentialsProvider): Disposable { return this.askpass.registerCredentialsProvider(provider); } diff --git a/extensions/git/src/postCommitCommands.ts b/extensions/git/src/postCommitCommands.ts new file mode 100644 index 0000000000..43028a8533 --- /dev/null +++ b/extensions/git/src/postCommitCommands.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vscode-nls'; +import { Command, Disposable, Event } from 'vscode'; +import { PostCommitCommandsProvider } from './api/git'; + +export interface IPostCommitCommandsProviderRegistry { + readonly onDidChangePostCommitCommandsProviders: Event; + + getPostCommitCommandsProviders(): PostCommitCommandsProvider[]; + registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; +} + +const localize = nls.loadMessageBundle(); + +export class GitPostCommitCommandsProvider implements PostCommitCommandsProvider { + getCommands(): Command[] { + return [ + { + command: 'git.push', + title: localize('scm secondary button commit and push', "Commit & Push") + }, + { + command: 'git.sync', + title: localize('scm secondary button commit and sync', "Commit & Sync") + }, + ]; + } +} diff --git a/extensions/git/src/protocolHandler.ts b/extensions/git/src/protocolHandler.ts index 3a82b3ab25..228375b74e 100644 --- a/extensions/git/src/protocolHandler.ts +++ b/extensions/git/src/protocolHandler.ts @@ -38,7 +38,15 @@ export class GitProtocolHandler implements UriHandler { let cloneUri: Uri; try { - cloneUri = Uri.parse(Array.isArray(data.url) ? data.url[0] : data.url, true); + let rawUri = Array.isArray(data.url) ? data.url[0] : data.url; + + // Handle SSH Uri + // Ex: git@github.com:microsoft/vscode.git + rawUri = rawUri.replace(/^(git@[^\/:]+)(:)/i, 'ssh://$1/'); + + cloneUri = Uri.parse(rawUri, true); + + // Validate against supported schemes if (!schemes.has(cloneUri.scheme.toLowerCase())) { throw new Error('Unsupported scheme.'); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index d2e39bdb47..c03dd255ca 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,8 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode'; +import * as picomatch from 'picomatch'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git'; @@ -14,13 +15,14 @@ import { debounce, memoize, throttle } from './decorators'; import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, logTimestamp, onceEvent, pathEquals, relativePath } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; -import { Log, LogLevel } from './log'; +import { LogLevel, OutputChannelLogger } from './log'; import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { ActionButtonCommand } from './actionButton'; +import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -296,6 +298,10 @@ export class Resource implements SourceControlResourceState { const command = this._commandResolver.resolveChangeCommand(this); await commands.executeCommand(command.command, ...(command.arguments || [])); } + + clone() { + return new Resource(this._commandResolver, this._resourceGroupType, this._resourceUri, this._type, this._useIcons, this._renameResourceUri); + } } export const enum Operation { @@ -450,6 +456,13 @@ class ProgressManager { const onDidChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git', Uri.file(this.repository.root))); onDidChange(_ => this.updateEnablement()); this.updateEnablement(); + + this.repository.onDidChangeOperations(() => { + const commitInProgress = this.repository.operations.isRunning(Operation.Commit); + + this.repository.sourceControl.inputBox.enabled = !commitInProgress; + commands.executeCommand('setContext', 'commitInProgress', commitInProgress); + }); } private updateEnablement(): void { @@ -504,10 +517,10 @@ class FileEventLogger { constructor( private onWorkspaceWorkingTreeFileChange: Event, private onDotGitFileChange: Event, - private outputChannel: OutputChannel + private outputChannelLogger: OutputChannelLogger ) { - this.logLevelDisposable = Log.onDidChangeLogLevel(this.onDidChangeLogLevel, this); - this.onDidChangeLogLevel(Log.logLevel); + this.logLevelDisposable = outputChannelLogger.onDidChangeLogLevel(this.onDidChangeLogLevel, this); + this.onDidChangeLogLevel(outputChannelLogger.currentLogLevel); } private onDidChangeLogLevel(level: LogLevel): void { @@ -518,8 +531,8 @@ class FileEventLogger { } this.eventDisposable = combinedDisposable([ - this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [wt] Change: ${uri.fsPath}`)), - this.onDotGitFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [.git] Change: ${uri.fsPath}`)) + this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannelLogger.logDebug(`[wt] Change: ${uri.fsPath}`)), + this.onDotGitFileChange(uri => this.outputChannelLogger.logDebug(`[.git] Change: ${uri.fsPath}`)) ]); } @@ -539,14 +552,14 @@ class DotGitWatcher implements IFileWatcher { constructor( private repository: Repository, - private outputChannel: OutputChannel + private outputChannelLogger: OutputChannelLogger ) { const rootWatcher = watch(repository.dotGit.path); this.disposables.push(rootWatcher); // Ignore changes to the "index.lock" file, and watchman fsmonitor hook (https://git-scm.com/docs/githooks#_fsmonitor_watchman) cookie files. // Watchman creates a cookie file inside the git directory whenever a query is run (https://facebook.github.io/watchman/docs/cookies.html). - const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path)); + const filteredRootWatcher = filterEvent(rootWatcher.event, uri => uri.scheme === 'file' && !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path)); this.event = anyEvent(filteredRootWatcher, this.emitter.event); repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables); @@ -570,9 +583,7 @@ class DotGitWatcher implements IFileWatcher { this.transientDisposables.push(upstreamWatcher); upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); } catch (err) { - if (Log.logLevel <= LogLevel.Error) { - this.outputChannel.appendLine(`${logTimestamp()} Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`); - } + this.outputChannelLogger.logWarning(`Failed to watch ref '${upstreamPath}', is most likely packed.`); } } @@ -605,11 +616,20 @@ class ResourceCommandResolver { const title = this.getTitle(resource); if (!resource.leftUri) { - return { - command: 'vscode.open', - title: localize('open', "Open"), - arguments: [resource.rightUri, { override: resource.type === Status.BOTH_MODIFIED ? false : undefined }, title] - }; + const bothModified = resource.type === Status.BOTH_MODIFIED; + if (resource.rightUri && workspace.getConfiguration('git').get('mergeEditor', false) && (bothModified || resource.type === Status.BOTH_ADDED)) { + return { + command: '_git.openMergeEditor', + title: localize('open.merge', "Open Merge"), + arguments: [resource.rightUri] + }; + } else { + return { + command: 'vscode.open', + title: localize('open', "Open"), + arguments: [resource.rightUri, { override: bothModified ? false : undefined }, title] + }; + } } else { return { command: 'vscode.diff', @@ -849,6 +869,7 @@ export class Repository implements Disposable { private isRepositoryHuge: false | { limit: number } = false; private didWarnAboutLimit = false; + private isBranchProtectedMatcher: picomatch.Matcher | undefined; private resourceCommandResolver = new ResourceCommandResolver(this); private disposables: Disposable[] = []; @@ -856,8 +877,9 @@ export class Repository implements Disposable { private readonly repository: BaseRepository, private pushErrorHandlerRegistry: IPushErrorHandlerRegistry, remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry, + postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry, globalState: Memento, - outputChannel: OutputChannel, + outputChannelLogger: OutputChannelLogger, private telemetryReporter: TelemetryReporter ) { const repositoryWatcher = workspace.createFileSystemWatcher(new RelativePattern(Uri.file(repository.root), '**')); @@ -869,13 +891,11 @@ export class Repository implements Disposable { let onRepositoryDotGitFileChange: Event; try { - const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); + const dotGitFileWatcher = new DotGitWatcher(this, outputChannelLogger); onRepositoryDotGitFileChange = dotGitFileWatcher.event; this.disposables.push(dotGitFileWatcher); } catch (err) { - if (Log.logLevel <= LogLevel.Error) { - outputChannel.appendLine(`${logTimestamp()} Failed to watch path:'${this.dotGit.path}' or commonPath:'${this.dotGit.commonPath}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); - } + outputChannelLogger.logError(`Failed to watch path:'${this.dotGit.path}' or commonPath:'${this.dotGit.commonPath}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); onRepositoryDotGitFileChange = filterEvent(onRepositoryFileChange, uri => /\.git($|\/)/.test(uri.path)); } @@ -889,7 +909,7 @@ export class Repository implements Disposable { // Relevate repository changes should trigger virtual document change events onRepositoryDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); - this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannel)); + this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannelLogger)); const root = Uri.file(repository.root); this._sourceControl = scm.createSourceControl('git', 'Git', root); @@ -916,13 +936,19 @@ export class Repository implements Disposable { onConfigListener(updateIndexGroupVisibility, this, this.disposables); updateIndexGroupVisibility(); + workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('git.mergeEditor')) { + this.mergeGroup.resourceStates = this.mergeGroup.resourceStates.map(r => r.clone()); + } + }, undefined, this.disposables); + filterEvent(workspace.onDidChangeConfiguration, e => - e.affectsConfiguration('git.branchSortOrder', root) + e.affectsConfiguration('git.branchProtection', root) + || e.affectsConfiguration('git.branchSortOrder', root) || e.affectsConfiguration('git.untrackedChanges', root) || e.affectsConfiguration('git.ignoreSubmodules', root) || e.affectsConfiguration('git.openDiffOnClick', root) - || e.affectsConfiguration('git.rebaseWhenSync', root) - || e.affectsConfiguration('git.showUnpublishedCommitsButton', root) + || e.affectsConfiguration('git.showActionButton', root) )(this.updateModelState, this, this.disposables); const updateInputBoxVisibility = () => { @@ -963,12 +989,16 @@ export class Repository implements Disposable { } }, null, this.disposables); + const onDidChangeBranchProtection = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchProtection', root)); + onDidChangeBranchProtection(this.updateBranchProtectionMatcher, this, this.disposables); + this.updateBranchProtectionMatcher(); + const statusBar = new StatusBarCommands(this, remoteSourcePublisherRegistry); this.disposables.push(statusBar); statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables); this._sourceControl.statusBarCommands = statusBar.commands; - const actionButton = new ActionButtonCommand(this); + const actionButton = new ActionButtonCommand(this, postCommitCommandsProviderRegistry); this.disposables.push(actionButton); actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button); this._sourceControl.actionButton = actionButton.button; @@ -1014,7 +1044,7 @@ export class Repository implements Disposable { } let lineNumber = 0; - let start = 0, end; + let start = 0; let match: RegExpExecArray | null; const regex = /\r?\n/g; @@ -1023,7 +1053,7 @@ export class Repository implements Disposable { lineNumber++; } - end = match ? match.index : text.length; + const end = match ? match.index : text.length; const line = text.substring(start, end); @@ -1267,7 +1297,7 @@ export class Repository implements Disposable { }); } - closeDiffEditors(indexResources: string[], workingTreeResources: string[], ignoreSetting: boolean = false): void { + closeDiffEditors(indexResources: string[] | undefined, workingTreeResources: string[] | undefined, ignoreSetting: boolean = false): void { const config = workspace.getConfiguration('git', Uri.file(this.root)); if (!config.get('closeDiffOnOperation', false) && !ignoreSetting) { return; } @@ -1276,11 +1306,11 @@ export class Repository implements Disposable { for (const tab of window.tabGroups.all.map(g => g.tabs).flat()) { const { input } = tab; if (input instanceof TabInputTextDiff || input instanceof TabInputNotebookDiff) { - if (input.modified.scheme === 'git' && indexResources.some(r => pathEquals(r, input.modified.fsPath))) { + if (input.modified.scheme === 'git' && (indexResources === undefined || indexResources.some(r => pathEquals(r, input.modified.fsPath)))) { // Index diffEditorTabsToClose.push(tab); } - if (input.modified.scheme === 'file' && input.original.scheme === 'git' && workingTreeResources.some(r => pathEquals(r, input.modified.fsPath))) { + if (input.modified.scheme === 'file' && input.original.scheme === 'git' && (workingTreeResources === undefined || workingTreeResources.some(r => pathEquals(r, input.modified.fsPath)))) { // Working Tree diffEditorTabsToClose.push(tab); } @@ -1386,15 +1416,15 @@ export class Repository implements Disposable { } @throttle - async fetchAll(): Promise { - await this._fetch({ all: true }); + async fetchAll(cancellationToken?: CancellationToken): Promise { + await this._fetch({ all: true, cancellationToken }); } async fetch(options: FetchOptions): Promise { await this._fetch(options); } - private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean } = {}): Promise { + private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean; cancellationToken?: CancellationToken } = {}): Promise { if (!options.prune) { const config = workspace.getConfiguration('git', Uri.file(this.root)); const prune = config.get('pruneOnFetch'); @@ -1439,7 +1469,7 @@ export class Repository implements Disposable { // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.fetch({ all: true }); + await this.fetchAll(); } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { @@ -1479,13 +1509,8 @@ export class Repository implements Disposable { } @throttle - sync(head: Branch): Promise { - return this._sync(head, false); - } - - @throttle - async syncRebase(head: Branch): Promise { - return this._sync(head, true); + sync(head: Branch, rebase: boolean): Promise { + return this._sync(head, rebase); } private async _sync(head: Branch, rebase: boolean): Promise { @@ -1510,7 +1535,7 @@ export class Repository implements Disposable { const fn = async (cancellationToken?: CancellationToken) => { // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.fetch({ all: true, cancellationToken }); + await this.fetchAll(cancellationToken); } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { @@ -1868,9 +1893,10 @@ export class Repository implements Disposable { if (didHitLimit) { /* __GDPR__ "statusLimit" : { - "ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "owner": "lszomoru", + "ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Setting indicating whether submodules are ignored" }, + "limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Setting indicating the limit of status entries" }, + "statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Total number of status entries" } } */ this.telemetryReporter.sendTelemetryEvent('statusLimit', { ignoreSubmodules: String(ignoreSubmodules) }, { limit, statusLength }); @@ -2002,6 +2028,9 @@ export class Repository implements Disposable { // set count badge this.setCountBadge(); + // set mergeChanges context + commands.executeCommand('setContext', 'git.mergeChanges', merge.map(item => item.resourceUri.toString())); + this._onDidChangeStatus.fire(); this._sourceControl.commitTemplate = await this.getInputTemplate(); @@ -2188,6 +2217,21 @@ export class Repository implements Disposable { } } + private updateBranchProtectionMatcher(): void { + const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const branchProtectionGlobs = scopedConfig.get('branchProtection')!.map(bp => bp.trim()).filter(bp => bp !== ''); + + if (branchProtectionGlobs.length === 0) { + this.isBranchProtectedMatcher = undefined; + } else { + this.isBranchProtectedMatcher = picomatch(branchProtectionGlobs); + } + } + + public isBranchProtected(name: string = this.HEAD?.name ?? ''): boolean { + return this.isBranchProtectedMatcher ? this.isBranchProtectedMatcher(name) : false; + } + dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 0605fc1b3b..1a478436ac 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -9,7 +9,7 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument, const result: string[] = []; let currentLine = 0; - for (let diff of diffs) { + for (const diff of diffs) { const isInsertion = diff.originalEndLineNumber === 0; const isDeletion = diff.modifiedEndLineNumber === 0; diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts index 0e41d6ee87..a178f4c2c1 100644 --- a/extensions/git/src/statusbar.ts +++ b/extensions/git/src/statusbar.ts @@ -24,7 +24,8 @@ class CheckoutStatusBar { get command(): Command | undefined { const rebasing = !!this.repository.rebaseCommit; - const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}`; + const isBranchProtected = this.repository.isBranchProtected(); + const title = `${isBranchProtected ? '$(lock)' : '$(git-branch)'} ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}`; return { command: 'git.checkout', @@ -144,10 +145,7 @@ class SyncStatusBar { text += this.repository.syncLabel; } - const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); - const rebaseWhenSync = config.get('rebaseWhenSync'); - - command = rebaseWhenSync ? 'git.syncRebase' : 'git.sync'; + command = 'git.sync'; tooltip = this.repository.syncTooltip; } else { icon = '$(cloud-upload)'; diff --git a/extensions/git/src/terminal.ts b/extensions/git/src/terminal.ts index 780d52eb49..b473152056 100644 --- a/extensions/git/src/terminal.ts +++ b/extensions/git/src/terminal.ts @@ -6,27 +6,15 @@ import { ExtensionContext, workspace } from 'vscode'; import { filterEvent, IDisposable } from './util'; +export interface ITerminalEnvironmentProvider { + getTerminalEnv(): { [key: string]: string }; +} + export class TerminalEnvironmentManager { private readonly disposable: IDisposable; - private _enabled = false; - private set enabled(enabled: boolean) { - if (this._enabled === enabled) { - return; - } - - this._enabled = enabled; - this.context.environmentVariableCollection.clear(); - - if (enabled) { - for (const name of Object.keys(this.env)) { - this.context.environmentVariableCollection.replace(name, this.env[name]); - } - } - } - - constructor(private readonly context: ExtensionContext, private readonly env: { [key: string]: string }) { + constructor(private readonly context: ExtensionContext, private readonly envProviders: (ITerminalEnvironmentProvider | undefined)[]) { this.disposable = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')) (this.refresh, this); @@ -35,7 +23,19 @@ export class TerminalEnvironmentManager { private refresh(): void { const config = workspace.getConfiguration('git', null); - this.enabled = config.get('enabled', true) && config.get('terminalAuthentication', true); + this.context.environmentVariableCollection.clear(); + + if (!config.get('enabled', true)) { + return; + } + + for (const envProvider of this.envProviders) { + const terminalEnv = envProvider?.getTerminalEnv() ?? {}; + + for (const name of Object.keys(terminalEnv)) { + this.context.environmentVariableCollection.replace(name, terminalEnv[name]); + } + } } dispose(): void { diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index 137be34a41..5d394abe54 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -205,6 +205,7 @@ john.doe@mail.com 1580811030 1580811031 8e5a374372b8393906c7e380dbb09349c5385554 +main,branch This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{ @@ -215,6 +216,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main', 'branch'], }]); }); @@ -225,6 +227,7 @@ john.doe@mail.com 1580811030 1580811031 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 +main This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_MULTIPLE_PARENTS), [{ @@ -235,6 +238,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main'], }]); }); @@ -245,6 +249,7 @@ john.doe@mail.com 1580811030 1580811031 +main This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_NO_PARENTS), [{ @@ -255,6 +260,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main'], }]); }); }); diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index 1731329fe0..7e3b2cf9a4 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -51,3 +51,14 @@ export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Ur query: JSON.stringify(params) }); } + +/** + * Assuming `uri` is being merged it creates uris for `base`, `ours`, and `theirs` + */ +export function toMergeUris(uri: Uri): { base: Uri; ours: Uri; theirs: Uri } { + return { + base: toGitUri(uri, ':1'), + ours: toGitUri(uri, ':2'), + theirs: toGitUri(uri, ':3'), + }; +} diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index a6dc15399f..c7082cd41a 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -16,10 +16,6 @@ export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); } -export function logTimestamp(): string { - return `[${new Date().toISOString()}]`; -} - export interface IDisposable { dispose(): void; } @@ -55,9 +51,7 @@ export function anyEvent(...events: Event[]): Event { return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); - if (disposables) { - disposables.push(result); - } + disposables?.push(result); return result; }; @@ -93,7 +87,7 @@ export function eventToPromise(event: Event): Promise { } export function once(fn: (...args: any[]) => any): (...args: any[]) => any { - let didRun = false; + const didRun = false; return (...args) => { if (didRun) { @@ -223,11 +217,11 @@ export async function grep(filename: string, pattern: RegExp): Promise export function readBytes(stream: Readable, bytes: number): Promise { return new Promise((complete, error) => { let done = false; - let buffer = Buffer.allocUnsafe(bytes); + const buffer = Buffer.allocUnsafe(bytes); let bytesRead = 0; stream.on('data', (data: Buffer) => { - let bytesToRead = Math.min(bytes - bytesRead, data.length); + const bytesToRead = Math.min(bytes - bytesRead, data.length); data.copy(buffer, bytesRead, 0, bytesToRead); bytesRead += bytesToRead; diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 1399727505..c62c25401f 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -14,7 +14,7 @@ "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", - "../../src/vscode-dts/vscode.proposed.tabs.d.ts", + "../../src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts", "../../src/vscode-dts/vscode.proposed.timeline.d.ts", "../types/lib.textEncoder.d.ts" ] diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index aaa2dd0edf..f58d3d4b07 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -2,6 +2,47 @@ # yarn lockfile v1 +"@joaomoreno/unique-names-generator@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@joaomoreno/unique-names-generator/-/unique-names-generator-5.0.0.tgz#a67fe66e3d825c929fc97abfdf17fd80a72beab0" + integrity sha512-3kP6z7aoGEoM3tvhTBZioYa1QFkovOU8uxAlVclnZlXivwF/WTE5EcOzvoDdM+jtjJyWvCMDR1Q4RBjDqupD3A== + +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + "@tokenizer/token@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" @@ -29,15 +70,23 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== +"@types/picomatch@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" + integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== + "@types/which@^1.0.28": version "1.0.28" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" "@vscode/iconv-lite-umd@0.7.0": version "0.7.0" @@ -58,7 +107,6 @@ file-type@16.5.4: strtok3 "^6.2.4" token-types "^4.1.1" - ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -68,6 +116,7 @@ inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -83,6 +132,11 @@ peek-readable@^4.1.0: resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== +picomatch@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 7f7888de72..a130c19992 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -48,7 +48,7 @@ } } }, - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "main": "./out/extension.js", "browser": "./dist/browser/extension.js", "scripts": { @@ -61,9 +61,9 @@ "dependencies": { "node-fetch": "2.6.7", "uuid": "8.1.0", - "@vscode/extension-telemetry": "0.4.10", + "@vscode/extension-telemetry": "0.6.2", "vscode-nls": "^5.0.0", - "vscode-tas-client": "^0.1.42" + "vscode-tas-client": "^0.1.47" }, "devDependencies": { "@types/node": "16.x", diff --git a/extensions/github-authentication/src/common/utils.ts b/extensions/github-authentication/src/common/utils.ts index 3be4c12909..a11dc6fbdd 100644 --- a/extensions/github-authentication/src/common/utils.ts +++ b/extensions/github-authentication/src/common/utils.ts @@ -51,7 +51,7 @@ export function promiseFromEvent( event: Event, adapter: PromiseAdapter = passthrough): { promise: Promise; cancel: EventEmitter } { let subscription: Disposable; - let cancel = new EventEmitter(); + const cancel = new EventEmitter(); return { promise: new Promise((resolve, reject) => { cancel.event(_ => reject('Cancelled')); diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 7e55fc3a20..7c917e6f34 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -153,8 +153,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid const scopesSeen = new Set(); const sessionPromises = sessionData.map(async (session: SessionData) => { // For GitHub scope list, order doesn't matter so we immediately sort the scopes - const sortedScopes = session.scopes.sort(); - const scopesStr = sortedScopes.join(' '); + const scopesStr = [...session.scopes].sort().join(' '); if (scopesSeen.has(scopesStr)) { return undefined; } @@ -181,7 +180,9 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid : userInfo?.accountName ?? '', id: session.account?.id ?? userInfo?.id ?? '' }, - scopes: sortedScopes, + // we set this to session.scopes to maintain the original order of the scopes requested + // by the extension that called getSession() + scopes: session.scopes, accessToken: session.accessToken }; }); @@ -208,22 +209,25 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid public async createSession(scopes: string[]): Promise { try { - // For GitHub scope list, order doesn't matter so we immediately sort the scopes - const sortedScopes = scopes.sort(); + // For GitHub scope list, order doesn't matter so we use a sorted scope to determine + // if we've got a session already. + const sortedScopes = [...scopes].sort(); /* __GDPR__ "login" : { - "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + "owner": "TylerLeonhardt", + "comment": "Used to determine how much usage the GitHub Auth Provider gets.", + "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." } } */ this._telemetryReporter?.sendTelemetryEvent('login', { - scopes: JSON.stringify(sortedScopes), + scopes: JSON.stringify(scopes), }); const scopeString = sortedScopes.join(' '); const token = await this._githubServer.login(scopeString); - const session = await this.tokenToSession(token, sortedScopes); + const session = await this.tokenToSession(token, scopes); this.afterSessionLoad(session); const sessions = await this._sessionsPromise; @@ -244,14 +248,14 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid // If login was cancelled, do not notify user. if (e === 'Cancelled' || e.message === 'Cancelled') { /* __GDPR__ - "loginCancelled" : { } + "loginCancelled" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users cancel the login flow." } */ this._telemetryReporter?.sendTelemetryEvent('loginCancelled'); throw e; } /* __GDPR__ - "loginFailed" : { } + "loginFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users run into an error login flow." } */ this._telemetryReporter?.sendTelemetryEvent('loginFailed'); @@ -274,7 +278,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid public async removeSession(id: string) { try { /* __GDPR__ - "logout" : { } + "logout" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users log out of an account." } */ this._telemetryReporter?.sendTelemetryEvent('logout'); @@ -294,7 +298,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid } } catch (e) { /* __GDPR__ - "logoutFailed" : { } + "logoutFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often logging out of an account fails." } */ this._telemetryReporter?.sendTelemetryEvent('logoutFailed'); diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index c9460f211b..2ccf5e2530 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -496,6 +496,7 @@ export class GitHubServer implements IGitHubServer { /* __GDPR__ "session" : { + "owner": "TylerLeonhardt", "isEdu": { "classification": "NonIdentifiableDemographicInfo", "purpose": "FeatureInsight" } } */ @@ -530,6 +531,7 @@ export class GitHubServer implements IGitHubServer { /* __GDPR__ "ghe-session" : { + "owner": "TylerLeonhardt", "version": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -601,6 +603,7 @@ export class GitHubEnterpriseServer implements IGitHubServer { /* __GDPR__ "ghe-session" : { + "owner": "TylerLeonhardt", "version": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 4a8e456dac..f515adebd2 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -2,6 +2,42 @@ # yarn lockfile v1 +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + "@types/node-fetch@^2.5.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" @@ -25,10 +61,13 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" asynckit@^0.4.0: version "0.4.0" @@ -87,10 +126,10 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -tas-client@0.1.45: - version "0.1.45" - resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.45.tgz#83bbf73f8458a0f527f9a389f7e1c37f63a64a76" - integrity sha512-IG9UmCpDbDPK23UByQ27rLybkRZYEx2eC9EkieXdwPKKjZPD2zPwfQmyGnZrZet4FUt3yj0ytkwz+liR9Nz/nA== +tas-client@0.1.58: + version "0.1.58" + resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.58.tgz#67d66bf0e27df5276ebc751105e6ad47791c36d8" + integrity sha512-fOWii4wQXuo9Zl0oXgvjBzZWzKc5MmUR6XQWX93WU2c1SaP1plPo/zvXP8kpbZ9fvegFOHdapszYqMTRq/SRtg== dependencies: axios "^0.26.1" @@ -109,12 +148,12 @@ vscode-nls@^5.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== -vscode-tas-client@^0.1.42: - version "0.1.47" - resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.47.tgz#d66795cbbaa231aba659b6c40d43927d73596375" - integrity sha512-SlEPDi+0gwxor4ANzBtXwqROPQdQkClHeVJgnkvdDF5Xnl407htCsabTPAq4Di8muObORtLchqQS/k1ocaGDEg== +vscode-tas-client@^0.1.47: + version "0.1.63" + resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.63.tgz#df89e67e9bf7ecb46471a0fb8a4a522d2aafad65" + integrity sha512-TY5TPyibzi6rNmuUB7eRVqpzLzNfQYrrIl/0/F8ukrrbzOrKVvS31hM3urE+tbaVrnT+TMYXL16GhX57vEowhA== dependencies: - tas-client "0.1.45" + tas-client "0.1.58" webidl-conversions@^3.0.0: version "3.0.1" diff --git a/extensions/github/package.json b/extensions/github/package.json index af7b544f59..a5de87a74b 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -25,11 +25,33 @@ "supported": true } }, + "enabledApiProposals": [ + "contribShareMenu", + "contribEditSessions" + ], "contributes": { "commands": [ { "command": "github.publish", "title": "Publish to GitHub" + }, + { + "command": "github.copyVscodeDevLink", + "title": "Copy vscode.dev Link" + }, + { + "command": "github.copyVscodeDevLinkFile", + "title": "Copy vscode.dev Link" + }, + { + "command": "github.openOnVscodeDev", + "title": "Open on vscode.dev" + } + ], + "continueEditSession": [ + { + "command": "github.openOnVscodeDev", + "when": "github.hasGitHubRepo" } ], "menus": { @@ -37,6 +59,30 @@ { "command": "github.publish", "when": "git-base.gitEnabled" + }, + { + "command": "github.copyVscodeDevLink", + "when": "false" + }, + { + "command": "github.copyVscodeDevLinkFile", + "when": "false" + }, + { + "command": "github.openOnVscodeDev", + "when": "false" + } + ], + "file/share": [ + { + "command": "github.copyVscodeDevLinkFile", + "when": "github.hasGitHubRepo" + } + ], + "editor/context/share": [ + { + "command": "github.copyVscodeDevLink", + "when": "github.hasGitHubRepo && resourceScheme != untitled" } ] }, diff --git a/extensions/github/package.nls.json b/extensions/github/package.nls.json index 7e769b96d3..2e4869a234 100644 --- a/extensions/github/package.nls.json +++ b/extensions/github/package.nls.json @@ -6,6 +6,8 @@ "welcome.publishFolder": { "message": "You can also directly publish this folder to a GitHub repository. Once published, you'll have access to source control features powered by git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)", "comment": [ + "{Locked='$(github)'}", + "Do not translate '$(github)'. It will be rendered as an icon", "{Locked='](command:github.publish'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" @@ -14,6 +16,8 @@ "welcome.publishWorkspaceFolder": { "message": "You can also directly publish a workspace folder to a GitHub repository. Once published, you'll have access to source control features powered by git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)", "comment": [ + "{Locked='$(github)'}", + "Do not translate '$(github)'. It will be rendered as an icon", "{Locked='](command:github.publish'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" diff --git a/extensions/github/src/auth.ts b/extensions/github/src/auth.ts index 2f66e40295..d143423ccd 100644 --- a/extensions/github/src/auth.ts +++ b/extensions/github/src/auth.ts @@ -24,7 +24,7 @@ function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent { } } -const scopes = ['repo', 'workflow']; +const scopes = ['repo', 'workflow', 'user:email', 'read:user']; export async function getSession(): Promise { return await authentication.getSession('github', scopes, { createIfNone: true }); diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts index 71e6eb7756..bd7b0a48d4 100644 --- a/extensions/github/src/commands.ts +++ b/extensions/github/src/commands.ts @@ -7,6 +7,32 @@ import * as vscode from 'vscode'; import { API as GitAPI } from './typings/git'; import { publishRepository } from './publish'; import { DisposableStore } from './util'; +import { getPermalink } from './links'; + +function getVscodeDevHost(): string { + return `https://${vscode.env.appName.toLowerCase().includes('insiders') ? 'insiders.' : ''}vscode.dev/github`; +} + +async function copyVscodeDevLink(gitAPI: GitAPI, useSelection: boolean) { + try { + const permalink = getPermalink(gitAPI, useSelection, getVscodeDevHost()); + if (permalink) { + return vscode.env.clipboard.writeText(permalink); + } + } catch (err) { + vscode.window.showErrorMessage(err.message); + } +} + +async function openVscodeDevLink(gitAPI: GitAPI): Promise { + try { + const permalink = getPermalink(gitAPI, true, getVscodeDevHost()); + return permalink ? vscode.Uri.parse(permalink) : undefined; + } catch (err) { + vscode.window.showErrorMessage(err.message); + return undefined; + } +} export function registerCommands(gitAPI: GitAPI): vscode.Disposable { const disposables = new DisposableStore(); @@ -19,5 +45,17 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable { } })); + disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLink', async () => { + return copyVscodeDevLink(gitAPI, true); + })); + + disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLinkFile', async () => { + return copyVscodeDevLink(gitAPI, false); + })); + + disposables.add(vscode.commands.registerCommand('github.openOnVscodeDev', async () => { + return openVscodeDevLink(gitAPI); + })); + return disposables; } diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts index aa1697e83e..cdb8e6c299 100644 --- a/extensions/github/src/extension.ts +++ b/extensions/github/src/extension.ts @@ -5,10 +5,10 @@ import { commands, Disposable, ExtensionContext, extensions } from 'vscode'; import { GithubRemoteSourceProvider } from './remoteSourceProvider'; -import { GitExtension } from './typings/git'; +import { API, GitExtension } from './typings/git'; import { registerCommands } from './commands'; import { GithubCredentialProviderManager } from './credentialProvider'; -import { DisposableStore } from './util'; +import { DisposableStore, repositoryHasGitHubRemote } from './util'; import { GithubPushErrorHandler } from './pushErrorHandler'; import { GitBaseExtension } from './typings/git-base'; import { GithubRemoteSourcePublisher } from './remoteSourcePublisher'; @@ -48,6 +48,21 @@ function initializeGitBaseExtension(): Disposable { return disposables; } +function setGitHubContext(gitAPI: API, disposables: DisposableStore) { + if (gitAPI.repositories.find(repo => repositoryHasGitHubRemote(repo))) { + commands.executeCommand('setContext', 'github.hasGitHubRepo', true); + } else { + const openRepoDisposable = gitAPI.onDidOpenRepository(async e => { + await e.status(); + if (repositoryHasGitHubRemote(e)) { + commands.executeCommand('setContext', 'github.hasGitHubRepo', true); + openRepoDisposable.dispose(); + } + }); + disposables.add(openRepoDisposable); + } +} + function initializeGitExtension(): Disposable { const disposables = new DisposableStore(); @@ -64,6 +79,7 @@ function initializeGitExtension(): Disposable { disposables.add(new GithubCredentialProviderManager(gitAPI)); disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler())); disposables.add(gitAPI.registerRemoteSourcePublisher(new GithubRemoteSourcePublisher(gitAPI))); + setGitHubContext(gitAPI, disposables); commands.executeCommand('setContext', 'git-base.gitEnabled', true); } else { diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts new file mode 100644 index 0000000000..51e88652f4 --- /dev/null +++ b/extensions/github/src/links.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { API as GitAPI, Repository } from './typings/git'; +import { getRepositoryFromUrl } from './util'; + +export function isFileInRepo(repository: Repository, file: vscode.Uri): boolean { + return file.path.toLowerCase() === repository.rootUri.path.toLowerCase() || + (file.path.toLowerCase().startsWith(repository.rootUri.path.toLowerCase()) && + file.path.substring(repository.rootUri.path.length).startsWith('/')); +} + +export function getRepositoryForFile(gitAPI: GitAPI, file: vscode.Uri): Repository | undefined { + for (const repository of gitAPI.repositories) { + if (isFileInRepo(repository, file)) { + return repository; + } + } + return undefined; +} + +enum LinkType { + File = 1, + Notebook = 2 +} + +interface IFilePosition { + type: LinkType.File; + uri: vscode.Uri; + range: vscode.Range | undefined; +} + +interface INotebookPosition { + type: LinkType.Notebook; + uri: vscode.Uri; + cellIndex: number; + range: vscode.Range | undefined; +} + +function getFileAndPosition(): IFilePosition | INotebookPosition | undefined { + let uri: vscode.Uri | undefined; + let range: vscode.Range | undefined; + if (vscode.window.activeTextEditor) { + uri = vscode.window.activeTextEditor.document.uri; + + if (uri.scheme === 'vscode-notebook-cell' && vscode.window.activeNotebookEditor?.notebook.uri.fsPath === uri.fsPath) { + // if the active editor is a notebook editor and the focus is inside any a cell text editor + // generate deep link for text selection for the notebook cell. + const cell = vscode.window.activeNotebookEditor.notebook.getCells().find(cell => cell.document.uri.fragment === uri?.fragment); + const cellIndex = cell?.index ?? vscode.window.activeNotebookEditor.selection.start; + const range = cell !== undefined ? vscode.window.activeTextEditor.selection : undefined; + return { type: LinkType.Notebook, uri, cellIndex, range }; + } else { + // the active editor is a text editor + range = vscode.window.activeTextEditor.selection; + return { type: LinkType.File, uri, range }; + } + } + + if (vscode.window.activeNotebookEditor) { + // if the active editor is a notebook editor but the focus is not inside any cell text editor, generate deep link for the cell selection in the notebook document. + return { type: LinkType.Notebook, uri: vscode.window.activeNotebookEditor.notebook.uri, cellIndex: vscode.window.activeNotebookEditor.selection.start, range: undefined }; + } + + return undefined; +} + +function rangeString(range: vscode.Range | undefined) { + if (!range) { + return ''; + } + let hash = `#L${range.start.line + 1}`; + if (range.start.line !== range.end.line) { + hash += `-L${range.end.line + 1}`; + } + return hash; +} + +export function notebookCellRangeString(index: number | undefined, range: vscode.Range | undefined) { + if (index === undefined) { + return ''; + } + + if (!range) { + return `#C${index + 1}`; + } + + let hash = `#C${index + 1}:L${range.start.line + 1}`; + if (range.start.line !== range.end.line) { + hash += `-L${range.end.line + 1}`; + } + return hash; +} + +export function getPermalink(gitAPI: GitAPI, useSelection: boolean, hostPrefix?: string): string | undefined { + hostPrefix = hostPrefix ?? 'https://github.com'; + const fileAndPosition = getFileAndPosition(); + if (!fileAndPosition) { + return; + } + const uri = fileAndPosition.uri; + + // Use the first repo if we cannot determine a repo from the uri. + const gitRepo = (uri ? getRepositoryForFile(gitAPI, uri) : gitAPI.repositories[0]) ?? gitAPI.repositories[0]; + if (!gitRepo) { + return; + } + let repo: { owner: string; repo: string } | undefined; + gitRepo.state.remotes.find(remote => { + if (remote.fetchUrl) { + const foundRepo = getRepositoryFromUrl(remote.fetchUrl); + if (foundRepo && (remote.name === gitRepo.state.HEAD?.upstream?.remote)) { + repo = foundRepo; + return; + } else if (foundRepo && !repo) { + repo = foundRepo; + } + } + return; + }); + if (!repo) { + return; + } + + const commitHash = (gitRepo.state.HEAD?.ahead === 0) ? `/blob/${gitRepo.state.HEAD?.commit}` : ''; + const fileSegments = fileAndPosition.type === LinkType.File + ? (useSelection ? `${uri.path.substring(gitRepo.rootUri.path.length)}${rangeString(fileAndPosition.range)}` : '') + : (useSelection ? `${uri.path.substring(gitRepo.rootUri.path.length)}${notebookCellRangeString(fileAndPosition.cellIndex, fileAndPosition.range)}` : ''); + + return `${hostPrefix}/${repo.owner}/${repo.repo}${commitHash + }${fileSegments}`; +} diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts index c1083a563c..435abf3d62 100644 --- a/extensions/github/src/pushErrorHandler.ts +++ b/extensions/github/src/pushErrorHandler.ts @@ -21,8 +21,9 @@ export function isInCodespaces(): boolean { async function handlePushError(repository: Repository, remote: Remote, refspec: string, owner: string, repo: string): Promise { const yes = localize('create a fork', "Create Fork"); const no = localize('no', "No"); + const askFork = localize('fork', "You don't have permissions to push to '{0}/{1}' on GitHub. Would you like to create a fork and push to it instead?", owner, repo); - const answer = await window.showInformationMessage(localize('fork', "You don't have permissions to push to '{0}/{1}' on GitHub. Would you like to create a fork and push to it instead?", owner, repo), yes, no); + const answer = await window.showInformationMessage(askFork, yes, no); if (answer !== yes) { return; } @@ -102,18 +103,19 @@ async function handlePushError(repository: Repository, remote: Remote, refspec: let title = `Update ${remoteName}`; const head = repository.state.HEAD?.name; + let body: string | undefined; + if (head) { const commit = await repository.getCommit(head); - title = commit.message.replace(/\n.*$/m, ''); + title = commit.message.split('\n')[0]; + body = commit.message.slice(title.length + 1).trim(); } - let body: string | undefined; - const templates = await findPullRequestTemplates(repository.rootUri); if (templates.length > 0) { templates.sort((a, b) => a.path.localeCompare(b.path)); - const template = await pickPullRequestTemplate(templates); + const template = await pickPullRequestTemplate(repository.rootUri, templates); if (template) { body = new TextDecoder('utf-8').decode(await workspace.fs.readFile(template)); @@ -190,8 +192,8 @@ export async function findPullRequestTemplates(repositoryRootUri: Uri): Promise< return results.flatMap(x => x.status === 'fulfilled' && x.value || []); } -export async function pickPullRequestTemplate(templates: Uri[]): Promise { - const quickPickItemFromUri = (x: Uri) => ({ label: x.path, template: x }); +export async function pickPullRequestTemplate(repositoryRootUri: Uri, templates: Uri[]): Promise { + const quickPickItemFromUri = (x: Uri) => ({ label: path.relative(repositoryRootUri.path, x.path), template: x }); const quickPickItems = [ { label: localize('no pr template', "No template"), @@ -201,7 +203,8 @@ export async function pickPullRequestTemplate(templates: Uri[]): Promise('gitProtocol'); @@ -107,7 +97,7 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { let page = 1; while (true) { - let res = await octokit.repos.listBranches({ ...repository, per_page: 100, page }); + const res = await octokit.repos.listBranches({ ...repository, per_page: 100, page }); if (res.data.length === 0) { break; diff --git a/extensions/github/src/test/github.test.ts b/extensions/github/src/test/github.test.ts index c9da68ed30..cac622810d 100644 --- a/extensions/github/src/test/github.test.ts +++ b/extensions/github/src/test/github.test.ts @@ -18,15 +18,15 @@ suite('github smoke test', function () { test('should find all templates', async function () { const expectedValuesSorted = [ - '/PULL_REQUEST_TEMPLATE/a.md', - '/PULL_REQUEST_TEMPLATE/b.md', - '/docs/PULL_REQUEST_TEMPLATE.md', - '/docs/PULL_REQUEST_TEMPLATE/a.md', - '/docs/PULL_REQUEST_TEMPLATE/b.md', - '/.github/PULL_REQUEST_TEMPLATE.md', - '/.github/PULL_REQUEST_TEMPLATE/a.md', - '/.github/PULL_REQUEST_TEMPLATE/b.md', - '/PULL_REQUEST_TEMPLATE.md' + 'PULL_REQUEST_TEMPLATE/a.md', + 'PULL_REQUEST_TEMPLATE/b.md', + 'docs/PULL_REQUEST_TEMPLATE.md', + 'docs/PULL_REQUEST_TEMPLATE/a.md', + 'docs/PULL_REQUEST_TEMPLATE/b.md', + '.github/PULL_REQUEST_TEMPLATE.md', + '.github/PULL_REQUEST_TEMPLATE/a.md', + '.github/PULL_REQUEST_TEMPLATE/b.md', + 'PULL_REQUEST_TEMPLATE.md' ]; expectedValuesSorted.sort(); @@ -43,7 +43,7 @@ suite('github smoke test', function () { const template1 = Uri.file("some-imaginary-template-1"); const templates = [template0, template1]; - const pick = pickPullRequestTemplate(templates); + const pick = pickPullRequestTemplate(Uri.file("/"), templates); await commands.executeCommand('workbench.action.quickOpenSelectNext'); await commands.executeCommand('workbench.action.quickOpenSelectNext'); @@ -55,7 +55,7 @@ suite('github smoke test', function () { test('selecting first quick-pick item should return undefined', async () => { const templates = [Uri.file("some-imaginary-file")]; - const pick = pickPullRequestTemplate(templates); + const pick = pickPullRequestTemplate(Uri.file("/"), templates); await commands.executeCommand('workbench.action.quickOpenSelectNext'); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); diff --git a/extensions/github/src/util.ts b/extensions/github/src/util.ts index ea41cc6173..7069d1e53b 100644 --- a/extensions/github/src/util.ts +++ b/extensions/github/src/util.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { Repository } from './typings/git'; export class DisposableStore { @@ -21,3 +22,18 @@ export class DisposableStore { this.disposables.clear(); } } + +export function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined { + const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/i.exec(url) + || /^git@github\.com:([^/]+)\/([^/]+?)(\.git)?$/i.exec(url); + return match ? { owner: match[1], repo: match[2] } : undefined; +} + +export function getRepositoryFromQuery(query: string): { owner: string; repo: string } | undefined { + const match = /^([^/]+)\/([^/]+)$/i.exec(query); + return match ? { owner: match[1], repo: match[2] } : undefined; +} + +export function repositoryHasGitHubRemote(repository: Repository) { + return !!repository.state.remotes.find(remote => remote.fetchUrl ? getRepositoryFromUrl(remote.fetchUrl) : undefined); +} diff --git a/extensions/html-language-features/client/src/languageParticipants.ts b/extensions/html-language-features/client/src/languageParticipants.ts new file mode 100644 index 0000000000..005299308d --- /dev/null +++ b/extensions/html-language-features/client/src/languageParticipants.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DocumentSelector } from 'vscode-languageclient'; +import { Event, EventEmitter, extensions } from 'vscode'; + +/** + * HTML language participant contribution. + */ +interface LanguageParticipantContribution { + /** + * The id of the language which participates with the HTML language server. + */ + languageId: string; + /** + * true if the language activates the auto insertion and false otherwise. + */ + autoInsert?: boolean; +} + +export interface LanguageParticipants { + readonly onDidChange: Event; + readonly documentSelector: DocumentSelector; + hasLanguage(languageId: string): boolean; + useAutoInsert(languageId: string): boolean; + dispose(): void; +} + +export function getLanguageParticipants(): LanguageParticipants { + const onDidChangeEmmiter = new EventEmitter(); + let languages = new Set(); + let autoInsert = new Set(); + + function update() { + const oldLanguages = languages, oldAutoInsert = autoInsert; + + languages = new Set(); + languages.add('html'); + autoInsert = new Set(); + autoInsert.add('html'); + + for (const extension of extensions.allAcrossExtensionHosts) { + const htmlLanguageParticipants = extension.packageJSON?.contributes?.htmlLanguageParticipants as LanguageParticipantContribution[]; + if (Array.isArray(htmlLanguageParticipants)) { + for (const htmlLanguageParticipant of htmlLanguageParticipants) { + const languageId = htmlLanguageParticipant.languageId; + if (typeof languageId === 'string') { + languages.add(languageId); + if (htmlLanguageParticipant.autoInsert !== false) { + autoInsert.add(languageId); + } + } + } + } + } + return !isEqualSet(languages, oldLanguages) || !isEqualSet(oldLanguages, oldAutoInsert); + } + update(); + + const changeListener = extensions.onDidChange(_ => { + if (update()) { + onDidChangeEmmiter.fire(); + } + }); + + return { + onDidChange: onDidChangeEmmiter.event, + get documentSelector() { return Array.from(languages); }, + hasLanguage(languageId: string) { return languages.has(languageId); }, + useAutoInsert(languageId: string) { return autoInsert.has(languageId); }, + dispose: () => changeListener.dispose() + }; +} + +function isEqualSet(s1: Set, s2: Set) { + if (s1.size !== s2.size) { + return false; + } + for (const e of s1) { + if (!s2.has(e)) { + return false; + } + } + return true; +} diff --git a/extensions/html-language-features/server/src/utils/validation.ts b/extensions/html-language-features/server/src/utils/validation.ts new file mode 100644 index 0000000000..5738e7e501 --- /dev/null +++ b/extensions/html-language-features/server/src/utils/validation.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-html-languageservice'; +import { formatError, runSafe } from './runner'; +import { RuntimeEnvironment } from '../htmlServer'; + +export type Validator = (textDocument: TextDocument) => Promise; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; + +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + const pendingValidationRequests: { [uri: string]: Disposable } = {}; + const validationDelayMs = 500; + + const disposables: Disposable[] = []; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }, undefined, disposables); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }, undefined, disposables); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + request.dispose(); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { + if (request === pendingValidationRequests[textDocument.uri]) { + try { + const diagnostics = await validate(textDocument); + if (request === pendingValidationRequests[textDocument.uri]) { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + } + delete pendingValidationRequests[textDocument.uri]; + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + }, validationDelayMs); + } + + return { + requestRefresh: () => { + documents.all().forEach(triggerValidation); + }, + dispose: () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + const keys = Object.keys(pendingValidationRequests); + for (const key of keys) { + pendingValidationRequests[key].dispose(); + delete pendingValidationRequests[key]; + } + } + }; +} + +export function registerDiagnosticsPullSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafe(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + registration.dispose(); + } + }; + +} diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index 8805db1ede..66cee34b5d 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -259,7 +259,7 @@ firstZoom(); } - let delta = e.deltaY > 0 ? 1 : -1; + const delta = e.deltaY > 0 ? 1 : -1; updateScale(scale * (1 - delta * SCALE_PINCH_FACTOR)); }, { passive: false }); diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index 57bbb33821..1ad296a990 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -10,7 +10,7 @@ "publisher": "vscode", "icon": "icon.png", "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { "vscode": "^1.39.0" }, @@ -79,7 +79,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "0.4.10", + "@vscode/extension-telemetry": "0.6.2", "vscode-nls": "^5.0.0" }, "repository": { diff --git a/extensions/image-preview/yarn.lock b/extensions/image-preview/yarn.lock index 2e316e14a9..da44f29eaa 100644 --- a/extensions/image-preview/yarn.lock +++ b/extensions/image-preview/yarn.lock @@ -2,10 +2,49 @@ # yarn lockfile v1 -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" vscode-nls@^5.0.0: version "5.0.0" diff --git a/extensions/ipynb/.gitignore b/extensions/ipynb/.gitignore index 170d890802..8c0ca40ca2 100644 --- a/extensions/ipynb/.gitignore +++ b/extensions/ipynb/.gitignore @@ -2,3 +2,4 @@ out dist node_modules *.vsix +notebook-out diff --git a/extensions/ipynb/.vscodeignore b/extensions/ipynb/.vscodeignore index 08245c0148..f45314d0c1 100644 --- a/extensions/ipynb/.vscodeignore +++ b/extensions/ipynb/.vscodeignore @@ -6,4 +6,4 @@ extension.webpack.config.js extension-browser.webpack.config.js yarn.lock .gitignore - +esbuild.js diff --git a/extensions/ipynb/esbuild.js b/extensions/ipynb/esbuild.js new file mode 100644 index 0000000000..182f55ef6d --- /dev/null +++ b/extensions/ipynb/esbuild.js @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +//@ts-check + +const path = require('path'); +const fse = require('fs-extra'); +const esbuild = require('esbuild'); + +const args = process.argv.slice(2); + +const isWatch = args.indexOf('--watch') >= 0; + +let outputRoot = __dirname; +const outputRootIndex = args.indexOf('--outputRoot'); +if (outputRootIndex >= 0) { + outputRoot = args[outputRootIndex + 1]; +} + +const srcDir = path.join(__dirname, 'src'); +const outDir = path.join(outputRoot, 'notebook-out'); + +async function build() { + await esbuild.build({ + entryPoints: [ + path.join(srcDir, 'cellAttachmentRenderer.ts'), + ], + bundle: true, + minify: false, + sourcemap: false, + format: 'esm', + outdir: outDir, + platform: 'browser', + target: ['es2020'], + }); +} + + +build().catch(() => process.exit(1)); + +if (isWatch) { + const watcher = require('@parcel/watcher'); + watcher.subscribe(srcDir, () => { + return build(); + }); +} diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index c8feabc17a..29c7742e41 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -52,6 +52,16 @@ "priority": "default" } ], + "notebookRenderer": [ + { + "id": "vscode.markdown-it-cell-attachment-renderer", + "displayName": "Markdown it ipynb Cell Attachment renderer", + "entrypoint": { + "extends": "vscode.markdown-it-renderer", + "path": "./notebook-out/cellAttachmentRenderer.js" + } + } + ], "menus": { "file/newFile": [ { @@ -71,8 +81,9 @@ } }, "scripts": { - "compile": "npx gulp compile-extension:ipynb", - "watch": "npx gulp watch-extension:ipynb" + "compile": "npx gulp compile-extension:ipynb && npm run build-notebook", + "watch": "npx gulp watch-extension:ipynb", + "build-notebook": "node ./esbuild" }, "dependencies": { "@enonic/fnv-plus": "^1.3.0", @@ -81,6 +92,7 @@ }, "devDependencies": { "@jupyterlab/nbformat": "^3.2.9", + "@types/markdown-it": "12.2.3", "@types/uuid": "^8.3.1" }, "repository": { diff --git a/extensions/ipynb/src/cellAttachmentRenderer.ts b/extensions/ipynb/src/cellAttachmentRenderer.ts new file mode 100644 index 0000000000..844e2293a0 --- /dev/null +++ b/extensions/ipynb/src/cellAttachmentRenderer.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as MarkdownIt from 'markdown-it'; +import type * as MarkdownItToken from 'markdown-it/lib/token'; +import type { RendererContext } from 'vscode-notebook-renderer'; + +interface MarkdownItRenderer { + extendMarkdownIt(fn: (md: MarkdownIt) => void): void; +} + +export async function activate(ctx: RendererContext) { + const markdownItRenderer = (await ctx.getRenderer('vscode.markdown-it-renderer')) as MarkdownItRenderer | any; + if (!markdownItRenderer) { + throw new Error(`Could not load 'vscode.markdown-it-renderer'`); + } + + markdownItRenderer.extendMarkdownIt((md: MarkdownIt) => { + const original = md.renderer.rules.image; + md.renderer.rules.image = (tokens: MarkdownItToken[], idx: number, options, env, self) => { + const token = tokens[idx]; + const src = token.attrGet('src'); + const attachments: Record> = env.outputItem.metadata?.custom?.attachments; // this stores attachment entries for every image in the cell + if (attachments && src) { + const imageAttachment = attachments[src.replace('attachment:', '')]; + if (imageAttachment) { + // objEntries will always be length 1, with objEntries[0] holding [0]=mime,[1]=b64 + // if length = 0, something is wrong with the attachment, mime/b64 weren't copied over + const objEntries = Object.entries(imageAttachment); + if (objEntries.length) { + const [attachmentKey, attachmentVal] = objEntries[0]; + const b64Markdown = 'data:' + attachmentKey + ';base64,' + attachmentVal; + token.attrSet('src', b64Markdown); + } + } + } + + if (original) { + return original(tokens, idx, options, env, self); + } else { + return self.renderToken(tokens, idx, options); + } + }; + }); +} diff --git a/extensions/ipynb/yarn.lock b/extensions/ipynb/yarn.lock index c932570ecc..7b5488e710 100644 --- a/extensions/ipynb/yarn.lock +++ b/extensions/ipynb/yarn.lock @@ -8,21 +8,39 @@ integrity sha512-BCN9uNWH8AmiP7BXBJqEinUY9KXalmRzo+L0cB/mQsmFfzODxwQrbvxCHXUNH2iP+qKkWYtB4vyy8N62PViMFw== "@jupyterlab/nbformat@^3.2.9": - version "3.2.9" - resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-3.2.9.tgz#e7d854719612133498af4280d9a8caa0873205b0" - integrity sha512-WSf9OQo8yfFjyodbXRdFoaNwMkaAL5jFZiD6V2f8HqI380ipansWrrV7R9CGzPfgKHpUGZMO1tYKmUwzMhvZ4w== + version "3.4.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-3.4.3.tgz#cbab1bf507677b7f0f309d8353fc83fe5a973c82" + integrity sha512-i/yADrwhhAJJCUOTa+fEBMyJO7fvX9Y73I0B7V6dQhGcrmrEKLC3wk4yOo63+jRntd5+dupbiOtz3w1ncIXwIA== dependencies: - "@lumino/coreutils" "^1.5.3" + "@lumino/coreutils" "^1.11.0" -"@lumino/coreutils@^1.5.3": +"@lumino/coreutils@^1.11.0": version "1.12.0" resolved "https://registry.yarnpkg.com/@lumino/coreutils/-/coreutils-1.12.0.tgz#fbdef760f736eaf2bd396a5c6fc3a68a4b449b15" integrity sha512-DSglh4ylmLi820CNx9soJmDJCpUgymckdWeGWuN0Ash5g60oQvrQDfosVxEhzmNvtvXv45WZEqSBzDP6E5SEmQ== +"@types/linkify-it@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" + integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== + +"@types/markdown-it@12.2.3": + version "12.2.3" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" + integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ== + dependencies: + "@types/linkify-it" "*" + "@types/mdurl" "*" + +"@types/mdurl@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" + integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== + "@types/uuid@^8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" - integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== detect-indent@^6.0.0: version "6.1.0" diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index 8fa9cb9786..72997cc3ec 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -107,7 +107,7 @@ } }, "wordPattern": { - "pattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>/\\?\\s]+)", + "pattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>/\\?\\s]+)", }, "indentationRules": { "decreaseIndentPattern": { diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets index 47a6f40d20..9448fd140d 100644 --- a/extensions/javascript/snippets/javascript.code-snippets +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -190,5 +190,14 @@ "console.error($1);" ], "description": "Log error to the console" + }, + "new Promise": { + "prefix": "newpromise", + "body": [ + "new Promise((resolve, reject) => {", + "\t$TM_SELECTED_TEXT$0", + "})" + ], + "description": "Create a new Promise" } } diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 04a23eca11..e957c06c1b 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/4d30ff834ec324f56291addd197aa1e423cedfdd", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/0dfae8cc4807fecfbf8f1add095d9817df824c95", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -2271,11 +2271,47 @@ "name": "keyword.control.from.js", "match": "\\bfrom\\b" }, + { + "include": "#import-export-assert-clause" + }, { "include": "#import-export-clause" } ] }, + "import-export-assert-clause": { + "begin": "(?\\s*$)", + "begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name|resolution-mode)\\s*=\\s*((\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`)))+\\s*/>\\s*$)", "beginCaptures": { "1": { "name": "punctuation.definition.comment.js" @@ -5092,7 +5128,7 @@ "patterns": [ { "name": "entity.other.attribute-name.directive.js", - "match": "path|types|no-default-lib|lib|name" + "match": "path|types|no-default-lib|lib|name|resolution-mode" }, { "name": "keyword.operator.assignment.js", diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 5d3448bdfe..90476c2a91 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/4d30ff834ec324f56291addd197aa1e423cedfdd", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/0dfae8cc4807fecfbf8f1add095d9817df824c95", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -2271,11 +2271,47 @@ "name": "keyword.control.from.js.jsx", "match": "\\bfrom\\b" }, + { + "include": "#import-export-assert-clause" + }, { "include": "#import-export-clause" } ] }, + "import-export-assert-clause": { + "begin": "(?\\s*$)", + "begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name|resolution-mode)\\s*=\\s*((\\'([^\\'\\\\]|\\\\.)*\\')|(\\\"([^\\\"\\\\]|\\\\.)*\\\")|(\\`([^\\`\\\\]|\\\\.)*\\`)))+\\s*/>\\s*$)", "beginCaptures": { "1": { "name": "punctuation.definition.comment.js.jsx" @@ -5092,7 +5128,7 @@ "patterns": [ { "name": "entity.other.attribute-name.directive.js.jsx", - "match": "path|types|no-default-lib|lib|name" + "match": "path|types|no-default-lib|lib|name|resolution-mode" }, { "name": "keyword.operator.assignment.js.jsx", diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts index e3863900a1..42a792f9f6 100644 --- a/extensions/json-language-features/client/src/browser/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext, Uri } from 'vscode'; -import { LanguageClientOptions } from 'vscode-languageclient'; +import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor, SchemaRequestService } from '../jsonClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -14,8 +14,10 @@ declare const Worker: { declare function fetch(uri: string, options: any): any; +let client: BaseLanguageClient | undefined; + // this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export async function activate(context: ExtensionContext) { const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/jsonServerMain.js'); try { const worker = new Worker(serverMain.toString()); @@ -32,9 +34,16 @@ export function activate(context: ExtensionContext) { } }; - startClient(context, newLanguageClient, { schemaRequests }); + client = await startClient(context, newLanguageClient, { schemaRequests }); } catch (e) { console.log(e); } } + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } +} diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 40ceda25e8..ccfe6da151 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -9,18 +9,19 @@ const localize = nls.loadMessageBundle(); export type JSONLanguageStatus = { schemas: string[] }; import { - workspace, window, languages, commands, ExtensionContext, extensions, Uri, - Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, + workspace, window, languages, commands, ExtensionContext, extensions, Uri, ColorInformation, + Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient, ProvideFoldingRangeSignature, ProvideDocumentSymbolsSignature, ProvideDocumentColorsSignature } from 'vscode-languageclient'; + import { hash } from './utils/hash'; -import { createLanguageStatusItem } from './languageStatus'; +import { createDocumentColorsLimitItem, createDocumentSymbolsLimitItem, createFoldingRangeLimitItem, createLanguageStatusItem, createLimitStatusItem } from './languageStatus'; namespace VSCodeContentRequest { export const type: RequestType = new RequestType('vscode/content'); @@ -52,14 +53,11 @@ namespace SchemaAssociationNotification { export const type: NotificationType = new NotificationType('json/schemaAssociations'); } -namespace ResultLimitReachedNotification { - export const type: NotificationType = new NotificationType('json/resultLimitReached'); -} - -interface Settings { +type Settings = { json?: { schemas?: JSONSchemaSettings[]; format?: { enable?: boolean }; + keepLines?: { enable?: boolean }; validate?: { enable?: boolean }; resultLimit?: number; }; @@ -67,25 +65,22 @@ interface Settings { proxy?: string; proxyStrictSSL?: boolean; }; -} +}; -export interface JSONSchemaSettings { +export type JSONSchemaSettings = { fileMatch?: string[]; url?: string; schema?: any; -} +}; -namespace SettingIds { +export namespace SettingIds { export const enableFormatter = 'json.format.enable'; + export const enableKeepLines = 'json.format.keepLines'; export const enableValidation = 'json.validate.enable'; export const enableSchemaDownload = 'json.schemaDownload.enable'; export const maxItemsComputed = 'json.maxItemsComputed'; } -namespace StorageIds { - export const maxItemsExceededInformation = 'json.maxItemsExceededInformation'; -} - export interface TelemetryReporter { sendTelemetryEvent(eventName: string, properties?: { [key: string]: string; @@ -94,7 +89,7 @@ export interface TelemetryReporter { }): void; } -export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient; export interface Runtime { schemaRequests: SchemaRequestService; @@ -108,7 +103,9 @@ export interface SchemaRequestService { export const languageServerDescription = localize('jsonserver.name', 'JSON Language Server'); -export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { +let resultLimit = 5000; + +export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise { const toDispose = context.subscriptions; @@ -126,6 +123,11 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua let isClientReady = false; + const foldingRangeLimitStatusBarItem = createLimitStatusItem((limit: number) => createFoldingRangeLimitItem(documentSelector, SettingIds.maxItemsComputed, limit)); + const documentSymbolsLimitStatusbarItem = createLimitStatusItem((limit: number) => createDocumentSymbolsLimitItem(documentSelector, SettingIds.maxItemsComputed, limit)); + const documentColorsLimitStatusbarItem = createLimitStatusItem((limit: number) => createDocumentColorsLimitItem(documentSelector, SettingIds.maxItemsComputed, limit)); + toDispose.push(foldingRangeLimitStatusBarItem, documentSymbolsLimitStatusbarItem, documentColorsLimitStatusbarItem); + toDispose.push(commands.registerCommand('json.clearCache', async () => { if (isClientReady && runtime.schemaRequests.clearCache) { const cachedSchemas = await runtime.schemaRequests.clearCache(); @@ -210,6 +212,60 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua return r.then(updateHover); } return updateHover(r); + }, + provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken, next: ProvideFoldingRangeSignature) { + function checkLimit(r: FoldingRange[] | null | undefined): FoldingRange[] | null | undefined { + if (Array.isArray(r) && r.length > resultLimit) { + r.length = resultLimit; // truncate + foldingRangeLimitStatusBarItem.update(document, resultLimit); + } else { + foldingRangeLimitStatusBarItem.update(document, false); + } + return r; + } + const r = next(document, context, token); + if (isThenable(r)) { + return r.then(checkLimit); + } + return checkLimit(r); + }, + provideDocumentColors(document: TextDocument, token: CancellationToken, next: ProvideDocumentColorsSignature) { + function checkLimit(r: ColorInformation[] | null | undefined): ColorInformation[] | null | undefined { + if (Array.isArray(r) && r.length > resultLimit) { + r.length = resultLimit; // truncate + documentColorsLimitStatusbarItem.update(document, resultLimit); + } else { + documentColorsLimitStatusbarItem.update(document, false); + } + return r; + } + const r = next(document, token); + if (isThenable(r)) { + return r.then(checkLimit); + } + return checkLimit(r); + }, + provideDocumentSymbols(document: TextDocument, token: CancellationToken, next: ProvideDocumentSymbolsSignature) { + type T = SymbolInformation[] | DocumentSymbol[]; + function countDocumentSymbols(symbols: DocumentSymbol[]): number { + return symbols.reduce((previousValue, s) => previousValue + 1 + countDocumentSymbols(s.children), 0); + } + function isDocumentSymbol(r: T): r is DocumentSymbol[] { + return r[0] instanceof DocumentSymbol; + } + function checkLimit(r: T | null | undefined): T | null | undefined { + if (Array.isArray(r) && (isDocumentSymbol(r) ? countDocumentSymbols(r) : r.length) > resultLimit) { + documentSymbolsLimitStatusbarItem.update(document, resultLimit); + } else { + documentSymbolsLimitStatusbarItem.update(document, false); + } + return r; + } + const r = next(document, token); + if (isThenable(r)) { + return r.then(checkLimit); + } + return checkLimit(r); } } }; @@ -218,176 +274,162 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua const client = newLanguageClient('json', languageServerDescription, clientOptions); client.registerProposedFeatures(); - const disposable = client.start(); - toDispose.push(disposable); - client.onReady().then(() => { - isClientReady = true; + const schemaDocuments: { [uri: string]: boolean } = {}; - const schemaDocuments: { [uri: string]: boolean } = {}; - - // handle content request - client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { - const uri = Uri.parse(uriPath); - if (uri.scheme === 'untitled') { - return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString()))); - } - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return workspace.openTextDocument(uri).then(doc => { - schemaDocuments[uri.toString()] = true; - return doc.getText(); - }, error => { - return Promise.reject(new ResponseError(2, error.toString())); - }); - } else if (schemaDownloadEnabled) { - if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { - /* __GDPR__ - "json.schema" : { - "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); - } - return runtime.schemaRequests.getContent(uriPath).catch(e => { - return Promise.reject(new ResponseError(4, e.toString())); - }); - } else { - return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); - } - }); - - const handleContentChange = (uriString: string) => { - if (schemaDocuments[uriString]) { - client.sendNotification(SchemaContentChangeNotification.type, uriString); - return true; - } - return false; - }; - const handleActiveEditorChange = (activeEditor?: TextEditor) => { - if (!activeEditor) { - return; - } - - const activeDocUri = activeEditor.document.uri.toString(); - - if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { - schemaResolutionErrorStatusBarItem.show(); - } else { - schemaResolutionErrorStatusBarItem.hide(); - } - }; - - toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); - toDispose.push(workspace.onDidCloseTextDocument(d => { - const uriString = d.uri.toString(); - if (handleContentChange(uriString)) { - delete schemaDocuments[uriString]; - } - fileSchemaErrors.delete(uriString); - })); - toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); - - const handleRetryResolveSchemaCommand = () => { - if (window.activeTextEditor) { - schemaResolutionErrorStatusBarItem.text = '$(watch)'; - const activeDocUri = window.activeTextEditor.document.uri.toString(); - client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { - const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); - if (schemaErrorIndex !== -1) { - // Show schema resolution errors in status bar only; ref: #51032 - const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; - fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); - } else { - schemaResolutionErrorStatusBarItem.hide(); - } - schemaResolutionErrorStatusBarItem.text = '$(alert)'; - }); - } - }; - - toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); - - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); - - toDispose.push(extensions.onDidChange(_ => { - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); - })); - - // manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652. - updateFormatterRegistration(); - toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); - - updateSchemaDownloadSetting(); - - toDispose.push(workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(SettingIds.enableFormatter)) { - updateFormatterRegistration(); - } else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) { - updateSchemaDownloadSetting(); - } - })); - - client.onNotification(ResultLimitReachedNotification.type, async message => { - const shouldPrompt = context.globalState.get(StorageIds.maxItemsExceededInformation) !== false; - if (shouldPrompt) { - const ok = localize('ok', "OK"); - const openSettings = localize('goToSetting', 'Open Settings'); - const neverAgain = localize('yes never again', "Don't Show Again"); - const pick = await window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`, ok, openSettings, neverAgain); - if (pick === neverAgain) { - await context.globalState.update(StorageIds.maxItemsExceededInformation, false); - } else if (pick === openSettings) { - await commands.executeCommand('workbench.action.openSettings', SettingIds.maxItemsComputed); - } - } - }); - - toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri))); - - function updateFormatterRegistration() { - const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter); - if (!formatEnabled && rangeFormatting) { - rangeFormatting.dispose(); - rangeFormatting = undefined; - } else if (formatEnabled && !rangeFormatting) { - rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { - provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { - const filesConfig = workspace.getConfiguration('files', document); - const fileFormattingOptions = { - trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), - trimFinalNewlines: filesConfig.get('trimFinalNewlines'), - insertFinalNewline: filesConfig.get('insertFinalNewline'), - }; - const params: DocumentRangeFormattingParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - range: client.code2ProtocolConverter.asRange(range), - options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions) - }; - - return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( - client.protocol2CodeConverter.asTextEdits, - (error) => { - client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []); - return Promise.resolve([]); - } - ); - } - }); - } + // handle content request + client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { + const uri = Uri.parse(uriPath); + if (uri.scheme === 'untitled') { + return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString()))); } - - function updateSchemaDownloadSetting() { - schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false; - if (schemaDownloadEnabled) { - schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.'); - schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; - handleRetryResolveSchemaCommand(); - } else { - schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.'); - schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' }; + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return workspace.openTextDocument(uri).then(doc => { + schemaDocuments[uri.toString()] = true; + return doc.getText(); + }, error => { + return Promise.reject(new ResponseError(2, error.toString())); + }); + } else if (schemaDownloadEnabled) { + if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { + /* __GDPR__ + "json.schema" : { + "owner": "aeschli", + "comment": "Measure the use of the Azure resource manager schemas", + "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The azure schema URL that was requested." } + } + */ + runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); } + return runtime.schemaRequests.getContent(uriPath).catch(e => { + return Promise.reject(new ResponseError(4, e.toString())); + }); + } else { + return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); } - }); + + await client.start(); + + isClientReady = true; + + const handleContentChange = (uriString: string) => { + if (schemaDocuments[uriString]) { + client.sendNotification(SchemaContentChangeNotification.type, uriString); + return true; + } + return false; + }; + const handleActiveEditorChange = (activeEditor?: TextEditor) => { + if (!activeEditor) { + return; + } + + const activeDocUri = activeEditor.document.uri.toString(); + + if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { + schemaResolutionErrorStatusBarItem.show(); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + }; + + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); + toDispose.push(workspace.onDidCloseTextDocument(d => { + const uriString = d.uri.toString(); + if (handleContentChange(uriString)) { + delete schemaDocuments[uriString]; + } + fileSchemaErrors.delete(uriString); + })); + toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); + + const handleRetryResolveSchemaCommand = () => { + if (window.activeTextEditor) { + schemaResolutionErrorStatusBarItem.text = '$(watch)'; + const activeDocUri = window.activeTextEditor.document.uri.toString(); + client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { + const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); + if (schemaErrorIndex !== -1) { + // Show schema resolution errors in status bar only; ref: #51032 + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + }); + } + }; + + toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); + + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + + toDispose.push(extensions.onDidChange(_ => { + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + })); + + // manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652. + updateFormatterRegistration(); + toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); + + updateSchemaDownloadSetting(); + + toDispose.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(SettingIds.enableFormatter)) { + updateFormatterRegistration(); + } else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) { + updateSchemaDownloadSetting(); + } + })); + + toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri))); + + function updateFormatterRegistration() { + const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter); + if (!formatEnabled && rangeFormatting) { + rangeFormatting.dispose(); + rangeFormatting = undefined; + } else if (formatEnabled && !rangeFormatting) { + rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { + provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { + const filesConfig = workspace.getConfiguration('files', document); + const fileFormattingOptions = { + trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), + trimFinalNewlines: filesConfig.get('trimFinalNewlines'), + insertFinalNewline: filesConfig.get('insertFinalNewline'), + }; + const params: DocumentRangeFormattingParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + range: client.code2ProtocolConverter.asRange(range), + options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions) + }; + + return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( + client.protocol2CodeConverter.asTextEdits, + (error) => { + client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []); + return Promise.resolve([]); + } + ); + } + }); + } + } + + function updateSchemaDownloadSetting() { + schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false; + if (schemaDownloadEnabled) { + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.'); + schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; + handleRetryResolveSchemaCommand(); + } else { + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.'); + schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' }; + } + } + + return client; } function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] { @@ -430,7 +472,7 @@ function getSettings(): Settings { const configuration = workspace.getConfiguration(); const httpSettings = workspace.getConfiguration('http'); - const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000; + resultLimit = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000; const settings: Settings = { http: { @@ -440,8 +482,9 @@ function getSettings(): Settings { json: { validate: { enable: configuration.get(SettingIds.enableValidation) }, format: { enable: configuration.get(SettingIds.enableFormatter) }, + keepLines: { enable: configuration.get(SettingIds.enableKeepLines) }, schemas: [], - resultLimit + resultLimit: resultLimit + 1 // ask for one more so we can detect if the limit has been exceeded } }; const schemaSettingsById: { [schemaId: string]: JSONSchemaSettings } = Object.create(null); diff --git a/extensions/json-language-features/client/src/languageStatus.ts b/extensions/json-language-features/client/src/languageStatus.ts index ad63e17ec0..7b18cb6f86 100644 --- a/extensions/json-language-features/client/src/languageStatus.ts +++ b/extensions/json-language-features/client/src/languageStatus.ts @@ -3,7 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, languages, Uri, LanguageStatusSeverity, Disposable, commands, QuickPickItem, extensions, workspace, Extension, WorkspaceFolder, QuickPickItemKind, ThemeIcon } from 'vscode'; +import { + window, languages, Uri, Disposable, commands, QuickPickItem, + extensions, workspace, Extension, WorkspaceFolder, QuickPickItemKind, + ThemeIcon, TextDocument, LanguageStatusSeverity +} from 'vscode'; import { JSONLanguageStatus, JSONSchemaSettings } from './jsonClient'; import * as nls from 'vscode-nls'; @@ -187,13 +191,13 @@ export function createLanguageStatusItem(documentSelector: string[], statusReque statusItem.detail = undefined; if (schemas.length === 0) { statusItem.text = localize('status.noSchema.short', "No Schema Validation"); - statusItem.detail = localize('status.noSchema', 'No JSON schema configured.'); + statusItem.detail = localize('status.noSchema', 'no JSON schema configured'); } else if (schemas.length === 1) { statusItem.text = localize('status.withSchema.short', "Schema Validated"); - statusItem.detail = localize('status.singleSchema', 'JSON schema configured.'); + statusItem.detail = localize('status.singleSchema', 'JSON schema configured'); } else { statusItem.text = localize('status.withSchemas.short', "Schema Validated"); - statusItem.detail = localize('status.multipleSchema', 'Multiple JSON schemas configured.'); + statusItem.detail = localize('status.multipleSchema', 'multiple JSON schemas configured'); } statusItem.command = { command: '_json.showAssociatedSchemaList', @@ -217,3 +221,85 @@ export function createLanguageStatusItem(documentSelector: string[], statusReque return Disposable.from(statusItem, activeEditorListener, showSchemasCommand); } +export function createLimitStatusItem(newItem: (limit: number) => Disposable) { + let statusItem: Disposable | undefined; + const activeLimits: Map = new Map(); + + const toDispose: Disposable[] = []; + toDispose.push(window.onDidChangeActiveTextEditor(textEditor => { + statusItem?.dispose(); + statusItem = undefined; + const doc = textEditor?.document; + if (doc) { + const limit = activeLimits.get(doc); + if (limit !== undefined) { + statusItem = newItem(limit); + } + } + })); + toDispose.push(workspace.onDidCloseTextDocument(document => { + activeLimits.delete(document); + })); + + function update(document: TextDocument, limitApplied: number | false) { + if (limitApplied === false) { + activeLimits.delete(document); + if (statusItem && document === window.activeTextEditor?.document) { + statusItem.dispose(); + statusItem = undefined; + } + } else { + activeLimits.set(document, limitApplied); + if (document === window.activeTextEditor?.document) { + if (!statusItem || limitApplied !== activeLimits.get(document)) { + statusItem?.dispose(); + statusItem = newItem(limitApplied); + } + } + } + } + return { + update, + dispose() { + statusItem?.dispose(); + toDispose.forEach(d => d.dispose()); + toDispose.length = 0; + statusItem = undefined; + activeLimits.clear(); + } + }; +} + +const openSettingsCommand = 'workbench.action.openSettings'; +const configureSettingsLabel = localize('status.button.configure', "Configure"); + +export function createFoldingRangeLimitItem(documentSelector: string[], settingId: string, limit: number): Disposable { + const statusItem = languages.createLanguageStatusItem('json.foldingRangesStatus', documentSelector); + statusItem.name = localize('foldingRangesStatusItem.name', "JSON Folding Status"); + statusItem.severity = LanguageStatusSeverity.Warning; + statusItem.text = localize('status.limitedFoldingRanges.short', "Folding Ranges Limited"); + statusItem.detail = localize('status.limitedFoldingRanges.details', 'only {0} folding ranges shown', limit); + statusItem.command = { command: openSettingsCommand, arguments: [settingId], title: configureSettingsLabel }; + return Disposable.from(statusItem); +} + +export function createDocumentSymbolsLimitItem(documentSelector: string[], settingId: string, limit: number): Disposable { + const statusItem = languages.createLanguageStatusItem('json.documentSymbolsStatus', documentSelector); + statusItem.name = localize('documentSymbolsStatusItem.name', "JSON Outline Status"); + statusItem.severity = LanguageStatusSeverity.Warning; + statusItem.text = localize('status.limitedDocumentSymbols.short', "Outline Limited"); + statusItem.detail = localize('status.limitedDocumentSymbols.details', 'only {0} document symbols shown', limit); + statusItem.command = { command: openSettingsCommand, arguments: [settingId], title: configureSettingsLabel }; + return Disposable.from(statusItem); +} + +export function createDocumentColorsLimitItem(documentSelector: string[], settingId: string, limit: number): Disposable { + const statusItem = languages.createLanguageStatusItem('json.documentColorsStatus', documentSelector); + statusItem.name = localize('documentColorsStatusItem.name', "JSON Color Symbol Status"); + statusItem.severity = LanguageStatusSeverity.Warning; + statusItem.text = localize('status.limitedDocumentColors.short', "Color Symbols Limited"); + statusItem.detail = localize('status.limitedDocumentColors.details', 'only {0} color decorators shown', limit); + statusItem.command = { command: openSettingsCommand, arguments: [settingId], title: configureSettingsLabel }; + return Disposable.from(statusItem); +} + diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts index 6ba5724b15..46a2e4cd58 100644 --- a/extensions/json-language-features/client/src/node/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -5,7 +5,7 @@ import { ExtensionContext, OutputChannel, window, workspace } from 'vscode'; import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient'; -import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node'; import { promises as fs } from 'fs'; import * as path from 'path'; @@ -15,6 +15,7 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import { JSONSchemaCache } from './schemaCache'; let telemetry: TelemetryReporter | undefined; +let client: BaseLanguageClient | undefined; // this method is called when vs code is activated export async function activate(context: ExtensionContext) { @@ -45,11 +46,15 @@ export async function activate(context: ExtensionContext) { const schemaRequests = await getSchemaRequestService(context, log); - startClient(context, newLanguageClient, { schemaRequests, telemetry }); + client = await startClient(context, newLanguageClient, { schemaRequests, telemetry }); } -export function deactivate(): Promise { - return telemetry ? telemetry.dispose() : Promise.resolve(null); +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } + telemetry?.dispose(); } interface IPackageInfo { diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 25aef750a7..8f3345c2a6 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -5,7 +5,7 @@ "version": "1.0.0", "publisher": "vscode", "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { "vscode": "0.10.x" }, @@ -85,6 +85,12 @@ "default": true, "description": "%json.format.enable.desc%" }, + "json.format.keepLines": { + "type": "boolean", + "scope": "window", + "default": false, + "description": "%json.format.keepLines.desc%" + }, "json.trace.server": { "type": "string", "scope": "window", @@ -147,10 +153,10 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "0.5.0", + "@vscode/extension-telemetry": "0.6.2", "request-light": "^0.5.8", - "vscode-languageclient": "^7.0.0", - "vscode-nls": "^5.0.0" + "vscode-languageclient": "^8.0.2-next.5", + "vscode-nls": "^5.0.1" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index d732f6ed64..e1c2c8e608 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -7,6 +7,7 @@ "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", + "json.format.keepLines.desc" : "Keep all existing new lines when formatting.", "json.validate.enable.desc": "Enable/disable JSON validation.", "json.tracing.desc": "Traces the communication between Azure Data Studio and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index e82ae06d77..bc9b22e4ff 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -188,12 +188,6 @@ Notification: ### Item Limit If the setting `resultLimit` is set, the JSON language server will limit the number of folding ranges and document symbols computed. -When the limit is reached, a notification `json/resultLimitReached` is sent that can be shown that can be shown to the user. - -Notification: -- method: 'json/resultLimitReached' -- params: a human readable string to show to the user. - ## Try diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index cd04f24c3a..abae73c408 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,10 +12,10 @@ }, "main": "./out/node/jsonServerMain", "dependencies": { - "jsonc-parser": "^3.0.0", + "jsonc-parser": "^3.1.0", "request-light": "^0.5.8", - "vscode-json-languageservice": "^4.2.1", - "vscode-languageserver": "^7.0.0", + "vscode-json-languageservice": "^5.1.0", + "vscode-languageserver": "^8.0.2-next.5", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index aaf8f8b53e..db08e1dbf4 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -6,11 +6,12 @@ import { Connection, TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic } from 'vscode-languageserver'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Diagnostic, Range, Position } from 'vscode-json-languageservice'; +import { runSafe, runSafeAsync } from './utils/runner'; +import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; +import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { Utils, URI } from 'vscode-uri'; @@ -30,10 +31,6 @@ namespace SchemaContentChangeNotification { export const type: NotificationType = new NotificationType('json/schemaContent'); } -namespace ResultLimitReachedNotification { - export const type: NotificationType = new NotificationType('json/resultLimitReached'); -} - namespace ForceValidateRequest { export const type: RequestType = new RequestType('json/validate'); } @@ -68,7 +65,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) function getSchemaRequestService(handledSchemas: string[] = ['https', 'http', 'file']) { const builtInHandlers: { [protocol: string]: RequestService | undefined } = {}; - for (let protocol of handledSchemas) { + for (const protocol of handledSchemas) { if (protocol === 'file') { builtInHandlers[protocol] = runtime.file; } else if (protocol === 'http' || protocol === 'https') { @@ -113,11 +110,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let resultLimit = Number.MAX_VALUE; let formatterMaxNumberOfEdits = Number.MAX_VALUE; + let diagnosticsSupport: DiagnosticsSupport | undefined; + + // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { - const handledProtocols = params.initializationOptions?.handledSchemaProtocols; + const initializationOptions = params.initializationOptions as any || {}; + + const handledProtocols = initializationOptions?.handledSchemaProtocols; languageService = getLanguageService({ schemaRequestService: getSchemaRequestService(handledProtocols), @@ -139,10 +141,18 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean'); + dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof initializationOptions.provideFormatter !== 'boolean'); foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); - formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + formatterMaxNumberOfEdits = initializationOptions.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + + const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined); + if (supportsDiagnosticPull === undefined) { + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + } else { + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + } + const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { @@ -151,12 +161,17 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } : undefined, hoverProvider: true, documentSymbolProvider: true, - documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true, - documentFormattingProvider: params.initializationOptions?.provideFormatter === true, + documentRangeFormattingProvider: initializationOptions.provideFormatter === true, + documentFormattingProvider: initializationOptions.provideFormatter === true, colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, - documentLinkProvider: {} + documentLinkProvider: {}, + diagnosticProvider: { + documentSelector: null, + interFileDependencies: false, + workspaceDiagnostics: false + } }; return { capabilities }; @@ -169,6 +184,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) json?: { schemas?: JSONSchemaSettings[]; format?: { enable?: boolean }; + keepLines?: { enable?: boolean }; validate?: { enable?: boolean }; resultLimit?: number; }; @@ -185,58 +201,20 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } - const limitExceededWarnings = function () { - const pendingWarnings: { [uri: string]: { features: { [name: string]: string }; timeout?: Disposable } } = {}; - - const showLimitedNotification = (uri: string, resultLimit: number) => { - const warning = pendingWarnings[uri]; - connection.sendNotification(ResultLimitReachedNotification.type, `${Utils.basename(URI.parse(uri))}: For performance reasons, ${Object.keys(warning.features).join(' and ')} have been limited to ${resultLimit} items.`); - warning.timeout = undefined; - }; - - return { - cancel(uri: string) { - const warning = pendingWarnings[uri]; - if (warning && warning.timeout) { - warning.timeout.dispose(); - delete pendingWarnings[uri]; - } - }, - - onResultLimitExceeded(uri: string, resultLimit: number, name: string) { - return () => { - let warning = pendingWarnings[uri]; - if (warning) { - if (!warning.timeout) { - // already shown - return; - } - warning.features[name] = name; - warning.timeout.dispose(); - warning.timeout = runtime.timer.setTimeout(() => showLimitedNotification(uri, resultLimit), 2000); - } else { - warning = { features: { [name]: name } }; - warning.timeout = runtime.timer.setTimeout(() => showLimitedNotification(uri, resultLimit), 2000); - pendingWarnings[uri] = warning; - } - }; - } - }; - }(); let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; let schemaAssociations: ISchemaAssociations | SchemaConfiguration[] | undefined = undefined; let formatterRegistrations: Thenable[] | null = null; let validateEnabled = true; + let keepLinesEnabled = false; - // The settings have changed. Is send on server activation as well. + // The settings have changed. Is sent on server activation as well. connection.onDidChangeConfiguration((change) => { - let settings = change.settings; - if (runtime.configureHttpRequests) { - runtime.configureHttpRequests(settings?.http?.proxy, !!settings.http?.proxyStrictSSL); - } + const settings = change.settings; + runtime.configureHttpRequests?.(settings?.http?.proxy, !!settings.http?.proxyStrictSSL); jsonConfigurationSettings = settings.json?.schemas; validateEnabled = !!settings.json?.validate?.enable; + keepLinesEnabled = settings.json?.keepLines?.enable || false; updateConfiguration(); foldingRangeLimit = Math.trunc(Math.max(settings.json?.resultLimit || foldingRangeLimitDefault, 0)); @@ -279,25 +257,18 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) needsRevalidation = languageService.resetSchema(uriOrUris); } if (needsRevalidation) { - for (const doc of documents.all()) { - triggerValidation(doc); - } + diagnosticsSupport?.requestRefresh(); } }); // Retry schema validation on all open documents - connection.onRequest(ForceValidateRequest.type, uri => { - return new Promise(resolve => { - const document = documents.get(uri); - if (document) { - updateConfiguration(); - validateTextDocument(document, diagnostics => { - resolve(diagnostics); - }); - } else { - resolve([]); - } - }); + connection.onRequest(ForceValidateRequest.type, async uri => { + const document = documents.get(uri); + if (document) { + updateConfiguration(); + return await validateTextDocument(document); + } + return []; }); connection.onRequest(LanguageStatusRequest.type, async uri => { @@ -343,72 +314,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } languageService.configure(languageSettings); - // Revalidate any open text documents - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); } - // The content of a text document has changed. This event is emitted - // when the text document first opened or when its content has changed. - documents.onDidChangeContent((change) => { - limitExceededWarnings.cancel(change.document.uri); - triggerValidation(change.document); - }); - - // a document has closed: clear all diagnostics - documents.onDidClose(event => { - limitExceededWarnings.cancel(event.document.uri); - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); - }); - - const pendingValidationRequests: { [uri: string]: Disposable } = {}; - const validationDelayMs = 300; - - function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - request.dispose(); - delete pendingValidationRequests[textDocument.uri]; - } - } - - function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - if (validateEnabled) { - pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); - } else { - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] }); - } - } - - function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { - const respond = (diagnostics: Diagnostic[]) => { - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); - if (callback) { - callback(diagnostics); - } - }; + async function validateTextDocument(textDocument: TextDocument): Promise { if (textDocument.getText().length === 0) { - respond([]); // ignore empty documents - return; + return []; // ignore empty documents } const jsonDocument = getJSONDocument(textDocument); - const version = textDocument.version; - const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' }; - languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => { - runtime.timer.setImmediate(() => { - const currDocument = documents.get(textDocument.uri); - if (currDocument && currDocument.version === version) { - respond(diagnostics); // Send the computed diagnostics to VSCode. - } - }); - }, error => { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error)); - }); + return await languageService.doValidation(textDocument, jsonDocument, documentSettings); } connection.onDidChangeWatchedFiles((change) => { @@ -420,7 +335,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } }); if (hasChanges) { - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); } }); @@ -463,11 +378,10 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const document = documents.get(documentSymbolParams.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); - const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document symbols'); if (hierarchicalDocumentSymbolSupport) { - return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit, onResultLimitExceeded }); + return languageService.findDocumentSymbols2(document, jsonDocument, { resultLimit }); } else { - return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit, onResultLimitExceeded }); + return languageService.findDocumentSymbols(document, jsonDocument, { resultLimit }); } } return []; @@ -475,6 +389,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): TextEdit[] { + options.keepLines = keepLinesEnabled; const document = documents.get(textDocument.uri); if (document) { const edits = languageService.format(document, range ?? getFullRange(document), options); @@ -499,9 +414,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return runSafeAsync(runtime, async () => { const document = documents.get(params.textDocument.uri); if (document) { - const onResultLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, resultLimit, 'document colors'); + const jsonDocument = getJSONDocument(document); - return languageService.findDocumentColors(document, jsonDocument, { resultLimit, onResultLimitExceeded }); + return languageService.findDocumentColors(document, jsonDocument, { resultLimit }); } return []; }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); @@ -522,8 +437,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return runSafe(runtime, () => { const document = documents.get(params.textDocument.uri); if (document) { - const onRangeLimitExceeded = limitExceededWarnings.onResultLimitExceeded(document.uri, foldingRangeLimit, 'folding ranges'); - return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit, onRangeLimitExceeded }); + return languageService.getFoldingRanges(document, { rangeLimit: foldingRangeLimit }); } return null; }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); diff --git a/extensions/json-language-features/server/src/languageModelCache.ts b/extensions/json-language-features/server/src/languageModelCache.ts index 461fa24d69..dfe5bf2f37 100644 --- a/extensions/json-language-features/server/src/languageModelCache.ts +++ b/extensions/json-language-features/server/src/languageModelCache.ts @@ -18,10 +18,10 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let cleanupInterval: NodeJS.Timer | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { - let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; - let uris = Object.keys(languageModels); - for (let uri of uris) { - let languageModelInfo = languageModels[uri]; + const cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; + const uris = Object.keys(languageModels); + for (const uri of uris) { + const languageModelInfo = languageModels[uri]; if (languageModelInfo.cTime < cutoffTime) { delete languageModels[uri]; nModels--; @@ -32,14 +32,14 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime return { get(document: TextDocument): T { - let version = document.version; - let languageId = document.languageId; - let languageModelInfo = languageModels[document.uri]; + const version = document.version; + const languageId = document.languageId; + const languageModelInfo = languageModels[document.uri]; if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) { languageModelInfo.cTime = Date.now(); return languageModelInfo.languageModel; } - let languageModel = parse(document); + const languageModel = parse(document); languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() }; if (!languageModelInfo) { nModels++; @@ -48,8 +48,8 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime if (nModels === maxEntries) { let oldestTime = Number.MAX_VALUE; let oldestUri = null; - for (let uri in languageModels) { - let languageModelInfo = languageModels[uri]; + for (const uri in languageModels) { + const languageModelInfo = languageModels[uri]; if (languageModelInfo.cTime < oldestTime) { oldestUri = uri; oldestTime = languageModelInfo.cTime; @@ -64,7 +64,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime }, onDocumentRemoved(document: TextDocument) { - let uri = document.uri; + const uri = document.uri; if (languageModels[uri]) { delete languageModels[uri]; nModels--; diff --git a/extensions/json-language-features/server/src/utils/runner.ts b/extensions/json-language-features/server/src/utils/runner.ts index dc7f16415c..c9ba641cd6 100644 --- a/extensions/json-language-features/server/src/utils/runner.ts +++ b/extensions/json-language-features/server/src/utils/runner.ts @@ -8,7 +8,7 @@ import { RuntimeEnvironment } from '../jsonServer'; export function formatError(message: string, err: any): string { if (err instanceof Error) { - let error = err; + const error = err; return `${message}: ${error.message}\n${error.stack}`; } else if (typeof err === 'string') { return `${message}: ${err}`; @@ -47,7 +47,7 @@ export function runSafe(runtime: RuntimeEnvironment, func: () => T, errorV resolve(cancelValue()); } else { try { - let result = func(); + const result = func(); if (token.isCancellationRequested) { resolve(cancelValue()); return; diff --git a/extensions/json-language-features/server/src/utils/strings.ts b/extensions/json-language-features/server/src/utils/strings.ts index 62346a1da6..3faef1192e 100644 --- a/extensions/json-language-features/server/src/utils/strings.ts +++ b/extensions/json-language-features/server/src/utils/strings.ts @@ -7,7 +7,7 @@ * Determines if haystack ends with needle. */ export function endsWith(haystack: string, needle: string): boolean { - let diff = haystack.length - needle.length; + const diff = haystack.length - needle.length; if (diff > 0) { return haystack.lastIndexOf(needle) === diff; } else if (diff === 0) { diff --git a/extensions/json-language-features/server/src/utils/validation.ts b/extensions/json-language-features/server/src/utils/validation.ts new file mode 100644 index 0000000000..417bf63816 --- /dev/null +++ b/extensions/json-language-features/server/src/utils/validation.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-json-languageservice'; +import { formatError, runSafeAsync } from './runner'; +import { RuntimeEnvironment } from '../jsonServer'; + +export type Validator = (textDocument: TextDocument) => Promise; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; + +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + const pendingValidationRequests: { [uri: string]: Disposable } = {}; + const validationDelayMs = 500; + + const disposables: Disposable[] = []; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }, undefined, disposables); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }, undefined, disposables); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + request.dispose(); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { + if (request === pendingValidationRequests[textDocument.uri]) { + try { + const diagnostics = await validate(textDocument); + if (request === pendingValidationRequests[textDocument.uri]) { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + } + delete pendingValidationRequests[textDocument.uri]; + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + }, validationDelayMs); + } + + return { + requestRefresh: () => { + documents.all().forEach(triggerValidation); + }, + dispose: () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + const keys = Object.keys(pendingValidationRequests); + for (const key of keys) { + pendingValidationRequests[key].dispose(); + delete pendingValidationRequests[key]; + } + } + }; +} + +export function registerDiagnosticsPullSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafeAsync(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + registration.dispose(); + } + }; + +} diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 5cd1cf5f48..e0b300c908 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -12,61 +12,66 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -jsonc-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" - integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== +jsonc-parser@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d" + integrity sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg== request-light@^0.5.8: version "0.5.8" resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== -vscode-json-languageservice@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4" - integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA== +vscode-json-languageservice@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.1.0.tgz#b1f197a60338cb378189fcb41489a84846724dd9" + integrity sha512-D5612D7h/Gh4A0JmdttPveWzT9dur21WXvBHWKPdOt0sLO6ILz8vN6+IzWnvwDOVAEFTpzIAMVMZwbKZkwGGiA== dependencies: - jsonc-parser "^3.0.0" - vscode-languageserver-textdocument "^1.0.3" - vscode-languageserver-types "^3.16.0" - vscode-nls "^5.0.0" + jsonc-parser "^3.1.0" + vscode-languageserver-textdocument "^1.0.4" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" vscode-uri "^3.0.3" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.2-next.1: + version "8.0.2-next.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2-next.1.tgz#6bdc39fd194782032e34047eeefce562941259c6" + integrity sha512-sbbvGSWja7NVBLHPGawtgezc8DHYJaP4qfr/AaJiyDapWcSFtHyPtm18+LnYMLTmB7bhOUW/lf5PeeuLpP6bKA== -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.2-next.6: + version "3.17.2-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2-next.6.tgz#8f1dc0fcb29366b85f623a3f9af726de433b5fcc" + integrity sha512-WtsebNOOkWyNn4oFYoAMPC8Q/ZDoJ/K7Ja53OzTixiitvrl/RpXZETrtzH79R8P5kqCyx6VFBPb6KQILJfkDkA== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" + vscode-jsonrpc "8.0.2-next.1" + vscode-languageserver-types "3.17.2-next.2" -vscode-languageserver-textdocument@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff" - integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A== +vscode-languageserver-textdocument@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157" + integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ== -vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.2-next.2: + version "3.17.2-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2-next.2.tgz#af5d6978eee7682aab87c1419323f5b141ac6596" + integrity sha512-TiAkLABgqkVWdAlC3XlOfdhdjIAdVU4YntPUm9kKGbXr+MGwpVnKz2KZMNBcvG0CFx8Hi8qliL0iq+ndPB720w== -vscode-languageserver@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0" - integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== +vscode-languageserver-types@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== + +vscode-languageserver@^8.0.2-next.5: + version "8.0.2-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.2-next.5.tgz#39a2dd4c504fb88042375e7ac706a714bdaab4e5" + integrity sha512-2ZDb7O/4atS9mJKufPPz637z+51kCyZfgnobFW5eSrUdS3c0UB/nMS4Ng1EavYTX84GVaVMKCrmP0f2ceLmR0A== dependencies: - vscode-languageserver-protocol "3.16.0" + vscode-languageserver-protocol "3.17.2-next.6" -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== vscode-uri@^3.0.3: version "3.0.3" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 98216da36e..61f19346fd 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -2,15 +2,54 @@ # yarn lockfile v1 +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + "@types/node@16.x": version "16.11.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -"@vscode/extension-telemetry@0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.0.tgz#8214171e550393d577fc56326fa986c6800b831b" - integrity sha512-27FsgeVJvC4zVw7Ar3Ub+7vJswDt8RoBFpbgBwf8Xq/B2gaT8G6a+gkw3s2pQmjWGIqyu7TRA8e9rS8/vxv6NQ== +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" balanced-match@^1.0.0: version "1.0.0" @@ -49,44 +88,44 @@ request-light@^0.5.8: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== -semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.2-next.1: + version "8.0.2-next.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2-next.1.tgz#6bdc39fd194782032e34047eeefce562941259c6" + integrity sha512-sbbvGSWja7NVBLHPGawtgezc8DHYJaP4qfr/AaJiyDapWcSFtHyPtm18+LnYMLTmB7bhOUW/lf5PeeuLpP6bKA== -vscode-languageclient@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2" - integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg== +vscode-languageclient@^8.0.2-next.5: + version "8.0.2-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.2-next.5.tgz#3238a388585c3119e247f761b4355273cc2fd909" + integrity sha512-g87RJLHz0XlRyk6DOTbAk4JHcj8CKggXy4JiFL7OlhETkcYzTOR8d+Qdb4GqZr37PDs1Cl21omtTNK5LyR/RQg== dependencies: minimatch "^3.0.4" - semver "^7.3.4" - vscode-languageserver-protocol "3.16.0" + semver "^7.3.5" + vscode-languageserver-protocol "3.17.2-next.6" -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.2-next.6: + version "3.17.2-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2-next.6.tgz#8f1dc0fcb29366b85f623a3f9af726de433b5fcc" + integrity sha512-WtsebNOOkWyNn4oFYoAMPC8Q/ZDoJ/K7Ja53OzTixiitvrl/RpXZETrtzH79R8P5kqCyx6VFBPb6KQILJfkDkA== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" + vscode-jsonrpc "8.0.2-next.1" + vscode-languageserver-types "3.17.2-next.2" -vscode-languageserver-types@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.2-next.2: + version "3.17.2-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2-next.2.tgz#af5d6978eee7682aab87c1419323f5b141ac6596" + integrity sha512-TiAkLABgqkVWdAlC3XlOfdhdjIAdVU4YntPUm9kKGbXr+MGwpVnKz2KZMNBcvG0CFx8Hi8qliL0iq+ndPB720w== -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== yallist@^4.0.0: version "4.0.0" diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index 8f270cd2b2..f9ec3fec78 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -15,9 +15,8 @@ { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, { "open": "`", "close": "`", "notIn": ["string", "comment"] } ], - "wordPattern": "(\"(?:[^\\\\\\\"]*(?:\\\\.)?)*\"?)|[^\\s{}\\[\\],:]+", "indentationRules": { - "increaseIndentPattern": "({+(?=([^\"]*\"[^\"]*\")*[^\"}]*$))|(\\[+(?=([^\"]*\"[^\"]*\")*[^\"\\]]*$))", + "increaseIndentPattern": "({+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"}]*)$)|(\\[+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"\\]]*)$)", "decreaseIndentPattern": "^\\s*[}\\]],?\\s*$" } } diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 0a50694d7e..46b4bae228 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -33,7 +33,7 @@ "git": { "name": "microsoft/vscode-markdown-tm-grammar", "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", - "commitHash": "b068fcb2fbfa834e695505bfb02bbcc0b4edab8b" + "commitHash": "69d3321b4923ca2d5e8e900018887cc38b5fe04a" } }, "license": "MIT", diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index 281443efc2..69e4da10d2 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -67,6 +67,7 @@ "meta.embedded.block.javascript": "javascript", "meta.embedded.block.json": "json", "meta.embedded.block.jsonc": "jsonc", + "meta.embedded.block.latex": "latex", "meta.embedded.block.less": "less", "meta.embedded.block.objc": "objc", "meta.embedded.block.scss": "scss", @@ -91,8 +92,8 @@ ], "configurationDefaults": { "[markdown]": { - "editor.unicodeHighlight.ambiguousCharacters": false, - "editor.unicodeHighlight.invisibleCharacters": false + "editor.unicodeHighlight.ambiguousCharacters": false, + "editor.unicodeHighlight.invisibleCharacters": false } } }, diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index a0dfdc270f..895836a188 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/b068fcb2fbfa834e695505bfb02bbcc0b4edab8b", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/69d3321b4923ca2d5e8e900018887cc38b5fe04a", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2774,47 +2774,50 @@ "5": { "name": "punctuation.definition.metadata.markdown" }, - "6": { + "7": { "name": "punctuation.definition.link.markdown" }, - "7": { + "8": { "name": "markup.underline.link.markdown" }, "9": { "name": "punctuation.definition.link.markdown" }, "10": { - "name": "string.other.link.description.title.markdown" - }, - "11": { - "name": "punctuation.definition.string.begin.markdown" + "name": "markup.underline.link.markdown" }, "12": { - "name": "punctuation.definition.string.end.markdown" + "name": "string.other.link.description.title.markdown" }, "13": { - "name": "string.other.link.description.title.markdown" + "name": "punctuation.definition.string.begin.markdown" }, "14": { - "name": "punctuation.definition.string.begin.markdown" + "name": "punctuation.definition.string.end.markdown" }, "15": { - "name": "punctuation.definition.string.end.markdown" - }, - "16": { "name": "string.other.link.description.title.markdown" }, - "17": { + "16": { "name": "punctuation.definition.string.begin.markdown" }, - "18": { + "17": { "name": "punctuation.definition.string.end.markdown" }, + "18": { + "name": "string.other.link.description.title.markdown" + }, "19": { + "name": "punctuation.definition.string.begin.markdown" + }, + "20": { + "name": "punctuation.definition.string.end.markdown" + }, + "21": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n ((?>[^\\s()]+)|\\(\\g*\\))*)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)([^<>\\n]*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n # The title \n (?:\n ((\\()[^()]*(\\))) # Match title in parens…\n | ((\")[^\"]*(\")) # or in double quotes…\n | ((')[^']*(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.link.inline.markdown" }, "link-ref": { @@ -2838,7 +2841,7 @@ "name": "punctuation.definition.constant.end.markdown" } }, - "match": "(\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])(\\[)([^\\]]*+)(\\])", + "match": "(?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])(\\[)([^\\]]*+)(\\])", "name": "meta.link.reference.markdown" }, "link-ref-literal": { @@ -2859,7 +2862,7 @@ "name": "punctuation.definition.constant.end.markdown" } }, - "match": "(\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])[ ]?(\\[)(\\])", + "match": "(?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])[ ]?(\\[)(\\])", "name": "meta.link.reference.literal.markdown" }, "link-ref-shortcut": { @@ -2874,7 +2877,7 @@ "name": "punctuation.definition.link.title.end.markdown" } }, - "match": "(\\[)(\\S+?)(\\])", + "match": "(? = (ctx) => { @@ -26,6 +127,7 @@ export const activate: ActivationFunction = (ctx) => { markdownIt.linkify.set({ fuzzyLink: false }); addNamedHeaderRendering(markdownIt); + addLinkRenderer(markdownIt); const style = document.createElement('style'); style.textContent = ` @@ -206,7 +308,9 @@ export const activate: ActivationFunction = (ctx) => { previewNode.classList.remove('emptyMarkdownCell'); const markdownText = outputInfo.mime.startsWith('text/x-') ? `\`\`\`${outputInfo.mime.substr(7)}\n${text}\n\`\`\`` : (outputInfo.mime.startsWith('application/') ? `\`\`\`${outputInfo.mime.substr(12)}\n${text}\n\`\`\`` : text); - const unsanitizedRenderedMarkdown = markdownIt.render(markdownText); + const unsanitizedRenderedMarkdown = markdownIt.render(markdownText, { + outputItem: outputInfo, + }); previewNode.innerHTML = (ctx.workspace.isTrusted ? unsanitizedRenderedMarkdown : DOMPurify.sanitize(unsanitizedRenderedMarkdown, sanitizerOptions)) as string; @@ -225,12 +329,12 @@ function addNamedHeaderRendering(md: InstanceType): void { const originalHeaderOpen = md.renderer.rules.heading_open; md.renderer.rules.heading_open = (tokens: MarkdownItToken[], idx: number, options, env, self) => { const title = tokens[idx + 1].children!.reduce((acc, t) => acc + t.content, ''); - let slug = slugFromHeading(title); + let slug = slugify(title); if (slugCounter.has(slug)) { const count = slugCounter.get(slug)!; slugCounter.set(slug, count + 1); - slug = slugFromHeading(slug + '-' + (count + 1)); + slug = slugify(slug + '-' + (count + 1)); } else { slugCounter.set(slug, 0); } @@ -251,9 +355,26 @@ function addNamedHeaderRendering(md: InstanceType): void { }; } -function slugFromHeading(heading: string): string { +function addLinkRenderer(md: MarkdownIt): void { + const original = md.renderer.rules.link_open; + + md.renderer.rules.link_open = (tokens: MarkdownItToken[], idx: number, options, env, self) => { + const token = tokens[idx]; + const href = token.attrGet('href'); + if (typeof href === 'string' && href.startsWith('#')) { + token.attrSet('href', '#' + slugify(href.slice(1))); + } + if (original) { + return original(tokens, idx, options, env, self); + } else { + return self.renderToken(tokens, idx, options); + } + }; +} + +function slugify(text: string): string { const slugifiedHeading = encodeURI( - heading.trim() + text.trim() .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - // allow-any-unicode-next-line diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 35aab1cc38..952abb1a46 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -6,7 +6,7 @@ "icon": "icon.png", "publisher": "vscode", "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { "vscode": "^1.20.0" }, @@ -16,7 +16,7 @@ "Programming Languages" ], "enabledApiProposals": [ - "textEditorDrop" + "documentPaste" ], "activationEvents": [ "onLanguage:markdown", @@ -46,7 +46,7 @@ "contributes": { "notebookRenderer": [ { - "id": "markdownItRenderer", + "id": "vscode.markdown-it-renderer", "displayName": "Markdown it renderer", "entrypoint": "./notebook-out/index.js", "mimeTypes": [ @@ -398,59 +398,116 @@ "description": "%configuration.markdown.suggest.paths.enabled.description%", "scope": "resource" }, - "markdown.trace": { + "markdown.trace.extension": { "type": "string", "enum": [ "off", "verbose" ], "default": "off", - "description": "%markdown.trace.desc%", + "description": "%markdown.trace.extension.desc%", "scope": "window" }, + "markdown.trace.server": { + "type": "string", + "scope": "window", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "%markdown.trace.server.desc%" + }, "markdown.editor.drop.enabled": { "type": "boolean", "default": true, "markdownDescription": "%configuration.markdown.editor.drop.enabled%", "scope": "resource" }, + "markdown.experimental.editor.pasteLinks.enabled": { + "type": "boolean", + "scope": "resource", + "markdownDescription": "%configuration.markdown.editor.pasteLinks.enabled%", + "default": true, + "tags": [ + "experimental" + ] + }, "markdown.experimental.validate.enabled": { "type": "boolean", "scope": "resource", "description": "%configuration.markdown.experimental.validate.enabled.description%", - "default": false - }, - "markdown.experimental.validate.referenceLinks": { - "type": "string", - "scope": "resource", - "description": "%configuration.markdown.experimental.validate.referenceLinks.description%", - "default": "warning", - "enum": [ - "ignore", - "warning", - "error" + "default": false, + "tags": [ + "experimental" ] }, - "markdown.experimental.validate.headerLinks": { + "markdown.experimental.validate.referenceLinks.enabled": { "type": "string", "scope": "resource", - "description": "%configuration.markdown.experimental.validate.headerLinks.description%", + "markdownDescription": "%configuration.markdown.experimental.validate.referenceLinks.enabled.description%", "default": "warning", "enum": [ "ignore", "warning", "error" + ], + "tags": [ + "experimental" ] }, - "markdown.experimental.validate.fileLinks": { + "markdown.experimental.validate.fragmentLinks.enabled": { "type": "string", "scope": "resource", - "description": "%configuration.markdown.experimental.validate.fileLinks.description%", + "markdownDescription": "%configuration.markdown.experimental.validate.fragmentLinks.enabled.description%", "default": "warning", "enum": [ "ignore", "warning", "error" + ], + "tags": [ + "experimental" + ] + }, + "markdown.experimental.validate.fileLinks.enabled": { + "type": "string", + "scope": "resource", + "markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.enabled.description%", + "default": "warning", + "enum": [ + "ignore", + "warning", + "error" + ], + "tags": [ + "experimental" + ] + }, + "markdown.experimental.validate.fileLinks.markdownFragmentLinks": { + "type": "string", + "scope": "resource", + "markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.markdownFragmentLinks.description%", + "default": "ignore", + "enum": [ + "ignore", + "warning", + "error" + ], + "tags": [ + "experimental" + ] + }, + "markdown.experimental.validate.ignoreLinks": { + "type": "array", + "scope": "resource", + "markdownDescription": "%configuration.markdown.experimental.validate.ignoreLinks.description%", + "items": { + "type": "string" + }, + "tags": [ + "experimental" ] } } @@ -488,8 +545,8 @@ ] }, "scripts": { - "compile": "gulp compile-extension:markdown-language-features && npm run build-preview && npm run build-notebook", - "watch": "npm run build-preview && gulp watch-extension:markdown-language-features", + "compile": "gulp compile-extension:markdown-language-features-languageService && gulp compile-extension:markdown-language-features-server && gulp compile-extension:markdown-language-features && npm run build-preview && npm run build-notebook", + "watch": "npm run build-preview && gulp watch-extension:markdown-language-features watch-extension:markdown-language-features-languageService watch-extension:markdown-language-features-server", "vscode:prepublish": "npm run build-ext && npm run build-preview", "build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json", "build-notebook": "node ./esbuild-notebook", @@ -498,12 +555,14 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "0.4.10", - "dompurify": "^2.3.1", + "@vscode/extension-telemetry": "0.6.2", + "dompurify": "^2.3.3", "highlight.js": "^11.4.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.1", "morphdom": "^2.6.1", + "picomatch": "^2.3.1", + "vscode-languageclient": "^8.0.1", "vscode-languageserver-textdocument": "^1.0.4", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.3" @@ -512,9 +571,12 @@ "@types/dompurify": "^2.3.1", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "12.2.3", + "@types/picomatch": "^2.3.0", "@types/vscode-notebook-renderer": "^1.60.0", "@types/vscode-webview": "^1.57.0", - "lodash.throttle": "^4.1.1" + "lodash.throttle": "^4.1.1", + "vscode-languageserver-types": "^3.17.2", + "vscode-markdown-languageservice": "^0.0.0-alpha.10" }, "repository": { "type": "git", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index 2d9ba25800..1c32e447e0 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -17,7 +17,8 @@ "markdown.showSource.title": "Show Source", "markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the Markdown preview. Relative paths are interpreted relative to the folder open in the Explorer. If there is no open folder, they are interpreted relative to the location of the Markdown file. All '\\' need to be written as '\\\\'.", "markdown.showPreviewSecuritySelector.title": "Change Preview Security Settings", - "markdown.trace.desc": "Enable debug logging for the Markdown extension.", + "markdown.trace.extension.desc": "Enable debug logging for the Markdown extension.", + "markdown.trace.server.desc": "Traces the communication between VS Code and the Markdown language server.", "markdown.preview.refresh.title": "Refresh Preview", "markdown.preview.toggleLock.title": "Toggle Preview Locking", "markdown.findAllFileReferences": "Find File References", @@ -28,10 +29,13 @@ "configuration.markdown.links.openLocation.currentGroup": "Open links in the active editor group.", "configuration.markdown.links.openLocation.beside": "Open links beside the active editor.", "configuration.markdown.suggest.paths.enabled.description": "Enable/disable path suggestions for markdown links", - "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbenck.experimental.editor.dropIntoEditor.enabled#`.", + "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#editor.dropIntoEditor.enabled#`.", + "configuration.markdown.editor.pasteLinks.enabled": "Enable/disable pasting files into a Markdown editor inserts Markdown links. Requires enabling `#editor.experimental.pasteActions.enabled#`.", "configuration.markdown.experimental.validate.enabled.description": "Enable/disable all error reporting in Markdown files.", - "configuration.markdown.experimental.validate.referenceLinks.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", - "configuration.markdown.experimental.validate.headerLinks.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", - "configuration.markdown.experimental.validate.fileLinks.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.fragmentLinks.enabled.description": "Validate fragment links to headers in the current Markdown file, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.fileLinks.enabled.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.fileLinks.markdownFragmentLinks.description": "Validate the fragment part of links to headers in other files in Markdown files, e.g. `[link](/path/to/file.md#header)`. Inherits the setting value from `#markdown.experimental.validate.fragmentLinks.enabled#` by default.", + "configuration.markdown.experimental.validate.ignoreLinks.description": "Configure links that should not be validated. For example `/about` would not validate the link `[about](/about)`, while the glob `/assets/**/*.svg` would let you skip validation for any link to `.svg` files under the `assets` directory.", "workspaceTrust": "Required for loading styles configured in the workspace." } diff --git a/extensions/markdown-language-features/preview-src/loading.ts b/extensions/markdown-language-features/preview-src/loading.ts index 4c833c28ad..9a797632dc 100644 --- a/extensions/markdown-language-features/preview-src/loading.ts +++ b/extensions/markdown-language-features/preview-src/loading.ts @@ -29,9 +29,7 @@ export class StyleLoadingMonitor { return; } this.finishedLoading = true; - if (this.poster) { - this.poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); - } + this.poster?.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); }); } diff --git a/extensions/markdown-language-features/server/.npmignore b/extensions/markdown-language-features/server/.npmignore new file mode 100644 index 0000000000..bfd4215998 --- /dev/null +++ b/extensions/markdown-language-features/server/.npmignore @@ -0,0 +1,12 @@ +.vscode/ +.github/ +out/test/ +src/ +.eslintrc.js +.gitignore +tsconfig*.json +*.tsbuildinfo +*.map +example.cjs +CODE_OF_CONDUCT.md +SECURITY.md \ No newline at end of file diff --git a/extensions/markdown-language-features/server/.vscode/launch.json b/extensions/markdown-language-features/server/.vscode/launch.json new file mode 100644 index 0000000000..fd9033bffa --- /dev/null +++ b/extensions/markdown-language-features/server/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + "version": "0.1.0", + // List of configurations. Add new configurations or edit existing ones. + "configurations": [ + { + "name": "Attach", + "type": "node", + "request": "attach", + "port": 7997, + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/extensions/markdown-language-features/server/.vscode/settings.json b/extensions/markdown-language-features/server/.vscode/settings.json new file mode 100644 index 0000000000..7a73a41bfd --- /dev/null +++ b/extensions/markdown-language-features/server/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/extensions/markdown-language-features/server/.vscode/tasks.json b/extensions/markdown-language-features/server/.vscode/tasks.json new file mode 100644 index 0000000000..ecc951a7ba --- /dev/null +++ b/extensions/markdown-language-features/server/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + "version": "2.0.0", + "command": "npm", + "args": [ + "run", + "watch" + ], + "isBackground": true, + "problemMatcher": "$tsc-watch", + "tasks": [ + { + "label": "npm", + "type": "shell", + "command": "npm", + "args": [ + "run", + "watch" + ], + "isBackground": true, + "problemMatcher": "$tsc-watch", + "group": { + "_id": "build", + "isDefault": false + } + } + ] +} \ No newline at end of file diff --git a/extensions/markdown-language-features/server/README.md b/extensions/markdown-language-features/server/README.md new file mode 100644 index 0000000000..de4e33926c --- /dev/null +++ b/extensions/markdown-language-features/server/README.md @@ -0,0 +1,120 @@ +# Markdown Language Server + +> **❗ Import** This is still in development. While the language server is being used by VS Code, it has not yet been tested with other clients. + +The Markdown language server powers VS Code's built-in markdown support, providing tools for writing and browsing Markdown files. It runs as a separate executable and implements the [language server protocol](https://microsoft.github.io/language-server-protocol/overview). + +This server uses the [Markdown Language Service](https://github.com/microsoft/vscode-markdown-languageservice) to implement almost all of the language features. You can use that library if you need a library for working with Markdown instead of a full language server. + + +## Server capabilities + +- [Completions](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) for Markdown links. + +- [Folding](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange) of Markdown regions, block elements, and header sections. + +- [Smart selection](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange) for inline elements, block elements, and header sections. + +- [Document Symbols](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) for quick navigation to headers in a document. + +- [Workspace Symbols](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) for quick navigation to headers in the workspace + +- [Document links](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentLink) for making Markdown links in a document clickable. + +- [Find all references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references) to headers and links across all Markdown files in the workspace. + +- [Rename](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rename) of headers and links across all Markdown files in the workspace. + +- [Go to definition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition) from links to headers or link definitions. + +- (experimental) [Pull diagnostics (validation)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_pullDiagnostics) for links. + + + +## Client requirements + +### Initialization options + +The client can send the following initialization options to the server: + +- `markdownFileExtensions` Array file extensions that should be considered as Markdown. These should not include the leading `.`. For example: `['md', 'mdown', 'markdown']`. + +### Settings + +Clients may send a `workspace/didChangeConfiguration` notification to notify the server of settings changes. +The server supports the following settings: + +- `markdown` + - `suggest` + - `paths` + - `enabled` — Enable/disable path suggestions. + - `experimental` + - `validate` + - `enabled` — Enable/disable all validation. + - `referenceLinks` + - `enabled` — Enable/disable validation of reference links: `[text][ref]` + - `fragmentLinks` + - `enabled` — Enable/disable validation of links to fragments in the current files: `[text](#head)` + - `fileLinks` + - `enabled` — Enable/disable validation of links to file in the workspace. + - `markdownFragmentLinks` — Enable/disable validation of links to headers in other Markdown files. + - `ignoreLinks` — Array of glob patterns for files that should not be validated. + +### Custom requests + +To support all of the features of the language server, the client needs to implement a few custom request types. The definitions of these request types can be found in [`protocol.ts`](./src/protocol.ts) + +#### `markdown/parse` + +Get the tokens for a Markdown file. Clients are expected to use [Markdown-it](https://github.com/markdown-it/markdown-it) for this. + +We require that clients bring their own version of Markdown-it so that they can customize/extend Markdown-it. + +#### `markdown/fs/readFile` + +Read the contents of a file in the workspace. + +#### `markdown/fs/readDirectory` + +Read the contents of a directory in the workspace. + +#### `markdown/fs/stat` + +Check if a given file/directory exists in the workspace. + +#### `markdown/fs/watcher/create` + +Create a file watcher. This is needed for diagnostics support. + +#### `markdown/fs/watcher/delete` + +Delete a previously created file watcher. + +#### `markdown/findMarkdownFilesInWorkspace` + +Get a list of all markdown files in the workspace. + + +## Contribute + +The source code of the Markdown language server can be found in the [VSCode repository](https://github.com/microsoft/vscode) at [extensions/markdown-language-features/server](https://github.com/microsoft/vscode/tree/master/extensions/markdown-language-features/server). + +File issues and pull requests in the [VSCode GitHub Issues](https://github.com/microsoft/vscode/issues). See the document [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) on how to build and run from source. + +Most of the functionality of the server is located in libraries: + +- [vscode-markdown-languageservice](https://github.com/microsoft/vscode-markdown-languageservice) contains the implementation of all features as a reusable library. +- [vscode-languageserver-node](https://github.com/microsoft/vscode-languageserver-node) contains the implementation of language server for NodeJS. + +Help on any of these projects is very welcome. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the [MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt) License. + diff --git a/extensions/markdown-language-features/server/extension-browser.webpack.config.js b/extensions/markdown-language-features/server/extension-browser.webpack.config.js new file mode 100644 index 0000000000..6a9bd10794 --- /dev/null +++ b/extensions/markdown-language-features/server/extension-browser.webpack.config.js @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withBrowserDefaults = require('../../shared.webpack.config').browser; +const path = require('path'); + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/browser/main.ts', + }, + output: { + filename: 'main.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var', + library: 'serverExportVar' + } +}); diff --git a/extensions/markdown-language-features/server/extension.webpack.config.js b/extensions/markdown-language-features/server/extension.webpack.config.js new file mode 100644 index 0000000000..6b4fdccfea --- /dev/null +++ b/extensions/markdown-language-features/server/extension.webpack.config.js @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../../shared.webpack.config'); +const path = require('path'); + +module.exports = withDefaults({ + context: path.join(__dirname), + entry: { + extension: './src/node/main.ts', + }, + output: { + filename: 'main.js', + path: path.join(__dirname, 'dist', 'node'), + } +}); diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json new file mode 100644 index 0000000000..a6ef241f4f --- /dev/null +++ b/extensions/markdown-language-features/server/package.json @@ -0,0 +1,26 @@ +{ + "name": "vscode-markdown-languageserver", + "description": "Markdown language server", + "version": "0.0.0-alpha-1", + "author": "Microsoft Corporation", + "license": "MIT", + "engines": { + "node": "*" + }, + "main": "./out/node/main", + "browser": "./dist/browser/main", + "dependencies": { + "vscode-languageserver": "^8.0.2-next.5`", + "vscode-languageserver-textdocument": "^1.0.5", + "vscode-languageserver-types": "^3.17.1", + "vscode-markdown-languageservice": "^0.0.0-alpha.12", + "vscode-uri": "^3.0.3" + }, + "devDependencies": { + "@types/node": "16.x" + }, + "scripts": { + "compile": "gulp compile-extension:markdown-language-features-server", + "watch": "gulp watch-extension:markdown-language-features-server" + } +} diff --git a/extensions/markdown-language-features/server/src/browser/main.ts b/extensions/markdown-language-features/server/src/browser/main.ts new file mode 100644 index 0000000000..8caa4c51e3 --- /dev/null +++ b/extensions/markdown-language-features/server/src/browser/main.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser'; +import { startServer } from '../server'; + +declare let self: any; + +const messageReader = new BrowserMessageReader(self); +const messageWriter = new BrowserMessageWriter(self); + +const connection = createConnection(messageReader, messageWriter); + +startServer(connection); diff --git a/extensions/markdown-language-features/server/src/config.ts b/extensions/markdown-language-features/server/src/config.ts new file mode 100644 index 0000000000..1da18dfe3a --- /dev/null +++ b/extensions/markdown-language-features/server/src/config.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface LsConfiguration { + /** + * List of file extensions should be considered as markdown. + * + * These should not include the leading `.`. + */ + readonly markdownFileExtensions: readonly string[]; +} + +const defaultConfig: LsConfiguration = { + markdownFileExtensions: ['md'], +}; + +export function getLsConfiguration(overrides: Partial): LsConfiguration { + return { + ...defaultConfig, + ...overrides, + }; +} diff --git a/extensions/markdown-language-features/server/src/configuration.ts b/extensions/markdown-language-features/server/src/configuration.ts new file mode 100644 index 0000000000..f63656a1ed --- /dev/null +++ b/extensions/markdown-language-features/server/src/configuration.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Connection, Emitter } from 'vscode-languageserver'; +import { Disposable } from './util/dispose'; + +export type ValidateEnabled = 'ignore' | 'warning' | 'error'; + +interface Settings { + readonly markdown: { + readonly suggest: { + readonly paths: { + readonly enabled: boolean; + }; + }; + + readonly experimental: { + readonly validate: { + readonly enabled: true; + readonly referenceLinks: { + readonly enabled: ValidateEnabled; + }; + readonly fragmentLinks: { + readonly enabled: ValidateEnabled; + }; + readonly fileLinks: { + readonly enabled: ValidateEnabled; + readonly markdownFragmentLinks: ValidateEnabled; + }; + readonly ignoreLinks: readonly string[]; + }; + }; + }; +} + + +export class ConfigurationManager extends Disposable { + + private readonly _onDidChangeConfiguration = this._register(new Emitter()); + public readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; + + private _settings?: Settings; + + constructor(connection: Connection) { + super(); + + // The settings have changed. Is send on server activation as well. + this._register(connection.onDidChangeConfiguration((change) => { + this._settings = change.settings; + this._onDidChangeConfiguration.fire(this._settings!); + })); + } + + public getSettings(): Settings | undefined { + return this._settings; + } +} diff --git a/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts new file mode 100644 index 0000000000..11950bcc5e --- /dev/null +++ b/extensions/markdown-language-features/server/src/languageFeatures/diagnostics.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Connection, FullDocumentDiagnosticReport, UnchangedDocumentDiagnosticReport } from 'vscode-languageserver'; +import * as md from 'vscode-markdown-languageservice'; +import { disposeAll } from 'vscode-markdown-languageservice/out/util/dispose'; +import { Disposable } from 'vscode-notebook-renderer/events'; +import { URI } from 'vscode-uri'; +import { ConfigurationManager, ValidateEnabled } from '../configuration'; +import { VsCodeClientWorkspace } from '../workspace'; + +const defaultDiagnosticOptions: md.DiagnosticOptions = { + validateFileLinks: md.DiagnosticLevel.ignore, + validateReferences: md.DiagnosticLevel.ignore, + validateFragmentLinks: md.DiagnosticLevel.ignore, + validateMarkdownFileLinkFragments: md.DiagnosticLevel.ignore, + ignoreLinks: [], +}; + +function convertDiagnosticLevel(enabled: ValidateEnabled): md.DiagnosticLevel | undefined { + switch (enabled) { + case 'error': return md.DiagnosticLevel.error; + case 'warning': return md.DiagnosticLevel.warning; + case 'ignore': return md.DiagnosticLevel.ignore; + default: return md.DiagnosticLevel.ignore; + } +} + +function getDiagnosticsOptions(config: ConfigurationManager): md.DiagnosticOptions { + const settings = config.getSettings(); + if (!settings) { + return defaultDiagnosticOptions; + } + + return { + validateFileLinks: convertDiagnosticLevel(settings.markdown.experimental.validate.fileLinks.enabled), + validateReferences: convertDiagnosticLevel(settings.markdown.experimental.validate.referenceLinks.enabled), + validateFragmentLinks: convertDiagnosticLevel(settings.markdown.experimental.validate.fragmentLinks.enabled), + validateMarkdownFileLinkFragments: convertDiagnosticLevel(settings.markdown.experimental.validate.fileLinks.markdownFragmentLinks), + ignoreLinks: settings.markdown.experimental.validate.ignoreLinks, + }; +} + +export function registerValidateSupport( + connection: Connection, + workspace: VsCodeClientWorkspace, + ls: md.IMdLanguageService, + config: ConfigurationManager, +): Disposable { + let diagnosticOptions: md.DiagnosticOptions = defaultDiagnosticOptions; + function updateDiagnosticsSetting(): void { + diagnosticOptions = getDiagnosticsOptions(config); + } + + + const subs: Disposable[] = []; + const manager = ls.createPullDiagnosticsManager(); + subs.push(manager); + + subs.push(manager.onLinkedToFileChanged(() => { + // TODO: We only need to refresh certain files + connection.languages.diagnostics.refresh(); + })); + + connection.languages.diagnostics.on(async (params, token): Promise => { + if (!config.getSettings()?.markdown.experimental.validate.enabled) { + return { kind: 'full', items: [] }; + } + + const document = await workspace.openMarkdownDocument(URI.parse(params.textDocument.uri)); + if (!document) { + return { kind: 'full', items: [] }; + } + + const diagnostics = await manager.computeDiagnostics(document, diagnosticOptions, token); + return { + kind: 'full', + items: diagnostics, + }; + }); + + updateDiagnosticsSetting(); + subs.push(config.onDidChangeConfiguration(() => { + updateDiagnosticsSetting(); + connection.languages.diagnostics.refresh(); + })); + + return { + dispose: () => { + disposeAll(subs); + } + }; +} diff --git a/extensions/markdown-language-features/server/src/logging.ts b/extensions/markdown-language-features/server/src/logging.ts new file mode 100644 index 0000000000..3d90d90b40 --- /dev/null +++ b/extensions/markdown-language-features/server/src/logging.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILogger, LogLevel } from 'vscode-markdown-languageservice'; + +export class LogFunctionLogger implements ILogger { + + private static now(): string { + const now = new Date(); + return String(now.getUTCHours()).padStart(2, '0') + + ':' + String(now.getMinutes()).padStart(2, '0') + + ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0'); + } + + private static data2String(data: any): string { + if (data instanceof Error) { + if (typeof data.stack === 'string') { + return data.stack; + } + return data.message; + } + if (typeof data === 'string') { + return data; + } + return JSON.stringify(data, undefined, 2); + } + + constructor( + private readonly _logFn: typeof console.log + ) { } + + + public log(level: LogLevel, title: string, message: string, data?: any): void { + this.appendLine(`[${level} ${LogFunctionLogger.now()}] ${title}: ${message}`); + if (data) { + this.appendLine(LogFunctionLogger.data2String(data)); + } + } + + private appendLine(value: string): void { + this._logFn(value); + } +} + +export const consoleLogger = new LogFunctionLogger(console.log); diff --git a/extensions/markdown-language-features/server/src/node/main.ts b/extensions/markdown-language-features/server/src/node/main.ts new file mode 100644 index 0000000000..64c0a006cd --- /dev/null +++ b/extensions/markdown-language-features/server/src/node/main.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Connection, createConnection } from 'vscode-languageserver/node'; +import { startServer } from '../server'; + +// Create a connection for the server. +const connection: Connection = createConnection(); + +console.log = connection.console.log.bind(connection.console); +console.error = connection.console.error.bind(connection.console); + +process.on('unhandledRejection', (e: any) => { + connection.console.error(`Unhandled exception ${e}`); +}); + +startServer(connection); diff --git a/extensions/markdown-language-features/server/src/protocol.ts b/extensions/markdown-language-features/server/src/protocol.ts new file mode 100644 index 0000000000..77084d9266 --- /dev/null +++ b/extensions/markdown-language-features/server/src/protocol.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RequestType } from 'vscode-languageserver'; +import type * as lsp from 'vscode-languageserver-types'; +import type * as md from 'vscode-markdown-languageservice'; + +//#region From server +export const parse = new RequestType<{ uri: string }, md.Token[], any>('markdown/parse'); + +export const fs_readFile = new RequestType<{ uri: string }, number[], any>('markdown/fs/readFile'); +export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory'); +export const fs_stat = new RequestType<{ uri: string }, { isDirectory: boolean } | undefined, any>('markdown/fs/stat'); + +export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions }, void, any>('markdown/fs/watcher/create'); +export const fs_watcher_delete = new RequestType<{ id: number }, void, any>('markdown/fs/watcher/delete'); + +export const findMarkdownFilesInWorkspace = new RequestType<{}, string[], any>('markdown/findMarkdownFilesInWorkspace'); +//#endregion + +//#region To server +export const getReferencesToFileInWorkspace = new RequestType<{ uri: string }, lsp.Location[], any>('markdown/getReferencesToFileInWorkspace'); + +export const fs_watcher_onChange = new RequestType<{ id: number; uri: string; kind: 'create' | 'change' | 'delete' }, void, any>('markdown/fs/watcher/onChange'); +//#endregion diff --git a/extensions/markdown-language-features/server/src/server.ts b/extensions/markdown-language-features/server/src/server.ts new file mode 100644 index 0000000000..6ed6cc692c --- /dev/null +++ b/extensions/markdown-language-features/server/src/server.ts @@ -0,0 +1,252 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Connection, InitializeParams, InitializeResult, NotebookDocuments, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-languageserver-textdocument'; +import * as lsp from 'vscode-languageserver-types'; +import * as md from 'vscode-markdown-languageservice'; +import { IDisposable } from 'vscode-markdown-languageservice/out/util/dispose'; +import { URI } from 'vscode-uri'; +import { getLsConfiguration } from './config'; +import { ConfigurationManager } from './configuration'; +import { registerValidateSupport } from './languageFeatures/diagnostics'; +import { LogFunctionLogger } from './logging'; +import * as protocol from './protocol'; +import { VsCodeClientWorkspace } from './workspace'; + +export async function startServer(connection: Connection) { + const documents = new TextDocuments(TextDocument); + const notebooks = new NotebookDocuments(documents); + + const configurationManager = new ConfigurationManager(connection); + + let provider: md.IMdLanguageService | undefined; + let workspace: VsCodeClientWorkspace | undefined; + + connection.onInitialize((params: InitializeParams): InitializeResult => { + const parser = new class implements md.IMdParser { + slugifier = md.githubSlugifier; + + async tokenize(document: md.ITextDocument): Promise { + return await connection.sendRequest(protocol.parse, { uri: document.uri.toString() }); + } + }; + + const config = getLsConfiguration({ + markdownFileExtensions: params.initializationOptions.markdownFileExtensions, + }); + + const logger = new LogFunctionLogger(connection.console.log.bind(connection.console)); + workspace = new VsCodeClientWorkspace(connection, config, documents, notebooks, logger); + provider = md.createLanguageService({ + workspace, + parser, + logger, + markdownFileExtensions: config.markdownFileExtensions, + }); + + registerCompletionsSupport(connection, documents, provider, configurationManager); + registerValidateSupport(connection, workspace, provider, configurationManager); + + workspace.workspaceFolders = (params.workspaceFolders ?? []).map(x => URI.parse(x.uri)); + return { + capabilities: { + diagnosticProvider: { + documentSelector: null, + identifier: 'markdown', + interFileDependencies: true, + workspaceDiagnostics: false, + }, + completionProvider: { triggerCharacters: ['.', '/', '#'] }, + definitionProvider: true, + documentLinkProvider: { resolveProvider: true }, + documentSymbolProvider: true, + foldingRangeProvider: true, + renameProvider: { prepareProvider: true, }, + selectionRangeProvider: true, + workspaceSymbolProvider: true, + workspace: { + workspaceFolders: { + supported: true, + changeNotifications: true, + }, + } + } + }; + }); + + + connection.onDocumentLinks(async (params, token): Promise => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.getDocumentLinks(document, token); + } + } catch (e) { + console.error(e.stack); + } + return []; + }); + + connection.onDocumentLinkResolve(async (link, token): Promise => { + try { + return await provider!.resolveDocumentLink(link, token); + } catch (e) { + console.error(e.stack); + } + return undefined; + }); + + connection.onDocumentSymbol(async (params, token): Promise => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.getDocumentSymbols(document, token); + } + } catch (e) { + console.error(e.stack); + } + return []; + }); + + connection.onFoldingRanges(async (params, token): Promise => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.getFoldingRanges(document, token); + } + } catch (e) { + console.error(e.stack); + } + return []; + }); + + connection.onSelectionRanges(async (params, token): Promise => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.getSelectionRanges(document, params.positions, token); + } + } catch (e) { + console.error(e.stack); + } + return []; + }); + + connection.onWorkspaceSymbol(async (params, token): Promise => { + try { + return await provider!.getWorkspaceSymbols(params.query, token); + } catch (e) { + console.error(e.stack); + } + return []; + }); + + connection.onReferences(async (params, token): Promise => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.getReferences(document, params.position, params.context, token); + } + } catch (e) { + console.error(e.stack); + } + return []; + }); + + connection.onDefinition(async (params, token): Promise => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.getDefinition(document, params.position, token); + } + } catch (e) { + console.error(e.stack); + } + return undefined; + }); + + connection.onPrepareRename(async (params, token) => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + return await provider!.prepareRename(document, params.position, token); + } + } catch (e) { + console.error(e.stack); + } + return undefined; + }); + + connection.onRenameRequest(async (params, token) => { + try { + const document = documents.get(params.textDocument.uri); + if (document) { + const edit = await provider!.getRenameEdit(document, params.position, params.newName, token); + console.log(JSON.stringify(edit)); + return edit; + } + } catch (e) { + console.error(e.stack); + } + return undefined; + }); + + connection.onRequest(protocol.getReferencesToFileInWorkspace, (async (params: { uri: string }, token: CancellationToken) => { + try { + return await provider!.getFileReferences(URI.parse(params.uri), token); + } catch (e) { + console.error(e.stack); + } + return undefined; + })); + + documents.listen(connection); + notebooks.listen(connection); + connection.listen(); +} + + +function registerCompletionsSupport( + connection: Connection, + documents: TextDocuments, + ls: md.IMdLanguageService, + config: ConfigurationManager, +): IDisposable { + // let registration: Promise | undefined; + function update() { + // TODO: client still makes the request in this case. Figure our how to properly unregister. + return; + // const settings = config.getSettings(); + // if (settings?.markdown.suggest.paths.enabled) { + // if (!registration) { + // registration = connection.client.register(CompletionRequest.type); + // } + // } else { + // registration?.then(x => x.dispose()); + // registration = undefined; + // } + } + + connection.onCompletion(async (params, token): Promise => { + try { + const settings = config.getSettings(); + if (!settings?.markdown.suggest.paths.enabled) { + return []; + } + + const document = documents.get(params.textDocument.uri); + if (document) { + return await ls.getCompletionItems(document, params.position, params.context!, token); + } + } catch (e) { + console.error(e.stack); + } + return []; + }); + + update(); + return config.onDidChangeConfiguration(() => update()); +} diff --git a/extensions/markdown-language-features/server/src/util/arrays.ts b/extensions/markdown-language-features/server/src/util/arrays.ts new file mode 100644 index 0000000000..c68857a1c8 --- /dev/null +++ b/extensions/markdown-language-features/server/src/util/arrays.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * @returns New array with all falsy values removed. The original array IS NOT modified. + */ +export function coalesce(array: ReadonlyArray): T[] { + return array.filter(e => !!e); +} diff --git a/extensions/markdown-language-features/server/src/util/dispose.ts b/extensions/markdown-language-features/server/src/util/dispose.ts new file mode 100644 index 0000000000..5b2ece486d --- /dev/null +++ b/extensions/markdown-language-features/server/src/util/dispose.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export class MultiDisposeError extends Error { + constructor( + public readonly errors: any[] + ) { + super(`Encountered errors while disposing of store. Errors: [${errors.join(', ')}]`); + } +} + +export function disposeAll(disposables: Iterable) { + const errors: any[] = []; + + for (const disposable of disposables) { + try { + disposable.dispose(); + } catch (e) { + errors.push(e); + } + } + + if (errors.length === 1) { + throw errors[0]; + } else if (errors.length > 1) { + throw new MultiDisposeError(errors); + } +} + +export interface IDisposable { + dispose(): void; +} + +export abstract class Disposable { + private _isDisposed = false; + + protected _disposables: IDisposable[] = []; + + public dispose(): any { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + disposeAll(this._disposables); + } + + protected _register(value: T): T { + if (this._isDisposed) { + value.dispose(); + } else { + this._disposables.push(value); + } + return value; + } + + protected get isDisposed() { + return this._isDisposed; + } +} + +export class DisposableStore extends Disposable { + private readonly items = new Set(); + + public override dispose() { + super.dispose(); + disposeAll(this.items); + this.items.clear(); + } + + public add(item: T): T { + if (this.isDisposed) { + console.warn('Adding to disposed store. Item will be leaked'); + } + + this.items.add(item); + return item; + } +} diff --git a/extensions/markdown-language-features/server/src/util/file.ts b/extensions/markdown-language-features/server/src/util/file.ts new file mode 100644 index 0000000000..aa42c39470 --- /dev/null +++ b/extensions/markdown-language-features/server/src/util/file.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TextDocument } from 'vscode-languageserver-textdocument'; +import { URI, Utils } from 'vscode-uri'; +import { LsConfiguration } from '../config'; + +export function looksLikeMarkdownPath(config: LsConfiguration, resolvedHrefPath: URI) { + return config.markdownFileExtensions.includes(Utils.extname(URI.from(resolvedHrefPath)).toLowerCase().replace('.', '')); +} + +export function isMarkdownFile(document: TextDocument) { + return document.languageId === 'markdown'; +} diff --git a/extensions/markdown-language-features/server/src/util/limiter.ts b/extensions/markdown-language-features/server/src/util/limiter.ts new file mode 100644 index 0000000000..da9e4d8755 --- /dev/null +++ b/extensions/markdown-language-features/server/src/util/limiter.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface ILimitedTaskFactory { + factory: ITask>; + c: (value: T | Promise) => void; + e: (error?: unknown) => void; +} + +interface ITask { + (): T; +} + +/** + * A helper to queue N promises and run them all with a max degree of parallelism. The helper + * ensures that at any time no more than M promises are running at the same time. + * + * Taken from 'src/vs/base/common/async.ts' + */ +export class Limiter { + + private _size = 0; + private runningPromises: number; + private readonly maxDegreeOfParalellism: number; + private readonly outstandingPromises: ILimitedTaskFactory[]; + + constructor(maxDegreeOfParalellism: number) { + this.maxDegreeOfParalellism = maxDegreeOfParalellism; + this.outstandingPromises = []; + this.runningPromises = 0; + } + + get size(): number { + return this._size; + } + + queue(factory: ITask>): Promise { + this._size++; + + return new Promise((c, e) => { + this.outstandingPromises.push({ factory, c, e }); + this.consume(); + }); + } + + private consume(): void { + while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { + const iLimitedTask = this.outstandingPromises.shift()!; + this.runningPromises++; + + const promise = iLimitedTask.factory(); + promise.then(iLimitedTask.c, iLimitedTask.e); + promise.then(() => this.consumed(), () => this.consumed()); + } + } + + private consumed(): void { + this._size--; + this.runningPromises--; + + if (this.outstandingPromises.length > 0) { + this.consume(); + } + } +} diff --git a/extensions/markdown-language-features/server/src/util/resourceMap.ts b/extensions/markdown-language-features/server/src/util/resourceMap.ts new file mode 100644 index 0000000000..7be102bbf3 --- /dev/null +++ b/extensions/markdown-language-features/server/src/util/resourceMap.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vscode-uri'; + + +type ResourceToKey = (uri: URI) => string; + +const defaultResourceToKey = (resource: URI): string => resource.toString(); + +export class ResourceMap { + + private readonly map = new Map(); + + private readonly toKey: ResourceToKey; + + constructor(toKey: ResourceToKey = defaultResourceToKey) { + this.toKey = toKey; + } + + public set(uri: URI, value: T): this { + this.map.set(this.toKey(uri), { uri, value }); + return this; + } + + public get(resource: URI): T | undefined { + return this.map.get(this.toKey(resource))?.value; + } + + public has(resource: URI): boolean { + return this.map.has(this.toKey(resource)); + } + + public get size(): number { + return this.map.size; + } + + public clear(): void { + this.map.clear(); + } + + public delete(resource: URI): boolean { + return this.map.delete(this.toKey(resource)); + } + + public *values(): IterableIterator { + for (const entry of this.map.values()) { + yield entry.value; + } + } + + public *keys(): IterableIterator { + for (const entry of this.map.values()) { + yield entry.uri; + } + } + + public *entries(): IterableIterator<[URI, T]> { + for (const entry of this.map.values()) { + yield [entry.uri, entry.value]; + } + } + + public [Symbol.iterator](): IterableIterator<[URI, T]> { + return this.entries(); + } +} diff --git a/extensions/markdown-language-features/server/src/util/schemes.ts b/extensions/markdown-language-features/server/src/util/schemes.ts new file mode 100644 index 0000000000..806953ed5a --- /dev/null +++ b/extensions/markdown-language-features/server/src/util/schemes.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const Schemes = Object.freeze({ + notebookCell: 'vscode-notebook-cell', +}); diff --git a/extensions/markdown-language-features/server/src/workspace.ts b/extensions/markdown-language-features/server/src/workspace.ts new file mode 100644 index 0000000000..83fa402e48 --- /dev/null +++ b/extensions/markdown-language-features/server/src/workspace.ts @@ -0,0 +1,251 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Connection, Emitter, FileChangeType, NotebookDocuments, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-languageserver-textdocument'; +import * as md from 'vscode-markdown-languageservice'; +import { ContainingDocumentContext, FileWatcherOptions, IFileSystemWatcher } from 'vscode-markdown-languageservice/out/workspace'; +import { URI } from 'vscode-uri'; +import { LsConfiguration } from './config'; +import * as protocol from './protocol'; +import { coalesce } from './util/arrays'; +import { isMarkdownFile, looksLikeMarkdownPath } from './util/file'; +import { Limiter } from './util/limiter'; +import { ResourceMap } from './util/resourceMap'; +import { Schemes } from './util/schemes'; + +declare const TextDecoder: any; + +export class VsCodeClientWorkspace implements md.IWorkspaceWithWatching { + + private readonly _onDidCreateMarkdownDocument = new Emitter(); + public readonly onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocument.event; + + private readonly _onDidChangeMarkdownDocument = new Emitter(); + public readonly onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocument.event; + + private readonly _onDidDeleteMarkdownDocument = new Emitter(); + public readonly onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocument.event; + + private readonly _documentCache = new ResourceMap(); + + private readonly _utf8Decoder = new TextDecoder('utf-8'); + + private _watcherPool = 0; + private readonly _watchers = new Map; + readonly onDidCreate: Emitter; + readonly onDidDelete: Emitter; + }>(); + + constructor( + private readonly connection: Connection, + private readonly config: LsConfiguration, + private readonly documents: TextDocuments, + private readonly notebooks: NotebookDocuments, + private readonly logger: md.ILogger, + ) { + documents.onDidOpen(e => { + this._documentCache.delete(URI.parse(e.document.uri)); + if (this.isRelevantMarkdownDocument(e.document)) { + this._onDidCreateMarkdownDocument.fire(e.document); + } + }); + + documents.onDidChangeContent(e => { + if (this.isRelevantMarkdownDocument(e.document)) { + this._onDidChangeMarkdownDocument.fire(e.document); + } + }); + + documents.onDidClose(e => { + this._documentCache.delete(URI.parse(e.document.uri)); + }); + + connection.onDidChangeWatchedFiles(async ({ changes }) => { + for (const change of changes) { + const resource = URI.parse(change.uri); + this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: onDidChangeWatchedFiles', `${change.type}: ${resource}`); + switch (change.type) { + case FileChangeType.Changed: { + this._documentCache.delete(resource); + const document = await this.openMarkdownDocument(resource); + if (document) { + this._onDidChangeMarkdownDocument.fire(document); + } + break; + } + case FileChangeType.Created: { + const document = await this.openMarkdownDocument(resource); + if (document) { + this._onDidCreateMarkdownDocument.fire(document); + } + break; + } + case FileChangeType.Deleted: { + this._documentCache.delete(resource); + this._onDidDeleteMarkdownDocument.fire(resource); + break; + } + } + } + }); + + connection.onRequest(protocol.fs_watcher_onChange, params => { + this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: fs_watcher_onChange', `${params.kind}: ${params.uri}`); + + const watcher = this._watchers.get(params.id); + if (!watcher) { + return; + } + + switch (params.kind) { + case 'create': watcher.onDidCreate.fire(URI.parse(params.uri)); return; + case 'change': watcher.onDidChange.fire(URI.parse(params.uri)); return; + case 'delete': watcher.onDidDelete.fire(URI.parse(params.uri)); return; + } + }); + } + + public listen() { + this.connection.workspace.onDidChangeWorkspaceFolders(async () => { + this.workspaceFolders = (await this.connection.workspace.getWorkspaceFolders() ?? []).map(x => URI.parse(x.uri)); + }); + } + + private _workspaceFolders: readonly URI[] = []; + + get workspaceFolders(): readonly URI[] { + return this._workspaceFolders; + } + + set workspaceFolders(value: readonly URI[]) { + this._workspaceFolders = value; + } + + async getAllMarkdownDocuments(): Promise> { + const maxConcurrent = 20; + + const foundFiles = new ResourceMap(); + const limiter = new Limiter(maxConcurrent); + + // Add files on disk + const resources = await this.connection.sendRequest(protocol.findMarkdownFilesInWorkspace, {}); + const onDiskResults = await Promise.all(resources.map(strResource => { + return limiter.queue(async () => { + const resource = URI.parse(strResource); + const doc = await this.openMarkdownDocument(resource); + if (doc) { + foundFiles.set(resource); + } + return doc; + }); + })); + + // Add opened files (such as untitled files) + const openTextDocumentResults = await Promise.all(this.documents.all() + .filter(doc => !foundFiles.has(URI.parse(doc.uri)) && this.isRelevantMarkdownDocument(doc))); + + return coalesce([...onDiskResults, ...openTextDocumentResults]); + } + + hasMarkdownDocument(resource: URI): boolean { + return !!this.documents.get(resource.toString()); + } + + async openMarkdownDocument(resource: URI): Promise { + const existing = this._documentCache.get(resource); + if (existing) { + return existing; + } + + const matchingDocument = this.documents.get(resource.toString()); + if (matchingDocument) { + this._documentCache.set(resource, matchingDocument); + return matchingDocument; + } + + if (!looksLikeMarkdownPath(this.config, resource)) { + return undefined; + } + + try { + const response = await this.connection.sendRequest(protocol.fs_readFile, { uri: resource.toString() }); + // TODO: LSP doesn't seem to handle Array buffers well + const bytes = new Uint8Array(response); + + // We assume that markdown is in UTF-8 + const text = this._utf8Decoder.decode(bytes); + const doc = TextDocument.create(resource.toString(), 'markdown', 0, text); + this._documentCache.set(resource, doc); + return doc; + } catch (e) { + return undefined; + } + } + + async stat(resource: URI): Promise { + this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: stat', `${resource}`); + if (this._documentCache.has(resource) || this.documents.get(resource.toString())) { + return { isDirectory: false }; + } + return this.connection.sendRequest(protocol.fs_stat, { uri: resource.toString() }); + } + + async readDirectory(resource: URI): Promise<[string, md.FileStat][]> { + this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: readDir', `${resource}`); + return this.connection.sendRequest(protocol.fs_readDirectory, { uri: resource.toString() }); + } + + getContainingDocument(resource: URI): ContainingDocumentContext | undefined { + if (resource.scheme === Schemes.notebookCell) { + const nb = this.notebooks.findNotebookDocumentForCell(resource.toString()); + if (nb) { + return { + uri: URI.parse(nb.uri), + children: nb.cells.map(cell => ({ uri: URI.parse(cell.document) })), + }; + } + } + return undefined; + } + + watchFile(resource: URI, options: FileWatcherOptions): IFileSystemWatcher { + const id = this._watcherPool++; + this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: watchFile', `(${id}) ${resource}`); + + const entry = { + resource, + options, + onDidCreate: new Emitter(), + onDidChange: new Emitter(), + onDidDelete: new Emitter(), + }; + this._watchers.set(id, entry); + + this.connection.sendRequest(protocol.fs_watcher_create, { + id, + uri: resource.toString(), + options, + }); + + return { + onDidCreate: entry.onDidCreate.event, + onDidChange: entry.onDidChange.event, + onDidDelete: entry.onDidDelete.event, + dispose: () => { + this.logger.log(md.LogLevel.Trace, 'VsCodeClientWorkspace: disposeWatcher', `(${id}) ${resource}`); + this.connection.sendRequest(protocol.fs_watcher_delete, { id }); + this._watchers.delete(id); + } + }; + } + + private isRelevantMarkdownDocument(doc: TextDocument) { + return isMarkdownFile(doc) && URI.parse(doc.uri).scheme !== 'vscode-bulkeditpreview'; + } +} diff --git a/extensions/markdown-language-features/server/tsconfig.json b/extensions/markdown-language-features/server/tsconfig.json new file mode 100644 index 0000000000..8b4aedde27 --- /dev/null +++ b/extensions/markdown-language-features/server/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out" + }, + "include": [ + "src/**/*" + ] +} diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock new file mode 100644 index 0000000000..d0d31f5998 --- /dev/null +++ b/extensions/markdown-language-features/server/yarn.lock @@ -0,0 +1,69 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@16.x": + version "16.11.43" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.43.tgz#555e5a743f76b6b897d47f945305b618525ddbe6" + integrity sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +vscode-jsonrpc@8.0.2-next.1: + version "8.0.2-next.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2-next.1.tgz#6bdc39fd194782032e34047eeefce562941259c6" + integrity sha512-sbbvGSWja7NVBLHPGawtgezc8DHYJaP4qfr/AaJiyDapWcSFtHyPtm18+LnYMLTmB7bhOUW/lf5PeeuLpP6bKA== + +vscode-languageserver-protocol@3.17.2-next.6: + version "3.17.2-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2-next.6.tgz#8f1dc0fcb29366b85f623a3f9af726de433b5fcc" + integrity sha512-WtsebNOOkWyNn4oFYoAMPC8Q/ZDoJ/K7Ja53OzTixiitvrl/RpXZETrtzH79R8P5kqCyx6VFBPb6KQILJfkDkA== + dependencies: + vscode-jsonrpc "8.0.2-next.1" + vscode-languageserver-types "3.17.2-next.2" + +vscode-languageserver-textdocument@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c" + integrity sha512-1ah7zyQjKBudnMiHbZmxz5bYNM9KKZYz+5VQLj+yr8l+9w3g+WAhCkUkWbhMEdC5u0ub4Ndiye/fDyS8ghIKQg== + +vscode-languageserver-types@3.17.2-next.2: + version "3.17.2-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2-next.2.tgz#af5d6978eee7682aab87c1419323f5b141ac6596" + integrity sha512-TiAkLABgqkVWdAlC3XlOfdhdjIAdVU4YntPUm9kKGbXr+MGwpVnKz2KZMNBcvG0CFx8Hi8qliL0iq+ndPB720w== + +vscode-languageserver-types@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== + +vscode-languageserver@^8.0.2-next.5`: + version "8.0.2-next.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.2-next.5.tgz#39a2dd4c504fb88042375e7ac706a714bdaab4e5" + integrity sha512-2ZDb7O/4atS9mJKufPPz637z+51kCyZfgnobFW5eSrUdS3c0UB/nMS4Ng1EavYTX84GVaVMKCrmP0f2ceLmR0A== + dependencies: + vscode-languageserver-protocol "3.17.2-next.6" + +vscode-markdown-languageservice@^0.0.0-alpha.12: + version "0.0.0-alpha.12" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.0.0-alpha.12.tgz#5a3c7559969c3cb455d508d48129c8e221589630" + integrity sha512-9dJ/GL6A9UUOcB1TpvgsbcwqsYjnxHx4jxDaqeZZEMWFSUySfp0PAn1ge2S2Qj00zypvsu0eCTGUNd56G1/BNQ== + dependencies: + picomatch "^2.3.1" + vscode-languageserver-textdocument "^1.0.5" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" + vscode-uri "^3.0.3" + +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== + +vscode-uri@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" + integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== diff --git a/extensions/markdown-language-features/src/client.ts b/extensions/markdown-language-features/src/client.ts new file mode 100644 index 0000000000..4d86fc36d9 --- /dev/null +++ b/extensions/markdown-language-features/src/client.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { BaseLanguageClient, LanguageClientOptions, NotebookDocumentSyncRegistrationType } from 'vscode-languageclient'; +import * as nls from 'vscode-nls'; +import { IMdParser } from './markdownEngine'; +import * as proto from './protocol'; +import { looksLikeMarkdownPath, markdownFileExtensions } from './util/file'; +import { IMdWorkspace } from './workspace'; + +const localize = nls.loadMessageBundle(); + +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient; + + +export async function startClient(factory: LanguageClientConstructor, workspace: IMdWorkspace, parser: IMdParser): Promise { + + const mdFileGlob = `**/*.{${markdownFileExtensions.join(',')}}`; + + const clientOptions: LanguageClientOptions = { + documentSelector: [{ language: 'markdown' }], + synchronize: { + configurationSection: ['markdown'], + fileEvents: vscode.workspace.createFileSystemWatcher(mdFileGlob), + }, + initializationOptions: { + markdownFileExtensions, + }, + diagnosticPullOptions: { + onChange: true, + onSave: true, + onTabs: true, + match(_documentSelector, resource) { + return looksLikeMarkdownPath(resource); + }, + }, + + }; + + const client = factory('markdown', localize('markdownServer.name', 'Markdown Language Server'), clientOptions); + + client.registerProposedFeatures(); + + const notebookFeature = client.getFeature(NotebookDocumentSyncRegistrationType.method); + if (notebookFeature !== undefined) { + notebookFeature.register({ + id: String(Date.now()), + registerOptions: { + notebookSelector: [{ + notebook: '*', + cells: [{ language: 'markdown' }] + }] + } + }); + } + + client.onRequest(proto.parse, async (e) => { + const uri = vscode.Uri.parse(e.uri); + const doc = await workspace.getOrLoadMarkdownDocument(uri); + if (doc) { + return parser.tokenize(doc); + } else { + return []; + } + }); + + client.onRequest(proto.fs_readFile, async (e): Promise => { + const uri = vscode.Uri.parse(e.uri); + return Array.from(await vscode.workspace.fs.readFile(uri)); + }); + + client.onRequest(proto.fs_stat, async (e): Promise<{ isDirectory: boolean } | undefined> => { + const uri = vscode.Uri.parse(e.uri); + try { + const stat = await vscode.workspace.fs.stat(uri); + return { isDirectory: stat.type === vscode.FileType.Directory }; + } catch { + return undefined; + } + }); + + client.onRequest(proto.fs_readDirectory, async (e): Promise<[string, { isDirectory: boolean }][]> => { + const uri = vscode.Uri.parse(e.uri); + const result = await vscode.workspace.fs.readDirectory(uri); + return result.map(([name, type]) => [name, { isDirectory: type === vscode.FileType.Directory }]); + }); + + client.onRequest(proto.findMarkdownFilesInWorkspace, async (): Promise => { + return (await vscode.workspace.findFiles(mdFileGlob, '**/node_modules/**')).map(x => x.toString()); + }); + + const watchers = new Map(); + + client.onRequest(proto.fs_watcher_create, async (params): Promise => { + const id = params.id; + const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.parse(params.uri), '*'), params.options.ignoreCreate, params.options.ignoreChange, params.options.ignoreDelete); + watchers.set(id, watcher); + watcher.onDidCreate(() => { client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind: 'create' }); }); + watcher.onDidChange(() => { client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind: 'change' }); }); + watcher.onDidDelete(() => { client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind: 'delete' }); }); + }); + + client.onRequest(proto.fs_watcher_delete, async (params): Promise => { + watchers.get(params.id)?.dispose(); + watchers.delete(params.id); + }); + + await client.start(); + + return client; +} diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index 9445fdbf43..dff7b05fb0 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -5,8 +5,9 @@ import * as vscode from 'vscode'; import { Command } from '../commandManager'; -import { MarkdownEngine } from '../markdownEngine'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { openDocumentLink } from '../util/openDocumentLink'; +import { Schemes } from '../util/schemes'; type UriComponents = { readonly scheme?: string; @@ -48,18 +49,18 @@ export class OpenDocumentLinkCommand implements Command { } public constructor( - private readonly engine: MarkdownEngine + private readonly tocProvider: MdTableOfContentsProvider, ) { } public async execute(args: OpenDocumentLinkArgs) { const fromResource = vscode.Uri.parse('').with(args.fromResource); const targetResource = reviveUri(args.parts).with({ fragment: args.fragment }); - return openDocumentLink(this.engine, targetResource, fromResource); + return openDocumentLink(this.tocProvider, targetResource, fromResource); } } function reviveUri(parts: any) { - if (parts.scheme === 'file') { + if (parts.scheme === Schemes.file) { return vscode.Uri.file(parts.path); } return vscode.Uri.parse('').with(parts); diff --git a/extensions/markdown-language-features/src/commands/refreshPreview.ts b/extensions/markdown-language-features/src/commands/refreshPreview.ts index 331e5375e9..1d983d2f55 100644 --- a/extensions/markdown-language-features/src/commands/refreshPreview.ts +++ b/extensions/markdown-language-features/src/commands/refreshPreview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Command } from '../commandManager'; -import { MarkdownEngine } from '../markdownEngine'; +import { MarkdownItEngine } from '../markdownEngine'; import { MarkdownPreviewManager } from '../preview/previewManager'; export class RefreshPreviewCommand implements Command { @@ -12,7 +12,7 @@ export class RefreshPreviewCommand implements Command { public constructor( private readonly webviewManager: MarkdownPreviewManager, - private readonly engine: MarkdownEngine + private readonly engine: MarkdownItEngine ) { } public execute() { diff --git a/extensions/markdown-language-features/src/commands/reloadPlugins.ts b/extensions/markdown-language-features/src/commands/reloadPlugins.ts index 9d757277c2..826697d545 100644 --- a/extensions/markdown-language-features/src/commands/reloadPlugins.ts +++ b/extensions/markdown-language-features/src/commands/reloadPlugins.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Command } from '../commandManager'; -import { MarkdownEngine } from '../markdownEngine'; +import { MarkdownItEngine } from '../markdownEngine'; import { MarkdownPreviewManager } from '../preview/previewManager'; export class ReloadPlugins implements Command { @@ -12,7 +12,7 @@ export class ReloadPlugins implements Command { public constructor( private readonly webviewManager: MarkdownPreviewManager, - private readonly engine: MarkdownEngine, + private readonly engine: MarkdownItEngine, ) { } public execute(): void { diff --git a/extensions/markdown-language-features/src/commands/renderDocument.ts b/extensions/markdown-language-features/src/commands/renderDocument.ts index 9400f130ce..7da7e52d72 100644 --- a/extensions/markdown-language-features/src/commands/renderDocument.ts +++ b/extensions/markdown-language-features/src/commands/renderDocument.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { Command } from '../commandManager'; -import { MarkdownEngine } from '../markdownEngine'; -import { SkinnyTextDocument } from '../workspaceContents'; +import { MarkdownItEngine } from '../markdownEngine'; +import { ITextDocument } from '../types/textDocument'; export class RenderDocument implements Command { public readonly id = 'markdown.api.render'; public constructor( - private readonly engine: MarkdownEngine + private readonly engine: MarkdownItEngine ) { } - public async execute(document: SkinnyTextDocument | string): Promise { + public async execute(document: ITextDocument | string): Promise { return (await (this.engine.render(document))).html; } } diff --git a/extensions/markdown-language-features/src/extension.browser.ts b/extensions/markdown-language-features/src/extension.browser.ts new file mode 100644 index 0000000000..65d1ad6bbb --- /dev/null +++ b/extensions/markdown-language-features/src/extension.browser.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { BaseLanguageClient, LanguageClient, LanguageClientOptions } from 'vscode-languageclient/browser'; +import { startClient } from './client'; +import { activateShared } from './extension.shared'; +import { VsCodeOutputLogger } from './logging'; +import { IMdParser, MarkdownItEngine } from './markdownEngine'; +import { getMarkdownExtensionContributions } from './markdownExtensions'; +import { githubSlugifier } from './slugify'; +import { IMdWorkspace, VsCodeMdWorkspace } from './workspace'; + +export async function activate(context: vscode.ExtensionContext) { + const contributions = getMarkdownExtensionContributions(context); + context.subscriptions.push(contributions); + + const logger = new VsCodeOutputLogger(); + context.subscriptions.push(logger); + + const engine = new MarkdownItEngine(contributions, githubSlugifier, logger); + + const workspace = new VsCodeMdWorkspace(); + context.subscriptions.push(workspace); + + const client = await startServer(context, workspace, engine); + context.subscriptions.push({ + dispose: () => client.stop() + }); + activateShared(context, client, workspace, engine, logger, contributions); +} + +function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace, parser: IMdParser): Promise { + const serverMain = vscode.Uri.joinPath(context.extensionUri, 'server/dist/browser/main.js'); + const worker = new Worker(serverMain.toString()); + + return startClient((id: string, name: string, clientOptions: LanguageClientOptions) => { + return new LanguageClient(id, name, clientOptions, worker); + }, workspace, parser); +} diff --git a/extensions/markdown-language-features/src/extension.shared.ts b/extensions/markdown-language-features/src/extension.shared.ts new file mode 100644 index 0000000000..6c5ee45e6d --- /dev/null +++ b/extensions/markdown-language-features/src/extension.shared.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { BaseLanguageClient } from 'vscode-languageclient'; +import { CommandManager } from './commandManager'; +import * as commands from './commands/index'; +import { registerPasteSupport } from './languageFeatures/copyPaste'; +import { registerDiagnosticSupport } from './languageFeatures/diagnostics'; +import { registerDropIntoEditorSupport } from './languageFeatures/dropIntoEditor'; +import { registerFindFileReferenceSupport } from './languageFeatures/fileReferences'; +import { ILogger } from './logging'; +import { MarkdownItEngine, MdParsingProvider } from './markdownEngine'; +import { MarkdownContributionProvider } from './markdownExtensions'; +import { MdDocumentRenderer } from './preview/documentRenderer'; +import { MarkdownPreviewManager } from './preview/previewManager'; +import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './preview/security'; +import { MdTableOfContentsProvider } from './tableOfContents'; +import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; +import { IMdWorkspace } from './workspace'; + +export function activateShared( + context: vscode.ExtensionContext, + client: BaseLanguageClient, + workspace: IMdWorkspace, + engine: MarkdownItEngine, + logger: ILogger, + contributions: MarkdownContributionProvider, +) { + const telemetryReporter = loadDefaultTelemetryReporter(); + context.subscriptions.push(telemetryReporter); + + const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState); + const commandManager = new CommandManager(); + + const parser = new MdParsingProvider(engine, workspace); + const tocProvider = new MdTableOfContentsProvider(parser, workspace, logger); + context.subscriptions.push(parser, tocProvider); + + const contentProvider = new MdDocumentRenderer(engine, context, cspArbiter, contributions, logger); + const previewManager = new MarkdownPreviewManager(contentProvider, workspace, logger, contributions, tocProvider); + context.subscriptions.push(previewManager); + + context.subscriptions.push(registerMarkdownLanguageFeatures(client, commandManager)); + context.subscriptions.push(registerMarkdownCommands(commandManager, previewManager, telemetryReporter, cspArbiter, engine, tocProvider)); + + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { + previewManager.updateConfiguration(); + })); +} + +function registerMarkdownLanguageFeatures( + client: BaseLanguageClient, + commandManager: CommandManager, +): vscode.Disposable { + const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' }; + return vscode.Disposable.from( + // Language features + registerDiagnosticSupport(selector, commandManager), + registerDropIntoEditorSupport(selector), + registerFindFileReferenceSupport(commandManager, client), + registerPasteSupport(selector), + ); +} + +function registerMarkdownCommands( + commandManager: CommandManager, + previewManager: MarkdownPreviewManager, + telemetryReporter: TelemetryReporter, + cspArbiter: ContentSecurityPolicyArbiter, + engine: MarkdownItEngine, + tocProvider: MdTableOfContentsProvider, +): vscode.Disposable { + const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager); + + commandManager.register(new commands.ShowPreviewCommand(previewManager, telemetryReporter)); + commandManager.register(new commands.ShowPreviewToSideCommand(previewManager, telemetryReporter)); + commandManager.register(new commands.ShowLockedPreviewToSideCommand(previewManager, telemetryReporter)); + commandManager.register(new commands.ShowSourceCommand(previewManager)); + commandManager.register(new commands.RefreshPreviewCommand(previewManager, engine)); + commandManager.register(new commands.MoveCursorToPositionCommand()); + commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager)); + commandManager.register(new commands.OpenDocumentLinkCommand(tocProvider)); + commandManager.register(new commands.ToggleLockCommand(previewManager)); + commandManager.register(new commands.RenderDocument(engine)); + commandManager.register(new commands.ReloadPlugins(previewManager, engine)); + return commandManager; +} diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index d4ff9e1ac0..797dfa620e 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -4,103 +4,50 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { CommandManager } from './commandManager'; -import * as commands from './commands/index'; -import { register as registerDiagnostics } from './languageFeatures/diagnostics'; -import { MdDefinitionProvider } from './languageFeatures/definitionProvider'; -import { MdLinkProvider } from './languageFeatures/documentLinkProvider'; -import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbolProvider'; -import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor'; -import { registerFindFileReferences } from './languageFeatures/fileReferences'; -import { MdFoldingProvider } from './languageFeatures/foldingProvider'; -import { MdPathCompletionProvider } from './languageFeatures/pathCompletions'; -import { MdReferencesProvider } from './languageFeatures/references'; -import { MdRenameProvider } from './languageFeatures/rename'; -import { MdSmartSelect } from './languageFeatures/smartSelect'; -import { MdWorkspaceSymbolProvider } from './languageFeatures/workspaceSymbolProvider'; -import { Logger } from './logger'; -import { MarkdownEngine } from './markdownEngine'; +import { BaseLanguageClient, LanguageClient, ServerOptions, TransportKind } from 'vscode-languageclient/node'; +import { startClient } from './client'; +import { activateShared } from './extension.shared'; +import { VsCodeOutputLogger } from './logging'; +import { IMdParser, MarkdownItEngine } from './markdownEngine'; import { getMarkdownExtensionContributions } from './markdownExtensions'; -import { MarkdownContentProvider } from './preview/previewContentProvider'; -import { MarkdownPreviewManager } from './preview/previewManager'; -import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './preview/security'; import { githubSlugifier } from './slugify'; -import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; -import { VsCodeMdWorkspaceContents } from './workspaceContents'; - - -export function activate(context: vscode.ExtensionContext) { - const telemetryReporter = loadDefaultTelemetryReporter(); - context.subscriptions.push(telemetryReporter); +import { IMdWorkspace, VsCodeMdWorkspace } from './workspace'; +export async function activate(context: vscode.ExtensionContext) { const contributions = getMarkdownExtensionContributions(context); context.subscriptions.push(contributions); - const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState); - const engine = new MarkdownEngine(contributions, githubSlugifier); - const logger = new Logger(); - const commandManager = new CommandManager(); + const logger = new VsCodeOutputLogger(); + context.subscriptions.push(logger); - const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, contributions, logger); - const symbolProvider = new MdDocumentSymbolProvider(engine); - const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions, engine); - context.subscriptions.push(previewManager); + const engine = new MarkdownItEngine(contributions, githubSlugifier, logger); - context.subscriptions.push(registerMarkdownLanguageFeatures(commandManager, symbolProvider, engine)); - context.subscriptions.push(registerMarkdownCommands(commandManager, previewManager, telemetryReporter, cspArbiter, engine)); + const workspace = new VsCodeMdWorkspace(); + context.subscriptions.push(workspace); - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { - logger.updateConfiguration(); - previewManager.updateConfiguration(); - })); + const client = await startServer(context, workspace, engine); + context.subscriptions.push({ + dispose: () => client.stop() + }); + activateShared(context, client, workspace, engine, logger, contributions); } -function registerMarkdownLanguageFeatures( - commandManager: CommandManager, - symbolProvider: MdDocumentSymbolProvider, - engine: MarkdownEngine -): vscode.Disposable { - const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' }; +function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace, parser: IMdParser): Promise { + const clientMain = vscode.extensions.getExtension('vscode.markdown-language-features')?.packageJSON?.main || ''; - const linkProvider = new MdLinkProvider(engine); - const workspaceContents = new VsCodeMdWorkspaceContents(); + const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/main`; + const serverModule = context.asAbsolutePath(serverMain); - const referencesProvider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - return vscode.Disposable.from( - vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), - vscode.languages.registerDocumentLinkProvider(selector, linkProvider), - vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)), - vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)), - vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)), - vscode.languages.registerReferenceProvider(selector, referencesProvider), - vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)), - vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)), - MdPathCompletionProvider.register(selector, engine, linkProvider), - registerDiagnostics(engine, workspaceContents, linkProvider), - registerDropIntoEditor(selector), - registerFindFileReferences(commandManager, referencesProvider), - ); -} - -function registerMarkdownCommands( - commandManager: CommandManager, - previewManager: MarkdownPreviewManager, - telemetryReporter: TelemetryReporter, - cspArbiter: ContentSecurityPolicyArbiter, - engine: MarkdownEngine -): vscode.Disposable { - const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager); - - commandManager.register(new commands.ShowPreviewCommand(previewManager, telemetryReporter)); - commandManager.register(new commands.ShowPreviewToSideCommand(previewManager, telemetryReporter)); - commandManager.register(new commands.ShowLockedPreviewToSideCommand(previewManager, telemetryReporter)); - commandManager.register(new commands.ShowSourceCommand(previewManager)); - commandManager.register(new commands.RefreshPreviewCommand(previewManager, engine)); - commandManager.register(new commands.MoveCursorToPositionCommand()); - commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager)); - commandManager.register(new commands.OpenDocumentLinkCommand(engine)); - commandManager.register(new commands.ToggleLockCommand(previewManager)); - commandManager.register(new commands.RenderDocument(engine)); - commandManager.register(new commands.ReloadPlugins(previewManager, engine)); - return commandManager; + // The debug options for the server + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (7000 + Math.round(Math.random() * 999))] }; + + // If the extension is launch in debug mode the debug server options are use + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + return startClient((id, name, clientOptions) => { + return new LanguageClient(id, name, serverOptions, clientOptions); + }, workspace, parser); } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts new file mode 100644 index 0000000000..d392970339 --- /dev/null +++ b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { tryGetUriListSnippet } from './dropIntoEditor'; + +export function registerPasteSupport(selector: vscode.DocumentSelector) { + return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider { + + async provideDocumentPasteEdits( + document: vscode.TextDocument, + _ranges: readonly vscode.Range[], + dataTransfer: vscode.DataTransfer, + token: vscode.CancellationToken, + ): Promise { + const enabled = vscode.workspace.getConfiguration('markdown', document).get('experimental.editor.pasteLinks.enabled', true); + if (!enabled) { + return; + } + + const snippet = await tryGetUriListSnippet(document, dataTransfer, token); + return snippet ? new vscode.DocumentPasteEdit(snippet) : undefined; + } + }, { + pasteMimeTypes: ['text/uri-list'] + }); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts deleted file mode 100644 index 599e604a34..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import { Disposable } from '../util/dispose'; -import { SkinnyTextDocument } from '../workspaceContents'; -import { MdReferencesProvider } from './references'; - -export class MdDefinitionProvider extends Disposable implements vscode.DefinitionProvider { - - constructor(private readonly referencesProvider: MdReferencesProvider) { - super(); - } - - async provideDefinition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const allRefs = await this.referencesProvider.getAllReferencesAtPosition(document, position, token); - - return allRefs.find(ref => ref.kind === 'link' && ref.isDefinition)?.location; - } -} diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index b397beaa54..1e76b78f62 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -5,294 +5,79 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; -import { noopToken } from '../test/util'; -import { Delayer } from '../util/async'; -import { Disposable } from '../util/dispose'; -import { isMarkdownFile } from '../util/file'; -import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider } from './documentLinkProvider'; -import { tryFindMdDocumentForLink } from './references'; +import { CommandManager } from '../commandManager'; const localize = nls.loadMessageBundle(); -export interface DiagnosticConfiguration { - /** - * Fired when the configuration changes. - */ - readonly onDidChange: vscode.Event; - - getOptions(resource: vscode.Uri): DiagnosticOptions; +// Copied from markdown language service +export enum DiagnosticCode { + link_noSuchReferences = 'link.no-such-reference', + link_noSuchHeaderInOwnFile = 'link.no-such-header-in-own-file', + link_noSuchFile = 'link.no-such-file', + link_noSuchHeaderInFile = 'link.no-such-header-in-file', } -export enum DiagnosticLevel { - ignore = 'ignore', - warning = 'warning', - error = 'error', -} -export interface DiagnosticOptions { - readonly enabled: boolean; - readonly validateReferences: DiagnosticLevel; - readonly validateOwnHeaders: DiagnosticLevel; - readonly validateFilePaths: DiagnosticLevel; -} +class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider { -function toSeverity(level: DiagnosticLevel): vscode.DiagnosticSeverity | undefined { - switch (level) { - case DiagnosticLevel.error: return vscode.DiagnosticSeverity.Error; - case DiagnosticLevel.warning: return vscode.DiagnosticSeverity.Warning; - case DiagnosticLevel.ignore: return undefined; - } -} + private static readonly _addToIgnoreLinksCommandId = '_markdown.addToIgnoreLinks'; -class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConfiguration { + private static readonly metadata: vscode.CodeActionProviderMetadata = { + providedCodeActionKinds: [ + vscode.CodeActionKind.QuickFix + ], + }; - private readonly _onDidChange = this._register(new vscode.EventEmitter()); - public readonly onDidChange = this._onDidChange.event; - - constructor() { - super(); - - this._register(vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('markdown.experimental.validate.enabled')) { - this._onDidChange.fire(); + public static register(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable { + const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToIgnoreLinksQuickFixProvider(), AddToIgnoreLinksQuickFixProvider.metadata); + const commandReg = commandManager.register({ + id: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId, + execute(resource: vscode.Uri, path: string) { + const settingId = 'experimental.validate.ignoreLinks'; + const config = vscode.workspace.getConfiguration('markdown', resource); + const paths = new Set(config.get(settingId, [])); + paths.add(path); + config.update(settingId, [...paths], vscode.ConfigurationTarget.WorkspaceFolder); } - })); + }); + return vscode.Disposable.from(reg, commandReg); } - public getOptions(resource: vscode.Uri): DiagnosticOptions { - const config = vscode.workspace.getConfiguration('markdown', resource); - return { - enabled: config.get('experimental.validate.enabled', false), - validateReferences: config.get('experimental.validate.referenceLinks', DiagnosticLevel.ignore), - validateOwnHeaders: config.get('experimental.validate.headerLinks', DiagnosticLevel.ignore), - validateFilePaths: config.get('experimental.validate.fileLinks', DiagnosticLevel.ignore), - }; - } -} + provideCodeActions(document: vscode.TextDocument, _range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, _token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { + const fixes: vscode.CodeAction[] = []; -export class DiagnosticManager extends Disposable { + for (const diagnostic of context.diagnostics) { + switch (diagnostic.code) { + case DiagnosticCode.link_noSuchReferences: + case DiagnosticCode.link_noSuchHeaderInOwnFile: + case DiagnosticCode.link_noSuchFile: + case DiagnosticCode.link_noSuchHeaderInFile: { + const hrefText = (diagnostic as any).data?.hrefText; + if (hrefText) { + const fix = new vscode.CodeAction( + localize('ignoreLinksQuickFix.title', "Exclude '{0}' from link validation.", hrefText), + vscode.CodeActionKind.QuickFix); - private readonly collection: vscode.DiagnosticCollection; - - private readonly pendingDiagnostics = new Set(); - private readonly diagnosticDelayer: Delayer; - - constructor( - private readonly computer: DiagnosticComputer, - private readonly configuration: DiagnosticConfiguration, - ) { - super(); - - this.diagnosticDelayer = new Delayer(300); - - this.collection = this._register(vscode.languages.createDiagnosticCollection('markdown')); - - this._register(this.configuration.onDidChange(() => { - this.rebuild(); - })); - - const onDocUpdated = (doc: vscode.TextDocument) => { - if (isMarkdownFile(doc)) { - this.pendingDiagnostics.add(doc.uri); - this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics()); - } - }; - - this._register(vscode.workspace.onDidOpenTextDocument(doc => { - onDocUpdated(doc); - })); - - this._register(vscode.workspace.onDidChangeTextDocument(e => { - onDocUpdated(e.document); - })); - - this._register(vscode.workspace.onDidCloseTextDocument(doc => { - this.pendingDiagnostics.delete(doc.uri); - this.collection.delete(doc.uri); - })); - - this.rebuild(); - } - - private recomputePendingDiagnostics(): void { - const pending = [...this.pendingDiagnostics]; - this.pendingDiagnostics.clear(); - - for (const resource of pending) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === resource.fsPath); - if (doc) { - this.update(doc); - } - } - } - - private async rebuild() { - this.collection.clear(); - - const allOpenedTabResources = this.getAllTabResources(); - await Promise.all( - vscode.workspace.textDocuments - .filter(doc => allOpenedTabResources.has(doc.uri.toString()) && isMarkdownFile(doc)) - .map(doc => this.update(doc))); - } - - private getAllTabResources() { - const openedTabDocs = new Map(); - for (const group of vscode.window.tabGroups.all) { - for (const tab of group.tabs) { - if (tab.input instanceof vscode.TabInputText) { - openedTabDocs.set(tab.input.uri.toString(), tab.input.uri); - } - } - } - return openedTabDocs; - } - - private async update(doc: vscode.TextDocument): Promise { - const diagnostics = await this.getDiagnostics(doc, noopToken); - this.collection.set(doc.uri, diagnostics); - } - - public async getDiagnostics(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise { - const config = this.configuration.getOptions(doc.uri); - if (!config.enabled) { - return []; - } - return this.computer.getDiagnostics(doc, config, token); - } -} - -export class DiagnosticComputer { - - constructor( - private readonly engine: MarkdownEngine, - private readonly workspaceContents: MdWorkspaceContents, - private readonly linkProvider: MdLinkProvider, - ) { } - - public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise { - const links = await this.linkProvider.getAllLinks(doc, token); - if (token.isCancellationRequested) { - return []; - } - - return (await Promise.all([ - this.validateFileLinks(doc, options, links, token), - Array.from(this.validateReferenceLinks(options, links)), - this.validateOwnHeaderLinks(doc, options, links, token), - ])).flat(); - } - - private async validateOwnHeaderLinks(doc: SkinnyTextDocument, options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { - const severity = toSeverity(options.validateOwnHeaders); - if (typeof severity === 'undefined') { - return []; - } - - const toc = await TableOfContents.create(this.engine, doc); - if (token.isCancellationRequested) { - return []; - } - - const diagnostics: vscode.Diagnostic[] = []; - for (const link of links) { - if (link.href.kind === 'internal' - && link.href.path.toString() === doc.uri.toString() - && link.href.fragment - && !toc.lookup(link.href.fragment) - ) { - diagnostics.push(new vscode.Diagnostic( - link.source.hrefRange, - localize('invalidHeaderLink', 'No header found: \'{0}\'', link.href.fragment), - severity)); - } - } - - return diagnostics; - } - - private *validateReferenceLinks(options: DiagnosticOptions, links: readonly MdLink[]): Iterable { - const severity = toSeverity(options.validateReferences); - if (typeof severity === 'undefined') { - return []; - } - - const definitionSet = new LinkDefinitionSet(links); - for (const link of links) { - if (link.href.kind === 'reference' && !definitionSet.lookup(link.href.ref)) { - yield new vscode.Diagnostic( - link.source.hrefRange, - localize('invalidReferenceLink', 'No link reference found: \'{0}\'', link.href.ref), - severity); - } - } - } - - private async validateFileLinks(doc: SkinnyTextDocument, options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { - const severity = toSeverity(options.validateFilePaths); - if (typeof severity === 'undefined') { - return []; - } - - const tocs = new Map(); - - // TODO: cache links so we don't recompute duplicate hrefs - // TODO: parallelize - - const diagnostics: vscode.Diagnostic[] = []; - for (const link of links) { - if (token.isCancellationRequested) { - return []; - } - - if (link.href.kind !== 'internal') { - continue; - } - - const hrefDoc = await tryFindMdDocumentForLink(link.href, this.workspaceContents); - if (hrefDoc && hrefDoc.uri.toString() === doc.uri.toString()) { - continue; - } - - if (!hrefDoc && !await this.workspaceContents.pathExists(link.href.path)) { - diagnostics.push( - new vscode.Diagnostic( - link.source.hrefRange, - localize('invalidPathLink', 'File does not exist at path: {0}', (link.href as InternalHref).path.toString(true)), - severity)); - } else if (hrefDoc) { - if (link.href.fragment) { - // validate fragment looks valid - let hrefDocToc = tocs.get(link.href.path.toString()); - if (!hrefDocToc) { - hrefDocToc = await TableOfContents.create(this.engine, hrefDoc); - tocs.set(link.href.path.toString(), hrefDocToc); - } - - if (!hrefDocToc.lookup(link.href.fragment)) { - diagnostics.push( - new vscode.Diagnostic( - link.source.hrefRange, - localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', (link.href as InternalHref).path.fragment), - severity)); + fix.command = { + command: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId, + title: '', + arguments: [document.uri, hrefText], + }; + fixes.push(fix); } + break; } } } - return diagnostics; + return fixes; } } -export function register( - engine: MarkdownEngine, - workspaceContents: MdWorkspaceContents, - linkProvider: MdLinkProvider, + +export function registerDiagnosticSupport( + selector: vscode.DocumentSelector, + commandManager: CommandManager, ): vscode.Disposable { - const configuration = new VSCodeDiagnosticConfiguration(); - const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration); - return vscode.Disposable.from(configuration, manager); + return AddToIgnoreLinksQuickFixProvider.register(selector, commandManager); } diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts deleted file mode 100644 index 89be468fac..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ /dev/null @@ -1,421 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import * as uri from 'vscode-uri'; -import { OpenDocumentLinkCommand } from '../commands/openDocumentLink'; -import { MarkdownEngine } from '../markdownEngine'; -import { coalesce } from '../util/arrays'; -import { getUriForLinkWithKnownExternalScheme, isOfScheme, Schemes } from '../util/schemes'; -import { SkinnyTextDocument } from '../workspaceContents'; - -const localize = nls.loadMessageBundle(); - -export interface ExternalHref { - readonly kind: 'external'; - readonly uri: vscode.Uri; -} - -export interface InternalHref { - readonly kind: 'internal'; - readonly path: vscode.Uri; - readonly fragment: string; -} - -export interface ReferenceHref { - readonly kind: 'reference'; - readonly ref: string; -} - -export type LinkHref = ExternalHref | InternalHref | ReferenceHref; - - -function parseLink( - document: SkinnyTextDocument, - link: string, -): ExternalHref | InternalHref | undefined { - const cleanLink = stripAngleBrackets(link); - const externalSchemeUri = getUriForLinkWithKnownExternalScheme(cleanLink); - if (externalSchemeUri) { - // Normalize VS Code links to target currently running version - if (isOfScheme(Schemes.vscode, link) || isOfScheme(Schemes['vscode-insiders'], link)) { - return { kind: 'external', uri: vscode.Uri.parse(link).with({ scheme: vscode.env.uriScheme }) }; - } - return { kind: 'external', uri: externalSchemeUri }; - } - - // Assume it must be an relative or absolute file path - // Use a fake scheme to avoid parse warnings - const tempUri = vscode.Uri.parse(`vscode-resource:${link}`); - - let resourceUri: vscode.Uri | undefined; - if (!tempUri.path) { - resourceUri = document.uri; - } else if (tempUri.path[0] === '/') { - const root = getWorkspaceFolder(document); - if (root) { - resourceUri = vscode.Uri.joinPath(root, tempUri.path); - } - } else { - if (document.uri.scheme === Schemes.untitled) { - const root = getWorkspaceFolder(document); - if (root) { - resourceUri = vscode.Uri.joinPath(root, tempUri.path); - } - } else { - const base = uri.Utils.dirname(document.uri); - resourceUri = vscode.Uri.joinPath(base, tempUri.path); - } - } - - if (!resourceUri) { - return undefined; - } - - return { - kind: 'internal', - path: resourceUri.with({ fragment: '' }), - fragment: tempUri.fragment, - }; -} - -function getWorkspaceFolder(document: SkinnyTextDocument) { - return vscode.workspace.getWorkspaceFolder(document.uri)?.uri - || vscode.workspace.workspaceFolders?.[0]?.uri; -} - -interface MdLinkSource { - readonly text: string; - readonly resource: vscode.Uri; - readonly hrefRange: vscode.Range; - readonly fragmentRange: vscode.Range | undefined; -} - -export interface MdInlineLink { - readonly kind: 'link'; - readonly source: MdLinkSource; - readonly href: LinkHref; -} - -export interface MdLinkDefinition { - readonly kind: 'definition'; - readonly source: MdLinkSource; - readonly ref: { - readonly range: vscode.Range; - readonly text: string; - }; - readonly href: ExternalHref | InternalHref; -} - -export type MdLink = MdInlineLink | MdLinkDefinition; - -function extractDocumentLink( - document: SkinnyTextDocument, - pre: number, - link: string, - matchIndex: number | undefined -): MdLink | undefined { - const offset = (matchIndex || 0) + pre; - const linkStart = document.positionAt(offset); - const linkEnd = document.positionAt(offset + link.length); - try { - const linkTarget = parseLink(document, link); - if (!linkTarget) { - return undefined; - } - return { - kind: 'link', - href: linkTarget, - source: { - text: link, - resource: document.uri, - hrefRange: new vscode.Range(linkStart, linkEnd), - fragmentRange: getFragmentRange(link, linkStart, linkEnd), - } - }; - } catch { - return undefined; - } -} - -function getFragmentRange(text: string, start: vscode.Position, end: vscode.Position): vscode.Range | undefined { - const index = text.indexOf('#'); - if (index < 0) { - return undefined; - } - return new vscode.Range(start.translate({ characterDelta: index + 1 }), end); -} - -const angleBracketLinkRe = /^<(.*)>$/; - -/** - * Used to strip brackets from the markdown link - * - * will be transformed to http://example.com -*/ -function stripAngleBrackets(link: string) { - return link.replace(angleBracketLinkRe, '$1'); -} - -/** - * Matches `[text](link)` - */ -const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*(".*?")?\)/g; - -/** - * Matches `[text][ref]` - */ -const referenceLinkPattern = /(?:(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]|\[\s*?([^\s\]]*?)\])(?![\:\(])/g; - -/** - * Matches `` - */ -const autoLinkPattern = /\<(\w+:[^\>\s]+)\>/g; - -/** - * Matches `[text]: link` - */ -const definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)([^<]\S*|<[^>]+>)/gm; - -const inlineCodePattern = /(?:^|[^`])(`+)(?:.+?|.*?(?:(?:\r?\n).+?)*?)(?:\r?\n)?\1(?:$|[^`])/gm; - -interface CodeInDocument { - /** - * code blocks and fences each represented by [line_start,line_end). - */ - readonly multiline: ReadonlyArray<[number, number]>; - - /** - * inline code spans each represented by {@link vscode.Range}. - */ - readonly inline: readonly vscode.Range[]; -} - -async function findCode(document: SkinnyTextDocument, engine: MarkdownEngine): Promise { - const tokens = await engine.parse(document); - const multiline = tokens.filter(t => (t.type === 'code_block' || t.type === 'fence') && !!t.map).map(t => t.map) as [number, number][]; - - const text = document.getText(); - const inline = [...text.matchAll(inlineCodePattern)].map(match => { - const start = match.index || 0; - return new vscode.Range(document.positionAt(start), document.positionAt(start + match[0].length)); - }); - - return { multiline, inline }; -} - -function isLinkInsideCode(code: CodeInDocument, linkHrefRange: vscode.Range) { - return code.multiline.some(interval => linkHrefRange.start.line >= interval[0] && linkHrefRange.start.line < interval[1]) || - code.inline.some(position => position.intersection(linkHrefRange)); -} - -export class MdLinkProvider implements vscode.DocumentLinkProvider { - - constructor( - private readonly engine: MarkdownEngine - ) { } - - public async provideDocumentLinks( - document: SkinnyTextDocument, - token: vscode.CancellationToken - ): Promise { - const allLinks = await this.getAllLinks(document, token); - if (token.isCancellationRequested) { - return []; - } - - const definitionSet = new LinkDefinitionSet(allLinks); - return coalesce(allLinks - .map(data => this.toValidDocumentLink(data, definitionSet))); - } - - private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined { - switch (link.href.kind) { - case 'external': { - return new vscode.DocumentLink(link.source.hrefRange, link.href.uri); - } - case 'internal': { - const uri = OpenDocumentLinkCommand.createCommandUri(link.source.resource, link.href.path, link.href.fragment); - const documentLink = new vscode.DocumentLink(link.source.hrefRange, uri); - documentLink.tooltip = localize('documentLink.tooltip', 'Follow link'); - return documentLink; - } - case 'reference': { - const def = definitionSet.lookup(link.href.ref); - if (def) { - return new vscode.DocumentLink( - link.source.hrefRange, - vscode.Uri.parse(`command:_markdown.moveCursorToPosition?${encodeURIComponent(JSON.stringify([def.source.hrefRange.start.line, def.source.hrefRange.start.character]))}`)); - } else { - return undefined; - } - } - } - } - - public async getAllLinks(document: SkinnyTextDocument, token: vscode.CancellationToken): Promise { - const codeInDocument = await findCode(document, this.engine); - if (token.isCancellationRequested) { - return []; - } - - return Array.from([ - ...this.getInlineLinks(document, codeInDocument), - ...this.getReferenceLinks(document, codeInDocument), - ...this.getLinkDefinitions2(document, codeInDocument), - ...this.getAutoLinks(document, codeInDocument), - ]); - } - - private *getInlineLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { - const text = document.getText(); - - for (const match of text.matchAll(linkPattern)) { - const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); - if (matchImageData && !isLinkInsideCode(codeInDocument, matchImageData.source.hrefRange)) { - yield matchImageData; - } - const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index); - if (matchLinkData && !isLinkInsideCode(codeInDocument, matchLinkData.source.hrefRange)) { - yield matchLinkData; - } - } - } - - private *getAutoLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { - const text = document.getText(); - - for (const match of text.matchAll(autoLinkPattern)) { - const link = match[1]; - const linkTarget = parseLink(document, link); - if (linkTarget) { - const offset = (match.index ?? 0) + 1; - const linkStart = document.positionAt(offset); - const linkEnd = document.positionAt(offset + link.length); - const hrefRange = new vscode.Range(linkStart, linkEnd); - if (isLinkInsideCode(codeInDocument, hrefRange)) { - continue; - } - yield { - kind: 'link', - href: linkTarget, - source: { - text: link, - resource: document.uri, - hrefRange: new vscode.Range(linkStart, linkEnd), - fragmentRange: getFragmentRange(link, linkStart, linkEnd), - } - }; - } - } - } - - private *getReferenceLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { - const text = document.getText(); - for (const match of text.matchAll(referenceLinkPattern)) { - let linkStart: vscode.Position; - let linkEnd: vscode.Position; - let reference = match[3]; - if (reference) { // [text][ref] - const pre = match[1]; - const offset = (match.index || 0) + pre.length; - linkStart = document.positionAt(offset); - linkEnd = document.positionAt(offset + reference.length); - } else if (match[4]) { // [ref][], [ref] - reference = match[4]; - const offset = (match.index || 0) + 1; - linkStart = document.positionAt(offset); - linkEnd = document.positionAt(offset + reference.length); - } else { - continue; - } - - const hrefRange = new vscode.Range(linkStart, linkEnd); - if (isLinkInsideCode(codeInDocument, hrefRange)) { - continue; - } - - yield { - kind: 'link', - source: { - text: reference, - resource: document.uri, - hrefRange, - fragmentRange: undefined, - }, - href: { - kind: 'reference', - ref: reference, - } - }; - } - } - - public async getLinkDefinitions(document: SkinnyTextDocument): Promise> { - const codeInDocument = await findCode(document, this.engine); - return this.getLinkDefinitions2(document, codeInDocument); - } - - private *getLinkDefinitions2(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { - const text = document.getText(); - for (const match of text.matchAll(definitionPattern)) { - const pre = match[1]; - const reference = match[2]; - const link = match[3].trim(); - const offset = (match.index || 0) + pre.length; - - const refStart = document.positionAt((match.index ?? 0) + 1); - const refRange = new vscode.Range(refStart, refStart.translate({ characterDelta: reference.length })); - - let linkStart: vscode.Position; - let linkEnd: vscode.Position; - let text: string; - if (angleBracketLinkRe.test(link)) { - linkStart = document.positionAt(offset + 1); - linkEnd = document.positionAt(offset + link.length - 1); - text = link.substring(1, link.length - 1); - } else { - linkStart = document.positionAt(offset); - linkEnd = document.positionAt(offset + link.length); - text = link; - } - const hrefRange = new vscode.Range(linkStart, linkEnd); - if (isLinkInsideCode(codeInDocument, hrefRange)) { - continue; - } - const target = parseLink(document, text); - if (target) { - yield { - kind: 'definition', - source: { - text: link, - resource: document.uri, - hrefRange, - fragmentRange: getFragmentRange(link, linkStart, linkEnd), - }, - ref: { text: reference, range: refRange }, - href: target, - }; - } - } - } -} - -export class LinkDefinitionSet { - private readonly _map = new Map(); - - constructor(links: Iterable) { - for (const link of links) { - if (link.kind === 'definition') { - this._map.set(link.ref.text, link); - } - } - } - - public lookup(ref: string): MdLinkDefinition | undefined { - return this._map.get(ref); - } -} diff --git a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts deleted file mode 100644 index 6c73efd64a..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts +++ /dev/null @@ -1,76 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; -import { SkinnyTextDocument } from '../workspaceContents'; - -interface MarkdownSymbol { - readonly level: number; - readonly parent: MarkdownSymbol | undefined; - readonly children: vscode.DocumentSymbol[]; -} - -export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider { - - constructor( - private readonly engine: MarkdownEngine - ) { } - - public async provideDocumentSymbolInformation(document: SkinnyTextDocument): Promise { - const toc = await TableOfContents.create(this.engine, document); - return toc.entries.map(entry => this.toSymbolInformation(entry)); - } - - public async provideDocumentSymbols(document: SkinnyTextDocument): Promise { - const toc = await TableOfContents.create(this.engine, document); - const root: MarkdownSymbol = { - level: -Infinity, - children: [], - parent: undefined - }; - this.buildTree(root, toc.entries); - return root.children; - } - - private buildTree(parent: MarkdownSymbol, entries: readonly TocEntry[]) { - if (!entries.length) { - return; - } - - const entry = entries[0]; - const symbol = this.toDocumentSymbol(entry); - symbol.children = []; - - while (parent && entry.level <= parent.level) { - parent = parent.parent!; - } - parent.children.push(symbol); - this.buildTree({ level: entry.level, children: symbol.children, parent }, entries.slice(1)); - } - - - private toSymbolInformation(entry: TocEntry): vscode.SymbolInformation { - return new vscode.SymbolInformation( - this.getSymbolName(entry), - vscode.SymbolKind.String, - '', - entry.sectionLocation); - } - - private toDocumentSymbol(entry: TocEntry) { - return new vscode.DocumentSymbol( - this.getSymbolName(entry), - '', - vscode.SymbolKind.String, - entry.sectionLocation.range, - entry.sectionLocation.range); - } - - private getSymbolName(entry: TocEntry): string { - return '#'.repeat(entry.level) + ' ' + entry.text; - } -} diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index 40322feb75..5c2e49e78b 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -23,49 +23,54 @@ const imageFileExtensions = new Set([ '.webp', ]); -export function registerDropIntoEditor(selector: vscode.DocumentSelector) { - return vscode.languages.registerDocumentOnDropProvider(selector, new class implements vscode.DocumentOnDropProvider { - async provideDocumentOnDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): Promise { +export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector) { + return vscode.languages.registerDocumentDropEditProvider(selector, new class implements vscode.DocumentDropEditProvider { + async provideDocumentDropEdits(document: vscode.TextDocument, _position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true); if (!enabled) { - return; - } - - const urlList = await dataTransfer.get('text/uri-list')?.asString(); - if (!urlList) { return undefined; } - const uris: vscode.Uri[] = []; - for (const resource of urlList.split('\n')) { - try { - uris.push(vscode.Uri.parse(resource)); - } catch { - // noop - } - } - - if (!uris.length) { - return; - } - - const snippet = new vscode.SnippetString(); - uris.forEach((uri, i) => { - const mdPath = document.uri.scheme === uri.scheme - ? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath)) - : uri.toString(false); - - const ext = URI.Utils.extname(uri).toLowerCase(); - snippet.appendText(imageFileExtensions.has(ext) ? '![' : '['); - snippet.appendTabstop(); - snippet.appendText(`](${mdPath})`); - - if (i <= uris.length - 1 && uris.length > 1) { - snippet.appendText(' '); - } - }); - - return new vscode.SnippetTextEdit(new vscode.Range(position, position), snippet); + const snippet = await tryGetUriListSnippet(document, dataTransfer, token); + return snippet ? new vscode.DocumentDropEdit(snippet) : undefined; } }); } + +export async function tryGetUriListSnippet(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { + const urlList = await dataTransfer.get('text/uri-list')?.asString(); + if (!urlList || token.isCancellationRequested) { + return undefined; + } + + const uris: vscode.Uri[] = []; + for (const resource of urlList.split('\n')) { + try { + uris.push(vscode.Uri.parse(resource)); + } catch { + // noop + } + } + + if (!uris.length) { + return; + } + + const snippet = new vscode.SnippetString(); + uris.forEach((uri, i) => { + const mdPath = document.uri.scheme === uri.scheme + ? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath).replace(/\\/g, '/')) + : uri.toString(false); + + const ext = URI.Utils.extname(uri).toLowerCase(); + snippet.appendText(imageFileExtensions.has(ext) ? '![' : '['); + snippet.appendTabstop(); + snippet.appendText(`](${mdPath})`); + + if (i <= uris.length - 1 && uris.length > 1) { + snippet.appendText(' '); + } + }); + + return snippet; +} diff --git a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts index eb5b1a0b6f..6ca2e06180 100644 --- a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts +++ b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { BaseLanguageClient } from 'vscode-languageclient'; import * as nls from 'vscode-nls'; import { Command, CommandManager } from '../commandManager'; -import { MdReferencesProvider } from './references'; +import { getReferencesToFileInWorkspace } from '../protocol'; const localize = nls.loadMessageBundle(); @@ -16,7 +17,7 @@ export class FindFileReferencesCommand implements Command { public readonly id = 'markdown.findAllFileReferences'; constructor( - private readonly referencesProvider: MdReferencesProvider, + private readonly client: BaseLanguageClient, ) { } public async execute(resource?: vscode.Uri) { @@ -33,8 +34,9 @@ export class FindFileReferencesCommand implements Command { location: vscode.ProgressLocation.Window, title: localize('progress.title', "Finding file references") }, async (_progress, token) => { - const references = await this.referencesProvider.getAllReferencesToFile(resource!, token); - const locations = references.map(ref => ref.location); + const locations = (await this.client.sendRequest(getReferencesToFileInWorkspace, { uri: resource!.toString() }, token)).map(loc => { + return new vscode.Location(vscode.Uri.parse(loc.uri), new vscode.Range(loc.range.start.line, loc.range.start.character, loc.range.end.line, loc.range.end.character)); + }); const config = vscode.workspace.getConfiguration('references'); const existingSetting = config.inspect('preferredLocation'); @@ -49,6 +51,9 @@ export class FindFileReferencesCommand implements Command { } } -export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesProvider): vscode.Disposable { - return commandManager.register(new FindFileReferencesCommand(referencesProvider)); +export function registerFindFileReferenceSupport( + commandManager: CommandManager, + client: BaseLanguageClient, +): vscode.Disposable { + return commandManager.register(new FindFileReferencesCommand(client)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts deleted file mode 100644 index 61407e671b..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts +++ /dev/null @@ -1,113 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import Token = require('markdown-it/lib/token'); -import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; -import { SkinnyTextDocument } from '../workspaceContents'; - -const rangeLimit = 5000; - -interface MarkdownItTokenWithMap extends Token { - map: [number, number]; -} - -export class MdFoldingProvider implements vscode.FoldingRangeProvider { - - constructor( - private readonly engine: MarkdownEngine - ) { } - - public async provideFoldingRanges( - document: SkinnyTextDocument, - _: vscode.FoldingContext, - _token: vscode.CancellationToken - ): Promise { - const foldables = await Promise.all([ - this.getRegions(document), - this.getHeaderFoldingRanges(document), - this.getBlockFoldingRanges(document) - ]); - return foldables.flat().slice(0, rangeLimit); - } - - private async getRegions(document: SkinnyTextDocument): Promise { - const tokens = await this.engine.parse(document); - const regionMarkers = tokens.filter(isRegionMarker) - .map(token => ({ line: token.map[0], isStart: isStartRegion(token.content) })); - - const nestingStack: { line: number; isStart: boolean }[] = []; - return regionMarkers - .map(marker => { - if (marker.isStart) { - nestingStack.push(marker); - } else if (nestingStack.length && nestingStack[nestingStack.length - 1].isStart) { - return new vscode.FoldingRange(nestingStack.pop()!.line, marker.line, vscode.FoldingRangeKind.Region); - } else { - // noop: invalid nesting (i.e. [end, start] or [start, end, end]) - } - return null; - }) - .filter((region: vscode.FoldingRange | null): region is vscode.FoldingRange => !!region); - } - - private async getHeaderFoldingRanges(document: SkinnyTextDocument) { - const toc = await TableOfContents.create(this.engine, document); - return toc.entries.map(entry => { - let endLine = entry.sectionLocation.range.end.line; - if (document.lineAt(endLine).isEmptyOrWhitespace && endLine >= entry.line + 1) { - endLine = endLine - 1; - } - return new vscode.FoldingRange(entry.line, endLine); - }); - } - - private async getBlockFoldingRanges(document: SkinnyTextDocument): Promise { - const tokens = await this.engine.parse(document); - const multiLineListItems = tokens.filter(isFoldableToken); - return multiLineListItems.map(listItem => { - const start = listItem.map[0]; - let end = listItem.map[1] - 1; - if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) { - end = end - 1; - } - return new vscode.FoldingRange(start, end, this.getFoldingRangeKind(listItem)); - }); - } - - private getFoldingRangeKind(listItem: Token): vscode.FoldingRangeKind | undefined { - return listItem.type === 'html_block' && listItem.content.startsWith('/.test(t); -const isEndRegion = (t: string) => /^\s*/.test(t); - -const isRegionMarker = (token: Token): token is MarkdownItTokenWithMap => - !!token.map && token.type === 'html_block' && (isStartRegion(token.content) || isEndRegion(token.content)); - -const isFoldableToken = (token: Token): token is MarkdownItTokenWithMap => { - if (!token.map) { - return false; - } - - switch (token.type) { - case 'fence': - case 'list_item_open': - return token.map[1] > token.map[0]; - - case 'html_block': - if (isRegionMarker(token)) { - return false; - } - return token.map[1] > token.map[0] + 1; - - default: - return false; - } -}; diff --git a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts b/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts deleted file mode 100644 index 85c92b4c51..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts +++ /dev/null @@ -1,353 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { dirname, resolve } from 'path'; -import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; -import { resolveUriToMarkdownFile } from '../util/openDocumentLink'; -import { SkinnyTextDocument } from '../workspaceContents'; -import { MdLinkProvider } from './documentLinkProvider'; - -enum CompletionContextKind { - /** `[...](|)` */ - Link, - - /** `[...][|]` */ - ReferenceLink, - - /** `[]: |` */ - LinkDefinition, -} - -interface AnchorContext { - /** - * Link text before the `#`. - * - * For `[text](xy#z|abc)` this is `xy`. - */ - readonly beforeAnchor: string; - - /** - * Text of the anchor before the current position. - * - * For `[text](xy#z|abc)` this is `z`. - */ - readonly anchorPrefix: string; -} - -interface CompletionContext { - readonly kind: CompletionContextKind; - - /** - * Text of the link before the current position - * - * For `[text](xy#z|abc)` this is `xy#z`. - */ - readonly linkPrefix: string; - - /** - * Position of the start of the link. - * - * For `[text](xy#z|abc)` this is the position before `xy`. - */ - readonly linkTextStartPosition: vscode.Position; - - /** - * Text of the link after the current position. - * - * For `[text](xy#z|abc)` this is `abc`. - */ - readonly linkSuffix: string; - - /** - * Info if the link looks like it is for an anchor: `[](#header)` - */ - readonly anchorInfo?: AnchorContext; -} - -function tryDecodeUriComponent(str: string): string { - try { - return decodeURIComponent(str); - } catch { - return str; - } -} - -export class MdPathCompletionProvider implements vscode.CompletionItemProvider { - - public static register( - selector: vscode.DocumentSelector, - engine: MarkdownEngine, - linkProvider: MdLinkProvider, - ): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine, linkProvider), '.', '/', '#'); - } - - constructor( - private readonly engine: MarkdownEngine, - private readonly linkProvider: MdLinkProvider, - ) { } - - public async provideCompletionItems(document: SkinnyTextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise { - if (!this.arePathSuggestionEnabled(document)) { - return []; - } - - const context = this.getPathCompletionContext(document, position); - if (!context) { - return []; - } - - switch (context.kind) { - case CompletionContextKind.ReferenceLink: { - const items: vscode.CompletionItem[] = []; - for await (const item of this.provideReferenceSuggestions(document, position, context)) { - items.push(item); - } - return items; - } - - case CompletionContextKind.LinkDefinition: - case CompletionContextKind.Link: { - const items: vscode.CompletionItem[] = []; - - const isAnchorInCurrentDoc = context.anchorInfo && context.anchorInfo.beforeAnchor.length === 0; - - // Add anchor #links in current doc - if (context.linkPrefix.length === 0 || isAnchorInCurrentDoc) { - const insertRange = new vscode.Range(context.linkTextStartPosition, position); - for await (const item of this.provideHeaderSuggestions(document, position, context, insertRange)) { - items.push(item); - } - } - - if (!isAnchorInCurrentDoc) { - if (context.anchorInfo) { // Anchor to a different document - const rawUri = this.resolveReference(document, context.anchorInfo.beforeAnchor); - if (rawUri) { - const otherDoc = await resolveUriToMarkdownFile(rawUri); - if (otherDoc) { - const anchorStartPosition = position.translate({ characterDelta: -(context.anchorInfo.anchorPrefix.length + 1) }); - const range = new vscode.Range(anchorStartPosition, position); - for await (const item of this.provideHeaderSuggestions(otherDoc, position, context, range)) { - items.push(item); - } - } - } - } else { // Normal path suggestions - for await (const item of this.providePathSuggestions(document, position, context)) { - items.push(item); - } - } - } - - return items; - } - } - } - - private arePathSuggestionEnabled(document: SkinnyTextDocument): boolean { - const config = vscode.workspace.getConfiguration('markdown', document.uri); - return config.get('suggest.paths.enabled', true); - } - - /// [...](...| - private readonly linkStartPattern = /\[([^\]]*?)\]\(\s*([^\s\(\)]*)$/; - - /// [...][...| - private readonly referenceLinkStartPattern = /\[([^\]]*?)\]\[\s*([^\s\(\)]*)$/; - - /// [id]: | - private readonly definitionPattern = /^\s*\[[\w\-]+\]:\s*([^\s]*)$/m; - - private getPathCompletionContext(document: SkinnyTextDocument, position: vscode.Position): CompletionContext | undefined { - const line = document.lineAt(position.line).text; - - const linePrefixText = line.slice(0, position.character); - const lineSuffixText = line.slice(position.character); - - const linkPrefixMatch = linePrefixText.match(this.linkStartPattern); - if (linkPrefixMatch) { - const prefix = linkPrefixMatch[2]; - if (this.refLooksLikeUrl(prefix)) { - return undefined; - } - - const suffix = lineSuffixText.match(/^[^\)\s]*/); - return { - kind: CompletionContextKind.Link, - linkPrefix: tryDecodeUriComponent(prefix), - linkTextStartPosition: position.translate({ characterDelta: -prefix.length }), - linkSuffix: suffix ? suffix[0] : '', - anchorInfo: this.getAnchorContext(prefix), - }; - } - - const definitionLinkPrefixMatch = linePrefixText.match(this.definitionPattern); - if (definitionLinkPrefixMatch) { - const prefix = definitionLinkPrefixMatch[1]; - if (this.refLooksLikeUrl(prefix)) { - return undefined; - } - - const suffix = lineSuffixText.match(/^[^\s]*/); - return { - kind: CompletionContextKind.LinkDefinition, - linkPrefix: tryDecodeUriComponent(prefix), - linkTextStartPosition: position.translate({ characterDelta: -prefix.length }), - linkSuffix: suffix ? suffix[0] : '', - anchorInfo: this.getAnchorContext(prefix), - }; - } - - const referenceLinkPrefixMatch = linePrefixText.match(this.referenceLinkStartPattern); - if (referenceLinkPrefixMatch) { - const prefix = referenceLinkPrefixMatch[2]; - const suffix = lineSuffixText.match(/^[^\]\s]*/); - return { - kind: CompletionContextKind.ReferenceLink, - linkPrefix: prefix, - linkTextStartPosition: position.translate({ characterDelta: -prefix.length }), - linkSuffix: suffix ? suffix[0] : '', - }; - } - - return undefined; - } - - /** - * Check if {@param ref} looks like a 'http:' style url. - */ - private refLooksLikeUrl(prefix: string): boolean { - return /^\s*[\w\d\-]+:/.test(prefix); - } - - private getAnchorContext(prefix: string): AnchorContext | undefined { - const anchorMatch = prefix.match(/^(.*)#([\w\d\-]*)$/); - if (!anchorMatch) { - return undefined; - } - return { - beforeAnchor: anchorMatch[1], - anchorPrefix: anchorMatch[2], - }; - } - - private async *provideReferenceSuggestions(document: SkinnyTextDocument, position: vscode.Position, context: CompletionContext): AsyncIterable { - const insertionRange = new vscode.Range(context.linkTextStartPosition, position); - const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length })); - - const definitions = await this.linkProvider.getLinkDefinitions(document); - for (const def of definitions) { - yield { - kind: vscode.CompletionItemKind.Reference, - label: def.ref.text, - range: { - inserting: insertionRange, - replacing: replacementRange, - }, - }; - } - } - - private async *provideHeaderSuggestions(document: SkinnyTextDocument, position: vscode.Position, context: CompletionContext, insertionRange: vscode.Range): AsyncIterable { - const toc = await TableOfContents.createForDocumentOrNotebook(this.engine, document); - for (const entry of toc.entries) { - const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length })); - yield { - kind: vscode.CompletionItemKind.Reference, - label: '#' + decodeURIComponent(entry.slug.value), - range: { - inserting: insertionRange, - replacing: replacementRange, - }, - }; - } - } - - private async *providePathSuggestions(document: SkinnyTextDocument, position: vscode.Position, context: CompletionContext): AsyncIterable { - const valueBeforeLastSlash = context.linkPrefix.substring(0, context.linkPrefix.lastIndexOf('/') + 1); // keep the last slash - - const parentDir = this.resolveReference(document, valueBeforeLastSlash || '.'); - if (!parentDir) { - return; - } - - const pathSegmentStart = position.translate({ characterDelta: valueBeforeLastSlash.length - context.linkPrefix.length }); - const insertRange = new vscode.Range(pathSegmentStart, position); - - const pathSegmentEnd = position.translate({ characterDelta: context.linkSuffix.length }); - const replacementRange = new vscode.Range(pathSegmentStart, pathSegmentEnd); - - let dirInfo: Array<[string, vscode.FileType]>; - try { - dirInfo = await vscode.workspace.fs.readDirectory(parentDir); - } catch { - return; - } - - for (const [name, type] of dirInfo) { - // Exclude paths that start with `.` - if (name.startsWith('.')) { - continue; - } - - const isDir = type === vscode.FileType.Directory; - yield { - label: isDir ? name + '/' : name, - insertText: isDir ? encodeURIComponent(name) + '/' : encodeURIComponent(name), - kind: isDir ? vscode.CompletionItemKind.Folder : vscode.CompletionItemKind.File, - range: { - inserting: insertRange, - replacing: replacementRange, - }, - command: isDir ? { command: 'editor.action.triggerSuggest', title: '' } : undefined, - }; - } - } - - private resolveReference(document: SkinnyTextDocument, ref: string): vscode.Uri | undefined { - const docUri = this.getFileUriOfTextDocument(document); - - if (ref.startsWith('/')) { - const workspaceFolder = vscode.workspace.getWorkspaceFolder(docUri); - if (workspaceFolder) { - return vscode.Uri.joinPath(workspaceFolder.uri, ref); - } else { - return this.resolvePath(docUri, ref.slice(1)); - } - } - - return this.resolvePath(docUri, ref); - } - - private resolvePath(root: vscode.Uri, ref: string): vscode.Uri | undefined { - try { - if (root.scheme === 'file') { - return vscode.Uri.file(resolve(dirname(root.fsPath), ref)); - } else { - return root.with({ - path: resolve(dirname(root.path), ref), - }); - } - } catch { - return undefined; - } - } - - private getFileUriOfTextDocument(document: SkinnyTextDocument) { - if (document.uri.scheme === 'vscode-notebook-cell') { - const notebook = vscode.workspace.notebookDocuments - .find(notebook => notebook.getCells().some(cell => cell.document === document)); - - if (notebook) { - return notebook.uri; - } - } - - return document.uri; - } -} diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts deleted file mode 100644 index aa2e1f1ee9..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ /dev/null @@ -1,308 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import * as uri from 'vscode-uri'; -import { MarkdownEngine } from '../markdownEngine'; -import { Slugifier } from '../slugify'; -import { TableOfContents, TocEntry } from '../tableOfContents'; -import { noopToken } from '../test/util'; -import { Disposable } from '../util/dispose'; -import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { InternalHref, MdLink, MdLinkProvider } from './documentLinkProvider'; -import { MdWorkspaceCache } from './workspaceCache'; - - -/** - * A link in a markdown file. - */ -export interface MdLinkReference { - readonly kind: 'link'; - readonly isTriggerLocation: boolean; - readonly isDefinition: boolean; - readonly location: vscode.Location; - - readonly link: MdLink; -} - -/** - * A header in a markdown file. - */ -export interface MdHeaderReference { - readonly kind: 'header'; - - readonly isTriggerLocation: boolean; - readonly isDefinition: boolean; - - /** - * The range of the header. - * - * In `# a b c #` this would be the range of `# a b c #` - */ - readonly location: vscode.Location; - - /** - * The text of the header. - * - * In `# a b c #` this would be `a b c` - */ - readonly headerText: string; - - /** - * The range of the header text itself. - * - * In `# a b c #` this would be the range of `a b c` - */ - readonly headerTextLocation: vscode.Location; -} - -export type MdReference = MdLinkReference | MdHeaderReference; - -export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider { - - private readonly _linkCache: MdWorkspaceCache; - - public constructor( - private readonly linkProvider: MdLinkProvider, - private readonly workspaceContents: MdWorkspaceContents, - private readonly engine: MarkdownEngine, - private readonly slugifier: Slugifier, - ) { - super(); - - this._linkCache = this._register(new MdWorkspaceCache(workspaceContents, doc => linkProvider.getAllLinks(doc, noopToken))); - } - - async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { - const allRefs = await this.getAllReferencesAtPosition(document, position, token); - - return allRefs - .filter(ref => context.includeDeclaration || !ref.isDefinition) - .map(ref => ref.location); - } - - public async getAllReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const toc = await TableOfContents.create(this.engine, document); - if (token.isCancellationRequested) { - return []; - } - - const header = toc.entries.find(entry => entry.line === position.line); - if (header) { - return this.getReferencesToHeader(document, header); - } else { - return this.getReferencesToLinkAtPosition(document, position, token); - } - } - - private async getReferencesToHeader(document: SkinnyTextDocument, header: TocEntry): Promise { - const links = (await this._linkCache.getAll()).flat(); - - const references: MdReference[] = []; - - references.push({ - kind: 'header', - isTriggerLocation: true, - isDefinition: true, - location: header.headerLocation, - headerText: header.text, - headerTextLocation: header.headerTextLocation - }); - - for (const link of links) { - if (link.href.kind === 'internal' - && this.looksLikeLinkToDoc(link.href, document.uri) - && this.slugifier.fromHeading(link.href.fragment).value === header.slug.value - ) { - references.push({ - kind: 'link', - isTriggerLocation: false, - isDefinition: false, - link, - location: new vscode.Location(link.source.resource, link.source.hrefRange), - }); - } - } - - return references; - } - - private async getReferencesToLinkAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const docLinks = await this.linkProvider.getAllLinks(document, token); - - for (const link of docLinks) { - if (link.kind === 'definition') { - // We could be in either the ref name or the definition - if (link.ref.range.contains(position)) { - return Array.from(this.getReferencesToLinkReference(docLinks, link.ref.text, { resource: document.uri, range: link.ref.range })); - } else if (link.source.hrefRange.contains(position)) { - return this.getReferencesToLink(link, position, token); - } - } else { - if (link.source.hrefRange.contains(position)) { - return this.getReferencesToLink(link, position, token); - } - } - } - - return []; - } - - private async getReferencesToLink(sourceLink: MdLink, triggerPosition: vscode.Position, token: vscode.CancellationToken): Promise { - const allLinksInWorkspace = (await this._linkCache.getAll()).flat(); - if (token.isCancellationRequested) { - return []; - } - - if (sourceLink.href.kind === 'reference') { - return Array.from(this.getReferencesToLinkReference(allLinksInWorkspace, sourceLink.href.ref, { resource: sourceLink.source.resource, range: sourceLink.source.hrefRange })); - } - - if (sourceLink.href.kind === 'external') { - const references: MdReference[] = []; - - for (const link of allLinksInWorkspace) { - if (link.href.kind === 'external' && link.href.uri.toString() === sourceLink.href.uri.toString()) { - const isTriggerLocation = sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange); - references.push({ - kind: 'link', - isTriggerLocation, - isDefinition: false, - link, - location: new vscode.Location(link.source.resource, link.source.hrefRange), - }); - } - } - return references; - } - - const targetDoc = await tryFindMdDocumentForLink(sourceLink.href, this.workspaceContents); - if (token.isCancellationRequested) { - return []; - } - - const references: MdReference[] = []; - - if (targetDoc && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { - const toc = await TableOfContents.create(this.engine, targetDoc); - const entry = toc.lookup(sourceLink.href.fragment); - if (entry) { - references.push({ - kind: 'header', - isTriggerLocation: false, - isDefinition: true, - location: entry.headerLocation, - headerText: entry.text, - headerTextLocation: entry.headerTextLocation - }); - } - - for (const link of allLinksInWorkspace) { - if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, targetDoc.uri)) { - continue; - } - - if (this.slugifier.fromHeading(link.href.fragment).equals(this.slugifier.fromHeading(sourceLink.href.fragment))) { - const isTriggerLocation = sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange); - references.push({ - kind: 'link', - isTriggerLocation, - isDefinition: false, - link, - location: new vscode.Location(link.source.resource, link.source.hrefRange), - }); - } - } - } else { // Triggered on a link without a fragment so we only require matching the file and ignore fragments - references.push(...this.findAllLinksToFile(targetDoc?.uri ?? sourceLink.href.path, allLinksInWorkspace, sourceLink)); - } - - return references; - } - - private looksLikeLinkToDoc(href: InternalHref, targetDoc: vscode.Uri) { - return href.path.fsPath === targetDoc.fsPath - || uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.fsPath; - } - - public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise { - const allLinksInWorkspace = (await this._linkCache.getAll()).flat(); - return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined)); - } - - private * findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable { - for (const link of allLinksInWorkspace) { - if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resource)) { - continue; - } - - // Exclude cases where the file is implicitly referencing itself - if (link.source.text.startsWith('#') && link.source.resource.fsPath === resource.fsPath) { - continue; - } - - const isTriggerLocation = !!sourceLink && sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange); - const pathRange = this.getPathRange(link); - yield { - kind: 'link', - isTriggerLocation, - isDefinition: false, - link, - location: new vscode.Location(link.source.resource, pathRange), - }; - } - } - - private * getReferencesToLinkReference(allLinks: Iterable, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable { - for (const link of allLinks) { - let ref: string; - if (link.kind === 'definition') { - ref = link.ref.text; - } else if (link.href.kind === 'reference') { - ref = link.href.ref; - } else { - continue; - } - - if (ref === refToFind && link.source.resource.fsPath === from.resource.fsPath) { - const isTriggerLocation = from.resource.fsPath === link.source.resource.fsPath && ( - (link.href.kind === 'reference' && from.range.isEqual(link.source.hrefRange)) || (link.kind === 'definition' && from.range.isEqual(link.ref.range))); - - const pathRange = this.getPathRange(link); - yield { - kind: 'link', - isTriggerLocation, - isDefinition: link.kind === 'definition', - link, - location: new vscode.Location(from.resource, pathRange), - }; - } - } - } - - /** - * Get just the range of the file path, dropping the fragment - */ - private getPathRange(link: MdLink): vscode.Range { - return link.source.fragmentRange - ? link.source.hrefRange.with(undefined, link.source.fragmentRange.start.translate(0, -1)) - : link.source.hrefRange; - } -} - -export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise { - const targetDoc = await workspaceContents.getMarkdownDocument(href.path); - if (targetDoc) { - return targetDoc; - } - - // We don't think the file exists. If it doesn't already have an extension, try tacking on a `.md` and using that instead - if (uri.Utils.extname(href.path) === '') { - const dotMdResource = href.path.with({ path: href.path.path + '.md' }); - return workspaceContents.getMarkdownDocument(dotMdResource); - } - - return undefined; -} - diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts deleted file mode 100644 index 398ef6486b..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ /dev/null @@ -1,272 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import * as URI from 'vscode-uri'; -import { Slugifier } from '../slugify'; -import { Disposable } from '../util/dispose'; -import { resolveDocumentLink } from '../util/openDocumentLink'; -import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { InternalHref } from './documentLinkProvider'; -import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; - -const localize = nls.loadMessageBundle(); - - -export interface MdReferencesResponse { - references: MdReference[]; - triggerRef: MdReference; -} - -interface MdFileRenameEdit { - readonly from: vscode.Uri; - readonly to: vscode.Uri; -} - -/** - * Type with additional metadata about the edits for testing - * - * This is needed since `vscode.WorkspaceEdit` does not expose info on file renames. - */ -export interface MdWorkspaceEdit { - readonly edit: vscode.WorkspaceEdit; - - readonly fileRenames?: ReadonlyArray; -} - -function tryDecodeUri(str: string): string { - try { - return decodeURI(str); - } catch { - return str; - } -} - -export class MdRenameProvider extends Disposable implements vscode.RenameProvider { - - private cachedRefs?: { - readonly resource: vscode.Uri; - readonly version: number; - readonly position: vscode.Position; - readonly triggerRef: MdReference; - readonly references: MdReference[]; - } | undefined; - - private readonly renameNotSupportedText = localize('invalidRenameLocation', "Rename not supported at location"); - - public constructor( - private readonly referencesProvider: MdReferencesProvider, - private readonly workspaceContents: MdWorkspaceContents, - private readonly slugifier: Slugifier, - ) { - super(); - } - - public async prepareRename(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const allRefsInfo = await this.getAllReferences(document, position, token); - if (token.isCancellationRequested) { - return undefined; - } - - if (!allRefsInfo || !allRefsInfo.references.length) { - throw new Error(this.renameNotSupportedText); - } - - const triggerRef = allRefsInfo.triggerRef; - switch (triggerRef.kind) { - case 'header': { - return { range: triggerRef.headerTextLocation.range, placeholder: triggerRef.headerText }; - } - case 'link': { - if (triggerRef.link.kind === 'definition') { - // We may have been triggered on the ref or the definition itself - if (triggerRef.link.ref.range.contains(position)) { - return { range: triggerRef.link.ref.range, placeholder: triggerRef.link.ref.text }; - } - } - - if (triggerRef.link.href.kind === 'external') { - return { range: triggerRef.link.source.hrefRange, placeholder: document.getText(triggerRef.link.source.hrefRange) }; - } - - // See if we are renaming the fragment or the path - const { fragmentRange } = triggerRef.link.source; - if (fragmentRange?.contains(position)) { - const declaration = this.findHeaderDeclaration(allRefsInfo.references); - if (declaration) { - return { range: fragmentRange, placeholder: declaration.headerText }; - } - return { range: fragmentRange, placeholder: document.getText(fragmentRange) }; - } - - const range = this.getFilePathRange(triggerRef); - if (!range) { - throw new Error(this.renameNotSupportedText); - } - return { range, placeholder: tryDecodeUri(document.getText(range)) }; - } - } - } - - private getFilePathRange(ref: MdLinkReference): vscode.Range { - if (ref.link.source.fragmentRange) { - return ref.link.source.hrefRange.with(undefined, ref.link.source.fragmentRange.start.translate(0, -1)); - } - return ref.link.source.hrefRange; - } - - private findHeaderDeclaration(references: readonly MdReference[]): MdHeaderReference | undefined { - return references.find(ref => ref.isDefinition && ref.kind === 'header') as MdHeaderReference | undefined; - } - - public async provideRenameEdits(document: SkinnyTextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise { - return (await this.provideRenameEditsImpl(document, position, newName, token))?.edit; - } - - public async provideRenameEditsImpl(document: SkinnyTextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise { - const allRefsInfo = await this.getAllReferences(document, position, token); - if (token.isCancellationRequested || !allRefsInfo || !allRefsInfo.references.length) { - return undefined; - } - - const triggerRef = allRefsInfo.triggerRef; - - if (triggerRef.kind === 'link' && ( - (triggerRef.link.kind === 'definition' && triggerRef.link.ref.range.contains(position)) || triggerRef.link.href.kind === 'reference' - )) { - return this.renameReferenceLinks(allRefsInfo, newName); - } else if (triggerRef.kind === 'link' && triggerRef.link.href.kind === 'external') { - return this.renameExternalLink(allRefsInfo, newName); - } else if (triggerRef.kind === 'header' || (triggerRef.kind === 'link' && triggerRef.link.source.fragmentRange?.contains(position) && (triggerRef.link.kind === 'definition' || triggerRef.link.kind === 'link' && triggerRef.link.href.kind === 'internal'))) { - return this.renameFragment(allRefsInfo, newName); - } else if (triggerRef.kind === 'link' && !triggerRef.link.source.fragmentRange?.contains(position) && triggerRef.link.kind === 'link' && triggerRef.link.href.kind === 'internal') { - return this.renameFilePath(triggerRef.link.source.resource, triggerRef.link.href, allRefsInfo, newName); - } - - return undefined; - } - - private async renameFilePath(triggerDocument: vscode.Uri, triggerHref: InternalHref, allRefsInfo: MdReferencesResponse, newName: string): Promise { - const edit = new vscode.WorkspaceEdit(); - const fileRenames: MdFileRenameEdit[] = []; - - const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); - const targetUri = targetDoc?.uri ?? triggerHref.path; - - const rawNewFilePath = resolveDocumentLink(newName, triggerDocument); - let resolvedNewFilePath = rawNewFilePath; - if (!URI.Utils.extname(resolvedNewFilePath)) { - // If the newly entered path doesn't have a file extension but the original file did - // tack on a .md file extension - if (URI.Utils.extname(targetUri)) { - resolvedNewFilePath = resolvedNewFilePath.with({ - path: resolvedNewFilePath.path + '.md' - }); - } - } - - // First rename the file - if (await this.workspaceContents.pathExists(targetUri)) { - fileRenames.push({ from: targetUri, to: resolvedNewFilePath }); - edit.renameFile(targetUri, resolvedNewFilePath); - } - - // Then update all refs to it - for (const ref of allRefsInfo.references) { - if (ref.kind === 'link') { - // Try to preserve style of existing links - let newPath: string; - if (ref.link.source.text.startsWith('/')) { - const root = resolveDocumentLink('/', ref.link.source.resource); - newPath = '/' + path.relative(root.toString(true), rawNewFilePath.toString(true)); - } else { - const rootDir = URI.Utils.dirname(ref.link.source.resource); - if (rootDir.scheme === rawNewFilePath.scheme && rootDir.scheme !== 'untitled') { - newPath = path.relative(rootDir.toString(true), rawNewFilePath.toString(true)); - if (newName.startsWith('./') && !newPath.startsWith('../') || newName.startsWith('.\\') && !newPath.startsWith('..\\')) { - newPath = './' + newPath; - } - } else { - newPath = newName; - } - } - edit.replace(ref.link.source.resource, this.getFilePathRange(ref), encodeURI(newPath.replace(/\\/g, '/'))); - } - } - - return { edit, fileRenames }; - } - - private renameFragment(allRefsInfo: MdReferencesResponse, newName: string): MdWorkspaceEdit { - const slug = this.slugifier.fromHeading(newName).value; - - const edit = new vscode.WorkspaceEdit(); - for (const ref of allRefsInfo.references) { - switch (ref.kind) { - case 'header': - edit.replace(ref.location.uri, ref.headerTextLocation.range, newName); - break; - - case 'link': - edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, !ref.link.source.fragmentRange || ref.link.href.kind === 'external' ? newName : slug); - break; - } - } - return { edit }; - } - - private renameExternalLink(allRefsInfo: MdReferencesResponse, newName: string): MdWorkspaceEdit { - const edit = new vscode.WorkspaceEdit(); - for (const ref of allRefsInfo.references) { - if (ref.kind === 'link') { - edit.replace(ref.link.source.resource, ref.location.range, newName); - } - } - return { edit }; - } - - private renameReferenceLinks(allRefsInfo: MdReferencesResponse, newName: string): MdWorkspaceEdit { - const edit = new vscode.WorkspaceEdit(); - for (const ref of allRefsInfo.references) { - if (ref.kind === 'link') { - if (ref.link.kind === 'definition') { - edit.replace(ref.link.source.resource, ref.link.ref.range, newName); - } else { - edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, newName); - } - } - } - return { edit }; - } - - private async getAllReferences(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const version = document.version; - - if (this.cachedRefs - && this.cachedRefs.resource.fsPath === document.uri.fsPath - && this.cachedRefs.version === document.version - && this.cachedRefs.position.isEqual(position) - ) { - return this.cachedRefs; - } - - const references = await this.referencesProvider.getAllReferencesAtPosition(document, position, token); - const triggerRef = references.find(ref => ref.isTriggerLocation); - if (!triggerRef) { - return undefined; - } - - this.cachedRefs = { - resource: document.uri, - version, - position, - references, - triggerRef - }; - return this.cachedRefs; - } -} - diff --git a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts deleted file mode 100644 index 275677f769..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts +++ /dev/null @@ -1,251 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import Token = require('markdown-it/lib/token'); -import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; -import { SkinnyTextDocument } from '../workspaceContents'; - -interface MarkdownItTokenWithMap extends Token { - map: [number, number]; -} - -export class MdSmartSelect implements vscode.SelectionRangeProvider { - - constructor( - private readonly engine: MarkdownEngine - ) { } - - public async provideSelectionRanges(document: SkinnyTextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise { - const promises = await Promise.all(positions.map((position) => { - return this.provideSelectionRange(document, position, _token); - })); - return promises.filter(item => item !== undefined) as vscode.SelectionRange[]; - } - - private async provideSelectionRange(document: SkinnyTextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise { - const headerRange = await this.getHeaderSelectionRange(document, position); - const blockRange = await this.getBlockSelectionRange(document, position, headerRange); - const inlineRange = await this.getInlineSelectionRange(document, position, blockRange); - return inlineRange || blockRange || headerRange; - } - private async getInlineSelectionRange(document: SkinnyTextDocument, position: vscode.Position, blockRange?: vscode.SelectionRange): Promise { - return createInlineRange(document, position, blockRange); - } - - private async getBlockSelectionRange(document: SkinnyTextDocument, position: vscode.Position, headerRange?: vscode.SelectionRange): Promise { - - const tokens = await this.engine.parse(document); - - const blockTokens = getBlockTokensForPosition(tokens, position, headerRange); - - if (blockTokens.length === 0) { - return undefined; - } - - let currentRange: vscode.SelectionRange | undefined = headerRange ? headerRange : createBlockRange(blockTokens.shift()!, document, position.line); - - for (let i = 0; i < blockTokens.length; i++) { - currentRange = createBlockRange(blockTokens[i], document, position.line, currentRange); - } - return currentRange; - } - - private async getHeaderSelectionRange(document: SkinnyTextDocument, position: vscode.Position): Promise { - const toc = await TableOfContents.create(this.engine, document); - - const headerInfo = getHeadersForPosition(toc.entries, position); - - const headers = headerInfo.headers; - - let currentRange: vscode.SelectionRange | undefined; - - for (let i = 0; i < headers.length; i++) { - currentRange = createHeaderRange(headers[i], i === headers.length - 1, headerInfo.headerOnThisLine, currentRange, getFirstChildHeader(document, headers[i], toc.entries)); - } - return currentRange; - } -} - -function getHeadersForPosition(toc: readonly TocEntry[], position: vscode.Position): { headers: TocEntry[]; headerOnThisLine: boolean } { - const enclosingHeaders = toc.filter(header => header.sectionLocation.range.start.line <= position.line && header.sectionLocation.range.end.line >= position.line); - const sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line)); - const onThisLine = toc.find(header => header.line === position.line) !== undefined; - return { - headers: sortedHeaders, - headerOnThisLine: onThisLine - }; -} - -function createHeaderRange(header: TocEntry, isClosestHeaderToPosition: boolean, onHeaderLine: boolean, parent?: vscode.SelectionRange, startOfChildRange?: vscode.Position): vscode.SelectionRange | undefined { - const range = header.sectionLocation.range; - const contentRange = new vscode.Range(range.start.translate(1), range.end); - if (onHeaderLine && isClosestHeaderToPosition && startOfChildRange) { - // selection was made on this header line, so select header and its content until the start of its first child - // then all of its content - return new vscode.SelectionRange(range.with(undefined, startOfChildRange), new vscode.SelectionRange(range, parent)); - } else if (onHeaderLine && isClosestHeaderToPosition) { - // selection was made on this header line and no children so expand to all of its content - return new vscode.SelectionRange(range, parent); - } else if (isClosestHeaderToPosition && startOfChildRange) { - // selection was made within content and has child so select content - // of this header then all content then header - return new vscode.SelectionRange(contentRange.with(undefined, startOfChildRange), new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(range, parent)))); - } else { - // not on this header line so select content then header - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(range, parent)); - } -} - -function getBlockTokensForPosition(tokens: Token[], position: vscode.Position, parent?: vscode.SelectionRange): MarkdownItTokenWithMap[] { - const enclosingTokens = tokens.filter((token): token is MarkdownItTokenWithMap => !!token.map && (token.map[0] <= position.line && token.map[1] > position.line) && (!parent || (token.map[0] >= parent.range.start.line && token.map[1] <= parent.range.end.line + 1)) && isBlockElement(token)); - if (enclosingTokens.length === 0) { - return []; - } - const sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0])); - return sortedTokens; -} - -function createBlockRange(block: MarkdownItTokenWithMap, document: SkinnyTextDocument, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - if (block.type === 'fence') { - return createFencedRange(block, cursorLine, document, parent); - } else { - let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0]; - let endLine = startLine === block.map[1] ? block.map[1] : block.map[1] - 1; - if (block.type === 'paragraph_open' && block.map[1] - block.map[0] === 2) { - startLine = endLine = cursorLine; - } else if (isList(block) && document.lineAt(endLine).isEmptyOrWhitespace) { - endLine = endLine - 1; - } - const range = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text?.length ?? 0); - if (parent?.range.contains(range) && !parent.range.isEqual(range)) { - return new vscode.SelectionRange(range, parent); - } else if (parent?.range.isEqual(range)) { - return parent; - } else { - return new vscode.SelectionRange(range); - } - } -} - -function createInlineRange(document: SkinnyTextDocument, cursorPosition: vscode.Position, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - const lineText = document.lineAt(cursorPosition.line).text; - const boldSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, parent); - const italicSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, parent); - let comboSelection: vscode.SelectionRange | undefined; - if (boldSelection && italicSelection && !boldSelection.range.isEqual(italicSelection.range)) { - if (boldSelection.range.contains(italicSelection.range)) { - comboSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, true, boldSelection); - } else if (italicSelection.range.contains(boldSelection.range)) { - comboSelection = createBoldRange(lineText, cursorPosition.character, cursorPosition.line, italicSelection); - } - } - const linkSelection = createLinkRange(lineText, cursorPosition.character, cursorPosition.line, comboSelection || boldSelection || italicSelection || parent); - const inlineCodeBlockSelection = createOtherInlineRange(lineText, cursorPosition.character, cursorPosition.line, false, linkSelection || parent); - return inlineCodeBlockSelection || linkSelection || comboSelection || boldSelection || italicSelection; -} - -function createFencedRange(token: MarkdownItTokenWithMap, cursorLine: number, document: SkinnyTextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange { - const startLine = token.map[0]; - const endLine = token.map[1] - 1; - const onFenceLine = cursorLine === startLine || cursorLine === endLine; - const fenceRange = new vscode.Range(startLine, 0, endLine, document.lineAt(endLine).text.length); - const contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(startLine + 1, 0, endLine - 1, document.lineAt(endLine - 1).text.length) : undefined; - if (contentRange) { - return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent)); - } else { - if (parent?.range.isEqual(fenceRange)) { - return parent; - } else { - return new vscode.SelectionRange(fenceRange, parent); - } - } -} - -function createBoldRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - const regex = /\*\*([^*]+\*?[^*]+\*?[^*]+)\*\*/gim; - const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); - if (matches.length) { - // should only be one match, so select first and index 0 contains the entire match - const bold = matches[0][0]; - const startIndex = lineText.indexOf(bold); - const cursorOnStars = cursorChar === startIndex || cursorChar === startIndex + 1 || cursorChar === startIndex + bold.length || cursorChar === startIndex + bold.length - 1; - const contentAndStars = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + bold.length), parent); - const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 2, cursorLine, startIndex + bold.length - 2), contentAndStars); - return cursorOnStars ? contentAndStars : content; - } - return undefined; -} - -function createOtherInlineRange(lineText: string, cursorChar: number, cursorLine: number, isItalic: boolean, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - const italicRegexes = [/(?:[^*]+)(\*([^*]+)(?:\*\*[^*]*\*\*)*([^*]+)\*)(?:[^*]+)/g, /^(?:[^*]*)(\*([^*]+)(?:\*\*[^*]*\*\*)*([^*]+)\*)(?:[^*]*)$/g]; - let matches = []; - if (isItalic) { - matches = [...lineText.matchAll(italicRegexes[0])].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); - if (!matches.length) { - matches = [...lineText.matchAll(italicRegexes[1])].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); - } - } else { - matches = [...lineText.matchAll(/\`[^\`]*\`/g)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length >= cursorChar); - } - if (matches.length) { - // should only be one match, so select first and select group 1 for italics because that contains just the italic section - // doesn't include the leading and trailing characters which are guaranteed to not be * so as not to be confused with bold - const match = isItalic ? matches[0][1] : matches[0][0]; - const startIndex = lineText.indexOf(match); - const cursorOnType = cursorChar === startIndex || cursorChar === startIndex + match.length; - const contentAndType = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex, cursorLine, startIndex + match.length), parent); - const content = new vscode.SelectionRange(new vscode.Range(cursorLine, startIndex + 1, cursorLine, startIndex + match.length - 1), contentAndType); - return cursorOnType ? contentAndType : content; - } - return undefined; -} - -function createLinkRange(lineText: string, cursorChar: number, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined { - const regex = /(\[[^\(\)]*\])(\([^\[\]]*\))/g; - const matches = [...lineText.matchAll(regex)].filter(match => lineText.indexOf(match[0]) <= cursorChar && lineText.indexOf(match[0]) + match[0].length > cursorChar); - - if (matches.length) { - // should only be one match, so select first and index 0 contains the entire match, so match = [text](url) - const link = matches[0][0]; - const linkRange = new vscode.SelectionRange(new vscode.Range(cursorLine, lineText.indexOf(link), cursorLine, lineText.indexOf(link) + link.length), parent); - - const linkText = matches[0][1]; - const url = matches[0][2]; - - // determine if cursor is within [text] or (url) in order to know which should be selected - const nearestType = cursorChar >= lineText.indexOf(linkText) && cursorChar < lineText.indexOf(linkText) + linkText.length ? linkText : url; - - const indexOfType = lineText.indexOf(nearestType); - // determine if cursor is on a bracket or paren and if so, return the [content] or (content), skipping over the content range - const cursorOnType = cursorChar === indexOfType || cursorChar === indexOfType + nearestType.length; - - const contentAndNearestType = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType, cursorLine, indexOfType + nearestType.length), linkRange); - const content = new vscode.SelectionRange(new vscode.Range(cursorLine, indexOfType + 1, cursorLine, indexOfType + nearestType.length - 1), contentAndNearestType); - return cursorOnType ? contentAndNearestType : content; - } - return undefined; -} - -function isList(token: Token): boolean { - return token.type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(token.type) : false; -} - -function isBlockElement(token: Token): boolean { - return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type); -} - -function getFirstChildHeader(document: SkinnyTextDocument, header?: TocEntry, toc?: readonly TocEntry[]): vscode.Position | undefined { - let childRange: vscode.Position | undefined; - if (header && toc) { - let children = toc.filter(t => header.sectionLocation.range.contains(t.sectionLocation.range) && t.sectionLocation.range.start.line > header.sectionLocation.range.start.line).sort((t1, t2) => t1.line - t2.line); - if (children.length > 0) { - childRange = children[0].sectionLocation.range.start; - const lineText = document.lineAt(childRange.line - 1).text; - return childRange ? childRange.translate(-1, lineText.length) : undefined; - } - } - return undefined; -} diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts deleted file mode 100644 index 63ac22ee40..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Disposable } from '../util/dispose'; -import { Lazy, lazy } from '../util/lazy'; -import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; - -/** - * Cache of information for markdown files in the workspace. - */ -export class MdWorkspaceCache extends Disposable { - - private readonly _cache = new Map>>(); - private _hasPopulatedCache = false; - - public constructor( - private readonly workspaceContents: MdWorkspaceContents, - private readonly getValue: (document: SkinnyTextDocument) => Promise, - ) { - super(); - } - - public async getAll(): Promise { - if (!this._hasPopulatedCache) { - await this.populateCache(); - this._hasPopulatedCache = true; - - this.workspaceContents.onDidChangeMarkdownDocument(this.onDidChangeDocument, this, this._disposables); - this.workspaceContents.onDidCreateMarkdownDocument(this.onDidChangeDocument, this, this._disposables); - this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this, this._disposables); - } - - return Promise.all(Array.from(this._cache.values(), x => x.value)); - } - - private async populateCache(): Promise { - const markdownDocumentUris = await this.workspaceContents.getAllMarkdownDocuments(); - for (const document of markdownDocumentUris) { - this.update(document); - } - } - - private key(resource: vscode.Uri): string { - return resource.toString(); - } - - private update(document: SkinnyTextDocument): void { - this._cache.set(this.key(document.uri), lazy(() => this.getValue(document))); - } - - private onDidChangeDocument(document: SkinnyTextDocument) { - this.update(document); - } - - private onDidDeleteDocument(resource: vscode.Uri) { - this._cache.delete(this.key(resource)); - } -} diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts deleted file mode 100644 index 3bd582dfc7..0000000000 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Disposable } from '../util/dispose'; -import { MdWorkspaceContents } from '../workspaceContents'; -import { MdDocumentSymbolProvider } from './documentSymbolProvider'; -import { MdWorkspaceCache } from './workspaceCache'; - -export class MdWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider { - - private readonly _cache: MdWorkspaceCache; - - public constructor( - symbolProvider: MdDocumentSymbolProvider, - workspaceContents: MdWorkspaceContents, - ) { - super(); - - this._cache = this._register(new MdWorkspaceCache(workspaceContents, doc => symbolProvider.provideDocumentSymbolInformation(doc))); - } - - public async provideWorkspaceSymbols(query: string): Promise { - const allSymbols = (await this._cache.getAll()).flat(); - return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); - } -} diff --git a/extensions/markdown-language-features/src/logger.ts b/extensions/markdown-language-features/src/logging.ts similarity index 59% rename from extensions/markdown-language-features/src/logger.ts rename to extensions/markdown-language-features/src/logging.ts index 4f1bddb09f..4273b4bba6 100644 --- a/extensions/markdown-language-features/src/logger.ts +++ b/extensions/markdown-language-features/src/logging.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { Disposable } from './util/dispose'; import { lazy } from './util/lazy'; enum Trace { @@ -25,57 +26,61 @@ namespace Trace { } } - -function isString(value: any): value is string { - return Object.prototype.toString.call(value) === '[object String]'; +export interface ILogger { + verbose(title: string, message: string, data?: any): void; } -export class Logger { +export class VsCodeOutputLogger extends Disposable implements ILogger { private trace?: Trace; - private readonly outputChannel = lazy(() => vscode.window.createOutputChannel('Markdown')); + private readonly outputChannel = lazy(() => this._register(vscode.window.createOutputChannel('Markdown'))); constructor() { + super(); + + this._register(vscode.workspace.onDidChangeConfiguration(() => { + this.updateConfiguration(); + })); + this.updateConfiguration(); } - public log(message: string, data?: any): void { + public verbose(title: string, message: string, data?: any): void { if (this.trace === Trace.Verbose) { - this.appendLine(`[Log - ${this.now()}] ${message}`); + this.appendLine(`[Verbose ${this.now()}] ${title}: ${message}`); if (data) { - this.appendLine(Logger.data2String(data)); + this.appendLine(VsCodeOutputLogger.data2String(data)); } } } - private now(): string { const now = new Date(); return String(now.getUTCHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0') - + ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + now.getMilliseconds(); + + ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0'); } - public updateConfiguration() { + private updateConfiguration(): void { this.trace = this.readTrace(); } - private appendLine(value: string) { - return this.outputChannel.value.appendLine(value); + private appendLine(value: string): void { + this.outputChannel.value.appendLine(value); } private readTrace(): Trace { - return Trace.fromString(vscode.workspace.getConfiguration().get('markdown.trace', 'off')); + return Trace.fromString(vscode.workspace.getConfiguration().get('markdown.trace.extension', 'off')); } private static data2String(data: any): string { if (data instanceof Error) { - if (isString(data.stack)) { + if (typeof data.stack === 'string') { return data.stack; } - return (data as Error).message; + return data.message; } - if (isString(data)) { + if (typeof data === 'string') { return data; } return JSON.stringify(data, undefined, 2); diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 3e3f5d660a..0a521d5046 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -3,15 +3,19 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import MarkdownIt = require('markdown-it'); -import Token = require('markdown-it/lib/token'); +import type MarkdownIt = require('markdown-it'); +import type Token = require('markdown-it/lib/token'); import * as vscode from 'vscode'; +import { ILogger } from './logging'; import { MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; +import { ITextDocument } from './types/textDocument'; +import { Disposable } from './util/dispose'; import { stringHash } from './util/hash'; import { WebviewResourceProvider } from './util/resources'; import { isOfScheme, Schemes } from './util/schemes'; -import { SkinnyTextDocument } from './workspaceContents'; +import { MdDocumentInfoCache } from './util/workspaceCache'; +import { IMdWorkspace } from './workspace'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; @@ -53,7 +57,7 @@ class TokenCache { }; private tokens?: Token[]; - public tryGetCached(document: SkinnyTextDocument, config: MarkdownItConfig): Token[] | undefined { + public tryGetCached(document: ITextDocument, config: MarkdownItConfig): Token[] | undefined { if (this.cachedDocument && this.cachedDocument.uri.toString() === document.uri.toString() && this.cachedDocument.version === document.version @@ -65,7 +69,7 @@ class TokenCache { return undefined; } - public update(document: SkinnyTextDocument, config: MarkdownItConfig, tokens: Token[]) { + public update(document: ITextDocument, config: MarkdownItConfig, tokens: Token[]) { this.cachedDocument = { uri: document.uri, version: document.version, @@ -91,17 +95,28 @@ interface RenderEnv { resourceProvider: WebviewResourceProvider | undefined; } -export class MarkdownEngine { +export interface IMdParser { + readonly slugifier: Slugifier; + + tokenize(document: ITextDocument): Promise; +} + +export class MarkdownItEngine implements IMdParser { private md?: Promise; private _slugCount = new Map(); private _tokenCache = new TokenCache(); + public readonly slugifier: Slugifier; + public constructor( private readonly contributionProvider: MarkdownContributionProvider, - private readonly slugifier: Slugifier, + slugifier: Slugifier, + private readonly logger: ILogger, ) { + this.slugifier = slugifier; + contributionProvider.onContributionsChanged(() => { // Markdown plugin contributions may have changed this.md = undefined; @@ -159,7 +174,7 @@ export class MarkdownEngine { } private tokenizeDocument( - document: SkinnyTextDocument, + document: ITextDocument, config: MarkdownItConfig, engine: MarkdownIt ): Token[] { @@ -169,6 +184,7 @@ export class MarkdownEngine { return cached; } + this.logger.verbose('MarkdownItEngine', `tokenizeDocument - ${document.uri}`); const tokens = this.tokenizeString(document.getText(), engine); this._tokenCache.update(document, config, tokens); return tokens; @@ -184,7 +200,7 @@ export class MarkdownEngine { this._slugCount = new Map(); } - public async render(input: SkinnyTextDocument | string, resourceProvider?: WebviewResourceProvider): Promise { + public async render(input: ITextDocument | string, resourceProvider?: WebviewResourceProvider): Promise { const config = this.getConfig(typeof input === 'string' ? undefined : input.uri); const engine = await this.getEngine(config); @@ -209,7 +225,7 @@ export class MarkdownEngine { }; } - public async parse(document: SkinnyTextDocument): Promise { + public async tokenize(document: ITextDocument): Promise { const config = this.getConfig(document.uri); const engine = await this.getEngine(config); return this.tokenizeDocument(document, config, engine); @@ -423,3 +439,27 @@ function normalizeHighlightLang(lang: string | undefined) { return lang; } } + +export class MdParsingProvider extends Disposable implements IMdParser { + + private readonly _cache: MdDocumentInfoCache; + + public readonly slugifier: Slugifier; + + constructor( + engine: MarkdownItEngine, + workspace: IMdWorkspace, + ) { + super(); + + this.slugifier = engine.slugifier; + + this._cache = this._register(new MdDocumentInfoCache(workspace, doc => { + return engine.tokenize(doc); + })); + } + + public tokenize(document: ITextDocument): Promise { + return this._cache.getForDocument(document); + } +} diff --git a/extensions/markdown-language-features/src/preview/previewContentProvider.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts similarity index 91% rename from extensions/markdown-language-features/src/preview/previewContentProvider.ts rename to extensions/markdown-language-features/src/preview/documentRenderer.ts index 74621d404a..3c8ca0afdf 100644 --- a/extensions/markdown-language-features/src/preview/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -6,9 +6,10 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as uri from 'vscode-uri'; -import { Logger } from '../logger'; -import { MarkdownEngine } from '../markdownEngine'; +import { ILogger } from '../logging'; +import { MarkdownItEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; +import { escapeAttribute, getNonce } from '../util/dom'; import { WebviewResourceProvider } from '../util/resources'; import { MarkdownPreviewConfiguration, MarkdownPreviewConfigurationManager } from './previewConfig'; import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from './security'; @@ -35,23 +36,19 @@ const previewStrings = { 'Content Disabled Security Warning') }; -function escapeAttribute(value: string | vscode.Uri): string { - return value.toString().replace(/"/g, '"'); -} - export interface MarkdownContentProviderOutput { html: string; containingImages: { src: string }[]; } -export class MarkdownContentProvider { +export class MdDocumentRenderer { constructor( - private readonly engine: MarkdownEngine, + private readonly engine: MarkdownItEngine, private readonly context: vscode.ExtensionContext, private readonly cspArbiter: ContentSecurityPolicyArbiter, private readonly contributionProvider: MarkdownContributionProvider, - private readonly logger: Logger + private readonly logger: ILogger ) { this.iconPath = { dark: vscode.Uri.joinPath(this.context.extensionUri, 'media', 'preview-dark.svg'), @@ -61,12 +58,13 @@ export class MarkdownContentProvider { public readonly iconPath: { light: vscode.Uri; dark: vscode.Uri }; - public async provideTextDocumentContent( + public async renderDocument( markdownDocument: vscode.TextDocument, resourceProvider: WebviewResourceProvider, previewConfigurations: MarkdownPreviewConfigurationManager, initialLine: number | undefined = undefined, - state?: any + state: any | undefined, + token: vscode.CancellationToken ): Promise { const sourceUri = markdownDocument.uri; const config = previewConfigurations.loadAndCacheConfiguration(sourceUri); @@ -82,13 +80,17 @@ export class MarkdownContentProvider { webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(), }; - this.logger.log('provideTextDocumentContent', initialData); + this.logger.verbose('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); // Content Security Policy const nonce = getNonce(); const csp = this.getCsp(resourceProvider, sourceUri, nonce); - const body = await this.markdownBody(markdownDocument, resourceProvider); + const body = await this.renderBody(markdownDocument, resourceProvider); + if (token.isCancellationRequested) { + return { html: '', containingImages: [] }; + } + const html = ` @@ -113,7 +115,7 @@ export class MarkdownContentProvider { }; } - public async markdownBody( + public async renderBody( markdownDocument: vscode.TextDocument, resourceProvider: WebviewResourceProvider, ): Promise { @@ -125,9 +127,7 @@ export class MarkdownContentProvider { }; } - public provideFileNotFoundContent( - resource: vscode.Uri, - ): string { + public renderFileNotFoundDocument(resource: vscode.Uri): string { const resourcePath = uri.Utils.basename(resource); const body = localize('preview.notFound', '{0} cannot be found', resourcePath); return ` @@ -246,12 +246,3 @@ export class MarkdownContentProvider { } } } - -function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 64; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index 665cb3d73b..ff7c4138bd 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -6,16 +6,17 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as uri from 'vscode-uri'; -import { Logger } from '../logger'; -import { MarkdownEngine } from '../markdownEngine'; +import { ILogger } from '../logging'; import { MarkdownContributionProvider } from '../markdownExtensions'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; import { openDocumentLink, resolveDocumentLink, resolveUriToMarkdownFile } from '../util/openDocumentLink'; import { WebviewResourceProvider } from '../util/resources'; import { urlToUri } from '../util/url'; +import { IMdWorkspace } from '../workspace'; +import { MdDocumentRenderer } from './documentRenderer'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContentProvider } from './previewContentProvider'; import { scrollEditorToLine, StartingScrollFragment, StartingScrollLine, StartingScrollLocation } from './scrolling'; import { getVisibleLine, LastScrollLocation, TopmostLineMonitor } from './topmostLineMonitor'; @@ -109,16 +110,19 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { private readonly _onScrollEmitter = this._register(new vscode.EventEmitter()); public readonly onScroll = this._onScrollEmitter.event; + private readonly _disposeCts = this._register(new vscode.CancellationTokenSource()); + constructor( webview: vscode.WebviewPanel, resource: vscode.Uri, startingScroll: StartingScrollLocation | undefined, private readonly delegate: MarkdownPreviewDelegate, - private readonly engine: MarkdownEngine, - private readonly _contentProvider: MarkdownContentProvider, + private readonly _contentProvider: MdDocumentRenderer, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, - private readonly _logger: Logger, + private readonly _workspace: IMdWorkspace, + private readonly _logger: ILogger, private readonly _contributionProvider: MarkdownContributionProvider, + private readonly _tocProvider: MdTableOfContentsProvider, ) { super(); @@ -202,6 +206,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } override dispose() { + this._disposeCts.cancel(); + super.dispose(); this._disposed = true; @@ -265,7 +271,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { return; } - this._logger.log('updateForView', { markdownFile: this._resource }); + this._logger.verbose('MarkdownPreview', 'updateForView', { markdownFile: this._resource }); this.line = topLine; this.postMessage({ type: 'updateView', @@ -286,7 +292,9 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { try { document = await vscode.workspace.openTextDocument(this._resource); } catch { - await this.showFileNotFoundError(); + if (!this._disposed) { + await this.showFileNotFoundError(); + } return; } @@ -306,8 +314,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { this.currentVersion = pendingVersion; const content = await (shouldReloadPage - ? this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state) - : this._contentProvider.markdownBody(document, this)); + ? this._contentProvider.renderDocument(document, this, this._previewConfigurations, this.line, this.state, this._disposeCts.token) + : this._contentProvider.renderBody(document, this)); // Another call to `doUpdate` may have happened. // Make sure we are still updating for the correct document @@ -364,7 +372,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } private async showFileNotFoundError() { - this._webviewPanel.webview.html = this._contentProvider.provideFileNotFoundContent(this._resource); + this._webviewPanel.webview.html = this._contentProvider.renderFileNotFoundDocument(this._resource); } private updateWebviewContent(html: string, reloadPage: boolean): void { @@ -443,14 +451,14 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { const config = vscode.workspace.getConfiguration('markdown', this.resource); const openLinks = config.get('preview.openMarkdownLinks', 'inPreview'); if (openLinks === 'inPreview') { - const linkedDoc = await resolveUriToMarkdownFile(targetResource); + const linkedDoc = await resolveUriToMarkdownFile(this._workspace, targetResource); if (linkedDoc) { this.delegate.openPreviewLinkToMarkdownFile(linkedDoc.uri, targetResource.fragment); return; } } - return openDocumentLink(this.engine, targetResource, this.resource); + return openDocumentLink(this._tocProvider, targetResource, this.resource); } //#region WebviewResourceProvider @@ -466,7 +474,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { //#endregion } -export interface ManagedMarkdownPreview { +export interface IManagedMarkdownPreview { readonly resource: vscode.Uri; readonly resourceColumn: vscode.ViewColumn; @@ -486,22 +494,23 @@ export interface ManagedMarkdownPreview { ): boolean; } -export class StaticMarkdownPreview extends Disposable implements ManagedMarkdownPreview { +export class StaticMarkdownPreview extends Disposable implements IManagedMarkdownPreview { public static readonly customEditorViewType = 'vscode.markdown.preview.editor'; public static revive( resource: vscode.Uri, webview: vscode.WebviewPanel, - contentProvider: MarkdownContentProvider, + contentProvider: MdDocumentRenderer, previewConfigurations: MarkdownPreviewConfigurationManager, topmostLineMonitor: TopmostLineMonitor, - logger: Logger, + workspace: IMdWorkspace, + logger: ILogger, contributionProvider: MarkdownContributionProvider, - engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, scrollLine?: number, ): StaticMarkdownPreview { - return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, topmostLineMonitor, logger, contributionProvider, engine, scrollLine); + return new StaticMarkdownPreview(webview, resource, contentProvider, previewConfigurations, topmostLineMonitor, workspace, logger, contributionProvider, tocProvider, scrollLine); } private readonly preview: MarkdownPreview; @@ -509,12 +518,13 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown private constructor( private readonly _webviewPanel: vscode.WebviewPanel, resource: vscode.Uri, - contentProvider: MarkdownContentProvider, + contentProvider: MdDocumentRenderer, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, topmostLineMonitor: TopmostLineMonitor, - logger: Logger, + workspace: IMdWorkspace, + logger: ILogger, contributionProvider: MarkdownContributionProvider, - engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, scrollLine?: number, ) { super(); @@ -526,7 +536,7 @@ export class StaticMarkdownPreview extends Disposable implements ManagedMarkdown fragment }), StaticMarkdownPreview.customEditorViewType, this._webviewPanel.viewColumn); } - }, engine, contentProvider, _previewConfigurations, logger, contributionProvider)); + }, contentProvider, _previewConfigurations, workspace, logger, contributionProvider, tocProvider)); this._register(this._webviewPanel.onDidDispose(() => { this.dispose(); @@ -592,7 +602,7 @@ interface DynamicPreviewInput { readonly line?: number; } -export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdownPreview { +export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdownPreview { public static readonly viewType = 'markdown.preview'; @@ -605,28 +615,30 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow public static revive( input: DynamicPreviewInput, webview: vscode.WebviewPanel, - contentProvider: MarkdownContentProvider, + contentProvider: MdDocumentRenderer, previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, + workspace: IMdWorkspace, + logger: ILogger, topmostLineMonitor: TopmostLineMonitor, contributionProvider: MarkdownContributionProvider, - engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, ): DynamicMarkdownPreview { webview.iconPath = contentProvider.iconPath; return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); + contentProvider, previewConfigurations, workspace, logger, topmostLineMonitor, contributionProvider, tocProvider); } public static create( input: DynamicPreviewInput, previewColumn: vscode.ViewColumn, - contentProvider: MarkdownContentProvider, + contentProvider: MdDocumentRenderer, previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, + workspace: IMdWorkspace, + logger: ILogger, topmostLineMonitor: TopmostLineMonitor, contributionProvider: MarkdownContributionProvider, - engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, ): DynamicMarkdownPreview { const webview = vscode.window.createWebviewPanel( DynamicMarkdownPreview.viewType, @@ -636,18 +648,19 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow webview.iconPath = contentProvider.iconPath; return new DynamicMarkdownPreview(webview, input, - contentProvider, previewConfigurations, logger, topmostLineMonitor, contributionProvider, engine); + contentProvider, previewConfigurations, workspace, logger, topmostLineMonitor, contributionProvider, tocProvider); } private constructor( webview: vscode.WebviewPanel, input: DynamicPreviewInput, - private readonly _contentProvider: MarkdownContentProvider, + private readonly _contentProvider: MdDocumentRenderer, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, - private readonly _logger: Logger, + private readonly _workspace: IMdWorkspace, + private readonly _logger: ILogger, private readonly _topmostLineMonitor: TopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, - private readonly _engine: MarkdownEngine, + private readonly _tocProvider: MdTableOfContentsProvider, ) { super(); @@ -799,10 +812,11 @@ export class DynamicMarkdownPreview extends Disposable implements ManagedMarkdow this.update(link, fragment ? new StartingScrollFragment(fragment) : undefined); } }, - this._engine, this._contentProvider, this._previewConfigurations, + this._workspace, this._logger, - this._contributionProvider); + this._contributionProvider, + this._tocProvider); } } diff --git a/extensions/markdown-language-features/src/preview/previewManager.ts b/extensions/markdown-language-features/src/preview/previewManager.ts index a30379fc6f..89a78d12bb 100644 --- a/extensions/markdown-language-features/src/preview/previewManager.ts +++ b/extensions/markdown-language-features/src/preview/previewManager.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Logger } from '../logger'; -import { MarkdownEngine } from '../markdownEngine'; +import { ILogger } from '../logging'; import { MarkdownContributionProvider } from '../markdownExtensions'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { Disposable, disposeAll } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; -import { DynamicMarkdownPreview, ManagedMarkdownPreview, StaticMarkdownPreview } from './preview'; +import { IMdWorkspace } from '../workspace'; +import { MdDocumentRenderer } from './documentRenderer'; +import { DynamicMarkdownPreview, IManagedMarkdownPreview, StaticMarkdownPreview } from './preview'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContentProvider } from './previewContentProvider'; import { scrollEditorToLine, StartingScrollFragment } from './scrolling'; import { TopmostLineMonitor } from './topmostLineMonitor'; @@ -21,7 +22,7 @@ export interface DynamicPreviewSettings { readonly locked: boolean; } -class PreviewStore extends Disposable { +class PreviewStore extends Disposable { private readonly _previews = new Set(); @@ -65,13 +66,14 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview private readonly _dynamicPreviews = this._register(new PreviewStore()); private readonly _staticPreviews = this._register(new PreviewStore()); - private _activePreview: ManagedMarkdownPreview | undefined = undefined; + private _activePreview: IManagedMarkdownPreview | undefined = undefined; public constructor( - private readonly _contentProvider: MarkdownContentProvider, - private readonly _logger: Logger, + private readonly _contentProvider: MdDocumentRenderer, + private readonly _workspace: IMdWorkspace, + private readonly _logger: ILogger, private readonly _contributions: MarkdownContributionProvider, - private readonly _engine: MarkdownEngine, + private readonly _tocProvider: MdTableOfContentsProvider, ) { super(); @@ -163,10 +165,11 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview webview, this._contentProvider, this._previewConfigurations, + this._workspace, this._logger, this._topmostLineMonitor, this._contributions, - this._engine); + this._tocProvider); this.registerDynamicPreview(preview); } @@ -182,9 +185,10 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this._contentProvider, this._previewConfigurations, this._topmostLineMonitor, + this._workspace, this._logger, this._contributions, - this._engine, + this._tocProvider, lineNumber ); this.registerStaticPreview(preview); @@ -206,10 +210,11 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview previewSettings.previewColumn, this._contentProvider, this._previewConfigurations, + this._workspace, this._logger, this._topmostLineMonitor, this._contributions, - this._engine); + this._tocProvider); this.setPreviewActiveContext(true); this._activePreview = preview; @@ -243,7 +248,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview return preview; } - private trackActive(preview: ManagedMarkdownPreview): void { + private trackActive(preview: IManagedMarkdownPreview): void { preview.onDidChangeViewState(({ webviewPanel }) => { this.setPreviewActiveContext(webviewPanel.active); this._activePreview = webviewPanel.active ? preview : undefined; diff --git a/extensions/markdown-language-features/src/preview/topmostLineMonitor.ts b/extensions/markdown-language-features/src/preview/topmostLineMonitor.ts index 2a8d50aef6..bc5b0614bf 100644 --- a/extensions/markdown-language-features/src/preview/topmostLineMonitor.ts +++ b/extensions/markdown-language-features/src/preview/topmostLineMonitor.ts @@ -6,6 +6,7 @@ import * as vscode from 'vscode'; import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; +import { ResourceMap } from '../util/resourceMap'; export interface LastScrollLocation { readonly line: number; @@ -14,10 +15,10 @@ export interface LastScrollLocation { export class TopmostLineMonitor extends Disposable { - private readonly pendingUpdates = new Map(); + private readonly pendingUpdates = new ResourceMap(); private readonly throttle = 50; - private previousTextEditorInfo = new Map(); - private previousStaticEditorInfo = new Map(); + private previousTextEditorInfo = new ResourceMap(); + private previousStaticEditorInfo = new ResourceMap(); constructor() { super(); @@ -42,28 +43,28 @@ export class TopmostLineMonitor extends Disposable { public readonly onDidChanged = this._onChanged.event; public setPreviousStaticEditorLine(scrollLocation: LastScrollLocation): void { - this.previousStaticEditorInfo.set(scrollLocation.uri.toString(), scrollLocation); + this.previousStaticEditorInfo.set(scrollLocation.uri, scrollLocation); } public getPreviousStaticEditorLineByUri(resource: vscode.Uri): number | undefined { - const scrollLoc = this.previousStaticEditorInfo.get(resource.toString()); - this.previousStaticEditorInfo.delete(resource.toString()); + const scrollLoc = this.previousStaticEditorInfo.get(resource); + this.previousStaticEditorInfo.delete(resource); return scrollLoc?.line; } public setPreviousTextEditorLine(scrollLocation: LastScrollLocation): void { - this.previousTextEditorInfo.set(scrollLocation.uri.toString(), scrollLocation); + this.previousTextEditorInfo.set(scrollLocation.uri, scrollLocation); } public getPreviousTextEditorLineByUri(resource: vscode.Uri): number | undefined { - const scrollLoc = this.previousTextEditorInfo.get(resource.toString()); - this.previousTextEditorInfo.delete(resource.toString()); + const scrollLoc = this.previousTextEditorInfo.get(resource); + this.previousTextEditorInfo.delete(resource); return scrollLoc?.line; } public getPreviousStaticTextEditorLineByUri(resource: vscode.Uri): number | undefined { - const state = this.previousStaticEditorInfo.get(resource.toString()); + const state = this.previousStaticEditorInfo.get(resource); return state?.line; } @@ -71,21 +72,20 @@ export class TopmostLineMonitor extends Disposable { resource: vscode.Uri, line: number ) { - const key = resource.toString(); - if (!this.pendingUpdates.has(key)) { + if (!this.pendingUpdates.has(resource)) { // schedule update setTimeout(() => { - if (this.pendingUpdates.has(key)) { + if (this.pendingUpdates.has(resource)) { this._onChanged.fire({ resource, - line: this.pendingUpdates.get(key) as number + line: this.pendingUpdates.get(resource) as number }); - this.pendingUpdates.delete(key); + this.pendingUpdates.delete(resource); } }, this.throttle); } - this.pendingUpdates.set(key, line); + this.pendingUpdates.set(resource, line); } } diff --git a/extensions/markdown-language-features/src/protocol.ts b/extensions/markdown-language-features/src/protocol.ts new file mode 100644 index 0000000000..0eec124bd5 --- /dev/null +++ b/extensions/markdown-language-features/src/protocol.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Token = require('markdown-it/lib/token'); +import { RequestType } from 'vscode-languageclient'; +import type * as lsp from 'vscode-languageserver-types'; +import type * as md from 'vscode-markdown-languageservice'; + +//#region From server +export const parse = new RequestType<{ uri: string }, Token[], any>('markdown/parse'); + +export const fs_readFile = new RequestType<{ uri: string }, number[], any>('markdown/fs/readFile'); +export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory'); +export const fs_stat = new RequestType<{ uri: string }, { isDirectory: boolean } | undefined, any>('markdown/fs/stat'); + +export const fs_watcher_create = new RequestType<{ id: number; uri: string; options: md.FileWatcherOptions }, void, any>('markdown/fs/watcher/create'); +export const fs_watcher_delete = new RequestType<{ id: number }, void, any>('markdown/fs/watcher/delete'); + +export const findMarkdownFilesInWorkspace = new RequestType<{}, string[], any>('markdown/findMarkdownFilesInWorkspace'); +//#endregion + +//#region To server +export const getReferencesToFileInWorkspace = new RequestType<{ uri: string }, lsp.Location[], any>('markdown/getReferencesToFileInWorkspace'); + +export const fs_watcher_onChange = new RequestType<{ id: number; uri: string; kind: 'create' | 'change' | 'delete' }, void, any>('markdown/fs/watcher/onChange'); +//#endregion diff --git a/extensions/markdown-language-features/src/slugify.ts b/extensions/markdown-language-features/src/slugify.ts index c5faf9a388..f157861278 100644 --- a/extensions/markdown-language-features/src/slugify.ts +++ b/extensions/markdown-language-features/src/slugify.ts @@ -24,7 +24,7 @@ export const githubSlugifier: Slugifier = new class implements Slugifier { .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - // allow-any-unicode-next-line - .replace(/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators + .replace(/[\]\[\!\/\'\"\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators .replace(/^\-+/, '') // Remove leading - .replace(/\-+$/, '') // Remove trailing - ); diff --git a/extensions/markdown-language-features/src/tableOfContents.ts b/extensions/markdown-language-features/src/tableOfContents.ts index 3cabcce034..12e84b9c3e 100644 --- a/extensions/markdown-language-features/src/tableOfContents.ts +++ b/extensions/markdown-language-features/src/tableOfContents.ts @@ -4,10 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { MarkdownEngine } from './markdownEngine'; -import { githubSlugifier, Slug } from './slugify'; +import { ILogger } from './logging'; +import { IMdParser } from './markdownEngine'; +import { githubSlugifier, Slug, Slugifier } from './slugify'; +import { getLine, ITextDocument } from './types/textDocument'; +import { Disposable } from './util/dispose'; import { isMarkdownFile } from './util/file'; -import { SkinnyTextDocument } from './workspaceContents'; +import { Schemes } from './util/schemes'; +import { MdDocumentInfoCache } from './util/workspaceCache'; +import { IMdWorkspace } from './workspace'; export interface TocEntry { readonly slug: Slug; @@ -61,35 +66,39 @@ export interface TocEntry { export class TableOfContents { - public static async create(engine: MarkdownEngine, document: SkinnyTextDocument,): Promise { - const entries = await this.buildToc(engine, document); - return new TableOfContents(entries); + public static async create(parser: IMdParser, document: ITextDocument,): Promise { + const entries = await this.buildToc(parser, document); + return new TableOfContents(entries, parser.slugifier); } - public static async createForDocumentOrNotebook(engine: MarkdownEngine, document: SkinnyTextDocument): Promise { - if (document.uri.scheme === 'vscode-notebook-cell') { + public static async createForDocumentOrNotebook(parser: IMdParser, document: ITextDocument): Promise { + if (document.uri.scheme === Schemes.notebookCell) { const notebook = vscode.workspace.notebookDocuments .find(notebook => notebook.getCells().some(cell => cell.document === document)); if (notebook) { - const entries: TocEntry[] = []; - - for (const cell of notebook.getCells()) { - if (cell.kind === vscode.NotebookCellKind.Markup && isMarkdownFile(cell.document)) { - entries.push(...(await this.buildToc(engine, cell.document))); - } - } - - return new TableOfContents(entries); + return TableOfContents.createForNotebook(parser, notebook); } } - return this.create(engine, document); + return this.create(parser, document); } - private static async buildToc(engine: MarkdownEngine, document: SkinnyTextDocument): Promise { + public static async createForNotebook(parser: IMdParser, notebook: vscode.NotebookDocument): Promise { + const entries: TocEntry[] = []; + + for (const cell of notebook.getCells()) { + if (cell.kind === vscode.NotebookCellKind.Markup && isMarkdownFile(cell.document)) { + entries.push(...(await this.buildToc(parser, cell.document))); + } + } + + return new TableOfContents(entries, parser.slugifier); + } + + private static async buildToc(parser: IMdParser, document: ITextDocument): Promise { const toc: TocEntry[] = []; - const tokens = await engine.parse(document); + const tokens = await parser.tokenize(document); const existingSlugEntries = new Map(); @@ -99,26 +108,26 @@ export class TableOfContents { } const lineNumber = heading.map[0]; - const line = document.lineAt(lineNumber); + const line = getLine(document, lineNumber); - let slug = githubSlugifier.fromHeading(line.text); + let slug = parser.slugifier.fromHeading(line); const existingSlugEntry = existingSlugEntries.get(slug.value); if (existingSlugEntry) { ++existingSlugEntry.count; - slug = githubSlugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); + slug = parser.slugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); } else { existingSlugEntries.set(slug.value, { count: 0 }); } const headerLocation = new vscode.Location(document.uri, - new vscode.Range(lineNumber, 0, lineNumber, line.text.length)); + new vscode.Range(lineNumber, 0, lineNumber, line.length)); const headerTextLocation = new vscode.Location(document.uri, - new vscode.Range(lineNumber, line.text.match(/^#+\s*/)?.[0].length ?? 0, lineNumber, line.text.length - (line.text.match(/\s*#*$/)?.[0].length ?? 0))); + new vscode.Range(lineNumber, line.match(/^#+\s*/)?.[0].length ?? 0, lineNumber, line.length - (line.match(/\s*#*$/)?.[0].length ?? 0))); toc.push({ slug, - text: TableOfContents.getHeaderText(line.text), + text: TableOfContents.getHeaderText(line), level: TableOfContents.getHeaderLevel(heading.markup), line: lineNumber, sectionLocation: headerLocation, // Populated in next steps @@ -142,7 +151,7 @@ export class TableOfContents { sectionLocation: new vscode.Location(document.uri, new vscode.Range( entry.sectionLocation.range.start, - new vscode.Position(endLine, document.lineAt(endLine).text.length))) + new vscode.Position(endLine, getLine(document, endLine).length))) }; }); } @@ -161,12 +170,44 @@ export class TableOfContents { return header.replace(/^\s*#+\s*(.*?)(\s+#+)?$/, (_, word) => word.trim()); } + public static readonly empty = new TableOfContents([], githubSlugifier); + private constructor( public readonly entries: readonly TocEntry[], + private readonly slugifier: Slugifier, ) { } public lookup(fragment: string): TocEntry | undefined { - const slug = githubSlugifier.fromHeading(fragment); + const slug = this.slugifier.fromHeading(fragment); return this.entries.find(entry => entry.slug.equals(slug)); } } + +export class MdTableOfContentsProvider extends Disposable { + + private readonly _cache: MdDocumentInfoCache; + + constructor( + private readonly parser: IMdParser, + workspace: IMdWorkspace, + private readonly logger: ILogger, + ) { + super(); + this._cache = this._register(new MdDocumentInfoCache(workspace, doc => { + this.logger.verbose('TableOfContentsProvider', `create - ${doc.uri}`); + return TableOfContents.create(parser, doc); + })); + } + + public async get(resource: vscode.Uri): Promise { + return await this._cache.get(resource) ?? TableOfContents.empty; + } + + public getForDocument(doc: ITextDocument): Promise { + return this._cache.getForDocument(doc); + } + + public createForNotebook(notebook: vscode.NotebookDocument): Promise { + return TableOfContents.createForNotebook(this.parser, notebook); + } +} diff --git a/extensions/markdown-language-features/src/test/definitionProvider.test.ts b/extensions/markdown-language-features/src/test/definitionProvider.test.ts deleted file mode 100644 index a254f81a63..0000000000 --- a/extensions/markdown-language-features/src/test/definitionProvider.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdDefinitionProvider } from '../languageFeatures/definitionProvider'; -import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { MdWorkspaceContents } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; -import { joinLines, noopToken, workspacePath } from './util'; - - -function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const referencesProvider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - const provider = new MdDefinitionProvider(referencesProvider); - return provider.provideDefinition(doc, pos, noopToken); -} - -function assertDefinitionsEqual(actualDef: vscode.Definition, ...expectedDefs: { uri: vscode.Uri; line: number; startCharacter?: number; endCharacter?: number }[]) { - const actualDefsArr = Array.isArray(actualDef) ? actualDef : [actualDef]; - - assert.strictEqual(actualDefsArr.length, expectedDefs.length, `Definition counts should match`); - - for (let i = 0; i < actualDefsArr.length; ++i) { - const actual = actualDefsArr[i]; - const expected = expectedDefs[i]; - assert.strictEqual(actual.uri.toString(), expected.uri.toString(), `Definition '${i}' has expected document`); - assert.strictEqual(actual.range.start.line, expected.line, `Definition '${i}' has expected start line`); - assert.strictEqual(actual.range.end.line, expected.line, `Definition '${i}' has expected end line`); - if (typeof expected.startCharacter !== 'undefined') { - assert.strictEqual(actual.range.start.character, expected.startCharacter, `Definition '${i}' has expected start character`); - } - if (typeof expected.endCharacter !== 'undefined') { - assert.strictEqual(actual.range.end.character, expected.endCharacter, `Definition '${i}' has expected end character`); - } - } -} - -suite('markdown: Go to definition', () => { - test('Should not return definition when on link text', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `[ref](#abc)`, - `[ref]: http://example.com`, - )); - - const defs = await getDefinition(doc, new vscode.Position(0, 1), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(defs, undefined); - }); - - test('Should find definition links within file from link', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[link 1][abc]`, // trigger here - ``, - `[abc]: https://example.com`, - )); - - const defs = await getDefinition(doc, new vscode.Position(0, 12), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertDefinitionsEqual(defs!, - { uri: docUri, line: 2 }, - ); - }); - - test('Should find definition links using shorthand', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[ref]`, // trigger 1 - ``, - `[yes][ref]`, // trigger 2 - ``, - `[ref]: /Hello.md` // trigger 3 - )); - - { - const defs = await getDefinition(doc, new vscode.Position(0, 2), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertDefinitionsEqual(defs!, - { uri: docUri, line: 4 }, - ); - } - { - const defs = await getDefinition(doc, new vscode.Position(2, 7), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertDefinitionsEqual(defs!, - { uri: docUri, line: 4 }, - ); - } - { - const defs = await getDefinition(doc, new vscode.Position(4, 2), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertDefinitionsEqual(defs!, - { uri: docUri, line: 4 }, - ); - } - }); - - test('Should find definition links within file from definition', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[link 1][abc]`, - ``, - `[abc]: https://example.com`, // trigger here - )); - - const defs = await getDefinition(doc, new vscode.Position(2, 3), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertDefinitionsEqual(defs!, - { uri: docUri, line: 2 }, - ); - }); - - test('Should not find definition links across files', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[link 1][abc]`, - ``, - `[abc]: https://example.com`, - )); - - const defs = await getDefinition(doc, new vscode.Position(0, 12), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(workspacePath('other.md'), joinLines( - `[link 1][abc]`, - ``, - `[abc]: https://example.com?bad`, - )) - ])); - assertDefinitionsEqual(defs!, - { uri: docUri, line: 2 }, - ); - }); -}); diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts deleted file mode 100644 index 34def0a3df..0000000000 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ /dev/null @@ -1,160 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as vscode from 'vscode'; -import 'mocha'; -import { DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions } from '../languageFeatures/diagnostics'; -import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { MdWorkspaceContents } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; -import { assertRangeEqual, joinLines, noopToken, workspacePath } from './util'; - - -function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents) { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const computer = new DiagnosticComputer(engine, workspaceContents, linkProvider); - return computer.getDiagnostics(doc, { - enabled: true, - validateFilePaths: DiagnosticLevel.warning, - validateOwnHeaders: DiagnosticLevel.warning, - validateReferences: DiagnosticLevel.warning, - }, noopToken); -} - -function createDiagnosticsManager(workspaceContents: MdWorkspaceContents, configuration = new MemoryDiagnosticConfiguration()) { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - return new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration); -} - -class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { - - private readonly _onDidChange = new vscode.EventEmitter(); - public readonly onDidChange = this._onDidChange.event; - - constructor( - private readonly enabled: boolean = true, - ) { } - - getOptions(_resource: vscode.Uri): DiagnosticOptions { - if (!this.enabled) { - return { - enabled: false, - validateFilePaths: DiagnosticLevel.ignore, - validateOwnHeaders: DiagnosticLevel.ignore, - validateReferences: DiagnosticLevel.ignore, - }; - } - return { - enabled: true, - validateFilePaths: DiagnosticLevel.warning, - validateOwnHeaders: DiagnosticLevel.warning, - validateReferences: DiagnosticLevel.warning, - }; - } -} - - -suite('markdown: Diagnostics', () => { - test('Should not return any diagnostics for empty document', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `text`, - )); - - const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics, []); - }); - - test('Should generate diagnostic for link to file that does not exist', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `[bad](/no/such/file.md)`, - `[good](/doc.md)`, - `[good-ref]: /doc.md`, - `[bad-ref]: /no/such/file.md`, - )); - - const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics.length, 2); - assertRangeEqual(new vscode.Range(0, 6, 0, 22), diagnostics[0].range); - assertRangeEqual(new vscode.Range(3, 11, 3, 27), diagnostics[1].range); - }); - - test('Should generate diagnostics for links to header that does not exist in current file', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `[good](#good-header)`, - `# Good Header`, - `[bad](#no-such-header)`, - `[good](#good-header)`, - `[good-ref]: #good-header`, - `[bad-ref]: #no-such-header`, - )); - - const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics.length, 2); - assertRangeEqual(new vscode.Range(2, 6, 2, 21), diagnostics[0].range); - assertRangeEqual(new vscode.Range(5, 11, 5, 26), diagnostics[1].range); - }); - - test('Should generate diagnostics for links to non-existent headers in other files', async () => { - const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( - `# My header`, - `[good](#my-header)`, - `[good](/doc1.md#my-header)`, - `[good](doc1.md#my-header)`, - `[good](/doc2.md#other-header)`, - `[bad](/doc2.md#no-such-other-header)`, - )); - - const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines( - `# Other header`, - )); - - const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1, doc2])); - assert.deepStrictEqual(diagnostics.length, 1); - assertRangeEqual(new vscode.Range(5, 6, 5, 35), diagnostics[0].range); - }); - - test('Should support links both with and without .md file extension', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `# My header`, - `[good](#my-header)`, - `[good](/doc.md#my-header)`, - `[good](doc.md#my-header)`, - `[good](/doc#my-header)`, - `[good](doc#my-header)`, - )); - - const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics.length, 0); - }); - - test('Should generate diagnostics for non-existent link reference', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `[good link][good]`, - `[bad link][no-such]`, - ``, - `[good]: http://example.com`, - )); - - const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics.length, 1); - assertRangeEqual(new vscode.Range(1, 11, 1, 18), diagnostics[0].range); - }); - - test('Should not generate diagnostics when validate is disabled', async () => { - const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( - `[text](#no-such-header)`, - `[text][no-such-ref]`, - )); - - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(false)); - const diagnostics = await manager.getDiagnostics(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); - }); -}); diff --git a/extensions/markdown-language-features/src/test/documentLink.test.ts b/extensions/markdown-language-features/src/test/documentLink.test.ts index 1f4a7aa7b9..480c896b1b 100644 --- a/extensions/markdown-language-features/src/test/documentLink.test.ts +++ b/extensions/markdown-language-features/src/test/documentLink.test.ts @@ -24,7 +24,7 @@ function workspaceFile(...segments: string[]) { async function getLinksForFile(file: vscode.Uri): Promise { debugLog('getting links', file.toString(), Date.now()); - const r = (await vscode.commands.executeCommand('vscode.executeLinkProvider', file))!; + const r = (await vscode.commands.executeCommand('vscode.executeLinkProvider', file, /*linkResolveCount*/ 100))!; debugLog('got links', file.toString(), Date.now()); return r; } @@ -134,7 +134,7 @@ async function getLinksForFile(file: vscode.Uri): Promise } }); - test('Should navigate to fragment within current untitled file', async () => { + test('Should navigate to fragment within current untitled file', async () => { // TODO: skip for now for ls migration const testFile = workspaceFile('x.md').with({ scheme: 'untitled' }); await withFileContents(testFile, joinLines( '[](#second)', @@ -171,7 +171,7 @@ async function withFileContents(file: vscode.Uri, contents: string): Promise { - test('Should not return anything for empty document', async () => { - const links = await getLinksForFile(''); - assert.strictEqual(links.length, 0); - }); - - test('Should not return anything for simple document without links', async () => { - const links = await getLinksForFile('# a\nfdasfdfsafsa'); - assert.strictEqual(links.length, 0); - }); - - test('Should detect basic http links', async () => { - const links = await getLinksForFile('a [b](https://example.com) c'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 25)); - }); - - test('Should detect basic workspace links', async () => { - { - const links = await getLinksForFile('a [b](./file) c'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 12)); - } - { - const links = await getLinksForFile('a [b](file.png) c'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 14)); - } - }); - - test('Should detect links with title', async () => { - const links = await getLinksForFile('a [b](https://example.com "abc") c'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 25)); - }); - - // #35245 - test('Should handle links with escaped characters in name', async () => { - const links = await getLinksForFile('a [b\\]](./file)'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 8, 0, 14)); - }); - - - test('Should handle links with balanced parens', async () => { - { - const links = await getLinksForFile('a [b](https://example.com/a()c) c'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 30)); - } - { - const links = await getLinksForFile('a [b](https://example.com/a(b)c) c'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 31)); - - } - { - // #49011 - const links = await getLinksForFile('[A link](http://ThisUrlhasParens/A_link(in_parens))'); - assert.strictEqual(links.length, 1); - const [link] = links; - assertRangeEqual(link.range, new vscode.Range(0, 9, 0, 50)); - } - }); - - test('Should handle two links without space', async () => { - const links = await getLinksForFile('a ([test](test)[test2](test2)) c'); - assert.strictEqual(links.length, 2); - const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0, 10, 0, 14)); - assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); - }); - - // #49238 - test('should handle hyperlinked images', async () => { - { - const links = await getLinksForFile('[![alt text](image.jpg)](https://example.com)'); - assert.strictEqual(links.length, 2); - const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0, 13, 0, 22)); - assertRangeEqual(link2.range, new vscode.Range(0, 25, 0, 44)); - } - { - const links = await getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); - assert.strictEqual(links.length, 2); - const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0, 7, 0, 21)); - assertRangeEqual(link2.range, new vscode.Range(0, 26, 0, 48)); - } - { - const links = await getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); - assert.strictEqual(links.length, 4); - const [link1, link2, link3, link4] = links; - assertRangeEqual(link1.range, new vscode.Range(0, 6, 0, 14)); - assertRangeEqual(link2.range, new vscode.Range(0, 17, 0, 26)); - assertRangeEqual(link3.range, new vscode.Range(0, 39, 0, 47)); - assertRangeEqual(link4.range, new vscode.Range(0, 50, 0, 59)); - } - }); - - test('Should not consider link references starting with ^ character valid (#107471)', async () => { - const links = await getLinksForFile('[^reference]: https://example.com'); - assert.strictEqual(links.length, 0); - }); - - test('Should find definitions links with spaces in angle brackets (#136073)', async () => { - const links = await getLinksForFile([ - '[a]: ', - '[b]: ', - ].join('\n')); - assert.strictEqual(links.length, 2); - - const [link1, link2] = links; - assertRangeEqual(link1.range, new vscode.Range(0, 6, 0, 9)); - assertRangeEqual(link2.range, new vscode.Range(1, 6, 1, 8)); - }); - - test('Should only find one link for reference sources [a]: source (#141285)', async () => { - const links = await getLinksForFile([ - '[Works]: https://microsoft.com', - ].join('\n')); - - assert.strictEqual(links.length, 1); - }); - - test('Should find links for referees with only one [] (#141285)', async () => { - let links = await getLinksForFile([ - '[ref]', - '[ref]: https://microsoft.com', - ].join('\n')); - assert.strictEqual(links.length, 2); - - links = await getLinksForFile([ - '[Does Not Work]', - '[def]: https://microsoft.com', - ].join('\n')); - assert.strictEqual(links.length, 1); - }); - - test('Should not find link for reference using one [] when source does not exist (#141285)', async () => { - const links = await getLinksForFile('[Works]'); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links in code fenced with backticks', async () => { - const text = joinLines( - '```', - '[b](https://example.com)', - '```'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links in code fenced with tilda', async () => { - const text = joinLines( - '~~~', - '[b](https://example.com)', - '~~~'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links in indented code', async () => { - const links = await getLinksForFile(' [b](https://example.com)'); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links in inline code span', async () => { - const links = await getLinksForFile('`[b](https://example.com)`'); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links with code span inside', async () => { - const links = await getLinksForFile('[li`nk](https://example.com`)'); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links in multiline inline code span', async () => { - const text = joinLines( - '`` ', - '[b](https://example.com)', - '``'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider link references in code fenced with backticks (#146714)', async () => { - const text = joinLines( - '```', - '[a] [bb]', - '```'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider reference sources in code fenced with backticks (#146714)', async () => { - const text = joinLines( - '```', - '[a]: http://example.com;', - '[b]: ;', - '[c]: (http://example.com);', - '```'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 0); - }); - - test('Should not consider links in multiline inline code span between between text', async () => { - const text = joinLines( - '[b](https://1.com) `[b](https://2.com)', - '` [b](https://3.com)'); - const links = await getLinksForFile(text); - assert.deepStrictEqual(links.map(l => l.target?.authority), ['1.com', '3.com']); - }); - - test('Should not consider links in multiline inline code span with new line after the first backtick', async () => { - const text = joinLines( - '`', - '[b](https://example.com)`'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 0); - }); - - test('Should not miss links in invalid multiline inline code span', async () => { - const text = joinLines( - '`` ', - '', - '[b](https://example.com)', - '', - '``'); - const links = await getLinksForFile(text); - assert.strictEqual(links.length, 1); - }); - - test('Should find autolinks', async () => { - const links = await getLinksForFile('pre post'); - assert.strictEqual(links.length, 1); - - const link = links[0]; - assertRangeEqual(link.range, new vscode.Range(0, 5, 0, 23)); - }); -}); diff --git a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts deleted file mode 100644 index 5dcad7ee65..0000000000 --- a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryDocument } from '../util/inMemoryDocument'; - - -const testFileName = vscode.Uri.file('test.md'); - - -function getSymbolsForFile(fileContents: string) { - const doc = new InMemoryDocument(testFileName, fileContents); - const provider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); - return provider.provideDocumentSymbols(doc); -} - - -suite('markdown.DocumentSymbolProvider', () => { - test('Should not return anything for empty document', async () => { - const symbols = await getSymbolsForFile(''); - assert.strictEqual(symbols.length, 0); - }); - - test('Should not return anything for document with no headers', async () => { - const symbols = await getSymbolsForFile('a\na'); - assert.strictEqual(symbols.length, 0); - }); - - test('Should not return anything for document with # but no real headers', async () => { - const symbols = await getSymbolsForFile('a#a\na#'); - assert.strictEqual(symbols.length, 0); - }); - - test('Should return single symbol for single header', async () => { - const symbols = await getSymbolsForFile('# h'); - assert.strictEqual(symbols.length, 1); - assert.strictEqual(symbols[0].name, '# h'); - }); - - test('Should not care about symbol level for single header', async () => { - const symbols = await getSymbolsForFile('### h'); - assert.strictEqual(symbols.length, 1); - assert.strictEqual(symbols[0].name, '### h'); - }); - - test('Should put symbols of same level in flat list', async () => { - const symbols = await getSymbolsForFile('## h\n## h2'); - assert.strictEqual(symbols.length, 2); - assert.strictEqual(symbols[0].name, '## h'); - assert.strictEqual(symbols[1].name, '## h2'); - }); - - test('Should nest symbol of level - 1 under parent', async () => { - - const symbols = await getSymbolsForFile('# h\n## h2\n## h3'); - assert.strictEqual(symbols.length, 1); - assert.strictEqual(symbols[0].name, '# h'); - assert.strictEqual(symbols[0].children.length, 2); - assert.strictEqual(symbols[0].children[0].name, '## h2'); - assert.strictEqual(symbols[0].children[1].name, '## h3'); - }); - - test('Should nest symbol of level - n under parent', async () => { - const symbols = await getSymbolsForFile('# h\n#### h2'); - assert.strictEqual(symbols.length, 1); - assert.strictEqual(symbols[0].name, '# h'); - assert.strictEqual(symbols[0].children.length, 1); - assert.strictEqual(symbols[0].children[0].name, '#### h2'); - }); - - test('Should flatten children where lower level occurs first', async () => { - const symbols = await getSymbolsForFile('# h\n### h2\n## h3'); - assert.strictEqual(symbols.length, 1); - assert.strictEqual(symbols[0].name, '# h'); - assert.strictEqual(symbols[0].children.length, 2); - assert.strictEqual(symbols[0].children[0].name, '### h2'); - assert.strictEqual(symbols[0].children[1].name, '## h3'); - }); - - test('Should handle line separator in file. Issue #63749', async () => { - const symbols = await getSymbolsForFile(`# A -- foo - -# B -- bar`); - assert.strictEqual(symbols.length, 2); - assert.strictEqual(symbols[0].name, '# A'); - assert.strictEqual(symbols[1].name, '# B'); - }); -}); - diff --git a/extensions/markdown-language-features/src/test/engine.test.ts b/extensions/markdown-language-features/src/test/engine.test.ts index 63a9ea92c1..0f0e0ed89f 100644 --- a/extensions/markdown-language-features/src/test/engine.test.ts +++ b/extensions/markdown-language-features/src/test/engine.test.ts @@ -6,8 +6,8 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { createNewMarkdownEngine } from './engine'; import { InMemoryDocument } from '../util/inMemoryDocument'; +import { createNewMarkdownEngine } from './engine'; const testFileName = vscode.Uri.file('test.md'); diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index 7e05cf41fb..422c2272cb 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; +import { MarkdownItEngine } from '../markdownEngine'; import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { githubSlugifier } from '../slugify'; import { Disposable } from '../util/dispose'; +import { nulLogger } from './nulLogging'; const emptyContributions = new class extends Disposable implements MarkdownContributionProvider { readonly extensionUri = vscode.Uri.file('/'); @@ -15,6 +16,6 @@ const emptyContributions = new class extends Disposable implements MarkdownContr readonly onContributionsChanged = this._register(new vscode.EventEmitter()).event; }; -export function createNewMarkdownEngine(): MarkdownEngine { - return new MarkdownEngine(emptyContributions, githubSlugifier); +export function createNewMarkdownEngine(): MarkdownItEngine { + return new MarkdownItEngine(emptyContributions, githubSlugifier, nulLogger); } diff --git a/extensions/markdown-language-features/src/test/fileReferences.test.ts b/extensions/markdown-language-features/src/test/fileReferences.test.ts deleted file mode 100644 index 36d60cfb70..0000000000 --- a/extensions/markdown-language-features/src/test/fileReferences.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; -import { MdReference, MdReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { MdWorkspaceContents } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; -import { joinLines, noopToken, workspacePath } from './util'; - - -function getFileReferences(resource: vscode.Uri, workspaceContents: MdWorkspaceContents) { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const provider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - return provider.getAllReferencesToFile(resource, noopToken); -} - -function assertReferencesEqual(actualRefs: readonly MdReference[], ...expectedRefs: { uri: vscode.Uri; line: number }[]) { - assert.strictEqual(actualRefs.length, expectedRefs.length, `Reference counts should match`); - - for (let i = 0; i < actualRefs.length; ++i) { - const actual = actualRefs[i].location; - const expected = expectedRefs[i]; - assert.strictEqual(actual.uri.toString(), expected.uri.toString(), `Ref '${i}' has expected document`); - assert.strictEqual(actual.range.start.line, expected.line, `Ref '${i}' has expected start line`); - assert.strictEqual(actual.range.end.line, expected.line, `Ref '${i}' has expected end line`); - } -} - -suite('markdown: find file references', () => { - - test('Should find basic references', async () => { - const docUri = workspacePath('doc.md'); - const otherUri = workspacePath('other.md'); - - const refs = await getFileReferences(otherUri, new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(docUri, joinLines( - `# header`, - `[link 1](./other.md)`, - `[link 2](./other.md)`, - )), - new InMemoryDocument(otherUri, joinLines( - `# header`, - `pre`, - `[link 3](./other.md)`, - `post`, - )), - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 1 }, - { uri: docUri, line: 2 }, - { uri: otherUri, line: 2 }, - ); - }); - - test('Should find references with and without file extensions', async () => { - const docUri = workspacePath('doc.md'); - const otherUri = workspacePath('other.md'); - - const refs = await getFileReferences(otherUri, new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(docUri, joinLines( - `# header`, - `[link 1](./other.md)`, - `[link 2](./other)`, - )), - new InMemoryDocument(otherUri, joinLines( - `# header`, - `pre`, - `[link 3](./other.md)`, - `[link 4](./other)`, - `post`, - )), - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 1 }, - { uri: docUri, line: 2 }, - { uri: otherUri, line: 2 }, - { uri: otherUri, line: 3 }, - ); - }); - - test('Should find references with headers on links', async () => { - const docUri = workspacePath('doc.md'); - const otherUri = workspacePath('other.md'); - - const refs = await getFileReferences(otherUri, new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(docUri, joinLines( - `# header`, - `[link 1](./other.md#sub-bla)`, - `[link 2](./other#sub-bla)`, - )), - new InMemoryDocument(otherUri, joinLines( - `# header`, - `pre`, - `[link 3](./other.md#sub-bla)`, - `[link 4](./other#sub-bla)`, - `post`, - )), - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 1 }, - { uri: docUri, line: 2 }, - { uri: otherUri, line: 2 }, - { uri: otherUri, line: 3 }, - ); - }); -}); diff --git a/extensions/markdown-language-features/src/test/foldingProvider.test.ts b/extensions/markdown-language-features/src/test/foldingProvider.test.ts deleted file mode 100644 index 7e13d732e3..0000000000 --- a/extensions/markdown-language-features/src/test/foldingProvider.test.ts +++ /dev/null @@ -1,223 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdFoldingProvider } from '../languageFeatures/foldingProvider'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { createNewMarkdownEngine } from './engine'; -import { joinLines } from './util'; - -const testFileName = vscode.Uri.file('test.md'); - -suite('markdown.FoldingProvider', () => { - test('Should not return anything for empty document', async () => { - const folds = await getFoldsForDocument(``); - assert.strictEqual(folds.length, 0); - }); - - test('Should not return anything for document without headers', async () => { - const folds = await getFoldsForDocument(joinLines( - `a`, - `**b** afas`, - `a#b`, - `a`, - )); - assert.strictEqual(folds.length, 0); - }); - - test('Should fold from header to end of document', async () => { - const folds = await getFoldsForDocument(joinLines( - `a`, - `# b`, - `c`, - `d`, - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 3); - }); - - test('Should leave single newline before next header', async () => { - const folds = await getFoldsForDocument(joinLines( - ``, - `# a`, - `x`, - ``, - `# b`, - `y`, - )); - assert.strictEqual(folds.length, 2); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 2); - }); - - test('Should collapse multiple newlines to single newline before next header', async () => { - const folds = await getFoldsForDocument(joinLines( - ``, - `# a`, - `x`, - ``, - ``, - ``, - `# b`, - `y` - )); - assert.strictEqual(folds.length, 2); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 4); - }); - - test('Should not collapse if there is no newline before next header', async () => { - const folds = await getFoldsForDocument(joinLines( - ``, - `# a`, - `x`, - `# b`, - `y`, - )); - assert.strictEqual(folds.length, 2); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 2); - }); - - test('Should fold nested markers', async () => { - const folds = await getFoldsForDocument(joinLines( - `a`, - ``, - `b`, - ``, - `b.a`, - ``, - `b`, - ``, - `b.b`, - ``, - `b`, - ``, - `a`, - )); - assert.strictEqual(folds.length, 3); - const [outer, first, second] = folds.sort((a, b) => a.start - b.start); - - assert.strictEqual(outer.start, 1); - assert.strictEqual(outer.end, 11); - assert.strictEqual(first.start, 3); - assert.strictEqual(first.end, 5); - assert.strictEqual(second.start, 7); - assert.strictEqual(second.end, 9); - }); - - test('Should fold from list to end of document', async () => { - const folds = await getFoldsForDocument(joinLines( - `a`, - `- b`, - `c`, - `d`, - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 3); - }); - - test('lists folds should span multiple lines of content', async () => { - const folds = await getFoldsForDocument(joinLines( - `a`, - `- This list item\n spans multiple\n lines.`, - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 3); - }); - - test('List should leave single blankline before new element', async () => { - const folds = await getFoldsForDocument(joinLines( - `- a`, - `a`, - ``, - ``, - `b` - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 0); - assert.strictEqual(firstFold.end, 2); - }); - - test('Should fold fenced code blocks', async () => { - const folds = await getFoldsForDocument(joinLines( - `~~~ts`, - `a`, - `~~~`, - `b`, - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 0); - assert.strictEqual(firstFold.end, 2); - }); - - test('Should fold fenced code blocks with yaml front matter', async () => { - const folds = await getFoldsForDocument(joinLines( - `---`, - `title: bla`, - `---`, - ``, - `~~~ts`, - `a`, - `~~~`, - ``, - `a`, - `a`, - `b`, - `a`, - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 4); - assert.strictEqual(firstFold.end, 6); - }); - - test('Should fold html blocks', async () => { - const folds = await getFoldsForDocument(joinLines( - `x`, - `
`, - ` fa`, - `
`, - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 3); - }); - - test('Should fold html block comments', async () => { - const folds = await getFoldsForDocument(joinLines( - `x`, - `` - )); - assert.strictEqual(folds.length, 1); - const firstFold = folds[0]; - assert.strictEqual(firstFold.start, 1); - assert.strictEqual(firstFold.end, 3); - assert.strictEqual(firstFold.kind, vscode.FoldingRangeKind.Comment); - }); -}); - - -async function getFoldsForDocument(contents: string) { - const doc = new InMemoryDocument(testFileName, contents); - const provider = new MdFoldingProvider(createNewMarkdownEngine()); - return await provider.provideFoldingRanges(doc, {}, new vscode.CancellationTokenSource().token); -} diff --git a/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts b/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts index 8f1e4a05fb..b3c20dc0f5 100644 --- a/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts +++ b/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts @@ -4,58 +4,80 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import * as path from 'path'; import * as vscode from 'vscode'; -import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { ITextDocument } from '../types/textDocument'; +import { Disposable } from '../util/dispose'; +import { ResourceMap } from '../util/resourceMap'; +import { IMdWorkspace } from '../workspace'; -export class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents { - private readonly _documents = new Map(); +export class InMemoryMdWorkspace extends Disposable implements IMdWorkspace { + private readonly _documents = new ResourceMap(uri => uri.fsPath); - constructor(documents: SkinnyTextDocument[]) { + constructor(documents: ITextDocument[]) { + super(); for (const doc of documents) { - this._documents.set(this.getKey(doc.uri), doc); + this._documents.set(doc.uri, doc); } } - public async getAllMarkdownDocuments() { + public values() { return Array.from(this._documents.values()); } - public async getMarkdownDocument(resource: vscode.Uri): Promise { - return this._documents.get(this.getKey(resource)); + public async getAllMarkdownDocuments() { + return this.values(); + } + + public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise { + return this._documents.get(resource); + } + + public hasMarkdownDocument(resolvedHrefPath: vscode.Uri): boolean { + return this._documents.has(resolvedHrefPath); } public async pathExists(resource: vscode.Uri): Promise { - return this._documents.has(this.getKey(resource)); + return this._documents.has(resource); } - private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter(); + public async readDirectory(resource: vscode.Uri): Promise<[string, vscode.FileType][]> { + const files = new Map(); + const pathPrefix = resource.fsPath + (resource.fsPath.endsWith('/') || resource.fsPath.endsWith('\\') ? '' : path.sep); + for (const doc of this._documents.values()) { + const path = doc.uri.fsPath; + if (path.startsWith(pathPrefix)) { + const parts = path.slice(pathPrefix.length).split(/\/|\\/g); + files.set(parts[0], parts.length > 1 ? vscode.FileType.Directory : vscode.FileType.File); + } + } + return Array.from(files.entries()); + } + + private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event; - private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter(); + private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); public onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocumentEmitter.event; - private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter(); + private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); public onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocumentEmitter.event; - public updateDocument(document: SkinnyTextDocument) { - this._documents.set(this.getKey(document.uri), document); + public updateDocument(document: ITextDocument) { + this._documents.set(document.uri, document); this._onDidChangeMarkdownDocumentEmitter.fire(document); } - public createDocument(document: SkinnyTextDocument) { - assert.ok(!this._documents.has(this.getKey(document.uri))); + public createDocument(document: ITextDocument) { + assert.ok(!this._documents.has(document.uri)); - this._documents.set(this.getKey(document.uri), document); + this._documents.set(document.uri, document); this._onDidCreateMarkdownDocumentEmitter.fire(document); } public deleteDocument(resource: vscode.Uri) { - this._documents.delete(this.getKey(resource)); + this._documents.delete(resource); this._onDidDeleteMarkdownDocumentEmitter.fire(resource); } - - private getKey(resource: vscode.Uri): string { - return resource.fsPath; - } } diff --git a/extensions/markdown-language-features/src/test/nulLogging.ts b/extensions/markdown-language-features/src/test/nulLogging.ts new file mode 100644 index 0000000000..d0db040fc1 --- /dev/null +++ b/extensions/markdown-language-features/src/test/nulLogging.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILogger } from '../logging'; + +export const nulLogger = new class implements ILogger { + verbose(): void { + // noop + } +}; diff --git a/extensions/markdown-language-features/src/test/pathCompletion.test.ts b/extensions/markdown-language-features/src/test/pathCompletion.test.ts deleted file mode 100644 index c6c0710ea6..0000000000 --- a/extensions/markdown-language-features/src/test/pathCompletion.test.ts +++ /dev/null @@ -1,169 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; -import { MdPathCompletionProvider } from '../languageFeatures/pathCompletions'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { createNewMarkdownEngine } from './engine'; -import { CURSOR, getCursorPositions, joinLines, noopToken, workspacePath } from './util'; - - -function getCompletionsAtCursor(resource: vscode.Uri, fileContents: string) { - const doc = new InMemoryDocument(resource, fileContents); - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const provider = new MdPathCompletionProvider(engine, linkProvider); - const cursorPositions = getCursorPositions(fileContents, doc); - return provider.provideCompletionItems(doc, cursorPositions[0], noopToken, { - triggerCharacter: undefined, - triggerKind: vscode.CompletionTriggerKind.Invoke, - }); -} - -suite('Markdown path completion provider', () => { - - setup(async () => { - // These tests assume that the markdown completion provider is already registered - await vscode.extensions.getExtension('vscode.markdown-language-features')!.activate(); - }); - - test('Should not return anything when triggered in empty doc', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), `${CURSOR}`); - assert.strictEqual(completions.length, 0); - }); - - test('Should return anchor completions', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[](#${CURSOR}`, - ``, - `# A b C`, - `# x y Z`, - )); - - assert.strictEqual(completions.length, 2); - assert.ok(completions.some(x => x.label === '#a-b-c'), 'Has a-b-c anchor completion'); - assert.ok(completions.some(x => x.label === '#x-y-z'), 'Has x-y-z anchor completion'); - }); - - test('Should not return suggestions for http links', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[](http:${CURSOR}`, - ``, - `# http`, - `# http:`, - `# https:`, - )); - - assert.strictEqual(completions.length, 0); - }); - - test('Should return relative path suggestions', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[](${CURSOR}`, - ``, - `# A b C`, - )); - - assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion'); - assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion'); - assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion'); - }); - - test('Should return relative path suggestions using ./', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[](./${CURSOR}`, - ``, - `# A b C`, - )); - - assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion'); - assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion'); - assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion'); - }); - - test('Should return absolute path suggestions using /', async () => { - const completions = await getCompletionsAtCursor(workspacePath('sub', 'new.md'), joinLines( - `[](/${CURSOR}`, - ``, - `# A b C`, - )); - - assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion'); - assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion'); - assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion'); - assert.ok(!completions.some(x => x.label === 'c.md'), 'Should not have c.md from sub folder'); - }); - - test('Should return anchor suggestions in other file', async () => { - const completions = await getCompletionsAtCursor(workspacePath('sub', 'new.md'), joinLines( - `[](/b.md#${CURSOR}`, - )); - - assert.ok(completions.some(x => x.label === '#b'), 'Has #b header completion'); - assert.ok(completions.some(x => x.label === '#header1'), 'Has #header1 header completion'); - }); - - test('Should reference links for current file', async () => { - const completions = await getCompletionsAtCursor(workspacePath('sub', 'new.md'), joinLines( - `[][${CURSOR}`, - ``, - `[ref-1]: bla`, - `[ref-2]: bla`, - )); - - assert.strictEqual(completions.length, 2); - assert.ok(completions.some(x => x.label === 'ref-1'), 'Has ref-1 reference completion'); - assert.ok(completions.some(x => x.label === 'ref-2'), 'Has ref-2 reference completion'); - }); - - test('Should complete headers in link definitions', async () => { - const completions = await getCompletionsAtCursor(workspacePath('sub', 'new.md'), joinLines( - `# a B c`, - `# x y Z`, - `[ref-1]: ${CURSOR}`, - )); - - assert.ok(completions.some(x => x.label === '#a-b-c'), 'Has #a-b-c header completion'); - assert.ok(completions.some(x => x.label === '#x-y-z'), 'Has #x-y-z header completion'); - }); - - test('Should complete relative paths in link definitions', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `# a B c`, - `[ref-1]: ${CURSOR}`, - )); - - assert.ok(completions.some(x => x.label === 'a.md'), 'Has a.md file completion'); - assert.ok(completions.some(x => x.label === 'b.md'), 'Has b.md file completion'); - assert.ok(completions.some(x => x.label === 'sub/'), 'Has sub folder completion'); - }); - - test('Should escape spaces in path names', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[](./sub/${CURSOR})` - )); - - assert.ok(completions.some(x => x.insertText === 'file%20with%20space.md'), 'Has encoded path completion'); - }); - - test('Should complete paths for path with encoded spaces', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[](./sub%20with%20space/${CURSOR})` - )); - - assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has file from space'); - }); - - test('Should complete definition path for path with encoded spaces', async () => { - const completions = await getCompletionsAtCursor(workspacePath('new.md'), joinLines( - `[def]: ./sub%20with%20space/${CURSOR}` - )); - - assert.ok(completions.some(x => x.insertText === 'file.md'), 'Has file from space'); - }); -}); diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts deleted file mode 100644 index d86c028c2c..0000000000 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ /dev/null @@ -1,580 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { MdWorkspaceContents } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; -import { joinLines, noopToken, workspacePath } from './util'; - - -function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const provider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken); -} - -function assertReferencesEqual(actualRefs: readonly vscode.Location[], ...expectedRefs: { uri: vscode.Uri; line: number; startCharacter?: number; endCharacter?: number }[]) { - assert.strictEqual(actualRefs.length, expectedRefs.length, `Reference counts should match`); - - for (let i = 0; i < actualRefs.length; ++i) { - const actual = actualRefs[i]; - const expected = expectedRefs[i]; - assert.strictEqual(actual.uri.toString(), expected.uri.toString(), `Ref '${i}' has expected document`); - assert.strictEqual(actual.range.start.line, expected.line, `Ref '${i}' has expected start line`); - assert.strictEqual(actual.range.end.line, expected.line, `Ref '${i}' has expected end line`); - if (typeof expected.startCharacter !== 'undefined') { - assert.strictEqual(actual.range.start.character, expected.startCharacter, `Ref '${i}' has expected start character`); - } - if (typeof expected.endCharacter !== 'undefined') { - assert.strictEqual(actual.range.end.character, expected.endCharacter, `Ref '${i}' has expected end character`); - } - } -} - -suite('markdown: find all references', () => { - test('Should not return references when not on header or link', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `# abc`, - ``, - `[link 1](#abc)`, - `text`, - )); - - { - const refs = await getReferences(doc, new vscode.Position(1, 0), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs, []); - } - { - const refs = await getReferences(doc, new vscode.Position(3, 2), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs, []); - } - }); - - test('Should find references from header within same file', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# abc`, - ``, - `[link 1](#abc)`, - `[not link](#noabc)`, - `[link 2](#abc)`, - )); - const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, - { uri, line: 2 }, - { uri, line: 4 }, - ); - }); - - test('Should not return references when on link text', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `[ref](#abc)`, - `[ref]: http://example.com`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 1), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs, []); - }); - - test('Should find references using normalized slug', async () => { - const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( - `# a B c`, - `[simple](#a-b-c)`, - `[start underscore](#_a-b-c)`, - `[different case](#a-B-C)`, - )); - - { - // Trigger header - const refs = await getReferences(doc, new vscode.Position(0, 0), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs!.length, 4); - } - { - // Trigger on line 1 - const refs = await getReferences(doc, new vscode.Position(1, 12), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs!.length, 4); - } - { - // Trigger on line 2 - const refs = await getReferences(doc, new vscode.Position(2, 24), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs!.length, 4); - } - { - // Trigger on line 3 - const refs = await getReferences(doc, new vscode.Position(3, 20), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(refs!.length, 4); - } - }); - - test('Should find references from header across files', async () => { - const docUri = workspacePath('doc.md'); - const other1Uri = workspacePath('sub', 'other.md'); - const other2Uri = workspacePath('other2.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `# abc`, - ``, - `[link 1](#abc)`, - )); - const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(other1Uri, joinLines( - `[not link](#abc)`, - `[not link](/doc.md#abz)`, - `[link](/doc.md#abc)`, - )), - new InMemoryDocument(other2Uri, joinLines( - `[not link](#abc)`, - `[not link](./doc.md#abz)`, - `[link](./doc.md#abc)`, - )) - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, // Header definition - { uri: docUri, line: 2 }, - { uri: other1Uri, line: 2 }, - { uri: other2Uri, line: 2 }, - ); - }); - - test('Should find references from header to link definitions ', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# abc`, - ``, - `[bla]: #abc` - )); - - const refs = await getReferences(doc, new vscode.Position(0, 3), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, // Header definition - { uri, line: 2 }, - ); - }); - - test('Should find header references from link definition', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# A b C`, - `[text][bla]`, - `[bla]: #a-b-c`, // trigger here - )); - - const refs = await getReferences(doc, new vscode.Position(2, 9), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, // Header definition - { uri, line: 2 }, - ); - }); - - test('Should find references from link within same file', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# abc`, - ``, - `[link 1](#abc)`, - `[not link](#noabc)`, - `[link 2](#abc)`, - )); - - const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, // Header definition - { uri, line: 2 }, - { uri, line: 4 }, - ); - }); - - test('Should find references from link across files', async () => { - const docUri = workspacePath('doc.md'); - const other1Uri = workspacePath('sub', 'other.md'); - const other2Uri = workspacePath('other2.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `# abc`, - ``, - `[link 1](#abc)`, - )); - const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(other1Uri, joinLines( - `[not link](#abc)`, - `[not link](/doc.md#abz)`, - `[with ext](/doc.md#abc)`, - `[without ext](/doc#abc)`, - )), - new InMemoryDocument(other2Uri, joinLines( - `[not link](#abc)`, - `[not link](./doc.md#abz)`, - `[link](./doc.md#abc)`, - )) - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, // Header definition - { uri: docUri, line: 2 }, - { uri: other1Uri, line: 2 }, // Other with ext - { uri: other1Uri, line: 3 }, // Other without ext - { uri: other2Uri, line: 2 }, // Other2 - ); - }); - - test('Should find references without requiring file extensions', async () => { - const docUri = workspacePath('doc.md'); - const other1Uri = workspacePath('other.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `# a B c`, - ``, - `[link 1](#a-b-c)`, - )); - const refs = await getReferences(doc, new vscode.Position(2, 10), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(other1Uri, joinLines( - `[not link](#a-b-c)`, - `[not link](/doc.md#a-b-z)`, - `[with ext](/doc.md#a-b-c)`, - `[without ext](/doc#a-b-c)`, - `[rel with ext](./doc.md#a-b-c)`, - `[rel without ext](./doc#a-b-c)`, - )), - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, // Header definition - { uri: docUri, line: 2 }, - { uri: other1Uri, line: 2 }, // Other with ext - { uri: other1Uri, line: 3 }, // Other without ext - { uri: other1Uri, line: 4 }, // Other relative link with ext - { uri: other1Uri, line: 5 }, // Other relative link without ext - ); - }); - - test('Should find references from link across files when triggered on link without file extension', async () => { - const docUri = workspacePath('doc.md'); - const other1Uri = workspacePath('sub', 'other.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `[with ext](./sub/other#header)`, - `[without ext](./sub/other.md#header)`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 23), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(other1Uri, joinLines( - `pre`, - `# header`, - `post`, - )), - ])); - - assertReferencesEqual(refs!, - { uri: other1Uri, line: 1 }, // Header definition - { uri: docUri, line: 0 }, - { uri: docUri, line: 1 }, - ); - }); - - test('Should include header references when triggered on file link', async () => { - const docUri = workspacePath('doc.md'); - const otherUri = workspacePath('sub', 'other.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `[with ext](./sub/other)`, - `[with ext](./sub/other#header)`, - `[without ext](./sub/other.md#no-such-header)`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 15), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(otherUri, joinLines( - `pre`, - `# header`, // Definition should not be included since we triggered on a file link - `post`, - )), - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 1 }, - { uri: docUri, line: 2 }, - ); - }); - - test('Should not include refs from other file to own header', async () => { - const docUri = workspacePath('doc.md'); - const otherUri = workspacePath('sub', 'other.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `[other](./sub/other)`, // trigger here - )); - - const refs = await getReferences(doc, new vscode.Position(0, 15), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(otherUri, joinLines( - `# header`, // Definition should not be included since we triggered on a file link - `[text](#header)`, // Ref should not be included since it is to own file - )), - ])); - - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - ); - }); - - test('Should find explicit references to own file ', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[bare](doc.md)`, // trigger here - `[rel](./doc.md)`, - `[abs](/doc.md)`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 12), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, - { uri, line: 1 }, - { uri, line: 2 }, - ); - }); - - test('Should support finding references to http uri', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[1](http://example.com)`, - `[no](https://example.com)`, - `[2](http://example.com)`, - `[3]: http://example.com`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, - { uri, line: 2 }, - { uri, line: 3 }, - ); - }); - - test('Should consider authority, scheme and paths when finding references to http uri', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[1](http://example.com/cat)`, - `[2](http://example.com)`, - `[3](http://example.com/dog)`, - `[4](http://example.com/cat/looong)`, - `[5](http://example.com/cat)`, - `[6](http://other.com/cat)`, - `[7](https://example.com/cat)`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, - { uri, line: 4 }, - ); - }); - - test('Should support finding references to http uri across files', async () => { - const uri1 = workspacePath('doc.md'); - const uri2 = workspacePath('doc2.md'); - const doc = new InMemoryDocument(uri1, joinLines( - `[1](http://example.com)`, - `[3]: http://example.com`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(uri2, joinLines( - `[other](http://example.com)`, - )) - ])); - assertReferencesEqual(refs!, - { uri: uri1, line: 0 }, - { uri: uri1, line: 1 }, - { uri: uri2, line: 0 }, - ); - }); - - test('Should support finding references to autolinked http links', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[1](http://example.com)`, - ``, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 13), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri, line: 0 }, - { uri, line: 1 }, - ); - }); - - test('Should distinguish between references to file and to header within file', async () => { - const docUri = workspacePath('doc.md'); - const other1Uri = workspacePath('sub', 'other.md'); - - const doc = new InMemoryDocument(docUri, joinLines( - `# abc`, - ``, - `[link 1](#abc)`, - )); - const otherDoc = new InMemoryDocument(other1Uri, joinLines( - `[link](/doc.md#abc)`, - `[link no text](/doc#abc)`, - )); - const workspaceContents = new InMemoryWorkspaceMarkdownDocuments([ - doc, - otherDoc, - ]); - { - // Check refs to header fragment - const headerRefs = await getReferences(otherDoc, new vscode.Position(0, 16), workspaceContents); - assertReferencesEqual(headerRefs!, - { uri: docUri, line: 0 }, // Header definition - { uri: docUri, line: 2 }, - { uri: other1Uri, line: 0 }, - { uri: other1Uri, line: 1 }, - ); - } - { - // Check refs to file itself from link with ext - const fileRefs = await getReferences(otherDoc, new vscode.Position(0, 9), workspaceContents); - assertReferencesEqual(fileRefs!, - { uri: other1Uri, line: 0, endCharacter: 14 }, - { uri: other1Uri, line: 1, endCharacter: 19 }, - ); - } - { - // Check refs to file itself from link without ext - const fileRefs = await getReferences(otherDoc, new vscode.Position(1, 17), workspaceContents); - assertReferencesEqual(fileRefs!, - { uri: other1Uri, line: 0 }, - { uri: other1Uri, line: 1 }, - ); - } - }); - - test('Should support finding references to unknown file', async () => { - const uri1 = workspacePath('doc1.md'); - const doc1 = new InMemoryDocument(uri1, joinLines( - `![img](/images/more/image.png)`, - ``, - `[ref]: /images/more/image.png`, - )); - - const uri2 = workspacePath('sub', 'doc2.md'); - const doc2 = new InMemoryDocument(uri2, joinLines( - `![img](/images/more/image.png)`, - )); - - - const refs = await getReferences(doc1, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc1, doc2])); - assertReferencesEqual(refs!, - { uri: uri1, line: 0 }, - { uri: uri1, line: 2 }, - { uri: uri2, line: 0 }, - ); - }); - - suite('Reference links', () => { - test('Should find reference links within file from link', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[link 1][abc]`, // trigger here - ``, - `[abc]: https://example.com`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 12), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 2 }, - ); - }); - - test('Should find reference links using shorthand', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[ref]`, // trigger 1 - ``, - `[yes][ref]`, // trigger 2 - ``, - `[ref]: /Hello.md` // trigger 3 - )); - - { - const refs = await getReferences(doc, new vscode.Position(0, 2), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 2 }, - { uri: docUri, line: 4 }, - ); - } - { - const refs = await getReferences(doc, new vscode.Position(2, 7), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 2 }, - { uri: docUri, line: 4 }, - ); - } - { - const refs = await getReferences(doc, new vscode.Position(4, 2), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 2 }, - { uri: docUri, line: 4 }, - ); - } - }); - - test('Should find reference links within file from definition', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[link 1][abc]`, - ``, - `[abc]: https://example.com`, // trigger here - )); - - const refs = await getReferences(doc, new vscode.Position(2, 3), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 2 }, - ); - }); - - test('Should not find reference links across files', async () => { - const docUri = workspacePath('doc.md'); - const doc = new InMemoryDocument(docUri, joinLines( - `[link 1][abc]`, - ``, - `[abc]: https://example.com`, - )); - - const refs = await getReferences(doc, new vscode.Position(0, 12), new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(workspacePath('other.md'), joinLines( - `[link 1][abc]`, - ``, - `[abc]: https://example.com?bad`, - )) - ])); - assertReferencesEqual(refs!, - { uri: docUri, line: 0 }, - { uri: docUri, line: 2 }, - ); - }); - }); -}); diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts deleted file mode 100644 index d40c6f4abe..0000000000 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ /dev/null @@ -1,616 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; -import { MdRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; -import { githubSlugifier } from '../slugify'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { MdWorkspaceContents } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; -import { assertRangeEqual, joinLines, noopToken, workspacePath } from './util'; - - -/** - * Get prepare rename info. - */ -function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents): Promise { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const referencesProvider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); - return renameProvider.prepareRename(doc, pos, noopToken); -} - -/** - * Get all the edits for the rename. - */ -function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspaceContents: MdWorkspaceContents): Promise { - const engine = createNewMarkdownEngine(); - const linkProvider = new MdLinkProvider(engine); - const referencesProvider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); - return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken); -} - -interface ExpectedTextEdit { - readonly uri: vscode.Uri; - readonly edits: readonly vscode.TextEdit[]; -} - -interface ExpectedFileRename { - readonly originalUri: vscode.Uri; - readonly newUri: vscode.Uri; -} - -function assertEditsEqual(actualEdit: MdWorkspaceEdit, ...expectedEdits: ReadonlyArray) { - // Check file renames - const expectedFileRenames = expectedEdits.filter(expected => 'originalUri' in expected) as ExpectedFileRename[]; - const actualFileRenames = actualEdit.fileRenames ?? []; - assert.strictEqual(actualFileRenames.length, expectedFileRenames.length, `File rename count should match`); - for (let i = 0; i < actualFileRenames.length; ++i) { - const expected = expectedFileRenames[i]; - const actual = actualFileRenames[i]; - assert.strictEqual(actual.from.toString(), expected.originalUri.toString(), `File rename '${i}' should have expected 'from' resource`); - assert.strictEqual(actual.to.toString(), expected.newUri.toString(), `File rename '${i}' should have expected 'to' resource`); - } - - // Check text edits - const actualTextEdits = actualEdit.edit.entries(); - const expectedTextEdits = expectedEdits.filter(expected => 'edits' in expected) as ExpectedTextEdit[]; - assert.strictEqual(actualTextEdits.length, expectedTextEdits.length, `Reference counts should match`); - for (let i = 0; i < actualTextEdits.length; ++i) { - const expected = expectedTextEdits[i]; - const actual = actualTextEdits[i]; - - if ('edits' in expected) { - assert.strictEqual(actual[0].toString(), expected.uri.toString(), `Ref '${i}' has expected document`); - - const actualEditForDoc = actual[1]; - const expectedEditsForDoc = expected.edits; - assert.strictEqual(actualEditForDoc.length, expectedEditsForDoc.length, `Edit counts for '${actual[0]}' should match`); - - for (let g = 0; g < actualEditForDoc.length; ++g) { - assertRangeEqual(actualEditForDoc[g].range, expectedEditsForDoc[g].range, `Edit '${g}' of '${actual[0]}' has expected expected range. Expected range: ${JSON.stringify(actualEditForDoc[g].range)}. Actual range: ${JSON.stringify(expectedEditsForDoc[g].range)}`); - assert.strictEqual(actualEditForDoc[g].newText, expectedEditsForDoc[g].newText, `Edit '${g}' of '${actual[0]}' has expected edits`); - } - } - } -} - -suite('markdown: rename', () => { - - setup(async () => { - // the tests make the assumption that link providers are already registered - await vscode.extensions.getExtension('vscode.markdown-language-features')!.activate(); - }); - - test('Rename on header should not include leading #', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# abc` - )); - - const info = await prepareRename(doc, new vscode.Position(0, 0), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertRangeEqual(info!.range, new vscode.Range(0, 2, 0, 5)); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 2, 0, 5), 'New Header') - ] - }); - }); - - test('Rename on header should include leading or trailing #s', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### abc ###` - )); - - const info = await prepareRename(doc, new vscode.Position(0, 0), new InMemoryWorkspaceMarkdownDocuments([doc])); - assertRangeEqual(info!.range, new vscode.Range(0, 4, 0, 7)); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 7), 'New Header') - ] - }); - }); - - test('Rename on header should pick up links in doc', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### A b C`, // rename here - `[text](#a-b-c)`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'), - ] - }); - }); - - test('Rename on link should use slug for link', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### A b C`, - `[text](#a-b-c)`, // rename here - )); - - const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "New Header", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'), - ] - }); - }); - - test('Rename on link definition should work', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### A b C`, - `[text](#a-b-c)`, - `[ref]: #a-b-c`// rename here - )); - - const edit = await getRenameEdits(doc, new vscode.Position(2, 10), "New Header", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'), - new vscode.TextEdit(new vscode.Range(2, 8, 2, 13), 'new-header'), - ] - }); - }); - - test('Rename on header should pick up links across files', async () => { - const uri = workspacePath('doc.md'); - const otherUri = workspacePath('other.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### A b C`, // rename here - `[text](#a-b-c)`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 0), "New Header", new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(otherUri, joinLines( - `[text](#a-b-c)`, // Should not find this - `[text](./doc.md#a-b-c)`, // But should find this - `[text](./doc#a-b-c)`, // And this - )) - ])); - assertEditsEqual(edit!, { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'), - ] - }, { - uri: otherUri, edits: [ - new vscode.TextEdit(new vscode.Range(1, 16, 1, 21), 'new-header'), - new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'), - ] - }); - }); - - test('Rename on link should pick up links across files', async () => { - const uri = workspacePath('doc.md'); - const otherUri = workspacePath('other.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### A b C`, - `[text](#a-b-c)`, // rename here - )); - - const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "New Header", new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(otherUri, joinLines( - `[text](#a-b-c)`, // Should not find this - `[text](./doc.md#a-b-c)`, // But should find this - `[text](./doc#a-b-c)`, // And this - )) - ])); - assertEditsEqual(edit!, { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'), - ] - }, { - uri: otherUri, edits: [ - new vscode.TextEdit(new vscode.Range(1, 16, 1, 21), 'new-header'), - new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'), - ] - }); - }); - - test('Rename on link in other file should pick up all refs', async () => { - const uri = workspacePath('doc.md'); - const otherUri = workspacePath('other.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### A b C`, - `[text](#a-b-c)`, - )); - - const otherDoc = new InMemoryDocument(otherUri, joinLines( - `[text](#a-b-c)`, - `[text](./doc.md#a-b-c)`, - `[text](./doc#a-b-c)` - )); - - const expectedEdits = [ - { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 9), 'New Header'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 13), 'new-header'), - ] - }, { - uri: otherUri, edits: [ - new vscode.TextEdit(new vscode.Range(1, 16, 1, 21), 'new-header'), - new vscode.TextEdit(new vscode.Range(2, 13, 2, 18), 'new-header'), - ] - } - ]; - - { - // Rename on header with file extension - const edit = await getRenameEdits(otherDoc, new vscode.Position(1, 17), "New Header", new InMemoryWorkspaceMarkdownDocuments([ - doc, - otherDoc - ])); - assertEditsEqual(edit!, ...expectedEdits); - } - { - // Rename on header without extension - const edit = await getRenameEdits(otherDoc, new vscode.Position(2, 15), "New Header", new InMemoryWorkspaceMarkdownDocuments([ - doc, - otherDoc - ])); - assertEditsEqual(edit!, ...expectedEdits); - } - }); - - test('Rename on reference should rename references and definition', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text][ref]`, // rename here - `[other][ref]`, - ``, - `[ref]: https://example.com`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 8), "new ref", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 10), 'new ref'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 11), 'new ref'), - new vscode.TextEdit(new vscode.Range(3, 1, 3, 4), 'new ref'), - ] - }); - }); - - test('Rename on definition should rename references and definitions', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text][ref]`, - `[other][ref]`, - ``, - `[ref]: https://example.com`, // rename here - )); - - const edit = await getRenameEdits(doc, new vscode.Position(3, 3), "new ref", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 10), 'new ref'), - new vscode.TextEdit(new vscode.Range(1, 8, 1, 11), 'new ref'), - new vscode.TextEdit(new vscode.Range(3, 1, 3, 4), 'new ref'), - ] - }); - }); - - test('Rename on definition entry should rename header and references', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# a B c`, - `[ref text][ref]`, - `[direct](#a-b-c)`, - `[ref]: #a-b-c`, // rename here - )); - - const preparedInfo = await prepareRename(doc, new vscode.Position(3, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.strictEqual(preparedInfo!.placeholder, 'a B c'); - assertRangeEqual(preparedInfo!.range, new vscode.Range(3, 8, 3, 13)); - - const edit = await getRenameEdits(doc, new vscode.Position(3, 10), "x Y z", new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 2, 0, 7), 'x Y z'), - new vscode.TextEdit(new vscode.Range(2, 10, 2, 15), 'x-y-z'), - new vscode.TextEdit(new vscode.Range(3, 8, 3, 13), 'x-y-z'), - ] - }); - }); - - test('Rename should not be supported on link text', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `# Header`, - `[text](#header)`, - )); - - await assert.rejects(prepareRename(doc, new vscode.Position(1, 2), new InMemoryWorkspaceMarkdownDocuments([doc]))); - }); - - test('Path rename should use file path as range', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](./doc.md)`, - `[ref]: ./doc.md`, - )); - - const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.strictEqual(info!.placeholder, './doc.md'); - assertRangeEqual(info!.range, new vscode.Range(0, 7, 0, 15)); - }); - - test('Path rename\'s range should excludes fragment', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](./doc.md#some-header)`, - `[ref]: ./doc.md#some-header`, - )); - - const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.strictEqual(info!.placeholder, './doc.md'); - assertRangeEqual(info!.range, new vscode.Range(0, 7, 0, 15)); - }); - - test('Path rename should update file and all refs', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](./doc.md)`, - `[ref]: ./doc.md`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 10), './sub/newDoc.md', new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - originalUri: uri, - newUri: workspacePath('sub', 'newDoc.md'), - }, { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 15), './sub/newDoc.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 15), './sub/newDoc.md'), - ] - }); - }); - - test('Path rename using absolute file path should anchor to workspace root', async () => { - const uri = workspacePath('sub', 'doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](/sub/doc.md)`, - `[ref]: /sub/doc.md`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/newSub/newDoc.md', new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - originalUri: uri, - newUri: workspacePath('newSub', 'newDoc.md'), - }, { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/newSub/newDoc.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/newSub/newDoc.md'), - ] - }); - }); - - test('Path rename should use un-encoded paths as placeholder', async () => { - const uri = workspacePath('sub', 'doc with spaces.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](/sub/doc%20with%20spaces.md)`, - )); - - const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.strictEqual(info!.placeholder, '/sub/doc with spaces.md'); - }); - - test('Path rename should encode paths', async () => { - const uri = workspacePath('sub', 'doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](/sub/doc.md)`, - `[ref]: /sub/doc.md`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/NEW sub/new DOC.md', new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - originalUri: uri, - newUri: workspacePath('NEW sub', 'new DOC.md'), - }, { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/NEW%20sub/new%20DOC.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/NEW%20sub/new%20DOC.md'), - ] - }); - }); - - test('Path rename should work with unknown files', async () => { - const uri1 = workspacePath('doc1.md'); - const doc1 = new InMemoryDocument(uri1, joinLines( - `![img](/images/more/image.png)`, - ``, - `[ref]: /images/more/image.png`, - )); - - const uri2 = workspacePath('sub', 'doc2.md'); - const doc2 = new InMemoryDocument(uri2, joinLines( - `![img](/images/more/image.png)`, - )); - - const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), '/img/test/new.png', new InMemoryWorkspaceMarkdownDocuments([ - doc1, - doc2 - ])); - assertEditsEqual(edit!, - // Should not have file edits since the files don't exist here - { - uri: uri1, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'), - new vscode.TextEdit(new vscode.Range(2, 7, 2, 29), '/img/test/new.png'), - ] - }, - { - uri: uri2, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'), - ] - }); - }); - - test('Path rename should use .md extension on extension-less link', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](/doc#header)`, - `[ref]: /doc#other`, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/new File', new InMemoryWorkspaceMarkdownDocuments([doc])); - assertEditsEqual(edit!, { - originalUri: uri, - newUri: workspacePath('new File.md'), // Rename on disk should use file extension - }, { - uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 11), '/new%20File'), // Links should continue to use extension-less paths - new vscode.TextEdit(new vscode.Range(1, 7, 1, 11), '/new%20File'), - ] - }); - }); - - // TODO: fails on windows - test.skip('Path rename should use correctly resolved paths across files', async () => { - const uri1 = workspacePath('sub', 'doc.md'); - const doc1 = new InMemoryDocument(uri1, joinLines( - `[text](./doc.md)`, - `[ref]: ./doc.md`, - )); - - const uri2 = workspacePath('doc2.md'); - const doc2 = new InMemoryDocument(uri2, joinLines( - `[text](./sub/doc.md)`, - `[ref]: ./sub/doc.md`, - )); - - const uri3 = workspacePath('sub2', 'doc3.md'); - const doc3 = new InMemoryDocument(uri3, joinLines( - `[text](../sub/doc.md)`, - `[ref]: ../sub/doc.md`, - )); - - const uri4 = workspacePath('sub2', 'doc4.md'); - const doc4 = new InMemoryDocument(uri4, joinLines( - `[text](/sub/doc.md)`, - `[ref]: /sub/doc.md`, - )); - - const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), './new/new-doc.md', new InMemoryWorkspaceMarkdownDocuments([ - doc1, doc2, doc3, doc4, - ])); - assertEditsEqual(edit!, { - originalUri: uri1, - newUri: workspacePath('sub', 'new', 'new-doc.md'), - }, { - uri: uri1, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 15), './new/new-doc.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 15), './new/new-doc.md'), - ] - }, { - uri: uri2, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 19), './sub/new/new-doc.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 19), './sub/new/new-doc.md'), - ] - }, { - uri: uri3, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 20), '../sub/new/new-doc.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 20), '../sub/new/new-doc.md'), - ] - }, { - uri: uri4, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/sub/new/new-doc.md'), - new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/sub/new/new-doc.md'), - ] - }); - }); - - test('Path rename should resolve on links without prefix', async () => { - const uri1 = workspacePath('sub', 'doc.md'); - const doc1 = new InMemoryDocument(uri1, joinLines( - `![text](sub2/doc3.md)`, - )); - - const uri2 = workspacePath('doc2.md'); - const doc2 = new InMemoryDocument(uri2, joinLines( - `![text](sub/sub2/doc3.md)`, - )); - - const uri3 = workspacePath('sub', 'sub2', 'doc3.md'); - const doc3 = new InMemoryDocument(uri3, joinLines()); - - const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), 'sub2/cat.md', new InMemoryWorkspaceMarkdownDocuments([ - doc1, doc2, doc3 - ])); - assertEditsEqual(edit!, { - originalUri: workspacePath('sub', 'sub2', 'doc3.md'), - newUri: workspacePath('sub', 'sub2', 'cat.md'), - }, { - uri: uri1, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 20), 'sub2/cat.md')] - }, { - uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 24), 'sub/sub2/cat.md')] - }); - }); - - test('Rename on link should use header text as placeholder', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `### a B c ###`, - `[text](#a-b-c)`, - )); - - const info = await prepareRename(doc, new vscode.Position(1, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.strictEqual(info!.placeholder, 'a B c'); - assertRangeEqual(info!.range, new vscode.Range(1, 8, 1, 13)); - }); - - test('Rename on http uri should work', async () => { - const uri1 = workspacePath('doc.md'); - const uri2 = workspacePath('doc2.md'); - const doc = new InMemoryDocument(uri1, joinLines( - `[1](http://example.com)`, - `[2]: http://example.com`, - ``, - )); - - const edit = await getRenameEdits(doc, new vscode.Position(1, 10), "https://example.com/sub", new InMemoryWorkspaceMarkdownDocuments([ - doc, - new InMemoryDocument(uri2, joinLines( - `[4](http://example.com)`, - )) - ])); - assertEditsEqual(edit!, { - uri: uri1, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 22), 'https://example.com/sub'), - new vscode.TextEdit(new vscode.Range(1, 5, 1, 23), 'https://example.com/sub'), - new vscode.TextEdit(new vscode.Range(2, 1, 2, 19), 'https://example.com/sub'), - ] - }, { - uri: uri2, edits: [ - new vscode.TextEdit(new vscode.Range(0, 4, 0, 22), 'https://example.com/sub'), - ] - }); - }); -}); diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts deleted file mode 100644 index 61800d4371..0000000000 --- a/extensions/markdown-language-features/src/test/smartSelect.test.ts +++ /dev/null @@ -1,726 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as vscode from 'vscode'; -import { MdSmartSelect } from '../languageFeatures/smartSelect'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { CURSOR, getCursorPositions, joinLines } from './util'; - -const testFileName = vscode.Uri.file('test.md'); - -suite('markdown.SmartSelect', () => { - test('Smart select single word', async () => { - const ranges = await getSelectionRangesForDocument(`Hel${CURSOR}lo`); - assertNestedLineNumbersEqual(ranges![0], [0, 0]); - }); - - test('Smart select multi-line paragraph', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. `, - `For example, the[node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter]`, - `(https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).` - )); - assertNestedLineNumbersEqual(ranges![0], [0, 2]); - }); - - test('Smart select paragraph', async () => { - const ranges = await getSelectionRangesForDocument(`Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).`); - - assertNestedLineNumbersEqual(ranges![0], [0, 0]); - }); - - test('Smart select html block', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `

`, - `${CURSOR}VS Code in action`, - `

`)); - - assertNestedLineNumbersEqual(ranges![0], [0, 2]); - }); - - test('Smart select header on header line', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# Header${CURSOR}`, - `Hello`)); - - assertNestedLineNumbersEqual(ranges![0], [0, 1]); - - }); - - test('Smart select single word w grandparent header on text line', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `## ParentHeader`, - `# Header`, - `${CURSOR}Hello` - )); - - assertNestedLineNumbersEqual(ranges![0], [2, 2], [1, 2]); - }); - - test('Smart select html block w parent header', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# Header`, - `${CURSOR}

`, - `VS Code in action`, - `

`)); - - assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 3], [0, 3]); - }); - - test('Smart select fenced code block', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `~~~`, - `a${CURSOR}`, - `~~~`)); - - assertNestedLineNumbersEqual(ranges![0], [0, 2]); - }); - - test('Smart select list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `- item 1`, - `- ${CURSOR}item 2`, - `- item 3`, - `- item 4`)); - assertNestedLineNumbersEqual(ranges![0], [1, 1], [0, 3]); - }); - - test('Smart select list with fenced code block', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `- item 1`, - `- ~~~`, - ` ${CURSOR}a`, - ` ~~~`, - `- item 3`, - `- item 4`)); - - assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 5]); - }); - - test('Smart select multi cursor', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `- ${CURSOR}item 1`, - `- ~~~`, - ` a`, - ` ~~~`, - `- ${CURSOR}item 3`, - `- item 4`)); - - assertNestedLineNumbersEqual(ranges![0], [0, 0], [0, 5]); - assertNestedLineNumbersEqual(ranges![1], [4, 4], [0, 5]); - }); - - test('Smart select nested block quotes', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `> item 1`, - `> item 2`, - `>> ${CURSOR}item 3`, - `>> item 4`)); - assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [0, 3]); - }); - - test('Smart select multi nested block quotes', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `> item 1`, - `>> item 2`, - `>>> ${CURSOR}item 3`, - `>>>> item 4`)); - assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [0, 3]); - }); - - test('Smart select subheader content', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - `content 1`, - `## sub header 1`, - `${CURSOR}content 2`, - `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]); - }); - - test('Smart select subheader line', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - `content 1`, - `## sub header 1${CURSOR}`, - `content 2`, - `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [2, 3], [1, 3], [0, 3]); - }); - - test('Smart select blank line', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - `content 1`, - `${CURSOR} `, - `content 2`, - `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 3]); - }); - - test('Smart select line between paragraphs', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `paragraph 1`, - `${CURSOR}`, - `paragraph 2`)); - - assertNestedLineNumbersEqual(ranges![0], [0, 2]); - }); - - test('Smart select empty document', async () => { - const ranges = await getSelectionRangesForDocument(``, [new vscode.Position(0, 0)]); - assert.strictEqual(ranges!.length, 0); - }); - - test('Smart select fenced code block then list then subheader content then subheader then header content then header', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - /* 00 */ `# main header 1`, - /* 01 */ `content 1`, - /* 02 */ `## sub header 1`, - /* 03 */ `- item 1`, - /* 04 */ `- ~~~`, - /* 05 */ ` ${CURSOR}a`, - /* 06 */ ` ~~~`, - /* 07 */ `- item 3`, - /* 08 */ `- item 4`, - /* 09 */ ``, - /* 10 */ `more content`, - /* 11 */ `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [4, 6], [3, 8], [3, 10], [2, 10], [1, 10], [0, 10]); - }); - - test('Smart select list with one element without selecting child subheader', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - /* 00 */ `# main header 1`, - /* 01 */ ``, - /* 02 */ `- list ${CURSOR}`, - /* 03 */ ``, - /* 04 */ `## sub header`, - /* 05 */ ``, - /* 06 */ `content 2`, - /* 07 */ `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [2, 2], [1, 3], [1, 6], [0, 6]); - }); - - test('Smart select content under header then subheaders and their content', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main ${CURSOR}header 1`, - ``, - `- list`, - `paragraph`, - `## sub header`, - ``, - `content 2`, - `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [0, 3], [0, 6]); - }); - - test('Smart select last blockquote element under header then subheaders and their content', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `> block`, - `> block`, - `>> block`, - `>> ${CURSOR}block`, - ``, - `paragraph`, - `## sub header`, - ``, - `content 2`, - `# main header 2`)); - - assertNestedLineNumbersEqual(ranges![0], [5, 5], [4, 5], [2, 5], [1, 7], [1, 10], [0, 10]); - }); - - test('Smart select content of subheader then subheader then content of main header then main header', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `> block`, - `> block`, - `>> block`, - `>> block`, - ``, - `paragraph`, - `## sub header`, - ``, - ``, - `${CURSOR}`, - ``, - `### main header 2`, - `- content 2`, - `- content 2`, - `- content 2`, - `content 2`)); - - assertNestedLineNumbersEqual(ranges![0], [11, 11], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]); - }); - - test('Smart select last line content of subheader then subheader then content of main header then main header', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `> block`, - `> block`, - `>> block`, - `>> block`, - ``, - `paragraph`, - `## sub header`, - ``, - ``, - ``, - ``, - `### main header 2`, - `- content 2`, - `- content 2`, - `- content 2`, - `- ${CURSOR}content 2`)); - - assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); - }); - - test('Smart select last line content after content of subheader then subheader then content of main header then main header', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `> block`, - `> block`, - `>> block`, - `>> block`, - ``, - `paragraph`, - `## sub header`, - ``, - ``, - ``, - ``, - `### main header 2`, - `- content 2`, - `- content 2`, - `- content 2`, - `- content 2${CURSOR}`)); - - assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]); - }); - - test('Smart select fenced code block then list then rest of content', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `> block`, - `> block`, - `>> block`, - `>> block`, - ``, - `- paragraph`, - `- ~~~`, - ` my`, - ` ${CURSOR}code`, - ` goes here`, - ` ~~~`, - `- content`, - `- content 2`, - `- content 2`, - `- content 2`, - `- content 2`)); - - assertNestedLineNumbersEqual(ranges![0], [9, 11], [8, 12], [8, 12], [7, 17], [1, 17], [0, 17]); - }); - - test('Smart select fenced code block then list then rest of content on fenced line', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `> block`, - `> block`, - `>> block`, - `>> block`, - ``, - `- paragraph`, - `- ~~~${CURSOR}`, - ` my`, - ` code`, - ` goes here`, - ` ~~~`, - `- content`, - `- content 2`, - `- content 2`, - `- content 2`, - `- content 2`)); - - assertNestedLineNumbersEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]); - }); - - test('Smart select without multiple ranges', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - ``, - `- ${CURSOR}paragraph`, - `- content`)); - - assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]); - }); - - test('Smart select on second level of a list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `* level 0`, - ` * level 1`, - ` * level 1`, - ` * level 2`, - ` * level 1`, - ` * level ${CURSOR}1`, - `* level 0`)); - - assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]); - }); - - test('Smart select on third level of a list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `* level 0`, - ` * level 1`, - ` * level 1`, - ` * level ${CURSOR}2`, - ` * level 2`, - ` * level 1`, - ` * level 1`, - `* level 0`)); - assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]); - }); - - test('Smart select level 2 then level 1', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `* level 1`, - ` * level ${CURSOR}2`, - ` * level 2`, - `* level 1`)); - assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]); - }); - - test('Smart select last list item', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `- level 1`, - `- level 2`, - `- level 2`, - `- level ${CURSOR}1`)); - assertNestedLineNumbersEqual(ranges![0], [3, 3], [0, 3]); - }); - - test('Smart select without multiple ranges', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - ``, - `- ${CURSOR}paragraph`, - `- content`)); - - assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]); - }); - - test('Smart select on second level of a list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `* level 0`, - ` * level 1`, - ` * level 1`, - ` * level 2`, - ` * level 1`, - ` * level ${CURSOR}1`, - `* level 0`)); - - assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]); - }); - - test('Smart select on third level of a list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `* level 0`, - ` * level 1`, - ` * level 1`, - ` * level ${CURSOR}2`, - ` * level 2`, - ` * level 1`, - ` * level 1`, - `* level 0`)); - assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]); - }); - - test('Smart select level 2 then level 1', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `* level 1`, - ` * level ${CURSOR}2`, - ` * level 2`, - `* level 1`)); - assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]); - }); - - test('Smart select bold', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `stuff here **new${CURSOR}item** and here` - )); - assertNestedRangesEqual(ranges![0], [0, 13, 0, 30], [0, 11, 0, 32], [0, 0, 0, 41]); - }); - - test('Smart select link', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `stuff here [text](https${CURSOR}://google.com) and here` - )); - assertNestedRangesEqual(ranges![0], [0, 18, 0, 46], [0, 17, 0, 47], [0, 11, 0, 47], [0, 0, 0, 56]); - }); - - test('Smart select brackets', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `stuff here [te${CURSOR}xt](https://google.com) and here` - )); - assertNestedRangesEqual(ranges![0], [0, 12, 0, 26], [0, 11, 0, 27], [0, 11, 0, 47], [0, 0, 0, 56]); - }); - - test('Smart select brackets under header in list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `- list`, - `paragraph`, - `## sub header`, - `- list`, - `- stuff here [te${CURSOR}xt](https://google.com) and here`, - `- list` - )); - assertNestedRangesEqual(ranges![0], [6, 14, 6, 28], [6, 13, 6, 29], [6, 13, 6, 49], [6, 0, 6, 58], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); - }); - - test('Smart select link under header in list', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `- list`, - `paragraph`, - `## sub header`, - `- list`, - `- stuff here [text](${CURSOR}https://google.com) and here`, - `- list` - )); - assertNestedRangesEqual(ranges![0], [6, 20, 6, 48], [6, 19, 6, 49], [6, 13, 6, 49], [6, 0, 6, 58], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); - }); - - test('Smart select bold within list where multiple bold elements exists', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `# main header 1`, - ``, - `- list`, - `paragraph`, - `## sub header`, - `- list`, - `- stuff here [text] **${CURSOR}items in here** and **here**`, - `- list` - )); - assertNestedRangesEqual(ranges![0], [6, 22, 6, 45], [6, 20, 6, 47], [6, 0, 6, 60], [5, 0, 7, 6], [4, 0, 7, 6], [1, 0, 7, 6], [0, 0, 7, 6]); - }); - - test('Smart select link in paragraph with multiple links', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `This[extension](https://marketplace.visualstudio.com/items?itemName=meganrogge.template-string-converter) addresses this [requ${CURSOR}est](https://github.com/microsoft/vscode/issues/56704) to convert Javascript/Typescript quotes to backticks when has been entered within a string.` - )); - assertNestedRangesEqual(ranges![0], [0, 123, 0, 140], [0, 122, 0, 141], [0, 122, 0, 191], [0, 0, 0, 283]); - }); - - test('Smart select bold link', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `**[extens${CURSOR}ion](https://google.com)**` - )); - assertNestedRangesEqual(ranges![0], [0, 3, 0, 22], [0, 2, 0, 23], [0, 2, 0, 43], [0, 2, 0, 43], [0, 0, 0, 45], [0, 0, 0, 45]); - }); - - test('Smart select inline code block', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `[\`code ${CURSOR} link\`]` - )); - assertNestedRangesEqual(ranges![0], [0, 2, 0, 22], [0, 1, 0, 23], [0, 0, 0, 24]); - }); - - test('Smart select link with inline code block text', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `[\`code ${CURSOR} link\`](http://example.com)` - )); - assertNestedRangesEqual(ranges![0], [0, 2, 0, 22], [0, 1, 0, 23], [0, 1, 0, 23], [0, 0, 0, 24], [0, 0, 0, 44], [0, 0, 0, 44]); - }); - - test('Smart select italic', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `*some nice ${CURSOR}text*` - )); - assertNestedRangesEqual(ranges![0], [0, 1, 0, 25], [0, 0, 0, 26], [0, 0, 0, 26]); - }); - - test('Smart select italic link', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `*[extens${CURSOR}ion](https://google.com)*` - )); - assertNestedRangesEqual(ranges![0], [0, 2, 0, 21], [0, 1, 0, 22], [0, 1, 0, 42], [0, 1, 0, 42], [0, 0, 0, 43], [0, 0, 0, 43]); - }); - - test('Smart select italic on end', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `*word1 word2 word3${CURSOR}*` - )); - assertNestedRangesEqual(ranges![0], [0, 1, 0, 28], [0, 0, 0, 29], [0, 0, 0, 29]); - }); - - test('Smart select italic then bold', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `outer text **bold words *italic ${CURSOR} words* bold words** outer text` - )); - assertNestedRangesEqual(ranges![0], [0, 25, 0, 48], [0, 24, 0, 49], [0, 13, 0, 60], [0, 11, 0, 62], [0, 0, 0, 73]); - }); - - test('Smart select bold then italic', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `outer text *italic words **bold ${CURSOR} words** italic words* outer text` - )); - assertNestedRangesEqual(ranges![0], [0, 27, 0, 48], [0, 25, 0, 50], [0, 12, 0, 63], [0, 11, 0, 64], [0, 0, 0, 75]); - }); - - test('Third level header from release notes', async () => { - const ranges = await getSelectionRangesForDocument( - joinLines( - `---`, - `Order: 60`, - `TOCTitle: October 2020`, - `PageTitle: Visual Studio Code October 2020`, - `MetaDescription: Learn what is new in the Visual Studio Code October 2020 Release (1.51)`, - `MetaSocialImage: 1_51/release-highlights.png`, - `Date: 2020-11-6`, - `DownloadVersion: 1.51.1`, - `---`, - `# October 2020 (version 1.51)`, - ``, - `**Update 1.51.1**: The update addresses these [issues](https://github.com/microsoft/vscode/issues?q=is%3Aissue+milestone%3A%22October+2020+Recovery%22+is%3Aclosed+).`, - ``, - ``, - ``, - `---`, - ``, - `Welcome to the October 2020 release of Visual Studio Code. As announced in the [October iteration plan](https://github.com/microsoft/vscode/issues/108473), we focused on housekeeping GitHub issues and pull requests as documented in our issue grooming guide.`, - ``, - `We also worked with our partners at GitHub on GitHub Codespaces, which ended up being more involved than originally anticipated. To that end, we'll continue working on housekeeping for part of the November iteration.`, - ``, - `During this housekeeping milestone, we also addressed several feature requests and community [pull requests](#thank-you). Read on to learn about new features and settings.`, - ``, - `## Workbench`, - ``, - `### More prominent pinned tabs`, - ``, - `${CURSOR}Pinned tabs will now always show their pin icon, even while inactive, to make them easier to identify. If an editor is both pinned and contains unsaved changes, the icon reflects both states.`, - ``, - `![Inactive pinned tabs showing pin icons](images/1_51/pinned-tabs.png)` - ) - ); - assertNestedRangesEqual(ranges![0], [27, 0, 27, 201], [26, 0, 29, 70], [25, 0, 29, 70], [24, 0, 29, 70], [23, 0, 29, 70], [10, 0, 29, 70], [9, 0, 29, 70]); - }); - -}); - - -function assertNestedLineNumbersEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) { - const lineage = getLineage(range); - assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length} ${getValues(lineage)}`); - for (let i = 0; i < lineage.length; i++) { - assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`); - } -} - -function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number, number, number][]) { - const lineage = getLineage(range); - assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length} ${getValues(lineage)}`); - for (let i = 0; i < lineage.length; i++) { - assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][2], `parent at a depth of ${i}`); - assert(lineage[i].range.start.character === expectedRanges[i][1], `parent at a depth of ${i} on start char`); - assert(lineage[i].range.end.character === expectedRanges[i][3], `parent at a depth of ${i} on end char`); - } -} - -function getLineage(range: vscode.SelectionRange): vscode.SelectionRange[] { - const result: vscode.SelectionRange[] = []; - let currentRange: vscode.SelectionRange | undefined = range; - while (currentRange) { - result.push(currentRange); - currentRange = currentRange.parent; - } - return result; -} - -function getValues(ranges: vscode.SelectionRange[]): string[] { - return ranges.map(range => { - return range.range.start.line + ' ' + range.range.start.character + ' ' + range.range.end.line + ' ' + range.range.end.character; - }); -} - -function assertLineNumbersEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) { - assert.strictEqual(selectionRange.range.start.line, startLine, `failed on start line ${message}`); - assert.strictEqual(selectionRange.range.end.line, endLine, `failed on end line ${message}`); -} - -function getSelectionRangesForDocument(contents: string, pos?: vscode.Position[]): Promise { - const doc = new InMemoryDocument(testFileName, contents); - const provider = new MdSmartSelect(createNewMarkdownEngine()); - const positions = pos ? pos : getCursorPositions(contents, doc); - return provider.provideSelectionRanges(doc, positions, new vscode.CancellationTokenSource().token); -} diff --git a/extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts b/extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts deleted file mode 100644 index 6e8029061e..0000000000 --- a/extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { TableOfContents } from '../tableOfContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryDocument } from '../util/inMemoryDocument'; - - -const testFileName = vscode.Uri.file('test.md'); - -suite('markdown.TableOfContentsProvider', () => { - test('Lookup should not return anything for empty document', async () => { - const doc = new InMemoryDocument(testFileName, ''); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual(provider.lookup(''), undefined); - assert.strictEqual(provider.lookup('foo'), undefined); - }); - - test('Lookup should not return anything for document with no headers', async () => { - const doc = new InMemoryDocument(testFileName, 'a *b*\nc'); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual(provider.lookup(''), undefined); - assert.strictEqual(provider.lookup('foo'), undefined); - assert.strictEqual(provider.lookup('a'), undefined); - assert.strictEqual(provider.lookup('b'), undefined); - }); - - test('Lookup should return basic #header', async () => { - const doc = new InMemoryDocument(testFileName, `# a\nx\n# c`); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - { - const entry = provider.lookup('a'); - assert.ok(entry); - assert.strictEqual(entry!.line, 0); - } - { - assert.strictEqual(provider.lookup('x'), undefined); - } - { - const entry = provider.lookup('c'); - assert.ok(entry); - assert.strictEqual(entry!.line, 2); - } - }); - - test('Lookups should be case in-sensitive', async () => { - const doc = new InMemoryDocument(testFileName, `# fOo\n`); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual((provider.lookup('fOo'))!.line, 0); - assert.strictEqual((provider.lookup('foo'))!.line, 0); - assert.strictEqual((provider.lookup('FOO'))!.line, 0); - }); - - test('Lookups should ignore leading and trailing white-space, and collapse internal whitespace', async () => { - const doc = new InMemoryDocument(testFileName, `# f o o \n`); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual((provider.lookup('f o o'))!.line, 0); - assert.strictEqual((provider.lookup(' f o o'))!.line, 0); - assert.strictEqual((provider.lookup(' f o o '))!.line, 0); - assert.strictEqual((provider.lookup('f o o'))!.line, 0); - assert.strictEqual((provider.lookup('f o o'))!.line, 0); - - assert.strictEqual(provider.lookup('f'), undefined); - assert.strictEqual(provider.lookup('foo'), undefined); - assert.strictEqual(provider.lookup('fo o'), undefined); - }); - - test('should handle special characters #44779', async () => { - const doc = new InMemoryDocument(testFileName, `# Indentação\n`); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual((provider.lookup('indentação'))!.line, 0); - }); - - test('should handle special characters 2, #48482', async () => { - const doc = new InMemoryDocument(testFileName, `# Инструкция - Делай Раз, Делай Два\n`); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual((provider.lookup('инструкция---делай-раз-делай-два'))!.line, 0); - }); - - test('should handle special characters 3, #37079', async () => { - const doc = new InMemoryDocument(testFileName, `## Header 2 -### Header 3 -## Заголовок 2 -### Заголовок 3 -### Заголовок Header 3 -## Заголовок`); - - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - assert.strictEqual((provider.lookup('header-2'))!.line, 0); - assert.strictEqual((provider.lookup('header-3'))!.line, 1); - assert.strictEqual((provider.lookup('Заголовок-2'))!.line, 2); - assert.strictEqual((provider.lookup('Заголовок-3'))!.line, 3); - assert.strictEqual((provider.lookup('Заголовок-header-3'))!.line, 4); - assert.strictEqual((provider.lookup('Заголовок'))!.line, 5); - }); - - test('Lookup should support suffixes for repeated headers', async () => { - const doc = new InMemoryDocument(testFileName, `# a\n# a\n## a`); - const provider = await TableOfContents.create(createNewMarkdownEngine(), doc); - - { - const entry = provider.lookup('a'); - assert.ok(entry); - assert.strictEqual(entry!.line, 0); - } - { - const entry = provider.lookup('a-1'); - assert.ok(entry); - assert.strictEqual(entry!.line, 1); - } - { - const entry = provider.lookup('a-2'); - assert.ok(entry); - assert.strictEqual(entry!.line, 2); - } - }); -}); diff --git a/extensions/markdown-language-features/src/test/util.ts b/extensions/markdown-language-features/src/test/util.ts index 9d7d4f6f6f..433e7ab32e 100644 --- a/extensions/markdown-language-features/src/test/util.ts +++ b/extensions/markdown-language-features/src/test/util.ts @@ -5,33 +5,11 @@ import * as assert from 'assert'; import * as os from 'os'; import * as vscode from 'vscode'; -import { InMemoryDocument } from '../util/inMemoryDocument'; +import { DisposableStore } from '../util/dispose'; export const joinLines = (...args: string[]) => args.join(os.platform() === 'win32' ? '\r\n' : '\n'); -export const noopToken = new class implements vscode.CancellationToken { - _onCancellationRequestedEmitter = new vscode.EventEmitter(); - onCancellationRequested = this._onCancellationRequestedEmitter.event; - - get isCancellationRequested() { return false; } -}; - -export const CURSOR = '$$CURSOR$$'; - -export function getCursorPositions(contents: string, doc: InMemoryDocument): vscode.Position[] { - let positions: vscode.Position[] = []; - let index = 0; - let wordLength = 0; - while (index !== -1) { - index = contents.indexOf(CURSOR, index + wordLength); - if (index !== -1) { - positions.push(doc.positionAt(index)); - } - wordLength = CURSOR.length; - } - return positions; -} export function workspacePath(...segments: string[]): vscode.Uri { return vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, ...segments); @@ -43,3 +21,14 @@ export function assertRangeEqual(expected: vscode.Range, actual: vscode.Range, m assert.strictEqual(expected.end.line, actual.end.line, message); assert.strictEqual(expected.end.character, actual.end.character, message); } + +export function withStore(fn: (this: Mocha.Context, store: DisposableStore) => Promise) { + return async function (this: Mocha.Context): Promise { + const store = new DisposableStore(); + try { + return await fn.call(this, store); + } finally { + store.dispose(); + } + }; +} diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts deleted file mode 100644 index 4fe98b6bd3..0000000000 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import 'mocha'; -import * as vscode from 'vscode'; -import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; -import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbolProvider'; -import { SkinnyTextDocument } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; -import { InMemoryDocument } from '../util/inMemoryDocument'; -import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; - - -const symbolProvider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); - -suite('markdown.WorkspaceSymbolProvider', () => { - test('Should not return anything for empty workspace', async () => { - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([])); - - assert.deepStrictEqual(await provider.provideWorkspaceSymbols(''), []); - }); - - test('Should return symbols from workspace with one markdown file', async () => { - const testFileName = vscode.Uri.file('test.md'); - - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(testFileName, `# header1\nabc\n## header2`) - ])); - - const symbols = await provider.provideWorkspaceSymbols(''); - assert.strictEqual(symbols.length, 2); - assert.strictEqual(symbols[0].name, '# header1'); - assert.strictEqual(symbols[1].name, '## header2'); - }); - - test('Should return all content basic workspace', async () => { - const fileNameCount = 10; - const files: SkinnyTextDocument[] = []; - for (let i = 0; i < fileNameCount; ++i) { - const testFileName = vscode.Uri.file(`test${i}.md`); - files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`)); - } - - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments(files)); - - const symbols = await provider.provideWorkspaceSymbols(''); - assert.strictEqual(symbols.length, fileNameCount * 2); - }); - - test('Should update results when markdown file changes symbols', async () => { - const testFileName = vscode.Uri.file('test.md'); - - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(testFileName, `# header1`, 1 /* version */) - ]); - - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); - - // Update file - workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); - const newSymbols = await provider.provideWorkspaceSymbols(''); - assert.strictEqual(newSymbols.length, 2); - assert.strictEqual(newSymbols[0].name, '# new header'); - assert.strictEqual(newSymbols[1].name, '## header2'); - }); - - test('Should remove results when file is deleted', async () => { - const testFileName = vscode.Uri.file('test.md'); - - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(testFileName, `# header1`) - ]); - - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); - - // delete file - workspaceFileProvider.deleteDocument(testFileName); - const newSymbols = await provider.provideWorkspaceSymbols(''); - assert.strictEqual(newSymbols.length, 0); - }); - - test('Should update results when markdown file is created', async () => { - const testFileName = vscode.Uri.file('test.md'); - - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(testFileName, `# header1`) - ]); - - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); - - // Creat file - workspaceFileProvider.createDocument(new InMemoryDocument(vscode.Uri.file('test2.md'), `# new header\nabc\n## header2`)); - const newSymbols = await provider.provideWorkspaceSymbols(''); - assert.strictEqual(newSymbols.length, 3); - }); -}); diff --git a/extensions/markdown-language-features/src/types/textDocument.ts b/extensions/markdown-language-features/src/types/textDocument.ts new file mode 100644 index 0000000000..7c1b74c062 --- /dev/null +++ b/extensions/markdown-language-features/src/types/textDocument.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +/** + * Minimal version of {@link vscode.TextDocument}. + */ +export interface ITextDocument { + readonly uri: vscode.Uri; + readonly version: number; + readonly lineCount: number; + + getText(range?: vscode.Range): string; + positionAt(offset: number): vscode.Position; +} + +export function getLine(doc: ITextDocument, line: number): string { + return doc.getText(new vscode.Range(line, 0, line, Number.MAX_VALUE)).replace(/\r?\n$/, ''); +} diff --git a/extensions/markdown-language-features/src/util/async.ts b/extensions/markdown-language-features/src/util/async.ts index 704fbfdeae..78c9b9b40e 100644 --- a/extensions/markdown-language-features/src/util/async.ts +++ b/extensions/markdown-language-features/src/util/async.ts @@ -25,6 +25,10 @@ export class Delayer { this.task = null; } + dispose() { + this.cancelTimeout(); + } + public trigger(task: ITask, delay: number = this.defaultDelay): Promise { this.task = task; if (delay >= 0) { diff --git a/extensions/markdown-language-features/src/util/cancellation.ts b/extensions/markdown-language-features/src/util/cancellation.ts new file mode 100644 index 0000000000..27f756f49b --- /dev/null +++ b/extensions/markdown-language-features/src/util/cancellation.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export const noopToken = new class implements vscode.CancellationToken { + _onCancellationRequestedEmitter = new vscode.EventEmitter(); + onCancellationRequested = this._onCancellationRequestedEmitter.event; + + get isCancellationRequested() { return false; } +}; diff --git a/extensions/markdown-language-features/src/util/dispose.ts b/extensions/markdown-language-features/src/util/dispose.ts index 43c903ff56..7526357e2c 100644 --- a/extensions/markdown-language-features/src/util/dispose.ts +++ b/extensions/markdown-language-features/src/util/dispose.ts @@ -5,13 +5,36 @@ import * as vscode from 'vscode'; -export function disposeAll(disposables: vscode.Disposable[]) { - while (disposables.length) { - const item = disposables.pop(); - item?.dispose(); +export class MultiDisposeError extends Error { + constructor( + public readonly errors: any[] + ) { + super(`Encountered errors while disposing of store. Errors: [${errors.join(', ')}]`); } } +export function disposeAll(disposables: Iterable) { + const errors: any[] = []; + + for (const disposable of disposables) { + try { + disposable.dispose(); + } catch (e) { + errors.push(e); + } + } + + if (errors.length === 1) { + throw errors[0]; + } else if (errors.length > 1) { + throw new MultiDisposeError(errors); + } +} + +export interface IDisposable { + dispose(): void; +} + export abstract class Disposable { private _isDisposed = false; @@ -25,7 +48,7 @@ export abstract class Disposable { disposeAll(this._disposables); } - protected _register(value: T): T { + protected _register(value: T): T { if (this._isDisposed) { value.dispose(); } else { @@ -38,3 +61,22 @@ export abstract class Disposable { return this._isDisposed; } } + +export class DisposableStore extends Disposable { + private readonly items = new Set(); + + public override dispose() { + super.dispose(); + disposeAll(this.items); + this.items.clear(); + } + + public add(item: T): T { + if (this.isDisposed) { + console.warn('Adding to disposed store. Item will be leaked'); + } + + this.items.add(item); + return item; + } +} diff --git a/extensions/markdown-language-features/src/util/dom.ts b/extensions/markdown-language-features/src/util/dom.ts new file mode 100644 index 0000000000..0435f52b71 --- /dev/null +++ b/extensions/markdown-language-features/src/util/dom.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; + +export function escapeAttribute(value: string | vscode.Uri): string { + return value.toString().replace(/"/g, '"'); +} + +export function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 64; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/extensions/markdown-language-features/src/util/file.ts b/extensions/markdown-language-features/src/util/file.ts index 7ce14bffe0..97082955e7 100644 --- a/extensions/markdown-language-features/src/util/file.ts +++ b/extensions/markdown-language-features/src/util/file.ts @@ -4,7 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as URI from 'vscode-uri'; + +export const markdownFileExtensions = Object.freeze([ + 'md', + 'mkd', + 'mdwn', + 'mdown', + 'markdown', + 'markdn', + 'mdtxt', + 'mdtext', + 'workbook', +]); export function isMarkdownFile(document: vscode.TextDocument) { return document.languageId === 'markdown'; } + +export function looksLikeMarkdownPath(resolvedHrefPath: vscode.Uri) { + return markdownFileExtensions.includes(URI.Utils.extname(resolvedHrefPath).toLowerCase().replace('.', '')); +} diff --git a/extensions/markdown-language-features/src/util/inMemoryDocument.ts b/extensions/markdown-language-features/src/util/inMemoryDocument.ts index e25038ce46..9e147bb64c 100644 --- a/extensions/markdown-language-features/src/util/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/util/inMemoryDocument.ts @@ -5,14 +5,12 @@ import * as vscode from 'vscode'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { SkinnyTextDocument, SkinnyTextLine } from '../workspaceContents'; +import { ITextDocument } from '../types/textDocument'; -export class InMemoryDocument implements SkinnyTextDocument { +export class InMemoryDocument implements ITextDocument { private readonly _doc: TextDocument; - private lines: SkinnyTextLine[] | undefined; - constructor( public readonly uri: vscode.Uri, contents: string, public readonly version = 0, @@ -25,16 +23,6 @@ export class InMemoryDocument implements SkinnyTextDocument { return this._doc.lineCount; } - lineAt(index: any): SkinnyTextLine { - if (!this.lines) { - this.lines = this._doc.getText().split(/\r?\n/).map(text => ({ - text, - get isEmptyOrWhitespace() { return /^\s*$/.test(text); } - })); - } - return this.lines[index]; - } - positionAt(offset: number): vscode.Position { const pos = this._doc.positionAt(offset); return new vscode.Position(pos.line, pos.character); diff --git a/extensions/markdown-language-features/src/util/openDocumentLink.ts b/extensions/markdown-language-features/src/util/openDocumentLink.ts index 85f7514b43..4a189feb64 100644 --- a/extensions/markdown-language-features/src/util/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/util/openDocumentLink.ts @@ -6,8 +6,9 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as uri from 'vscode-uri'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; +import { MdTableOfContentsProvider } from '../tableOfContents'; +import { ITextDocument } from '../types/textDocument'; +import { IMdWorkspace } from '../workspace'; import { isMarkdownFile } from './file'; export interface OpenDocumentLinkArgs { @@ -22,7 +23,7 @@ enum OpenMarkdownLinks { } export function resolveDocumentLink(href: string, markdownFile: vscode.Uri): vscode.Uri { - let [hrefPath, fragment] = href.split('#').map(c => decodeURIComponent(c)); + const [hrefPath, fragment] = href.split('#').map(c => decodeURIComponent(c)); if (hrefPath[0] === '/') { // Absolute path. Try to resolve relative to the workspace @@ -37,10 +38,10 @@ export function resolveDocumentLink(href: string, markdownFile: vscode.Uri): vsc return vscode.Uri.joinPath(dirnameUri, hrefPath).with({ fragment }); } -export async function openDocumentLink(engine: MarkdownEngine, targetResource: vscode.Uri, fromResource: vscode.Uri): Promise { +export async function openDocumentLink(tocProvider: MdTableOfContentsProvider, targetResource: vscode.Uri, fromResource: vscode.Uri): Promise { const column = getViewColumn(fromResource); - if (await tryNavigateToFragmentInActiveEditor(engine, targetResource)) { + if (await tryNavigateToFragmentInActiveEditor(tocProvider, targetResource)) { return; } @@ -58,7 +59,7 @@ export async function openDocumentLink(engine: MarkdownEngine, targetResource: v try { const stat = await vscode.workspace.fs.stat(dotMdResource); if (stat.type === vscode.FileType.File) { - await tryOpenMdFile(engine, dotMdResource, column); + await tryOpenMdFile(tocProvider, dotMdResource, column); return; } } catch { @@ -69,25 +70,33 @@ export async function openDocumentLink(engine: MarkdownEngine, targetResource: v return vscode.commands.executeCommand('revealInExplorer', targetResource); } - await tryOpenMdFile(engine, targetResource, column); + await tryOpenMdFile(tocProvider, targetResource, column); } -async function tryOpenMdFile(engine: MarkdownEngine, resource: vscode.Uri, column: vscode.ViewColumn): Promise { +async function tryOpenMdFile(tocProvider: MdTableOfContentsProvider, resource: vscode.Uri, column: vscode.ViewColumn): Promise { await vscode.commands.executeCommand('vscode.open', resource.with({ fragment: '' }), column); - return tryNavigateToFragmentInActiveEditor(engine, resource); + return tryNavigateToFragmentInActiveEditor(tocProvider, resource); } -async function tryNavigateToFragmentInActiveEditor(engine: MarkdownEngine, resource: vscode.Uri): Promise { +async function tryNavigateToFragmentInActiveEditor(tocProvider: MdTableOfContentsProvider, resource: vscode.Uri): Promise { + const notebookEditor = vscode.window.activeNotebookEditor; + if (notebookEditor?.notebook.uri.fsPath === resource.fsPath) { + if (await tryRevealLineInNotebook(tocProvider, notebookEditor, resource.fragment)) { + return true; + } + } + const activeEditor = vscode.window.activeTextEditor; if (activeEditor?.document.uri.fsPath === resource.fsPath) { if (isMarkdownFile(activeEditor.document)) { - if (await tryRevealLineUsingTocFragment(engine, activeEditor, resource.fragment)) { + if (await tryRevealLineUsingTocFragment(tocProvider, activeEditor, resource.fragment)) { return true; } } tryRevealLineUsingLineFragment(activeEditor, resource.fragment); return true; } + return false; } @@ -103,8 +112,26 @@ function getViewColumn(resource: vscode.Uri): vscode.ViewColumn { } } -async function tryRevealLineUsingTocFragment(engine: MarkdownEngine, editor: vscode.TextEditor, fragment: string): Promise { - const toc = await TableOfContents.create(engine, editor.document); +async function tryRevealLineInNotebook(tocProvider: MdTableOfContentsProvider, editor: vscode.NotebookEditor, fragment: string): Promise { + const toc = await tocProvider.createForNotebook(editor.notebook); + const entry = toc.lookup(fragment); + if (!entry) { + return false; + } + + const cell = editor.notebook.getCells().find(cell => cell.document.uri.toString() === entry.sectionLocation.uri.toString()); + if (!cell) { + return false; + } + + const range = new vscode.NotebookRange(cell.index, cell.index); + editor.selection = range; + editor.revealRange(range); + return true; +} + +async function tryRevealLineUsingTocFragment(tocProvider: MdTableOfContentsProvider, editor: vscode.TextEditor, fragment: string): Promise { + const toc = await tocProvider.getForDocument(editor.document); const entry = toc.lookup(fragment); if (entry) { const lineStart = new vscode.Range(entry.line, 0, entry.line, 0); @@ -129,9 +156,9 @@ function tryRevealLineUsingLineFragment(editor: vscode.TextEditor, fragment: str return false; } -export async function resolveUriToMarkdownFile(resource: vscode.Uri): Promise { +export async function resolveUriToMarkdownFile(workspace: IMdWorkspace, resource: vscode.Uri): Promise { try { - const doc = await tryResolveUriToMarkdownFile(resource); + const doc = await workspace.getOrLoadMarkdownDocument(resource); if (doc) { return doc; } @@ -141,21 +168,8 @@ export async function resolveUriToMarkdownFile(resource: vscode.Uri): Promise { - let document: vscode.TextDocument; - try { - document = await vscode.workspace.openTextDocument(resource); - } catch { - return undefined; - } - if (isMarkdownFile(document)) { - return document; - } - return undefined; -} diff --git a/extensions/markdown-language-features/src/util/resourceMap.ts b/extensions/markdown-language-features/src/util/resourceMap.ts new file mode 100644 index 0000000000..749227bad6 --- /dev/null +++ b/extensions/markdown-language-features/src/util/resourceMap.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +type ResourceToKey = (uri: vscode.Uri) => string; + +const defaultResourceToKey = (resource: vscode.Uri): string => resource.toString(); + +export class ResourceMap { + + private readonly map = new Map(); + + private readonly toKey: ResourceToKey; + + constructor(toKey: ResourceToKey = defaultResourceToKey) { + this.toKey = toKey; + } + + public set(uri: vscode.Uri, value: T): this { + this.map.set(this.toKey(uri), { uri, value }); + return this; + } + + public get(resource: vscode.Uri): T | undefined { + return this.map.get(this.toKey(resource))?.value; + } + + public has(resource: vscode.Uri): boolean { + return this.map.has(this.toKey(resource)); + } + + public get size(): number { + return this.map.size; + } + + public clear(): void { + this.map.clear(); + } + + public delete(resource: vscode.Uri): boolean { + return this.map.delete(this.toKey(resource)); + } + + public *values(): IterableIterator { + for (const entry of this.map.values()) { + yield entry.value; + } + } + + public *keys(): IterableIterator { + for (const entry of this.map.values()) { + yield entry.uri; + } + } + + public *entries(): IterableIterator<[vscode.Uri, T]> { + for (const entry of this.map.values()) { + yield [entry.uri, entry.value]; + } + } + + public [Symbol.iterator](): IterableIterator<[vscode.Uri, T]> { + return this.entries(); + } +} diff --git a/extensions/markdown-language-features/src/util/schemes.ts b/extensions/markdown-language-features/src/util/schemes.ts index 830bb7b96d..ff99b43f30 100644 --- a/extensions/markdown-language-features/src/util/schemes.ts +++ b/extensions/markdown-language-features/src/util/schemes.ts @@ -3,44 +3,15 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; - -export const Schemes = { - http: 'http:', - https: 'https:', - file: 'file:', +export const Schemes = Object.freeze({ + file: 'file', untitled: 'untitled', - mailto: 'mailto:', - data: 'data:', - vscode: 'vscode:', - 'vscode-insiders': 'vscode-insiders:', -}; - -const knownSchemes = [ - ...Object.values(Schemes), - `${vscode.env.uriScheme}:` -]; - -export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | undefined { - if (knownSchemes.some(knownScheme => isOfScheme(knownScheme, link))) { - return vscode.Uri.parse(link); - } - - return undefined; -} + mailto: 'mailto', + vscode: 'vscode', + 'vscode-insiders': 'vscode-insiders', + notebookCell: 'vscode-notebook-cell', +}); export function isOfScheme(scheme: string, link: string): boolean { - return link.toLowerCase().startsWith(scheme); + return link.toLowerCase().startsWith(scheme + ':'); } - -export const MarkdownFileExtensions: readonly string[] = [ - '.md', - '.mkd', - '.mdwn', - '.mdown', - '.markdown', - '.markdn', - '.mdtxt', - '.mdtext', - '.workbook', -]; diff --git a/src/vs/workbench/common/dnd.ts b/extensions/markdown-language-features/src/util/string.ts similarity index 71% rename from src/vs/workbench/common/dnd.ts rename to extensions/markdown-language-features/src/util/string.ts index 1af189eb76..5e23609d04 100644 --- a/src/vs/workbench/common/dnd.ts +++ b/extensions/markdown-language-features/src/util/string.ts @@ -3,9 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export interface IDataTransferItem { - asString(): Thenable; - value: any; +export function isEmptyOrWhitespace(str: string): boolean { + return /^\s*$/.test(str); } - -export type IDataTransfer = Map; diff --git a/extensions/markdown-language-features/src/util/workspaceCache.ts b/extensions/markdown-language-features/src/util/workspaceCache.ts new file mode 100644 index 0000000000..2231c7d109 --- /dev/null +++ b/extensions/markdown-language-features/src/util/workspaceCache.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { ITextDocument } from '../types/textDocument'; +import { IMdWorkspace } from '../workspace'; +import { Disposable } from './dispose'; +import { Lazy, lazy } from './lazy'; +import { ResourceMap } from './resourceMap'; + +class LazyResourceMap { + private readonly _map = new ResourceMap>>(); + + public has(resource: vscode.Uri): boolean { + return this._map.has(resource); + } + + public get(resource: vscode.Uri): Promise | undefined { + return this._map.get(resource)?.value; + } + + public set(resource: vscode.Uri, value: Lazy>) { + this._map.set(resource, value); + } + + public delete(resource: vscode.Uri) { + this._map.delete(resource); + } + + public entries(): Promise> { + return Promise.all(Array.from(this._map.entries(), async ([key, entry]) => { + return [key, await entry.value] as [vscode.Uri, T]; // {{SQL CARBON EDIT}} lewissanchez - Added strict typing + })); + } +} + +/** + * Cache of information per-document in the workspace. + * + * The values are computed lazily and invalidated when the document changes. + */ +export class MdDocumentInfoCache extends Disposable { + + private readonly _cache = new LazyResourceMap(); + private readonly _loadingDocuments = new ResourceMap>(); + + public constructor( + private readonly workspace: IMdWorkspace, + private readonly getValue: (document: ITextDocument) => Promise, + ) { + super(); + + this._register(this.workspace.onDidChangeMarkdownDocument(doc => this.invalidate(doc))); + this._register(this.workspace.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this)); + } + + public async get(resource: vscode.Uri): Promise { + let existing = this._cache.get(resource); + if (existing) { + return existing; + } + + const doc = await this.loadDocument(resource); + if (!doc) { + return undefined; + } + + // Check if we have invalidated + existing = this._cache.get(resource); + if (existing) { + return existing; + } + + return this.resetEntry(doc)?.value; + } + + public async getForDocument(document: ITextDocument): Promise { + const existing = this._cache.get(document.uri); + if (existing) { + return existing; + } + return this.resetEntry(document).value; + } + + private loadDocument(resource: vscode.Uri): Promise { + const existing = this._loadingDocuments.get(resource); + if (existing) { + return existing; + } + + const p = this.workspace.getOrLoadMarkdownDocument(resource); + this._loadingDocuments.set(resource, p); + p.finally(() => { + this._loadingDocuments.delete(resource); + }); + return p; + } + + private resetEntry(document: ITextDocument): Lazy> { + const value = lazy(() => this.getValue(document)); + this._cache.set(document.uri, value); + return value; + } + + private invalidate(document: ITextDocument): void { + if (this._cache.has(document.uri)) { + this.resetEntry(document); + } + } + + private onDidDeleteDocument(resource: vscode.Uri) { + this._cache.delete(resource); + } +} diff --git a/extensions/markdown-language-features/src/workspaceContents.ts b/extensions/markdown-language-features/src/workspace.ts similarity index 57% rename from extensions/markdown-language-features/src/workspaceContents.ts rename to extensions/markdown-language-features/src/workspace.ts index 386bd3d98d..a31ea18244 100644 --- a/extensions/markdown-language-features/src/workspaceContents.ts +++ b/extensions/markdown-language-features/src/workspace.ts @@ -4,48 +4,36 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { ITextDocument } from './types/textDocument'; import { coalesce } from './util/arrays'; import { Disposable } from './util/dispose'; -import { isMarkdownFile } from './util/file'; +import { isMarkdownFile, looksLikeMarkdownPath } from './util/file'; import { InMemoryDocument } from './util/inMemoryDocument'; import { Limiter } from './util/limiter'; - -/** - * Minimal version of {@link vscode.TextLine}. Used for mocking out in testing. - */ -export interface SkinnyTextLine { - readonly text: string; - readonly isEmptyOrWhitespace: boolean; -} - -/** - * Minimal version of {@link vscode.TextDocument}. Used for mocking out in testing. - */ -export interface SkinnyTextDocument { - readonly uri: vscode.Uri; - readonly version: number; - readonly lineCount: number; - - getText(range?: vscode.Range): string; - lineAt(line: number): SkinnyTextLine; - positionAt(offset: number): vscode.Position; -} +import { ResourceMap } from './util/resourceMap'; /** * Provides set of markdown files in the current workspace. */ -export interface MdWorkspaceContents { +export interface IMdWorkspace { /** * Get list of all known markdown files. */ - getAllMarkdownDocuments(): Promise>; + getAllMarkdownDocuments(): Promise>; - getMarkdownDocument(resource: vscode.Uri): Promise; + /** + * Check if a document already exists in the workspace contents. + */ + hasMarkdownDocument(resource: vscode.Uri): boolean; + + getOrLoadMarkdownDocument(resource: vscode.Uri): Promise; pathExists(resource: vscode.Uri): Promise; - readonly onDidChangeMarkdownDocument: vscode.Event; - readonly onDidCreateMarkdownDocument: vscode.Event; + readDirectory(resource: vscode.Uri): Promise<[string, vscode.FileType][]>; + + readonly onDidChangeMarkdownDocument: vscode.Event; + readonly onDidCreateMarkdownDocument: vscode.Event; readonly onDidDeleteMarkdownDocument: vscode.Event; } @@ -54,14 +42,16 @@ export interface MdWorkspaceContents { * * This includes both opened text documents and markdown files in the workspace. */ -export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspaceContents { +export class VsCodeMdWorkspace extends Disposable implements IMdWorkspace { - private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); - private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); + private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); + private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); private _watcher: vscode.FileSystemWatcher | undefined; + private readonly _documentCache = new ResourceMap(); + private readonly utf8Decoder = new TextDecoder('utf-8'); /** @@ -70,19 +60,19 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace * * @returns Array of processed .md files. */ - async getAllMarkdownDocuments(): Promise { + async getAllMarkdownDocuments(): Promise { const maxConcurrent = 20; - const foundFiles = new Set(); - const limiter = new Limiter(maxConcurrent); + const foundFiles = new ResourceMap(); + const limiter = new Limiter(maxConcurrent); // Add files on disk const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**'); const onDiskResults = await Promise.all(resources.map(resource => { return limiter.queue(async () => { - const doc = await this.getMarkdownDocument(resource); + const doc = await this.getOrLoadMarkdownDocument(resource); if (doc) { - foundFiles.add(doc.uri.toString()); + foundFiles.set(resource); } return doc; }); @@ -90,7 +80,7 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace // Add opened files (such as untitled files) const openTextDocumentResults = await Promise.all(vscode.workspace.textDocuments - .filter(doc => !foundFiles.has(doc.uri.toString()) && isMarkdownFile(doc))); + .filter(doc => !foundFiles.has(doc.uri) && this.isRelevantMarkdownDocument(doc))); return coalesce([...onDiskResults, ...openTextDocumentResults]); } @@ -118,47 +108,80 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md')); this._register(this._watcher.onDidChange(async resource => { - const document = await this.getMarkdownDocument(resource); + this._documentCache.delete(resource); + const document = await this.getOrLoadMarkdownDocument(resource); if (document) { this._onDidChangeMarkdownDocumentEmitter.fire(document); } })); this._register(this._watcher.onDidCreate(async resource => { - const document = await this.getMarkdownDocument(resource); + const document = await this.getOrLoadMarkdownDocument(resource); if (document) { this._onDidCreateMarkdownDocumentEmitter.fire(document); } })); this._register(this._watcher.onDidDelete(resource => { + this._documentCache.delete(resource); this._onDidDeleteMarkdownDocumentEmitter.fire(resource); })); + this._register(vscode.workspace.onDidOpenTextDocument(e => { + this._documentCache.delete(e.uri); + if (this.isRelevantMarkdownDocument(e)) { + this._onDidCreateMarkdownDocumentEmitter.fire(e); + } + })); + this._register(vscode.workspace.onDidChangeTextDocument(e => { - if (isMarkdownFile(e.document)) { + if (this.isRelevantMarkdownDocument(e.document)) { this._onDidChangeMarkdownDocumentEmitter.fire(e.document); } })); + + this._register(vscode.workspace.onDidCloseTextDocument(e => { + this._documentCache.delete(e.uri); + })); } - public async getMarkdownDocument(resource: vscode.Uri): Promise { - const matchingDocument = vscode.workspace.textDocuments.find((doc) => doc.uri.toString() === resource.toString()); + private isRelevantMarkdownDocument(doc: vscode.TextDocument) { + return isMarkdownFile(doc) && doc.uri.scheme !== 'vscode-bulkeditpreview'; + } + + public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise { + const existing = this._documentCache.get(resource); + if (existing) { + return existing; + } + + const matchingDocument = vscode.workspace.textDocuments.find((doc) => this.isRelevantMarkdownDocument(doc) && doc.uri.toString() === resource.toString()); if (matchingDocument) { + this._documentCache.set(resource, matchingDocument); return matchingDocument; } + if (!looksLikeMarkdownPath(resource)) { + return undefined; + } + try { const bytes = await vscode.workspace.fs.readFile(resource); // We assume that markdown is in UTF-8 const text = this.utf8Decoder.decode(bytes); - return new InMemoryDocument(resource, text, 0); + const doc = new InMemoryDocument(resource, text, 0); + this._documentCache.set(resource, doc); + return doc; } catch { return undefined; } } + public hasMarkdownDocument(resolvedHrefPath: vscode.Uri): boolean { + return this._documentCache.has(resolvedHrefPath); + } + public async pathExists(target: vscode.Uri): Promise { let targetResourceStat: vscode.FileStat | undefined; try { @@ -168,4 +191,8 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace } return targetResourceStat.type === vscode.FileType.File || targetResourceStat.type === vscode.FileType.Directory; } + + public async readDirectory(resource: vscode.Uri): Promise<[string, vscode.FileType][]> { + return vscode.workspace.fs.readDirectory(resource); + } } diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 097d805796..75edc8fdac 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -6,6 +6,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.textEditorDrop.d.ts", + "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" ] } diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 03cb648102..1ee3f6751e 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -2,6 +2,42 @@ # yarn lockfile v1 +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + "@types/dompurify@^2.3.1": version "2.3.3" resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.3.3.tgz#c24c92f698f77ed9cc9d9fa7888f90cf2bfaa23f" @@ -39,6 +75,11 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== +"@types/picomatch@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" + integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== + "@types/trusted-types@*": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" @@ -54,20 +95,41 @@ resolved "https://registry.yarnpkg.com/@types/vscode-webview/-/vscode-webview-1.57.0.tgz#bad5194d45ae8d03afc1c0f67f71ff5e7a243bbf" integrity sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA== -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -dompurify@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.1.tgz#a47059ca21fd1212d3c8f71fdea6943b8bfbdf6a" - integrity sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw== +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +dompurify@^2.3.3: + version "2.4.4" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.4.tgz#c17803931dd524e1b68e0e940a84567f9498f4bd" + integrity sha512-1e2SpqHiRx4DPvmRuXU5J0di3iQACwJM+mFGE2HAkkK7Tbnfk9WcghcAmyWc9CRrjyRRUpmuhPUH6LphQQR3EQ== entities@~2.1.0: version "2.1.0" @@ -91,6 +153,13 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + markdown-it-front-matter@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.2.1.tgz#dca49a827bb3cebb0528452c1d87dff276eb28dc" @@ -112,27 +181,104 @@ mdurl@^1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + morphdom@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.6.1.tgz#e868e24f989fa3183004b159aed643e628b4306e" integrity sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== + +vscode-languageclient@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301" + integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw== + dependencies: + minimatch "^3.0.4" + semver "^7.3.5" + vscode-languageserver-protocol "3.17.1" + +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== + dependencies: + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" + vscode-languageserver-textdocument@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157" integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ== +vscode-languageserver-textdocument@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c" + integrity sha512-1ah7zyQjKBudnMiHbZmxz5bYNM9KKZYz+5VQLj+yr8l+9w3g+WAhCkUkWbhMEdC5u0ub4Ndiye/fDyS8ghIKQg== + +vscode-languageserver-types@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== + +vscode-languageserver-types@^3.17.1, vscode-languageserver-types@^3.17.2: + version "3.17.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz#b2c2e7de405ad3d73a883e91989b850170ffc4f2" + integrity sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA== + +vscode-markdown-languageservice@^0.0.0-alpha.10: + version "0.0.0-alpha.10" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.0.0-alpha.10.tgz#53b69c981eed7fd5efa155ab8c0f169995568681" + integrity sha512-rJ85nJ+d45yCz9lBhipavoWXz/vW5FknqqUpLqhe3/2xkrhxt8zcekhSoDepgkKFcTORAFV6g1SnnqxbVhX+uA== + dependencies: + picomatch "^2.3.1" + vscode-languageserver-textdocument "^1.0.5" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" + vscode-uri "^3.0.3" + vscode-nls@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== + vscode-uri@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/markdown-math/.gitignore b/extensions/markdown-math/.gitignore index 67c177886f..93d9e664ea 100644 --- a/extensions/markdown-math/.gitignore +++ b/extensions/markdown-math/.gitignore @@ -1 +1,2 @@ notebook-out +languageService diff --git a/extensions/markdown-math/notebook/katex.ts b/extensions/markdown-math/notebook/katex.ts index c9576e56e9..8a58c6ea6d 100644 --- a/extensions/markdown-math/notebook/katex.ts +++ b/extensions/markdown-math/notebook/katex.ts @@ -8,9 +8,9 @@ import type { RendererContext } from 'vscode-notebook-renderer'; const styleHref = import.meta.url.replace(/katex.js$/, 'katex.min.css'); export async function activate(ctx: RendererContext) { - const markdownItRenderer = (await ctx.getRenderer('markdownItRenderer')) as undefined | any; + const markdownItRenderer = (await ctx.getRenderer('vscode.markdown-it-renderer')) as undefined | any; if (!markdownItRenderer) { - throw new Error('Could not load markdownItRenderer'); + throw new Error(`Could not load 'vscode.markdown-it-renderer'`); } // Add katex styles to be copied to shadow dom diff --git a/extensions/markdown-math/package.json b/extensions/markdown-math/package.json index 8267989dae..045da771eb 100644 --- a/extensions/markdown-math/package.json +++ b/extensions/markdown-math/package.json @@ -6,7 +6,7 @@ "icon": "icon.png", "publisher": "vscode", "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { "vscode": "^1.54.0" }, @@ -58,10 +58,10 @@ ], "notebookRenderer": [ { - "id": "markdownItRenderer-katex", + "id": "vscode.markdown-it-katex-extension", "displayName": "Markdown it KaTeX renderer", "entrypoint": { - "extends": "markdownItRenderer", + "extends": "vscode.markdown-it-renderer", "path": "./notebook-out/katex.js" } } @@ -90,7 +90,7 @@ "build-notebook": "node ./esbuild" }, "dependencies": { - "@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git" + "@iktakahiro/markdown-it-katex": "mjbvz/markdown-it-katex" }, "devDependencies": { "@types/markdown-it": "^0.0.0", diff --git a/extensions/markdown-math/preview-styles/index.css b/extensions/markdown-math/preview-styles/index.css index 183ac33479..dd205f5905 100644 --- a/extensions/markdown-math/preview-styles/index.css +++ b/extensions/markdown-math/preview-styles/index.css @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ .katex-error { diff --git a/extensions/markdown-math/yarn.lock b/extensions/markdown-math/yarn.lock index 013a6eb85f..2b97993578 100644 --- a/extensions/markdown-math/yarn.lock +++ b/extensions/markdown-math/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@iktakahiro/markdown-it-katex@https://github.com/mjbvz/markdown-it-katex.git": +"@iktakahiro/markdown-it-katex@mjbvz/markdown-it-katex": version "4.0.1" - resolved "https://github.com/mjbvz/markdown-it-katex.git#b1ed14de467031f5d4f9c1588dd1868cab0b8744" + resolved "https://codeload.github.com/mjbvz/markdown-it-katex/tar.gz/1e0d09f9174b3ee1537de2586ce8d8a460284ce4" dependencies: katex "^0.13.0" diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index 128538a8ad..efc984ed5b 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -6,7 +6,7 @@ "icon": "media/icon.png", "version": "1.0.0", "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { "vscode": "^1.5.0" }, @@ -77,6 +77,7 @@ "title": "%command.next%", "original": "Next Conflict", "command": "merge-conflict.next", + "enablement": "!isMergeEditor", "icon": "$(arrow-down)" }, { @@ -84,6 +85,7 @@ "title": "%command.previous%", "original": "Previous Conflict", "command": "merge-conflict.previous", + "enablement": "!isMergeEditor", "icon": "$(arrow-up)" }, { @@ -110,12 +112,12 @@ { "command": "merge-conflict.previous", "group": "navigation@1", - "when": "mergeConflictsCount && mergeConflictsCount != 0" + "when": "!isMergeEditor && mergeConflictsCount && mergeConflictsCount != 0" }, { "command": "merge-conflict.next", "group": "navigation@2", - "when": "mergeConflictsCount && mergeConflictsCount != 0" + "when": "!isMergeEditor && mergeConflictsCount && mergeConflictsCount != 0" } ] }, diff --git a/extensions/merge-conflict/src/codelensProvider.ts b/extensions/merge-conflict/src/codelensProvider.ts index 68a24c5677..7e99edcb9c 100644 --- a/extensions/merge-conflict/src/codelensProvider.ts +++ b/extensions/merge-conflict/src/codelensProvider.ts @@ -52,7 +52,7 @@ export default class MergeConflictCodeLensProvider implements vscode.CodeLensPro return null; } - let conflicts = await this.tracker.getConflicts(document); + const conflicts = await this.tracker.getConflicts(document); const conflictsCount = conflicts?.length ?? 0; vscode.commands.executeCommand('setContext', 'mergeConflictsCount', conflictsCount); @@ -60,28 +60,28 @@ export default class MergeConflictCodeLensProvider implements vscode.CodeLensPro return null; } - let items: vscode.CodeLens[] = []; + const items: vscode.CodeLens[] = []; conflicts.forEach(conflict => { - let acceptCurrentCommand: vscode.Command = { + const acceptCurrentCommand: vscode.Command = { command: 'merge-conflict.accept.current', title: localize('acceptCurrentChange', 'Accept Current Change'), arguments: ['known-conflict', conflict] }; - let acceptIncomingCommand: vscode.Command = { + const acceptIncomingCommand: vscode.Command = { command: 'merge-conflict.accept.incoming', title: localize('acceptIncomingChange', 'Accept Incoming Change'), arguments: ['known-conflict', conflict] }; - let acceptBothCommand: vscode.Command = { + const acceptBothCommand: vscode.Command = { command: 'merge-conflict.accept.both', title: localize('acceptBothChanges', 'Accept Both Changes'), arguments: ['known-conflict', conflict] }; - let diffCommand: vscode.Command = { + const diffCommand: vscode.Command = { command: 'merge-conflict.compare', title: localize('compareChanges', 'Compare Changes'), arguments: [conflict] diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index ed1aa229b4..10bead294f 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -107,8 +107,8 @@ export default class CommandHandler implements vscode.Disposable { const scheme = editor.document.uri.scheme; let range = conflict.current.content; - let leftRanges = conflicts.map(conflict => [conflict.current.content, conflict.range]); - let rightRanges = conflicts.map(conflict => [conflict.incoming.content, conflict.range]); + const leftRanges = conflicts.map(conflict => [conflict.current.content, conflict.range]); + const rightRanges = conflicts.map(conflict => [conflict.incoming.content, conflict.range]); const leftUri = editor.document.uri.with({ scheme: ContentProvider.scheme, @@ -120,7 +120,7 @@ export default class CommandHandler implements vscode.Disposable { const rightUri = leftUri.with({ query: JSON.stringify({ scheme, ranges: rightRanges }) }); let mergeConflictLineOffsets = 0; - for (let nextconflict of conflicts) { + for (const nextconflict of conflicts) { if (nextconflict.range.isEqual(conflict.range)) { break; } else { @@ -158,7 +158,7 @@ export default class CommandHandler implements vscode.Disposable { } async acceptSelection(editor: vscode.TextEditor): Promise { - let conflict = await this.findConflictContainingSelection(editor); + const conflict = await this.findConflictContainingSelection(editor); if (!conflict) { vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict')); @@ -202,7 +202,7 @@ export default class CommandHandler implements vscode.Disposable { } private async navigate(editor: vscode.TextEditor, direction: NavigationDirection): Promise { - let navigationResult = await this.findConflictForNavigation(editor, direction); + const navigationResult = await this.findConflictForNavigation(editor, direction); if (!navigationResult) { // Check for autoNavigateNextConflict, if it's enabled(which indicating no conflict remain), then do not show warning @@ -258,7 +258,7 @@ export default class CommandHandler implements vscode.Disposable { } private async acceptAll(type: interfaces.CommitType, editor: vscode.TextEditor): Promise { - let conflicts = await this.tracker.getConflicts(editor.document); + const conflicts = await this.tracker.getConflicts(editor.document); if (!conflicts || conflicts.length === 0) { vscode.window.showWarningMessage(localize('noConflicts', 'No merge conflicts found in this file')); @@ -323,7 +323,7 @@ export default class CommandHandler implements vscode.Disposable { return null; } - let selection = editor.selection.active; + const selection = editor.selection.active; if (conflicts.length === 1) { if (conflicts[0].range.contains(selection)) { return { diff --git a/extensions/merge-conflict/src/contentProvider.ts b/extensions/merge-conflict/src/contentProvider.ts index 54dae56174..40931039b4 100644 --- a/extensions/merge-conflict/src/contentProvider.ts +++ b/extensions/merge-conflict/src/contentProvider.ts @@ -32,7 +32,7 @@ export default class MergeConflictContentProvider implements vscode.TextDocument let lastPosition = new vscode.Position(0, 0); ranges.forEach(rangeObj => { - let [conflictRange, fullRange] = rangeObj; + const [conflictRange, fullRange] = rangeObj; const [start, end] = conflictRange; const [fullStart, fullEnd] = fullRange; @@ -41,7 +41,7 @@ export default class MergeConflictContentProvider implements vscode.TextDocument lastPosition = new vscode.Position(fullEnd.line, fullEnd.character); }); - let documentEnd = document.lineAt(document.lineCount - 1).range.end; + const documentEnd = document.lineAt(document.lineCount - 1).range.end; text += document.getText(new vscode.Range(lastPosition.line, lastPosition.character, documentEnd.line, documentEnd.character)); return text; diff --git a/extensions/merge-conflict/src/delayer.ts b/extensions/merge-conflict/src/delayer.ts index c67cd774c3..a3b64512f9 100644 --- a/extensions/merge-conflict/src/delayer.ts +++ b/extensions/merge-conflict/src/delayer.ts @@ -35,7 +35,7 @@ export class Delayer { }).then(() => { this.completionPromise = null; this.onSuccess = null; - let result = this.task!(); + const result = this.task!(); this.task = null; return result; }); @@ -56,7 +56,7 @@ export class Delayer { return null; } this.cancelTimeout(); - let result = this.completionPromise; + const result = this.completionPromise; this.onSuccess!(undefined); return result; } diff --git a/extensions/merge-conflict/src/documentMergeConflict.ts b/extensions/merge-conflict/src/documentMergeConflict.ts index 677e4e8410..d6ba25a743 100644 --- a/extensions/merge-conflict/src/documentMergeConflict.ts +++ b/extensions/merge-conflict/src/documentMergeConflict.ts @@ -45,11 +45,11 @@ export class DocumentMergeConflict implements interfaces.IDocumentMergeConflict // ] if (type === interfaces.CommitType.Current) { // Replace [ Conflict Range ] with [ Current Content ] - let content = document.getText(this.current.content); + const content = document.getText(this.current.content); this.replaceRangeWithContent(content, edit); } else if (type === interfaces.CommitType.Incoming) { - let content = document.getText(this.incoming.content); + const content = document.getText(this.incoming.content); this.replaceRangeWithContent(content, edit); } else if (type === interfaces.CommitType.Both) { diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index 9fd9da905a..ac3f4ab9a1 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -50,7 +50,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, getConflicts(document: vscode.TextDocument, origin: string): PromiseLike { // Attempt from cache - let key = this.getCacheKey(document); + const key = this.getCacheKey(document); if (!key) { // Document doesn't have a uri, can't cache it, so return @@ -67,11 +67,9 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, } return cacheItem.delayTask.trigger(() => { - let conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins)); + const conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins)); - if (this.cache) { - this.cache.delete(key!); - } + this.cache?.delete(key!); return conflicts; }); @@ -82,7 +80,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, return false; } - let key = this.getCacheKey(document); + const key = this.getCacheKey(document); if (!key) { return false; } @@ -100,7 +98,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, } forget(document: vscode.TextDocument) { - let key = this.getCacheKey(document); + const key = this.getCacheKey(document); if (key) { this.cache.delete(key); diff --git a/extensions/merge-conflict/src/mergeConflictParser.ts b/extensions/merge-conflict/src/mergeConflictParser.ts index 7889da782b..26794ad49e 100644 --- a/extensions/merge-conflict/src/mergeConflictParser.ts +++ b/extensions/merge-conflict/src/mergeConflictParser.ts @@ -67,7 +67,7 @@ export class MergeConflictParser { // Create a full descriptor from the lines that we matched. This can return // null if the descriptor could not be completed. - let completeDescriptor = MergeConflictParser.scanItemTolMergeConflictDescriptor(document, currentConflict); + const completeDescriptor = MergeConflictParser.scanItemTolMergeConflictDescriptor(document, currentConflict); if (completeDescriptor !== null) { conflictDescriptors.push(completeDescriptor); @@ -90,7 +90,7 @@ export class MergeConflictParser { return null; } - let tokenAfterCurrentBlock: vscode.TextLine = scanned.commonAncestors[0] || scanned.splitter; + const tokenAfterCurrentBlock: vscode.TextLine = scanned.commonAncestors[0] || scanned.splitter; // Assume that descriptor.current.header, descriptor.incoming.header and descriptor.splitter // have valid ranges, fill in content and total ranges from these parts. @@ -110,7 +110,7 @@ export class MergeConflictParser { name: scanned.startHeader.text.substring(startHeaderMarker.length + 1) }, commonAncestors: scanned.commonAncestors.map((currentTokenLine, index, commonAncestors) => { - let nextTokenLine = commonAncestors[index + 1] || scanned.splitter; + const nextTokenLine = commonAncestors[index + 1] || scanned.splitter; return { header: currentTokenLine.range, decoratorContent: new vscode.Range( @@ -146,7 +146,7 @@ export class MergeConflictParser { return false; } - let text = document.getText(); + const text = document.getText(); return text.includes(startHeaderMarker) && text.includes(endFooterMarker); } diff --git a/extensions/merge-conflict/src/mergeDecorator.ts b/extensions/merge-conflict/src/mergeDecorator.ts index b62df990c6..a56af985e2 100644 --- a/extensions/merge-conflict/src/mergeDecorator.ts +++ b/extensions/merge-conflict/src/mergeDecorator.ts @@ -137,7 +137,7 @@ export default class MergeDecorator implements vscode.Disposable { private generateBlockRenderOptions(backgroundColor: string, overviewRulerColor: string, config: interfaces.IExtensionConfiguration): vscode.DecorationRenderOptions { - let renderOptions: vscode.DecorationRenderOptions = {}; + const renderOptions: vscode.DecorationRenderOptions = {}; if (config.enableDecorations) { renderOptions.backgroundColor = new vscode.ThemeColor(backgroundColor); @@ -176,7 +176,7 @@ export default class MergeDecorator implements vscode.Disposable { try { this.updating.set(editor, true); - let conflicts = await this.tracker.getConflicts(editor.document); + const conflicts = await this.tracker.getConflicts(editor.document); if (vscode.window.visibleTextEditors.indexOf(editor) === -1) { return; } @@ -188,9 +188,9 @@ export default class MergeDecorator implements vscode.Disposable { // Store decorations keyed by the type of decoration, set decoration wants a "style" // to go with it, which will match this key (see constructor); - let matchDecorations: { [key: string]: vscode.Range[] } = {}; + const matchDecorations: { [key: string]: vscode.Range[] } = {}; - let pushDecoration = (key: string, d: vscode.Range) => { + const pushDecoration = (key: string, d: vscode.Range) => { matchDecorations[key] = matchDecorations[key] || []; matchDecorations[key].push(d); }; @@ -224,7 +224,7 @@ export default class MergeDecorator implements vscode.Disposable { // For each match we've generated, apply the generated decoration with the matching decoration type to the // editor instance. Keys in both matches and decorations should match. Object.keys(matchDecorations).forEach(decorationKey => { - let decorationType = this.decorations[decorationKey]; + const decorationType = this.decorations[decorationKey]; if (decorationType) { editor.setDecorations(decorationType, matchDecorations[decorationKey]); @@ -242,7 +242,7 @@ export default class MergeDecorator implements vscode.Disposable { // Race condition, while editing the settings, it's possible to // generate regions before the configuration has been refreshed - let decorationType = this.decorations[decorationKey]; + const decorationType = this.decorations[decorationKey]; if (decorationType) { editor.setDecorations(decorationType, []); diff --git a/extensions/merge-conflict/src/services.ts b/extensions/merge-conflict/src/services.ts index 172b0dd0fa..64de4ecd6b 100644 --- a/extensions/merge-conflict/src/services.ts +++ b/extensions/merge-conflict/src/services.ts @@ -21,7 +21,7 @@ export default class ServiceWrapper implements vscode.Disposable { begin() { - let configuration = this.createExtensionConfiguration(); + const configuration = this.createExtensionConfiguration(); const documentTracker = new DocumentTracker(); this.services.push( @@ -48,6 +48,19 @@ export default class ServiceWrapper implements vscode.Disposable { } createExtensionConfiguration(): interfaces.IExtensionConfiguration { + + // PRAGMATIC way to avoid conflicting with the new merge editor: when git opts into + // using the merge editor we disable this extension - for the merge editor but also + // for "other" editors + const gitConfig = vscode.workspace.getConfiguration('git'); + if (gitConfig.get('mergeEditor')) { + return { + enableCodeLens: false, + enableDecorations: false, + enableEditorOverview: false + }; + } + const workspaceConfiguration = vscode.workspace.getConfiguration(ConfigurationSectionName); const codeLensEnabled: boolean = workspaceConfiguration.get('codeLens.enabled', true); const decoratorsEnabled: boolean = workspaceConfiguration.get('decorators.enabled', true); @@ -64,4 +77,3 @@ export default class ServiceWrapper implements vscode.Disposable { this.services = []; } } - diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index 58118fefe5..f8b8a6931d 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -36,7 +36,7 @@ } ] }, - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "main": "./out/extension.js", "browser": "./dist/browser/extension.js", "scripts": { @@ -60,7 +60,7 @@ "sha.js": "2.4.11", "stream": "0.0.2", "uuid": "^8.2.0", - "@vscode/extension-telemetry": "0.4.10", + "@vscode/extension-telemetry": "0.6.2", "vscode-nls": "^5.0.0" }, "repository": { diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 3e30d4cd12..6ae27b3a2b 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -11,7 +11,7 @@ import * as nls from 'vscode-nls'; import { v4 as uuid } from 'uuid'; import fetch, { Response } from 'node-fetch'; import Logger from './logger'; -import { toBase64UrlEncoding } from './utils'; +import { isSupportedEnvironment, toBase64UrlEncoding } from './utils'; import { sha256 } from './env/node/sha256'; import { BetterTokenStorage, IDidChangeInOtherWindowEvent } from './betterSecretStorage'; import { LoopbackAuthServer } from './authServer'; @@ -109,7 +109,7 @@ export class AzureActiveDirectoryService { public async initialize(): Promise { Logger.info('Reading sessions from secret storage...'); - let sessions = await this._tokenStorage.getAll(); + const sessions = await this._tokenStorage.getAll(); Logger.info(`Got ${sessions.length} stored sessions`); const refreshes = sessions.map(async session => { @@ -319,13 +319,7 @@ export class AzureActiveDirectoryService { }, 5000); } - const token = await this.exchangeCodeForToken(codeToExchange, codeVerifier, scopeData); - if (token.expiresIn) { - this.setSessionTimeout(token.sessionId, token.refreshToken, scopeData, token.expiresIn * AzureActiveDirectoryService.REFRESH_TIMEOUT_MODIFIER); - } - await this.setToken(token, scopeData); - Logger.info(`Login successful for scopes: ${scopeData.scopeStr}`); - const session = await this.convertToSession(token); + const session = await this.exchangeCodeForSession(codeToExchange, codeVerifier, scopeData); return session; } @@ -352,12 +346,14 @@ export class AzureActiveDirectoryService { code_challenge_method: 'S256', code_challenge: codeChallenge, }); - let uri = vscode.Uri.parse(`${signInUrl}?${oauthStartQuery.toString()}`); + const uri = vscode.Uri.parse(`${signInUrl}?${oauthStartQuery.toString()}`); vscode.env.openExternal(uri); + let inputBox: vscode.InputBox | undefined; const timeoutPromise = new Promise((_: (value: vscode.AuthenticationSession) => void, reject) => { const wait = setTimeout(() => { clearTimeout(wait); + inputBox?.dispose(); reject('Login timed out.'); }, 1000 * 60 * 5); }); @@ -369,7 +365,12 @@ export class AzureActiveDirectoryService { // before completing it. let existingPromise = this._codeExchangePromises.get(scopeData.scopeStr); if (!existingPromise) { - existingPromise = this.handleCodeResponse(scopeData); + if (isSupportedEnvironment(callbackUri)) { + existingPromise = this.handleCodeResponse(scopeData); + } else { + inputBox = vscode.window.createInputBox(); + existingPromise = Promise.race([this.handleCodeInputBox(inputBox, codeVerifier, scopeData), this.handleCodeResponse(scopeData)]); + } this._codeExchangePromises.set(scopeData.scopeStr, existingPromise); } @@ -659,13 +660,7 @@ export class AzureActiveDirectoryService { throw new Error('No available code verifier'); } - const token = await this.exchangeCodeForToken(code, verifier, scopeData); - if (token.expiresIn) { - this.setSessionTimeout(token.sessionId, token.refreshToken, scopeData, token.expiresIn * AzureActiveDirectoryService.REFRESH_TIMEOUT_MODIFIER); - } - await this.setToken(token, scopeData); - - const session = await this.convertToSession(token); + const session = await this.exchangeCodeForSession(code, verifier, scopeData); resolve(session); } catch (err) { reject(err); @@ -680,8 +675,33 @@ export class AzureActiveDirectoryService { }); } - private async exchangeCodeForToken(code: string, codeVerifier: string, scopeData: IScopeData): Promise { + private async handleCodeInputBox(inputBox: vscode.InputBox, verifier: string, scopeData: IScopeData): Promise { + inputBox.ignoreFocusOut = true; + inputBox.title = localize('pasteCodeTitle', 'Microsoft Authentication'); + inputBox.prompt = localize('pasteCodePrompt', 'Provide the authorization code to complete the sign in flow.'); + inputBox.placeholder = localize('pasteCodePlaceholder', 'Paste authorization code here...'); + return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => { + inputBox.show(); + inputBox.onDidAccept(async () => { + const code = inputBox.value; + if (code) { + inputBox.dispose(); + const session = await this.exchangeCodeForSession(code, verifier, scopeData); + resolve(session); + } + }); + inputBox.onDidHide(() => { + if (!inputBox.value) { + inputBox.dispose(); + reject('Cancelled'); + } + }); + }); + } + + private async exchangeCodeForSession(code: string, codeVerifier: string, scopeData: IScopeData): Promise { Logger.info(`Exchanging login code for token for scopes: ${scopeData.scopeStr}`); + let token: IToken | undefined; try { const postData = querystring.stringify({ grant_type: 'authorization_code', @@ -698,11 +718,18 @@ export class AzureActiveDirectoryService { const json = await this.fetchTokenResponse(endpoint, postData, scopeData); Logger.info(`Exchanging login code for token (for scopes: ${scopeData.scopeStr}) succeeded!`); - return this.convertToTokenSync(json, scopeData); + token = this.convertToTokenSync(json, scopeData); } catch (e) { Logger.error(`Error exchanging code for token (for scopes ${scopeData.scopeStr}): ${e}`); throw e; } + + if (token.expiresIn) { + this.setSessionTimeout(token.sessionId, token.refreshToken, scopeData, token.expiresIn * AzureActiveDirectoryService.REFRESH_TIMEOUT_MODIFIER); + } + await this.setToken(token, scopeData); + Logger.info(`Login successful for scopes: ${scopeData.scopeStr}`); + return await this.convertToSession(token); } private async fetchTokenResponse(endpoint: string, postData: string, scopeData: IScopeData): Promise { diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index 41ebe75777..661d16b034 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -21,7 +21,9 @@ export async function activate(context: vscode.ExtensionContext) { try { /* __GDPR__ "login" : { - "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + "owner": "TylerLeonhardt", + "comment": "Used to determine the usage of the Microsoft Auth Provider.", + "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." } } */ telemetryReporter.sendTelemetryEvent('login', { @@ -34,7 +36,7 @@ export async function activate(context: vscode.ExtensionContext) { return session; } catch (e) { /* __GDPR__ - "loginFailed" : { } + "loginFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users run into issues with the login flow." } */ telemetryReporter.sendTelemetryEvent('loginFailed'); @@ -44,7 +46,7 @@ export async function activate(context: vscode.ExtensionContext) { removeSession: async (id: string) => { try { /* __GDPR__ - "logout" : { } + "logout" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users log out." } */ telemetryReporter.sendTelemetryEvent('logout'); @@ -54,7 +56,7 @@ export async function activate(context: vscode.ExtensionContext) { } } catch (e) { /* __GDPR__ - "logoutFailed" : { } + "logoutFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often fail to log out." } */ telemetryReporter.sendTelemetryEvent('logoutFailed'); } diff --git a/extensions/microsoft-authentication/src/utils.ts b/extensions/microsoft-authentication/src/utils.ts index 912cc56035..5371e02576 100644 --- a/extensions/microsoft-authentication/src/utils.ts +++ b/extensions/microsoft-authentication/src/utils.ts @@ -2,7 +2,40 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { env, UIKind, Uri } from 'vscode'; export function toBase64UrlEncoding(base64string: string) { return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding } + +const LOCALHOST_ADDRESSES = ['localhost', '127.0.0.1', '0:0:0:0:0:0:0:1', '::1']; +function isLocalhost(uri: Uri): boolean { + if (!/^https?$/i.test(uri.scheme)) { + return false; + } + const host = uri.authority.split(':')[0]; + return LOCALHOST_ADDRESSES.indexOf(host) >= 0; +} + +export function isSupportedEnvironment(uri: Uri): boolean { + if (env.uiKind === UIKind.Desktop) { + return true; + } + // local development (localhost:* or 127.0.0.1:*) + if (isLocalhost(uri)) { + return true; + } + // At this point we should only ever see https + if (uri.scheme !== 'https') { + return false; + } + + return ( + // vscode.dev & insiders.vscode.dev + /(?:^|\.)vscode\.dev$/.test(uri.authority) || + // github.dev & codespaces + /(?:^|\.)github\.dev$/.test(uri.authority) || + // github.dev/codespaces local setup (github.localhost) + /(?:^|\.)github\.localhost$/.test(uri.authority) + ); +} diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index b3ad2657f8..9328b1d4b1 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -2,6 +2,42 @@ # yarn lockfile v1 +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + "@types/node-fetch@^2.5.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" @@ -39,10 +75,13 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" asynckit@^0.4.0: version "0.4.0" diff --git a/extensions/notebook-renderers/package.json b/extensions/notebook-renderers/package.json index 15b6751e20..f78839bbee 100644 --- a/extensions/notebook-renderers/package.json +++ b/extensions/notebook-renderers/package.json @@ -17,7 +17,7 @@ "contributes": { "notebookRenderer": [ { - "id": "vscode-builtin-notebook-renderer", + "id": "vscode.builtin-renderer", "entrypoint": "./renderer-out/index.js", "displayName": "VS Code Builtin Notebook Output Renderer", "requiresMessaging": "never", diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index ae457fcaec..82f7637c10 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -10,13 +10,21 @@ interface IDisposable { dispose(): void; } +interface HtmlRenderingHook { + /** + * Invoked after the output item has been rendered but before it has been appended to the document. + * + * @return A new `HTMLElement` or `undefined` to continue using the provided element. + */ + postRender(outputItem: OutputItem, element: HTMLElement): HTMLElement | undefined; +} + function clearContainer(container: HTMLElement) { while (container.firstChild) { container.removeChild(container.firstChild); } } - function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable { const blob = new Blob([outputInfo.data()], { type: outputInfo.mime }); const src = URL.createObjectURL(blob); @@ -64,12 +72,17 @@ const domEval = (container: Element) => { } }; -function renderHTML(outputInfo: OutputItem, container: HTMLElement): void { +function renderHTML(outputInfo: OutputItem, container: HTMLElement, hooks: Iterable): void { clearContainer(container); + let element: HTMLElement = document.createElement('div'); const htmlContent = outputInfo.text(); - const element = document.createElement('div'); const trustedHtml = ttPolicy?.createHTML(htmlContent) ?? htmlContent; element.innerHTML = trustedHtml as string; + + for (const hook of hooks) { + element = hook.postRender(outputInfo, element) ?? element; + } + container.appendChild(element); domEval(element); } @@ -167,6 +180,8 @@ function renderText(outputInfo: OutputItem, container: HTMLElement, ctx: Rendere export const activate: ActivationFunction = (ctx) => { const disposables = new Map(); + const htmlHooks = new Set(); + const latestContext = ctx as (RendererContext & { readonly settings: { readonly lineLimit: number } }); const style = document.createElement('style'); @@ -210,6 +225,7 @@ export const activate: ActivationFunction = (ctx) => { } `; document.body.appendChild(style); + return { renderOutputItem: (outputInfo, element) => { switch (outputInfo.mime) { @@ -220,7 +236,7 @@ export const activate: ActivationFunction = (ctx) => { return; } - renderHTML(outputInfo, element); + renderHTML(outputInfo, element, htmlHooks); } break; case 'application/javascript': @@ -267,8 +283,6 @@ export const activate: ActivationFunction = (ctx) => { default: break; } - - }, disposeOutputItem: (id: string | undefined) => { if (id) { @@ -276,6 +290,14 @@ export const activate: ActivationFunction = (ctx) => { } else { disposables.forEach(d => d.dispose()); } + }, + experimental_registerHtmlRenderingHook: (hook: HtmlRenderingHook): IDisposable => { + htmlHooks.add(hook); + return { + dispose: () => { + htmlHooks.delete(hook); + } + }; } }; }; diff --git a/extensions/package.json b/extensions/package.json index 0ace79a008..31b83174c2 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -12,6 +12,6 @@ "devDependencies": { "@parcel/watcher": "2.0.5", "esbuild": "^0.11.12", - "vscode-grammar-updater": "^1.0.4" + "vscode-grammar-updater": "^1.1.0" } } diff --git a/extensions/resource-deployment/src/test/utils.ts b/extensions/resource-deployment/src/test/utils.ts index d537006d33..b62095d34e 100644 --- a/extensions/resource-deployment/src/test/utils.ts +++ b/extensions/resource-deployment/src/test/utils.ts @@ -7,7 +7,7 @@ export class Deferred { promise: Promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; - });; + }); resolve!: (value: T | PromiseLike) => void; reject!: (reason?: any) => void; } diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 4ba6e50e37..d78694d28f 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -225,7 +225,7 @@ function parseSearchResults(document: vscode.TextDocument, token?: vscode.Cancel const metadataOffset = (indentation + _lineNumber + separator).length; const targetRange = new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length); - let locations: Required[] = []; + const locations: Required[] = []; let lastEnd = metadataOffset; let offset = 0; @@ -256,7 +256,7 @@ function parseSearchResults(document: vscode.TextDocument, token?: vscode.Cancel } // Allow line number, indentation, etc to take you to definition as well. - let convenienceLocation: Required = { + const convenienceLocation: Required = { targetRange, targetSelectionRange: new vscode.Range(lineNumber, 0, lineNumber, 1), targetUri: currentTarget, diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index c3319c11f9..1e122b9537 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -13,11 +13,11 @@ const fs = require('fs'); const merge = require('merge-options'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { NLSBundlePlugin } = require('vscode-nls-dev/lib/webpack-bundler'); -const { DefinePlugin } = require('webpack'); +const { DefinePlugin, optimize } = require('webpack'); function withNodeDefaults(/**@type WebpackConfig*/extConfig) { /** @type WebpackConfig */ - let defaultConfig = { + const defaultConfig = { mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') target: 'node', // extensions run in a node context node: { @@ -94,7 +94,7 @@ function nodePlugins(context) { function withBrowserDefaults(/**@type WebpackConfig*/extConfig, /** @type AdditionalBrowserConfig */ additionalOptions = {}) { /** @type WebpackConfig */ - let defaultConfig = { + const defaultConfig = { mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') target: 'webworker', // extensions run in a webworker context resolve: { @@ -146,12 +146,16 @@ function withBrowserDefaults(/**@type WebpackConfig*/extConfig, /** @type Additi } const browserPlugins = [ + new optimize.LimitChunkCountPlugin({ + maxChunks: 1 + }), new CopyWebpackPlugin({ patterns: [ { from: 'src', to: '.', globOptions: { ignore: ['**/test/**', '**/*.ts'] }, noErrorOnMissing: true } ] }), new DefinePlugin({ + 'process.platform': JSON.stringify('web'), 'process.env': JSON.stringify({}), 'process.env.BROWSER_ENV': JSON.stringify('true') }) diff --git a/extensions/simple-browser/.vscodeignore b/extensions/simple-browser/.vscodeignore index ec298ce176..d1006b25b7 100644 --- a/extensions/simple-browser/.vscodeignore +++ b/extensions/simple-browser/.vscodeignore @@ -11,3 +11,4 @@ cgmanifest.json yarn.lock preview-src/** webpack.config.js +esbuild-preview.js diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 711ffbd81e..08344e234f 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -9,7 +9,7 @@ "icon": "media/icon.png", "publisher": "vscode", "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "engines": { "vscode": "^1.53.0" }, @@ -67,7 +67,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "0.4.10", + "@vscode/extension-telemetry": "0.6.2", "vscode-nls": "^5.0.0" }, "devDependencies": { diff --git a/extensions/simple-browser/yarn.lock b/extensions/simple-browser/yarn.lock index af6b2add33..5faf627618 100644 --- a/extensions/simple-browser/yarn.lock +++ b/extensions/simple-browser/yarn.lock @@ -2,15 +2,54 @@ # yarn lockfile v1 +"@microsoft/1ds-core-js@3.2.3", "@microsoft/1ds-core-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7" + integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.4" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/1ds-post-js@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216" + integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ== + dependencies: + "@microsoft/1ds-core-js" "3.2.3" + "@microsoft/applicationinsights-shims" "^2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-core-js@2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596" + integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ== + dependencies: + "@microsoft/applicationinsights-shims" "2.0.1" + "@microsoft/dynamicproto-js" "^1.1.6" + +"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" + integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== + +"@microsoft/dynamicproto-js@^1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" + integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== + "@types/vscode-webview@^1.57.0": version "1.57.0" resolved "https://registry.yarnpkg.com/@types/vscode-webview/-/vscode-webview-1.57.0.tgz#bad5194d45ae8d03afc1c0f67f71ff5e7a243bbf" integrity sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA== -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e" + integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w== + dependencies: + "@microsoft/1ds-core-js" "^3.2.3" + "@microsoft/1ds-post-js" "^3.2.3" vscode-codicons@^0.0.14: version "0.0.14" diff --git a/extensions/sql-migration/src/models/loginMigrationModel.ts b/extensions/sql-migration/src/models/loginMigrationModel.ts index 55b3daf5d1..3f645787c4 100644 --- a/extensions/sql-migration/src/models/loginMigrationModel.ts +++ b/extensions/sql-migration/src/models/loginMigrationModel.ts @@ -59,7 +59,7 @@ export interface Login { export class LoginMigrationModel { public resultsPerStep: Map; public collectedSourceLogins: boolean = false; - public collectedTargetLogins: boolean = false;; + public collectedTargetLogins: boolean = false; public loginsOnSource: LoginTableInfo[] = []; public loginsOnTarget: string[] = []; public loginMigrationsResult!: contracts.StartLoginMigrationResult; diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index da31bda5d0..82846c1e67 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} \ No newline at end of file +} diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 9797bff195..a3399137d3 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -624,4 +624,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 768a6b3038..8072b0bdd6 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -12,7 +12,7 @@ "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#BBBBBB", "input.placeholderForeground": "#A6A6A6", - "menu.background": "#252526", + "menu.background": "#303031", "menu.foreground": "#CCCCCC", "statusBarItem.remoteForeground": "#FFF", "statusBarItem.remoteBackground": "#16825D", diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index d2349f3124..ae3ab6645b 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -25,7 +25,8 @@ "notebook.cellBorderColor": "#E8E8E8", "notebook.selectedCellBackground": "#c8ddf150", "statusBarItem.errorBackground": "#c72e0f", - "list.activeSelectionIconForeground": "#FFF" + "list.activeSelectionIconForeground": "#FFF", + "list.focusAndSelectionOutline": "#90C2F9" }, "tokenColors": [ { diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 41d95b81af..412472af75 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -45,7 +45,8 @@ const nonBuiltInLanguages = { // { fileNames, extensions } const inheritIconFromLanguage = { "jsonc": 'json', "postcss": 'css', - "django-html": 'html' + "django-html": 'html', + "blade": 'php' } const FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 00e2991fb9..919b27b7c9 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0" + "commitHash": "2d10473b7575ec00c47eda751ea9caeec6b0b606" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index aeb87b845af6596a1b72e1590613ea35a538cc52..f0e47486995b58f1484c564f76d10182211ecd47 100644 GIT binary patch delta 33204 zcmV)DK*7J%q5{yO0u*;oMn(Vu00000kx&2&00000+?0_NUw?Q20K~u>1YiwlWnp9h z0E%b;001rk001^1`hC1;Xk}pl0E(CZ000~S001NhyaiHdZFG150E)B#001oj00M-0 zQvd*LZ)0Hq0E*-Q00Be*00BfE&ro`8VR&!=0E{RA0018V001BYdI}J3VQpmq0E|cg z00ABV00HLSN(vdwaBp*T0050>vHbc2e`IBKFIwNc=b`pgyXLBWs_L9RbB(9_be}^X zy6FkJY3Qb#skIetWDp1>2DAwZibjce2_$-Pc=+(X7!)*Dqmf{)xjaeWYT}T1V`B8` z4F)1N`Xe!NANAh)_O8>7@9BHL*LA9D*B-xNeQW*yYpw6=9L>Q$V&G?TMy{CKf1cZy z%c151>aU@075Nq-O$+%|)a#Ch{Z2ayvsVozi>Tdc&!Ql4d)Yo;aCmi+U>yxd!_KhN zPSA_z-*Nu=cl@sWXl#4U)~&DEcI9B(wt>c&&J~XF?(2G-Q-u{@^(Urldb(xmdD9tg z+a?qy5V8bEN4uB2{*qn~KDubWf4RDQHK{f3XjZ?4F`<+Za`OpIBm6CB6%nC2{#M`B z1U)rG2s2^0oIv$(T1P~da>sHvLj8CkOKKs(XHjzzx09yeHsYP6*=cXS2L1L9)F1Un zYiKlFMco<-dtt8&b?atrU?I4qg8VR$zmlbh1X&Akkqr8y;h9$mPtGT{f6r`YXr5t~ zZDD(AY{G}>sGjZd(D77G2v@dE$4*VlhbHHaYL=rNMMYPNCAF;*l_(XfK+!#+Us4Rk zu?ZGb^#fb6OoFcBd}+bAh)FnCIi;L)fmGG;jM7wlI<}d|%7$im?Q=CD;8|SNIH6o` z@OEAl%n-`fsMr=lUyiGdf2zePCU$6Qx-eo>r2>;2l5L3p2WjMVXu+P2dT!)KBX@*< z-EqLtGP_J7Vz~sk<)U++_|}&H|j-6ClRAwCmKL| zLh}wlu2Nx=t=8*CLE0a_6tXG`d}z?5IU4k$Q7=daBIyKQxM=pGf4S(Au)ec?>r%yzrSP0rC0nx+fkUN7+g~n z$8-FuomZv|uKHZj6t5mSMUm&<`^ZPW_sD}E`sAbz^Y|Zg<=k5CvQ&PWP~1ff(lY2v z(HM+|AOeBwi$t_Ge`NqfDU%%-3hmLbo61Z8wd?qsNBb~H|V|D80nQ%BYEJiJ#S~_MZ$LZl2jOv+Oo19V#H4z6ij6%2nbqSKY7IDib7bazGKNW*5*{ExoV+Z z8WrH1ilD7jpefV#w8;6n8**>UeH>bIG=zbc4m#OxK^J5ofW8)TfJGfY38E$(37}De zLd29_kK9ATxtu)(?xQ4}(??_GEGb z^a&qj{gsZ|b<~$P8usLo-k=AczGx1TfF~S(VOxPZ?dtKXE}Uv0iYNmeWca3FFye@( zBhYBDL0w&?{XP~L)1Y|Zbr{4GQ$~e}1uyDh=+e0$f8VloTy~e2Uq?J&?BBjrSly`* zytJ@$hn;VlPClxv&SETdp>$n`hxuZSKyML^(TdIR>jv$4p(=G7x-W_IP!K2z;kE_} z5pzsD6)BX%CR~&U)zbDTg8@%>^`87ZVo0h^tU7^()oKit$F6P~m_ZM>rVgA}b5-U3 zrJ6Yve-Ux=3ax~xrmOQ$M2;6>>RBCfBU2HlL^GHz3}))WTvTMYqDkLcFy3=08byJM z2I$m1_q=EP?D$z?zUMvfxksvqdva&xz{-_!Q@MFKzcK2Kk`5?_L`2Z}fiGphyN3D; zh&9`N(7QfAvw=S{{@3+W;fY%9M0jd<`3sscf8Y4{W81pbYIpn=uUu}G%jjsa)#^`8 zq0c_JxVW%*<>K>3(@BQRv8&x}z3M&Xb{YO^Q+;_>PRYhO(Q?0*dw1^lb5G~Kfjm@3 z9Vl695EfChEnuWp$*7l&>vhzYlCwYt=?SSx$&6z-ltX`F8me$|6prMsnzW3AQ6FwF ze;7(7B1ibB3zh|hPM!io0S+yo4p^oI0wW;|))3sGhL)tR=%um?@-BD)7Y|bVl3LyV zP)K9dlaGtwdMHf?9t`%%hYLX7+a1|#viYL07j;7@YbOa)<=Rdxs3J(Zw1`lXetQt6 z1OhyYo7@jOP{l~zf3wpj4gs6A<;X1cK{Gj+V2ZoqA(aE)Hh0twU*>KzNKtu@09V;1>c^(WCMdg;zz<4W^YNkT+U@zf*`2~wt z0ubRsh>hRzVg&LICSH-_3alQRHJil(CxT+Y6IgK+Y6#s7EX{Jr5rdNXDX^f#eXAj8FrlroAt#;{eO$S6$X#@RY52-Jg5=&OJF2fcWT$x1HG)+bo~&Amwb>%)wg>Vd+h!y1e1-ZWCIe2jG6%ONC+5p)|Y}F^%lUSreG74Hb^Bl z(fZ5pJ#k`veH|4C)3rtIq|!gM&^^}e9(&#K!bhhE=z%M*M8)yD(6KAmr-w;-ZqJq! zC-$}sH}wntNp3E;KX(w2e@9=ATKOQ6;tc>977lzEF92;vQ92Sf!Cxy8$tW50(sxg! zZ!kXco%o{;?Eg+{Z9W+-9*B1sGJPv=+P`OaVM>}@o6 zCOh}VQuvweW^SWeA_xk~P0xG%`-Xuw3!^0v2aAOjN~ z0Vj|owHZJnQo0~Dc`KzI99jlccU7wOs3-4$UTpP?Tbl7K5FpL=r_VW0IW%*_Ys`Wl zhgzUvK^{0LfB4#obBZ$}LsH@Kg&YCqbj?+#?2v zf)FMPnFA7UiG{;v1~Y`A2$Ar+zgC{_e+z25QG;` zM7&JAdIBU;wZR(B99SscVp)MYy$_Qc5hwxBlSC0g0riuH5hH)t5W)x~I)KX!or|-5 zDKY0DcV6zblE-G<11}$jFSMv^RLKS;{z7RFz-!o+uxx^ax*(cdlJit^W*Q%hs5H-cvE_HSI*$Cew+TUDFuuAr^bZ&0g^oP?p)# zw=}AhjK8;j%{tn7oCe1CTlDYXk5}zS6>npr-)WD^p>h3Zvt5H>%~6^&)K&gx}jAE zqGOQRh{1orAo-<>mfBN|5-F6on)wUfdVZreQaO6^oKeWY{6G=l+2?I5M_Yucu?ybX zYH#Ytuj4-kJ6y{&1P9$OBs5-}$PK6@iTp%Ibnw>^IRG&3Lm%J@b6c@c$!({YV9)8% z!e@>?yzMA@GbVe$F#XQ`jJZjHF?!2u8T}sG8qR-x;-U|ZE;?(g6S6z!!ZVk1>(aY| z06zh~iAGhlh5=5w&=G4`HnD}tFFXdKFKG+&B7)a;-Z1dy{-*k0XRkSRFtOKPJse%J zB|LcD>vxoT=S~f;n0Kc>Q~ZOe<41Q^%>JSIh4YjA&dZJzt9mxx@q;Q>t?Y-qFi8 z@>A{k>pDU7kEpOV{^40Ye;tg|SIIhc+9_$z*i|o6fIv;8&;SF<$d`29wbLV6b9V;) zBy+^@>|zN^79_cc5xj;b9tQyo2wlV!U(tUS{lshP(cJZ?Z`srKMBq8Kf-XH@NKlm( zm~9GL+h8$M2_R+Ub(1Z{a_k4(*Zo3e_UM&Sp&WayVo?WH!omWKsssoMJliPEUa@c~ zqpx0E@ob4N6g|*fP=<&g)!Z9O=B@}sIMX(>+C>RTuni}yYl7JXEDd#-unjoO3=Is( zFS-d}6}X9TKx6!(n{Xg;2F-2)KZyP>^Tz_OIhTH-r+>*6CNNhnuh2MC_5a&*IRmoF z;7WL|Q7!+!JT|=voT(|?`nj7lMOt_%U0LDH;?~VV!RJ}*IR^f+lG_En{QrN8zXVCp zlm4qgG8&xy0Z@9!e6o_#7FX4MGxP$WEzUI17ISxHy0th5w!$)n8KU7_w+nw?7_6=i zwqv?_Vu$avB~2lOt9mj7n&RhGJ%6a-=WVND7p&Ucy@R=xl{vDuHrUu0Y|7~lawD)l z0FoeeE^E0RvWOZ2z4*UAjP`$_eGk8M$tCFcCFI6ey=r{d6HlPGylQ;m{rBHLseg8@ zne$$8b!P-x%!5`Kbq~fFY^vxnQoo7k2%OWy>WkV=WXzB=fREB zr%yj|=n!zG3}5IIo@+z7qx4z8cs;k_GPwO{@|a)r{cYSGPk-bi-~L5^M0@!gc<$zz znVG@ndwP00$1^!04A@#LHw|{+K<=27mJ}LCB6CcoFmxg&seE)~HG2_Qqh2aQy;Lc} zwk4xKJJ2fXRxwzOSu}qN;27?M#Xw}bb?f@>{_go!qY&yEE=I+AYYXf+=Pm6SJRfVb zT09pM$Nrxos4P`zG-N@;S8tfH9a8~WF}FmHu5n5=&BAow+UlC33=UQi4*jHY!G`NI zmK<6&w6z`Q&|2*shr8Xw`&_1K!tf95DHUups_Yd`X{u<88;*YyC117mGw~hh*B5eH zE)RA00lm3^F@P5I)2$)?&If<@!=HZ7fp+WkL+is!j-r=te;aynqk8%FkB&cg_$)Ro zlM>uPwq!2Vq)8J-a^Tkxj2_AJWZrMg+P#NsU`_=4{CyptGYic9I@TjX`z*lm%eF#cf?9 zG=#~h*7W-4v@dwW#h?My*qqx|u|_}DnNu}?^>=Z%xTSw#7PPhfgNqOB*n5u7Vk2 z3s(-E0dV>WACN9mXw5VNF^hN##aT!_LSFlt#i&!iija`ynW1`$Te0G9+jl%)t<98* z`KDWr4Qu>Q zxL^;hiXdE9j9LUth$<1VHBYfrY?NZKK&`UiYFKU&8jn9I(TnF+k|v^vtgP@8O;5H> zO6tk$@!$ReJ#`_--qn&BrA8xtcYeWlOTOEQR3@pH2O1X|vM8?Hnw-U^JMsU_H6`+bVVE++1vwhi zSQJkk{<7i&`AKGj^UBn*!Z|bF+iDs#3IoHSYXeBk)P?* zD*0h=ZRgT3@to<_MG>ae@_7>-H^yHBjc>^~Y9OMZ!^8jta}YiE)B_JZh5e`A|Neic zaQOM-ciwsY^8~)gotJ;^jDGwIIRrgCEj@ahVSJIn>P)D!LPKAYRDE%~E2m8+%lxrZ84)%mHFdFOp+s&l1c|FX(7X6GAd*RVF_ z8zQvha^USZ1wVJCG3S`UmXd9c-@RGY%;_*irl7_wr%Xm#n9HfC(TN5vX3j8XoHmMp4iTA`m0FC%?Y zISf*QocUX18DStT5UH0l&p;6N(*8$x-AHa)4JxO1mOBP<~52~cS zBS#gIql%1z778K}d>g5E-hJ{UnCKtfeK+=y_Kv&W@eX91eBw&jy!#z@pVbHRWQRn5 zK$!$!s!?ZzUOf4&54L~5^riNLe@(VOS$p7HS6%h32Wp$P4Zlc^!#I{c_OkS;bYy}+ zz&xoJJ!#ryLwLjk2#0s=*=UNq82|Lkf33P?_kU-muIudkU;gr?*cAAZjkjN*>tFj< z^ya3<&G8NIA{baFc?Wv)`0aR$NsP0;f?p+G?*7~-a(|S2Cij1h)Qc;UVFv&rmzubp zh9k4a&)oY_2<4b$Gk}>JWWF3R$`D1Tmx7Aa&Sn!=Z%jG@ZgbCKPAHr2au`Xr_x zi#8-TQ>#aemZ1iEbmD2~>IYEfV>U_Oa(D40|kM5I(0xacUR?kX-J zRm;?IO~&q3O?S%7ER(#cDUFuJ2x75arNKf-l?6bC22v-z{*d@yf_1Gq0@1a7PP`GrP z5>@g`#lY2N8z84ZfzxhLyBB#Lx40UK=79=jWLK)#uc%ucV4U)Gj9v|mrA(Py7Q}u|k^IN4IZbFx z9Cb=g;1&vJ$II8qU1=Us19W%-fX*GZ$z9`{=Nsc|$m4j9IPJ##|M6XXqA`Eg59#7( zz~;7cdvoW*mB|E~)liV8cdVl{4NXEsiE%rFMJ#_~eJ7T!zay}r$Dwz=L* z-FpG6=bZqP0pV~@lE(8CA8z_Wx%`hR5eQE8iuUg_#hA21pKQrplg4-^!&|0@4FPs1 zS==LOr&aDu;AfD*t|;~9%k+rlGzQs8WKc3yY26WEhY7M+!=Oy$saXhR2Z9ISIZ=NQ z>p{sPoat1P#7GyhNBI7IP(@F*ZG!=+1K!0zXBZ1pv14*l3=57^wizNkR<(Me?3il1 zwYND%$*!ArUwFa(H-^6NU$fsZ322I|;7Vt4xMmoN1@NSaDaEEi@_ArTK!Y$b>J6wy zsmep%=t%!jOpwNSp-e$#*%cp}aJzpH*owNlb|2ky>qV9w$2T9m;DQ4;$4s*h_%@j7 z40G6ExADYs`?;cwm$kgaBgwK9Cq{amc2ks4Z&|v3nU_fWgI>J)FRs4&eKkxua(?!{ zHSFL&%6^XjIs5q$bo=;i==NI_hy4iM{tv49@`JDZB~b_1wtpr^0q4ZIPHulWcQ|)4 zcXRG-xt}2gg=m2Gpi9wf&>PUNptqoRp?lE>B+pIzM0TE#gtg;`-CCX>*2AH;p7DS$vMm6fR&MgSO1=0s`HYrX4y` z#wgZE7?7lsCflv2^3<1pR2Y9WlWfG{04^M*!MN0+GD;1R{7!>CK?r)p52LP3e@)RT z6ey9pWiuHx1Geo&oRt%Jx^%b-?xi8Ye#RTBNbb&vgEVeYQ+i_~>7NaF*qw;090yGp z1?_0iUm!ey3;l2eMeKkUwXjGXInp66E1J=*QNQW;;F64bbwkmGW0HT?hEY>i3EE2L z0FC@+zZZm{J^HW*>VdZx6GnY&Ht+%*1=>#C)^(xiZ!G6+p_tPEa@10V7?uHlR~aS@ z3Z7>e#)yI~%j5wAPzI7Clf+C@!wMK&Ca8)@lc``j0HbIeqcSi%hM39F$2jGLVciu? z4b+ln2vxPDQ<$q5G!=i0pTz=Yh2p^@OEpFF+Ice|2C#11VN78vFl8A=BU)7 z3O6~Ylr609TO|YrvO=G8xEGQjS+IP{l6Tpak}BGTDOVA8e!I!RUcfC(tXYO-gEcU9 zMP?RR)I^{XWRVpn^AWfK;~lFE%3qYtV(U$?wjapaLWT2~L|}i{YedD&F}MLJU=dFE zC{S=P%8wNbMc-A0sQX??kQLQ5^8nM9EwhuaGGK@Cf>=`x!!r!E2D+9IlGhj|6m%rd zn*b_=pp-PZs$tOCOqfvUE@uj;2-Cm_N~M9_q1eb1nbCvrxI`FZrlr%$vK1I{ey?Xx zI7b6Bwq6^~8kB#C*oXszF<&WJyr?kEY}MiDmeJleCD@_zk&F|f<^#Ryl%hdGrBzU& zN+y?TKV~JOQbsVgh$wk}-O_CzYI-A^F9#uJ)qKaaP5^)cQ~`)d(PSzI{0vmiIT*ns z4$cazS_%`;(1r;=B!;nNI+fs~`<7Fr`kOOcXWkDu5 zAvoh&OQ~wA6QYTE$+CGfung)%+UHagfzr4P6gr|GB1pmb2kG{o69lhQY1Za(xqZD; zY*I>fZF+xt0X2&AHRBRNiJ?*P5JF02`QWbJvZ5?3U2twUap}8v&P^-&VqyEt{%aL| zZT1|+z7p|4ecj?wVQOlCtJuKUAk*?oCo@FHL?f4h)do2QOJ1ywiqnKAj>5~nFdSvM zy;B1lwszk3b<1c*)9l|+JL&CRt0%(TyLNni4hDbrA0Q$~qp4Wy#YNrmO0tQ?#OF57 ztph?koI8=bJ@=cr`(!i}=z8X^lqemvG3cD2Ce9rJ&bmZPT)C%RCm=>+_8U@QL*M zTA}Nfu0H}-ye7SrAx=-i-7eaD?seClyLbF4q`AJUp`aQ(7F5Hq8sL{zCn!)3IL2_TJhJL^?GyNe}m@gx~thiV8a_8z8RBwBcZ@}uY=y{EQ4JDTQ*89kuSY0 zX_U#I2=^qfHQnD!q>~iP;BuHbprO?>^RBT+x3*!p39o7dgtSD%y2Mfx z!v^dHT3IKxf@zrH;qE0@+z5(}Ryu#33gw&Mg6>$}R@vA+8cc8c46X$(GMzh^yGb&U zG@?4GW9A_qHC|!;)5t0Sip?-|knNStyLm~o(;cSCOslBX>t?e{(iCWDLWvnNt&`px za1Hu_O0=4Qf(p>Sj9nJBTpy#agzXELDbba@V7ARd7~beD6pTRJtz%A9!QFpm*;WBi z5^OqP(Foo>4c6rF%lU&~s5^(ic6hd=oOE^zD0_nmW;mk~LLmJ_1WQ1G;%IQs_Od!{Sq6L7i>uaq(2eMWm_EeK<$2Sj>S3;tVyjg _vWY0fU&a8wxC(TmvRD8EQ> z!wiDuBJ?9i8^AQjPpDx2DXNvc0(%H;m-*1+Cmv!jn#MPK-bI9*@wJj1na_PR_b{|( z=9HhfSb-sR2JJ~`sh@u=PaG~%G}Cl~bY2w5w%jKf*a!$!0{n^xS#p$2BuHtOfc+_e zP4lSIAWNDoAYrgfR+ky=socx>RX4~YDp_=3Gc6QOk;oVnwdmn55)o*orkMb_EXS{< zX<&xP3zgWB9~A?~qdLg6t^>>yh((4)Wm*OSIRtuxfVan@JScz1Jmvtlh(R3-V}PF~ z)3MMDCyoJaI4&+T%bp^i0<%c?U7Jvpn!-$X8*ewsNV$I&3{fEDg$Bq+^2 zO#OISB&-*~;7PB+w}3Tt^aVWu@Y1$5f~omypMW+C>&-%y^LFSmL<6{76Xdg>HN-K@ z88BbUbz{-c6rmcHGSH}HJ_r?#k6dv6Ayn65^`y&)Lx`zrJYI}--=GcjiQstngOjx^ zDh0;piGK)_h<>adSCnV$;Mef z<~;sdlc+8@U3~V9#9D=}gb(t@!pSH)S!i9adHSjSyRRqaf*B-Ed38I`r6(6(JiYh` z{#xn!(uH@POTy!!^wq)_AyfBGrSI`B;#RB&$(ybs`91ksuS8}qxeWi0z2tF(_L7r5 zFD!p2r%%JjnXM}yeQtbk`}2=J`aJrB)#uQge*S2>bw&1>H5S9^8L@!Qq{o_H&+))SH%TrBN^;-Nq5Vcl^|Y z52BR^|MAeF4;?!651@6e@lW0*|80)(7x4cMl4t&0ytXrlUV8kVdmcwGe&N0E{Q~wL zJ$(A~;YZ1%M^2wU^5{fHLi}}T)#=>U+!1NB2hz~;)S)%8t5Gvc`;eMoUC!KZ?qm z`e=!40a@$j_T?^0ZIRSOvl&^bpjRYwNS)LkpynivHcg$E4)jq|PD1DmU_0og)O;2O z|Ddy3S~Ab(8&_;?RJxa)e=OwM?C7Gc3*BG2aHl8d367uIyJP$AJv)Ea_wBhgnZo15B6BHZTytt!ukSg$ z4%lxYF+_act$UNzDb(DyM}Ds2+w;4Y%;D_j73sA{_m3Y#t>&%2_3L}koToOI=*rf; zx9&#om|V4O&yHlGlNQKU;Gb`meFh~RPA1XISJDi!U`UV!NhW^>V0RYZmBycoB*lOd zA8rPDQ;aX;@o5r|48sCK91i3Z)pWM>TDlB^1nZKNPNK4M7nxTX$TJWVPKCyq&^&;L zfVWkuxgxUcqGS3_(ZGaS153$smD+^VOT`)uS-aS*&ee)}Zh-Bk9P_E7mLIfN3{GYb z9dM~83P@LUQ!Rfl2%4y*UlqFWcxPIAVbN`itDffAUW<49W>II{0|Thf=my|Wwb5v< zmqJwluom&D*eD3g0%TE=xoDW^_Ec@zn+4Pk52cP_9y{PMMKdIc&??l-SEsP8P^`rj zV?$#$5D-DA5B1uN*MSYP(viticYjLS(=6PP6qVt??csmN{zOY`GuF1HY+O%gLz=&K=BoJg)~~sp1&Z-Np9I z{z^Ue@ne54T#(cm+ojt1EnQ70K|3<0&hM^!y9tompsA~lH|%t432>U6%*g{8+)b_q zWIfF72EKk}?uOjoqpfH!x)S|5dMA1SJ%*l=T5C1S7$0V{s3i`aB#C9o`H9RTkWM6- z)-xLPWejBzH{~#ttPo5bpGaCiCf~oX@jMCJW)GMwUER) zP11jtu8?*%g0nn~K{giQ&zE-?$t;o)*gIJkuz4N6v@8|cFs*OYl&e>a1|7lM1Ia#y zlMW%>%>`bv0G5r`F+ezcI|4;o2O0!&>te~q;YD3dqZoukxGIc&tZ*b|6*~-h!QzH$ zO6%rS7|siY%J#%|iE(P^KE4Cnu^=M!Y(sxeK<2YVx&PDb%&d$I7Do2CIXuZmRnddT@qs_ zkAX^fMhKjJ{E$VFCeuIQWtui;s*%^B6o|^$0mB3=IYQc&$X%=1uB9?i1IPq~fGB@q zk*&Lp(2MexPoeOEOc`9(T;pz4dcqVskabb?IXN3!5)!7Ua$-6#A0?<%Rf`~|DvSh6 ziHyM@AX1J`Q35n+=Tlqx(2;m{Tqk(2Sv!*DYyr1OVR+i2-aij~V_|-&C$jd7Q64(sSVN&JL zgG4d&;@=b7cMvsRhjjvY2W1&0h^ym|`*05~ex+bLn&4{bvYC}+1#T`%iWL)Ll~l*Y zR8?80tO6{`+Z5Vv7Rqagpzb#MGa4E}_XH-XXvwWNksml?qX2`rz2OO}5TeYG3{ zJ(Qs|1b{K}EC*wQg1Et0jdULs45D&Iyt1KkM+AA9ZbB5*F*RkrvN9E`F4R+Tj4~J_ zC!n&a2#u~$W6wnxhjNy$M4ygH)0yv7dNAFn!qxl>(yvVN$y6(ZLdhO{2iKw5YfzSL zP&`_)VDD$ph^ufMz~S0upyGd}ykNS3kxXw_O19x5^lwNmXkY~Se3-A+qrk29c5Ls7 zNv!02{5QG3%>7mF`?-J0{d4Zc+{-d+e^XVZQ>=r{T;#9^x-hhGrs_a331C&A=&~^@ z)rg;lgXNaw^rK# zzN1ijC1gZ-Ac6J()GAz+CQpD-8g%5mN5D?YlQgZfiPTa%4xz6GQO^&2nS7B*RWpKu ztfL6ZnhaJ^0QG1~63ZpA10$KQmzc~ek5Ost|#WI-v@Oj#3dq+x$$sHw&;B1T?CDu|&_ zxf|L=08fCCDBvj}xPcaC=JA|ix+-(2X)C5?2^hqJnP!n5RgdRA*U8KI@x%i4W*1DM zTdraopi>9)OS4j!*+|WUPO*I>B1~6I-?EraxI*wHLNx_QFqLb^$dMrA*t7+MrmK%X zj_|o~L2Syo;aGnR?js7c|C2(%Gch5>W#Qeq@$cI!`UVP4Bt7 zjM3qhUTlV*OaySOYE`NX+4`XwUo8gM84z+!Y=j=6n@4|%9~g0W1>^u!Ffa^T57fl- zT(PvU8pqtQ6>jFWHtbLwzoq9*!`4|GubybOl109#CaTvwF^Ib+(BhWwD8lvHTHfT& zQh}^4ETzW--9F1X5dIfnbFuV1d48JB)nAa4HrXqGbYA)6*~ZGoN+bP1?X%nWy<+zp z8VK{pQ_g$_=I@%_jojH&Ss!#V-0j$|L9H@1Xqn1^LF!?`L1*Z=vb&cgJJg zocL*;AXnt>&V4!eO?WPF%Op+q3FV}kEcSI~-5h_=af3c^$w3-Ex3XwgXEUc>o`&J1 zHHqrd+!-kW6HEuVCxyq-)14ibplFR|nLR*X+Y%D8cUsN1}G*lOQ7;JZuvl>pygFf zBWkG0bQ+E=u7U=kSdHt&>r8+)(mxv!i>ZWS9e{&|WLOC7DPxpQR}0%@YQgGm245jR z0Culj{ZA;(65f!Wc9P)Zy`7|0O4Mp)L8X7?>G_#182Eg!D`}UZJf;TL6G|zp$TgX{ z%sl2>esE+fJYcq5I@OPTg~y_Js^si+00y>(jUDGTB({ppfYWr^HZ}lYDK-E-MY?Hy zohrF zpiWX~E@ArmX1H3;ZUrNlhGPBRC{s1r9HOWbZn6!Td?-bCw1!h9HKE^WI*4?_p9!ah zUOSPqjkC4NWVoR_81>UfMVt8{BA7Ewp_RGuGIw#J1cAk;d5Sh_PL?ACc?IY`+=04& zP(xMR4L99h8SBSGnNoQ|RD=>~{za1xJ{*5i6PyAc&zh>7_-fdZoW(3OZFZ?R<=F&i zx5G_Jp0nl6^XK)zc7OjV8ZCBRl7z3@{voOzj&WrF|^HSek<7I|iZ9Ry_y zB;MPbU#!WsjpPGWD1;DHm}M?=n&j$pgaN$&ppq!B?F9sRvSbWUK0sGqKUX1O$P`AR zG}M}RaM6Gpv-#z!341lwjKNqlYhby1-S9|A3`6Ly0j4|;wU3LuPDyETA%GjoNj=0c ztCVU`h!sHS3KMa8+HavtrIYVI7Jn#0WGXocEV&Yr3d50)U%esu4v|Kkgep9|7HZ+c zwRo|Ph#>oBj`V( zFQ7k0e~O+#e}(=Q{WtV1`X}@gz?Ltk`~_G8wy+0?Ho^rQ<0`Jv$L5hY#Y5@o{`9z6`$#zZ$;|zaC$UZ^Sp@H{rM7)A)A$8^Folitonn#J`2#i|@w| z;0N&s@k97O;7{OB;z#jk@MrNK;>YnH<0tSJ@qflo;-~R5_`l$pgKcO4 z+ua?gFLQe;sJAVRX#qR!$$y3YMsQ?UlRVf)Ai7EFE=-198CRAI_xagXKA**H*wIUa zaZNvfqwu!{O*Wc5Tt}I!ktYo~I^cIh{%$mF1{k<n2?=@N`UyN-UYN|JXLmy+_jUV`Y*2X( z+!3HpRFMlji9ttJuss6J5Dlfil1d@HA!rcRgHz-JrBLH$e}IC1JL+|sP_C|xM?hfYCSPQms0S z4tQ|vVAKSjEPvG|6ypOJ?(rpQoyW&y{_CfunjCj!E~Qvy(whJ(>A4iQdn%=fl|e&Y zCNDB2vHkInfwt1|k2pPr(;MCezy1?ik+&cJ8O49dZvqx_KwOT|n}F!v3uiE3KpXI= zFER?{TUcp~HxBFu0?l^sCFoIHJ$;LO8bTXn4q%6Qe1H6VP^?EWtxjQdewz1-(LJ(N zLMR<_yJ)LSaD^L6Q=_Cy(MTF`92CgL-xSWbJpbV z-?~FC8B*;r!lXlkRAWF|0!p0K3hqRkrCiVE48z6cRkDpK3e42pD zzo=sIAO@TQ$KP~}lLJF5eD zJm@TR@BuJyQ7PQQ7!_EHge}-SB_}SFUQRZbL z(`}x_bUpRZya-SHz-~+rV+1T7&5Y*`?v=T$;~F7~yH3aqA%<%%4_?YU8J1fLy&)~~wr#`ABy z^QzAfGaeggc-w8`KSwXdPfobwm=x2Q_^u47!)58RX2TJ_`%RaBX8f}^8l!SIXh(}A z8vpr~pG6MZf3CLk_1J1`a?kgZL+R{KF3v3G(_GAm->@^tRyva@(^)#0oTvrd54~jg zP1AE7#jq+GRInHB_pBaZQD>voF(Oy7%pE#I(PhFSTQ$36i?lqy>Zq!oofkQepUH)}Vy>E-AWb7Kn~Ny+>*&DxQY563&%P*lXHWur7Cz&lTt}K=p zKm7FXEq>`+*ItXaPXD;QGXA7rI(UdbQufZyn>!yL&mEF$S$49?E#X;zTe`Mvy6A;$ z(zR@LvM6Zx&t{{iF1q$46w;aG{mCnyCCrv z+#4jTld+A8j3*6Jh?lt8I*e()o7fUa~T6<4ix{a~j zy;${H>+SaX{`FRWYu#?CrlGauJODgE!@rqnMc0*S*Z`e059;(j!cS1n7sLEbFaI6T z1I@A-Bss=YKR%# z)@KBe9EWoI>{|B>KbC9f*5nA66g12I%0f!nicC=|I9y0V45zTkG&;rW+3MIGFxE`RN}Z7D=J zjOI>nNjRe`Bg&cOx$z{A?^)1ScCG}y-%HmyldxTmy{7ExVJ~IsGP5HY^g6$68_+P2 z0}zm|A>oq=K?O(_Jy|6}9#J_{R2S3WPlOk%<{=cm{F$6{-IpRUX+ zIzD=%2Hyrca?|AZ7F{fe^pmJa4S!iE9T>yunoNxWOXLDggI<(=hN6-EPJgi^=$-!N z!|0ZmzdJp1wgThhuvDXvUauzLT_{?)EjLs=aNksPD7?uxpZin z@*)@Pl=F(yRS6^~4gHC`IwG4!ntErt($N@gD%xImuz&`wkzBGkn~s_~rFoRDq zlhsKeEV$WRNJV;s$ULPbHJv&)4va+p=MRpe08CdDmi~_pzMgtkxI%PL2Bw_;rjsE` zCIQ%!NJ=MBcM`_Y2jMvsYh}Y!0YQ1H45cDPlL67#EY(dzv9!DZz5X)#SZu}t@Ed^G z=Cm;G6-W{WhN;QmAD2tl!&}ax*SVvB>Cs)#awc&O|FBG9xl_ zUn(=J>Q>#_Ze`tlskOUr_q|fLT572`Ee&eWhCm=&Sd4@Lo7EDFv1CAu#bRSI9t^`W z1_rR1K}JScgKT-=1AoK7cr1Cq7M}Mb(%v@_u?oe00wYfG4 zw=Uk=I=cCV;qVOn@AbYtOonB6VHNYW(zmiJK=TOhsspM1E(hJ$t59ro2?V`tU^Hv7^6MuHA2xX(9ei6KnCUMz-clLzL)cTWX1 z)3j`9R>M=le1F;Z9J?OS4_T&J3r_isvgeoQgHvJEl=g++s&=wNooqh;ARl}o?VK1M zdCl`)yPaO$diQO|#&c9ePf8<#flYTzu`VhmJgNb@_OioEtrFY4y~C zSw8;E=N&n8>$MkGtj_6`?sKf}c9tE^CSP9M&br%O>woZNmmYdmKa|@g^i)n%ddUf&{Lgp&y<6#<_6WWF!3V%RDvUy_ZxRrqhnq?-;Q7|1I(-xO% zB@38RXn)u4IgwR54D+1eJVl&a{RSuI5oe%I+H>TJYB@q^21B=fso67Ks0e!FYhdgO zopufZ<%JD*P(tu&Ob?AKC1DJ{LLsUTn*lOIjJ3s;TBXkAvQ@!pb(Meb6m>Mmu~WM>X)i(Kt$&u< zn!|0uqys181kVj}kKVTAlTU`N^Z?bG&cSlFO6lSk^e%!~a^{r6(5G4|LS9KJt5+76 zfVIoI1J(*O#*?n8XOo}Waiv_I=X$`1ec_8-eotrg4pS#fAS|?vg~fv>mb|1aXc?u4 zW?F#@lFM#Y;<~`i{}#w7JrS6WNq?j6yJ75JNU7slt#qo>EYrseZ!3JL@P|2ytJ*M1 zXApE#wxD*DklD=OsHcWon#>R^@Wx@DwF857lH7bA;G*_aivS%M`;NAha)Qx8T={^4 z;e(pgPM$CXZOUS*B0t-Bc+`bbX9^15nIL?^SVczwGar#R>y;$p(gc=Za(|#foRTSm z&IQm721R!rMx6Q~P45OJyS%$Tbl}ih-SveEZHJKnNq%IZjT(Z+M=Lu^C&~!HhR$Fb z^Q$PXL8c1}7!!puglPn=RWJ=f?2Gw^Ms?Twbxwf==?wQ#nWjrEbPQ8c$xU)sLpq#k zrWD+iK7)ZU7E(%tj>w11w0~g8?ef|sCoZYkTm*)-yfD`oNFDLxFt(jW(lbHBTHn0X zuysWKuynn)_AJxWiOw}*f@MQTbD%=mfpNyNbrBgZFE>1!$H2RcD66BPtK(7sb{Nc% zVR`{ziO_O`{jzRIsEVA*nSezqO6j7)<%QEKu2lJaD#UmJVnErfihn1eqcTQ2s+32= zHm({W{ydCZr#DYrs^s^cZh=sJ^y-J1_;FlW81c0eN8}^&&}HlEmmQLi;L&m?Kl<>e zKRx+wP~l#^5rN4{p68{OUaZb}bCY)$=jOWADjD8z1G&<+e(Y>re#l;Iw-)8_vY}jT z?H{`I)8CJqHOHK5*?-Or-dwe)Td5b#O;+c+@QAq^ZrIc3?`CJF_=AP088jY+R^=Hg%wIap9)IzbO2w@_z=7Fi;_(Io@fv zErPT+9?hblShhc3kd()>RW&i|q7BTtlY6W8k?X`$zTXdU4u3{`Z`P>^*8L~<)0ahF zoHkNZ7E6^VbPXe3+guyYcec7OPsrT-sFIcqvlzCU7aj8AxE9ACj4^cgr;%Z%%_Iq; zW8EHEs1H{+*NSc!&Uw~F&30HcjdEHU)+_WzjNgvqiiU6(I(`2oB$-cB&kXfrz+;GZ z;VD+BE^iKw9Dh99Uz+@R`;L_@OY{9kb9tdvum0;|tqA`cXm1Pso5%IQ^pm8X9#0ng zXAT`1Y%W*J)WN0Q$p_qOy|u91Z1}!rZLQq#qMx~SCR4kt2pXh`aV_1U9y_EtY<~0G z-y(NSevKZV{2@6_UiHd*f9uER{wBC)Z%ra(7r|H~?tiY%voT_uMF}6Xb&7yoD)1Z( ztIiIgUz>bMSoC({v%g^a`v0Q`#Yn%v`Kv(1=bYRcc%*lpnw(q^R7`FH6@z?BTEGJ) zcgRG3T1tAW-k8$MKZnJuuq|h=bc$)&nf^u!B>uO*O`OLbn|$VrUwp*_5B%w$l5@A* zGPxBF_J4MFNqL(r9VWZmj~(9G-rgp|?T5FqC}Zj;gZQm0g%gD{g_~9U^2_&DJb5d3 z^4*UNPiN5i=xd@Qa*^_wYscyhcsjmin8zs{C!;a%q9hdK7Q*hUs08$e!Mqb=Jdx>p zp=+}*^s~U$inYelGZ!1RV&GVg&UTdmgcry(`|MpKU`dBUKEC-Q4n3! z^natJwYkXN-nOH;5;siKNEQ|wovyuodfQoC$hyJND@Xs`bX%R~%aeM2b$&j{Kc;x5 ze?UJAlvXbsDqLH5R^dH`_Z2=;_;}%u3!g1K3S)4P2oQhDg(Gd{Q4q7F9PLpy&cj}C zJ9V0^CJ%VoAz5e8M@>(;{p46&>HfN_zJH~bR8=kn^x71ON&pcLT~gnJ*+kv}ENF-J z@>pr)LbHVme8VSnvR=Ap3~nAI`#%OpNL(Cl#={Z)!b!^rqClGU1?Amq*4kSO^QNYv zGTVQr%WA6})R^wCo5TVMV-VrHC7;$NRk*wslv`ETc0B5pJf9Q8(5OY_I=Sak6n|{2 z)eKIZq6RuB`s)%6cZB&6HZhjD4)S?z(XrxkY&nZ-7%<8+fjgk+i=-*ky~zg&sfOk9 zLZ$M>EUZ)(%H=AdAAeMLICpe+yVZ8U^cV0ybRxgCeUB!q2|QTm{>Ioji*+F`@I0Hh zuOJ+gaV20x1R=M9ak^l7TlB0u+JE@m=P0w$w~6W=?$l^nuraD@#9QPvkvd51WzVk7 zkoiy2BnOq zSY2svM83D%?8@qDEEo=($%G?g35x_X_{E@Tojv`)86%)JQWoDf7&) zh)OveBkTvv_%TlZZvGv5W*B4E+_c!=>S6y46{<8B%Ck`|_YJzktle!VcpSmPk~;2O zjJiYR4T(MygKV(5LVwibz>*zu_2luigmI30fqs(RcnEw1N`sR6qd~V%|FPO14v%y< z=cH@R1t3oeb3kJj+?jhucBH~Idf`cQDykc%QM4}XI0i)&~OJ)#L^9MmIp z`BM?*VAj5KE-guOzP095ilg=6?t!Y`4r*z+*Xxv@vEmy=xQR?2H=4M**dzM=Hs@6i zrI$a2VId6I0A(S$25J)_nt+JEDJ2WRnvNXO(0m;(g{nLPHH~md#Afo#1Q#GV9AUr1 zA`VprgP3213x63t+HJ-qU{h#BAe!PuxKk~JQfFK>w>Fp$vi^mjhu&LwH%OaD3y&AR zS@>Sz|1JE_!jB6-#a*g+A4*4&$~qm-yaaPc!yNr?8g(<>{8j$ZN?J@Y+`BzoOTt?Q z`MP%=%ZunIG*?x-2vM#KI5dWlF#A*WLfz3BsUW`b9)D(HlIrMzsLaZolHpPM%-c3! zA6F&Kmdy8c7Z8THs|H`9wjw#zI+1k5IO~jol*j65MnA?uNu4V$q=<*?RX1ydXHmIa zq=`T0x6o~l2(eNvm%|hcxL&nUPv<~~qSd|?B}rtL!^S6dP+1%aq{zI6W(})l#}=++ zOuDuS_J16Y-X6*7fqIhoWslqi${-B3ZI_#Fr5t4O$2Z0ES`*yVUD2HLdQ@uX7$ zM(SHdDzwaxrQWUg%%US?*>IW*O~-J6J_&VtRn4ZgEt5fqR2<84%S9h|A4|Q&J`tor z*AFc5j%tswUY$Fh43>Q_lpS4AQqE#CFl1DX%3ec-1qo;6vxqT{>f`z z{NlSm+q|^BvGc;s!3tZuaNm=cNA0NBTYuXkYrSK~ez>|gPWEhy6!d$$u!{4{-A`NI z35vp13Ds~>l`2%nU@+WaFo#&$Ja8=cjBiaoN{EbY!;|xKrkBL1<;b8ISWWuo#(`Gj zz$JL$b(4#yXVE1@U2oRN)lEAL14;Js@z5`U?tdJ!4RrF!J@qNi_o*_KDJ<6qdw)~A zbg1HR6*|;ZmVgK%&7KOBBzxe<$QFhup<{GRqz|40)&v8ZngOEpIx>i)S8_DaeLy>O z5ObCZl^Tb_^r#!nw6bDH4o@swCm>LP^4a9e!f6-Fb}?=iyL0^Tv%e|5RsX_=z_N5p z^H-d$QBDnkt}sxqN;B{*2cOC$K7Ysp8U?$U*LbyB-}M`@>_nk3z?#Ka>+()5^NNx) zeU7t}_GD-A>iT~C!Guoos8-B*n_~qj$4?jWPSkR^LafQR>{T?#=x1`N)7876YKLL^ zTZnZigQG#~-d2>=fkD^07}N$_JqpwLm6fd_h?xoVMj&9y@OsB@Tq+UQ7=KqO!Rna< zQREYYIYYZC40fH=#i0+I>i)o(0E|qpiJ?2o?;+uw=H-d z178$&E$Cnhv^MvSZL47J{C|5(#fi0BLlvs;sV$pq*d3s>&-2fL>Oe8H3iQ<05u##M z+kyjS2zkkED%C|`JaMN}U za-ud}(?iN3VL0e*tCYy=`I7o{?H(87u@}_LQ9;)#$Zee<_`uYO7*iHwCT~nkn&(8p z`_WaVKj=U?LhZ`5Q6a7qsM0V+PtzVYBEybN_VHh4;Ip0C+D|lHF6yN-oCErP#`H*m z7cgEmQJmm1djt~1K!50*GFWUj=C>5m)XL0;3-u7w6H;7S#27YBCF`Z=j-KJJCCsTv z!%$So9UC`ra3=?mpcuNOV%A>GW)}5j!X(3ZdmTeE1qP|Y|3owPD^!e$D^>TvhR~4NJCD&x6D}O~zZ{ema2!V)!W+er;F@KJgfVGJ0s<`8WNyZJX z9PAu~cg3`f#76IYILKW~w=kvvE{k+A`8WR%$$8h4rUzZa5jPwPGc?e;)Sd>SVVT6! zK{^WCSwG4mTle)Uv!kspCpX_o6R<}3Y62^DxXMGRS)lKNa$z&wP~N1N2i_9Y!0=HFa$pIrhjS3TxM{4D zs;R*^z)vupp)<-LtR0D&bF^~8_&kyX>YXye-cuv7Uw@mQ8wU-BLD3ikI~zEa&!H?e zIOqy$tCWybRYAkZj7WlJ05?^iP7zQEY+ZtgvREI2zMrfNmBQ`y2)MaLkt~#FB>WOL zGc&~WES`yhv*-;QFdS5%S&uD&W{0gT)_oQkp0(^kp)im<%*5pD%t(1?f;O>8z!oN^ z4HPDpP=7N=@&iu2m5q(U%^NbATp1s5wa)$13xWu4&E#n&UXpR z^sNu!DbJe9oA1*P6mHIQ7y~cBecRqx@tY#>E!?r4uF_0xSY**{Wz)44Z&B8r!b$VU z6iKa%sRc28y8<*&c|rS0e~Y}n5p(nwcNp;VFn{o3Fl%g4_RGT2Z4-J}NZ)d+i@GE1 zVrby*hoMWqwi|;;hVFKm?L=JHZ3mEj*`id!DN@-$g>M zx_>*OJ-LwoZI5gLefg24gO{W|!IAyb^FrX_20!6c{ z()l2DU1l`G$luQcGAy8zEeMt-MzQvrZk{PtbrbXFxWbUL|ZDSwU0 zt2-}$&#B8_;QE!yOU_>WhC5m!{Cp6H0S4ZGcoi<#c3tu{Hx)~Mc-hraz5a@`SDik2 zdod`TI;h3Xpp<5QsA+raSM+_LlgwkJ>W}aAYZZFbRp{v8PK;P5htadF?8&fUvyS?9!F z$D3||w==Umg7kY}+a!gCT7SK}xNxR$US;4#h%W>rl=Z1`_R(tm!nI;2is1wYks z2^YM&Ro=zxcgwBqT}MXJN#M2X?AsFeFsJalv)z^HI@~^l3veOD+iN7*$5%n^P;HM) zSMhPvDUXm+T`+o@r7@ai-^<~Xa&tuCq>fZj@Kz3PJhSlD3F6I61GIQ9PRM`sT+Oge zVWN72nt&A2^{k*|xqmusxF9+0$gan>1sq?Bo{&Tsx*ZrCOmQJ4%rFTGk_fFhvZkRd znsgejYe5BsZ4m~ST4oT$Iwz9%6sWhsG}F{g&oJ=rHk1ge)}y+lTsqQsxZ(Mx>A7&9 zWe8IPRwm1~35rZfXhe7HM8l%Js&9CpU&m`|Ilg@Td907TSbSfXh)Tj!(JW z1i2q2=9EWC6y?qh*yrd2g=?*U^j6m^v z4E+LI;l^7pJAd_GdBhJ_R>-QlV=)Ms+jWh}9YiCo#M9sxBiGsGl_FS@ot5H&){O6Ur?e!Nby#LJe!HEXC^3wu;k(L*&V$ zfx+A+uGwfSt-)>mjU@>xaItBC(&d#`Pnwci{(YE?Ab$#%&PYK`?=2C$k-NBm+2QI^ z|G>i7OG!C?_PEhZJ4bt-$=J6I%W+W`mu3WP^G+_MZ=y0L>8$S~HV3ldjLH_eA0eiU&y=G;j*L%ggP5sE>m;cgD z5jW6liGQZ?jipw+8UhDp(PF>6)Z95{+hxqQt98z%jbgWW>0+ryTx-5$)Q#s}eEH;K z((yybqTj!2`_v`BbVU;ePy5u%uDkf^*uB)Jn$T12V>g~D`lc^nzEwA?fr61W|qRjrB?|;TjLJvvhzu7D-7y5<63L_48i@Vuy zmx%FbxIxlcj*a-Ad;#MdOmarLopHZT#t|8(w+{}!FAti8{G%WeB5GXKbbkz1~O){V~O z1wiB-YyGq5Z*t+=+t)S<`||INpp)E!l4IXrEuJm$4!KH!Gh1Zosd#r_a)V3>nq8`n z&y%O+(MF;l!01oWX7`h9XeEF0mw{D*aEE_j)PfX`tILDX2?HA}7Ur4`42&aOF-aGdT`CAC#0~@q*(-+r{#t-!|c15MI;mncMYF5u)^^#-V#Ij_& zv)Z0Ne&B(llT~Rw6X9&9)ogcLmybttB_o#UTMnO;lci}g3NOqn)G)7xO8=&l;b|g& zERO3*H0!Rx`Uw-NN}RE0JpjWm9jI?!{lBX9pGa>toT8hH2gt z#6nu?>j6zOBOPJhe;vMsmYx0T3nlX^DJTDbE*3n> z3VZXq!fGnlBXA}J#>1E<^!^(+gC@E4!>jG?xzp>F^z!;qa_8he`uh6iX=Ux?xyAPS zEtC89?Um&M=3p9R!>J;Z%<^bW6K4&T0yMy^8k6s89)ErN5LfPU_>KM^xRu(SRNGW4 zXMK*u!ny;pih91S8JZp8@<0N5kzM!go|7eIyRKC=PBFKm5M`cezIHsL8m3n>!QKPS z>e!a>^+W+oWRfu*IBOWX5tx|b=w*XtCNp{64rnH!11eWq2fJ|j&_$cN?pBLN)qo;> z#>-Zu(*n2Rla6aCe_CGVkr|tQ2s4J;y%XNNH(F;^Ta<0KallMG6XOzAG%(){#tmAP z+}C}rcO64N8WZCosI6Ck2&{6*tK2MWzRs%-*lTG$ke`;SByPiG-5^o1%>!Nn7 z;ttSZrQdCfe$LdkNt7R~5jc$+jY4Qr%e~!cy5MmeqZ!7jdIF|pQPUOeintF#5${uJ zv|&EuO6`wU?_C1DMrs}Yr0g(FH)YxR(J*ub%`zbRu$RUfqaFD~z5aE3F@2Y}z9H}M zKzBabW5BCyfB$BmWlQFrzN@o~4@lGVZy}E^E`DCKy%Tr(H3_0+^60hH^!>Yi-=rr^ z+brF(^w`qXbH&?_A8&Q2_M#I^Fny_`4STDrM8El*DLebQLES=J$O>2Fdlsn*J41#( zjXWC%aWN0(SSM`^?wA@CK-7CSbAlNsru+-nsIw7yfASNZ1A=XIn8PN&x3RsRCFDlw zFw2_!zHPFTpmkd-7Kps1-6kT}38^u^UhhP9!#)A34RhAp+?hO(9n{&OHFERf0WtaR zCn86Z8*S6H51au8r6oW*^^F7_&6$O1>*BSQekr8UZGuVXY3;*^w3E0~_p?EDYQ$g= zQ?!Dqe~S57XM-K8md}Q2XV|5AUS6sol29#}kRN!(qBkkRJ6ZhG@RqV~|ZS1$%1MEHQ ze*^5p?2p)k?9bR=v#+ynv8S|ot)(q#muaW9=V&j|Uaj4&{kryM?IYU9wSTYuvG!-$ z6WTYl@937kuAkA*>F4!-slQAAi2iZ?bNUzcuj${^zoY+JfpHo(3diL1mj#V?5$i`R-@7jG7C6(1B2 zi$52Si*JbkCH~Q<7#(BTIBXm<|>Pbn6tk!0;% z+QYh}=xx#J;^lZxnyYa`+`t{Ee`6`4jg6FDgvlhh7)=T{+*Q8Woec*N4luvN0AN!=va3D-7%>6KE-? z)8}Jq& z(`>4vOx-h__8v5m_7h&j&=(8|h`WgR(*<6TKLyG}kjQa57(=sg_1B|asK;=uY{nVH zE}~d%fF06S%jcsZV))jSnvB@Jz+M%q^b!1zovq$cq1@aW<2Erq5uTQg2dFy7i9lR0 z;I0@u*;3d#Q@~wx;zl|te~LB&W#K)eakm$Xc7KfeCcL`nXf;C9CrbxeUlb9|A(V_uE%U<}oVSAwGk1dPj!JdH8; zBUEc^+%F0wZk3@FZc5vjjAIOZb&Md--L1F{H5o!x`munTjOwHfe~rK_%)?PPQPmp^ z$8j6_H!j8qMgYI@dy0{0JtRIC-IQ8+XX9dKZq38})Xh452%-6lt?c z@L8au$KyVga|7Pnbi8As~rLKmr)ZVh0(aK~7UMyzg( z+g%(kMLA=YuCfcGfkssy=}HB}Wk``}jC9n6ZWyZ}f?K-+VUATZ50gHWkwVvZ<3U#q z6!tMjx8Qy-O!MJDSsBo(yo-Sp#_?7cXE?$lBg|=U9JhEEe>(U^4?Zil@Ug z%EoyHFH~`b0#xs2TS)I}Egi@Xh$U59M=ItRY{(jp9FhfYDybkfG@EdlT6F=m31@OP z#L>i{z_G#!QDi#CJRoC`!Z;|SKF$LSMML)iAWYuQhAC(vST5X*bG3bt?Bv+k8~EfF z5MDP$5~(8$C_-AqjrCF9{P9-qyMWy?8YVj=LvSb2 zIvc5Px|`6s(AF*(7;vPeC_K(hh;*c$ignB|nUkDzMga_y<8w6uBa<0)CIKIlKy(%X zW0O~OZW^ovMeIu84j4gIM-i8$eUyY_0rMjUT|?!6lizeee+CBi(M>`8f{xPbb0`o$ zfMr3)_J%-SDBao!QHO$NYXl_1H*)W4m<4T+r7&JVA8N-6)0^WnpfQIc9@R4378EQmv(PO zzlLXOx~1v*%?LO33q+&3G^C@s=r=(OxXnVZ4{AuRe;}tMN7N||BPPHY)NuKYI2Z;< zbS~R)h3zBs7p8url%cPN+IxnlFm=qJrUNIcRAI$Kxdb;*D292ulmt+cgOF2jiDpqm zG-o-YCB@Xj_zV>Q2sh$}kb$Nd(`Xsj7l;s)#>!PhAx|+(mY5n)j-innB4a6pYuwD& z5L7G&e=S2S{Fvqmm34F;x&>~fpfG9(4{s5*>2w9|{}ZkZL78XpV%Qyt3e<*EW{%m` zCA64Bmth?CA-oKJSnwcx08^lNoVl9{c#G(oI*MK|Jg*x*#Z-G;+Vl#J-N&XU*%bZ_ zpQ9XO}Khr+NADz9<%Up?z# zvP9YMz}IJEuqKSW!t!Fe1q04G!fi&!;HyPlBG67oBQ!hOmcOXVTP#z3hL zW5wX@1Ehmpt{}_QZE6ivjq?=93_FKCLi1B3NB;XV*pcKNlrG{^7RlS;*k`64^(U&% zfAD%eL-Y(qBB~P@bK&DCmiBq9dyXKO!<&CX^%6zuWhC!s^x(l^LP!dZ1>H}`oAYKP z6{%+`%77Of-sc#?vYp zs!!0N(?kl$y+Bk)pv#<&u7_tEJC6eRvktqr$y!}V?e-uMl2OVjX{43K;O~=N;wE6UzOmP&}fZ_dx>GUBA z3qlQY8GVD%XAbOKqEP${L}7IDSY!_wUF@OmQAcA)*YUuZ=p1!j*ERQG_i~0r3>75g|1{P3}rRfV>pE| z94&@4>AGpj;R*dkpjnMsOwQ3+xDu3*=(N#vCeGe;X2{ZdKDv0b^i>vIAX2 z=T{B4YhsohX?l|TF4q}`jB5r$xkzd12GWqk0ys%ERKk?Fvp~PlU7_<5LqY^y%CY^X zZv@0QI+@`C!&nxsScJjDjOl`xfsjm(W6-G!B*$=u35gjBF3UP_r;RSFw57Nk-0XRm zf#}fG{O6!|SBY|Roja8%?bP-zq0u$gJZRC5jC0%L}!qNFiUQflbJ zt2nV+WK&t>F_?=K0bbGN8^)k;{e9qJ)43Zrg0Te*3G7O4?C?PN=2#uyeWh|ozQVnK$ zWTX2PrUYWjU4fVmkS>8En6pVJQKlG25&8}oe~GT-7b1h{BA19Tm~bWV5vKArC zQk66ZHD^>fly^RQTUaOt3?&qTIF;549x+ z$OU1bm<_C5*F4OBqtDF+KsCBDEd;0_8j-mX=i#h|c7vKme7G2=6f$dpvIcVsGZ;=A zF{=s84RnIWh$)aoM9p%p<~7Vfkh88Ze}F20-3wHjpo+Q*N>I3VGJ=MuF_sDk;87G4 zkJ+FVI$W7C=w?N*A;^oUvJxoTMHsxHk|rygP{$tB6%ekk*C*%mVs^GF=b2 z3ic>UN>p$$IU4k<4XxscZl~cKwTWRH9=eVZlo`~bDp+0(mWDmAD~KqxodoKef4XTS zWtI#A%~ufHnh6|2b5Vmu{3yJYE2T++VdIeLD((Y`P+5nV;2hy!QISTOO4Nm**=jZs zzN(l>usd~xCP!PhP8w*T8mN)eTwTi*<(c^~`3C(jTzG#pce z=S_35vp^$ay0XbodjkOstpiELe{%y8X^*G{0QGQmTtxa(X_u4@s>BbSY0nZhPK5=n zY~G*$+w-|tpnq5}(3hCB1Nb*=#_3?sM^t4e!ua^M^2f+g+47`}Q*WVhU%6=X#v}5* z7oNF|)Som2w~eB4an@SuymGN~LYl?HjmYrr%8`qrFxxr0w4t3$6Mbu{jeDz5F~Y0 zvW-sm%4N5bt(+~}ez>F`>144l7OXHV#$K%R)kHHyAWY!eW(UFRUJZn-ht{0VV%L}U zwOj`Q6_s7fbq%HU)ALd|r8qS$ZVIpB>F6&BdTL;}i7ZBrEp5z8f3Caspxe}RcjK;& ztLGP?Lf4&aMW$9--LfOo1Dnx}POi;MO$am0>TMH5usq=za66MpEAfg>klte2E;ef= zvO*5ZfCDO+G-tHsF}DyE<`D&a=1{77QblL5pubBtG0*dWJbCK1S(lPiyGPHQy1SE6 za(egTf5$E`_QCV?e~HrJdT3+g9hdI;rtHv<73@M0N-mJDXwS;SoD6;V*6+d_dH?Rt?wO;j zJG(opN6!-T)u&JUH~42iS*n-F!Ka=gzw!k9P3|GTGP&o58yhyvcqqE#RM3BT>VZ3Noa|h9;3YSnBkcX;k;#Sk zKm5Q0*>r17@3xAof-#IcYfxakNxhC6waK$n5lzT%0N`n#D>tfvU zEzPPSn#^}aXfKO#Cw6J+V&AFg#t~N~*E|Z6f5=ew6ll@KZQqW_MV9rl5?`wp5lk)y zn*eno;Mzv(E45^;7N9jZn%gWHXi4Cr6q#o`V6p}EDrmlvmC8n~J$HI*-nhGRGV-dL zXn?Lu49l}JvEGQ?NLd8@g{cEl-811}RmCPkliXC)$#Rf-7Cc>VdzI&)Y^8iySqqek zeAdU^x`!38zVEvh zjbo9<-681&4q=_{LqCN-pt!3iA0^iie|V!`YyXJM{iyvll59=>iZ&*Hz44QM|Ft#N zEo>EDQ}_&e?qVDUhO)HXDYyk+A;CPtoF@w19k*6`7+oHuJza^hVjF}LIx$USjdAl7 zsei9$Ry*@2r4!X{}l-(Q5g4-DY;Lj41}|8#1X9`fWtoWMuNeDlQM9Cn}FrmMXZ3 zMYuv=1}iOeV6Zz3%_dBw#p;;SUQ&z_CbaCMf_L=$4}Jnof=@p9?@>pB@A>afA-f0z+#sMKJ|$P3x(E9^4H+$p`wTr};T!yI-a^Hm0s zTZVd~v*OZb83b!|CPL-({04J+=?1FO{MCd^whuttYLrx42yO~bX;f7_ay3)GH-HXj z7;9^+R@f|@DBK79@tuX=E&N{Ly_l5(!$np|jDsg|tb*RCzsE^-7(yi?e;EACPx0qp zoq>WKz#OOT7O=J!dX=mzeuk0yvD$D{;NFN`&>Q5~zk_kk7b&J->(3Jsw}-*74@BIL zhwvS@+}N6CdOy??&DtpS%GhY0aOlywHM+>a*z zflzD1YIK;-frF891Y=r7e=C(c=7t$ZCX3)nEJ6rtG|ddxL5ru3K{)N3^FR9N3@jYa*&JcfBQa&$PH{Qhuw|8RKMaQKela4&A=f3x&61*cF&9D0K4 zqi7>EM>`{J6-1?`MNg%&#rT3zFCiPZ-}LO;*i~%x+Cv8}X2iM5j4nUKeU099`ucY@ zH|n&x(WDR2L*oaZfAmGyr%844!QR2676^tjyVqQrHXEf9_K)<3g_nYMmHSGg zW{Mg)OlJ5PsXVsRf8dtx%*|YdNMas${LIzW93)ue9_*++C$j_`7#F7eAeGe3eZz8v zxHlrdV&pqsqLOk41}@NTBc)*dMScQf5GY(44I#uueFN-K*Tmec661lvNGy%fwAW&o z@?dHi?KvYfJx`jB^p~zZ3^tlDbjvgjH$o@WbdMQN0-xo!f0p_>H$yPlyv9}vT5PL4 zUy2=2h~t`Jf;bY?(i@1EVS?Re6&(XCWA5B?EprS|ntRoD3#6_UY8D#Rx-DGY*!4AB zr&HObIcUC5%lmqsRY8BM<#8q}WE74OUVrM`pQ5J-=Hg6fVmMMe0F>KVQ7|o`qk9I; z4sTJK9bF-Bf2N>)n#Vv~h~D!rS-$q|l_jlNu4URh>a0LaTdV{n+8{AP(3L7mTg!kW z^igN)jy=%BQZZ-G0pZ4rp}0@CEYQAJ9(T{kcPMf6-EQEWU+4bp3X7ef_KPW#mnh zzr22(+<2&U{q?=cQ`e8OY;-*wEIo((;AH%q$xC|6f0-Sc{N-*9s~)TR-v~1A>6+#~ zKKwRVmd6U06;2k;7Op8=t8k?XomrBLsedl%2kq#eWnud-_WyA%n7+~sO|g~=eB~-Q zzT0lCf5Xh&8uwtZ*qKRD7=|D};L@1>REAUE{#|PO`U()Geay*_qSbS5s zP-Z6lvGPLb280AFPKs;0sq?=9mByKNc${NkWME(bVkP5+RkPyxZN4&aGrs_eFx+RE zvjmxDzRcVYq&OIuKs*33n+iCSnu_Ot$RZphx+W$jv?lZ?5GO7uW+}cckS)F~_%0GI zGA@2D)Gtym=rB+)vM}s1Br#Gls51OBjx=sHHZ{C9!Z$iMR5%zoGB|8Fq&Z?bj5@YD z?mLJ)oIWN#Tt1>dG(U(!Y(n@$q(kgPZbZsOT1GrZmPYzVhDYv5hDjhv+DZH@O5RHX wON>kWOlVq^TJl@MTlQQOTu@woT*O{HUSeNXU@~BGVIpBxVYXsmvn-4#1+x-0F#rGn delta 33189 zcmV)5K*_(*q5{;S0u*;oMn(Vu00000kyHQ+00000+`N$#Uw?c60L180exbH#Wnp9h z0E%n?001rk001^0n-;2QXk}pl0E(Od000~S001NhyaiHdZFG150E)N(001oj00M-0 zQvd*LZ)0Hq0E*}U00Be*00BfID^u2OVR&!=0E{dE0018V001BYdI}J3VQpmq0E|ok z00ABV00HLSN(vdwaBp*T0050_vHbc2e_UsECs^Np_oePuxAv<0s_H$xz1Al^={@UN zyKKp_B}?7}0)xN{wqqy61~~>B3;_qzj-5c8Wz3kK5Q7bA2n5HZNqcZ&HzX_x9TEZ! z9TOAWfu9qCdt5W;ySJWXW?<$ws#jIF?)oj~JLmsD=X{@XGzb5PfuG44xngd6e{O#+ zhnfqhzlOS16WSIO=q}m zn^2fQ$PyeK>t6cWOM5-|=%V@Nf9melq}I5jS^Xx)gi=Px%_lXD@HeAXM1<=2TYXm( z^z;xR%!J``0@cH59T8c|9nake_2Yposf7feMa@OrPMU(-hg}pA+t(&!hh2WA3^20#>N|quLWG%o&GU$(nXI~*aIiJ`*f3umPd4^fG zh3%=a2_L4TdbYlCW*k&Fp8=B#@&)0;2XK_{IgmS&X z+j&tiLnvFLVp|A(DXuoEe-@{h*rBQE!iY_k3QTfHwjur>q>4(4F7=-i{rYUHJ!XM`4;`a7|Gh z&+)5vUYRnu>T^X?yn5&qMV^26!yo?cBM*M?6O%g3v`52kDl-ApuH$bW?ZY6EM+fpWd52K=Ae9U0MbI8= zC>?1bdWkChWS5Y;9A+AK@_W_1AO<05j?#i(rIx8OhBUOOddCez1ZvwZP>hZasBLJJ zQk1CH4(M|bJFiTR*QuLl!r|1g7`1$A>6oD$r$=TmvOi^*fAow%8d1l4T}FQFsasdN zO=vG_GI-|3!QZ;U)irHt%gTC)5kGlIFqN4gAZT&@)KQZw3Sp)CjwRDrn^T$Qs)crG zRDf?Pg0@nDrcB$@A{XRt$h|f9F=)-v5C&R0=w!PEU66qQ`dY{V7Ipk2h?;ODfJO-t z5k$do9fv^=e||~H@FRH<3_>{E3wrW8IJY+%!a$XJs5=~i%nXKEKP;j?3|cwZlgSCt zCw!FkS2}9fQD5F@*po+kgC2bPqB%$co^bs6Z3XJIt0%6$XsUrIq6~D9;hTcNh$Ehk zK%>D1b#;~Y`&eL1gW|#0U=UAC85JfLyr_qvOXq@of6LZ!*lQg8MNnxs?=@hz9h~=L7*sv+ZrfD z%rWs)q)-ld-8LLA*njC>I4>6t1(m_ySiy$20h%GI=H9is>=OK zHFGK=f8x}YS_xB4SLdII952GuvpVEPrXo&>W-wbA%+!UssK{vC!{7NGmha<4*iK~sKUunIFh?+(lQQ4eYnA3 ze<+oR9O0ubSQZdEc?t{#IJAH|V3`&OjD#>)LvV*0T9Ue=m&z{4yWjy_JV@}E&zFNcVx54=8M8!)D5Amog_?^Ydf)^iXiFIB0^32?Ln9p zI4e^6>OrNVUI?SB16|T?azE@q6(f1ie@>e?1Z>ilqqEcp&E#N$DGtkR1C&lIqV1)M zMI282<$N4Qr`DDtnF`5+y@dPa7c5>0 zK!gh+Hh#y85y(53ctwsYuzGCPY!(Zg2#NtuV8v0WA#^jaG|M4J4NB&xz=9H+f0RR~ z48ue*U=V0!*mW{&))yP~|1I`b7(Cur1)WBZ(_K;ukJ&JYWfJxTiK2kRrCa1{M2v%WuvT7;TW{aHL9>|AmpFvFr zWno>|x_s%>ajRr##KbDp3Ny=Lf0{-aQ&j|KUO~dvUdh3TmcVAf$y_x+1Aq;H zZJ|@`WOJd>SgL=4gRT(@rUo{Ns$s=5v}6&pE%g=9eV|h?fl`9;3DsCLe^jngD5JQ> zE$97$w*vJLSXG%WhgD`nFTjt%s26}A5*SlKV*n5$Az;*5UkZBETL6=qf=y7`AeGoe z>o2|M<%2|uHvnW@Z8)Zjhc@gZ(vA~Y=-)%MjqPt)>;e;U5$SarK#*E-dM z2bTpAYStytP-`vK#zoSF-BmE0w8Uy#?)fQg{xaS@Fs zO%#fDM~ncfv?r!je>9;oK?r6t)gge7ni2sv6s=nH8>Hj)t-t-kr-V3s6@-Qw91!ZLr%eEtjBnUfWAdA#2Y~c#FRvFIgny)&# z%f$KU8;wg3IZw~U-$>B=Vs$+(lnV{7pwB$OIwxXkYzU3zf6e9gyk>|Mf^iI($&A4~ zA~4cbzeV}_PivMZ?I^&Og88$vHfjm~x7?Q8j@+eEYLiWZuz(~zNUeYbt+I|l1|~cL zP9RBYGk`>-bU|wJR!TcKv<#^3s#NPyPu>B&(CQbrG~-zyK$`7OpSMRjJafaV%z__> zTA*P;9ylcUf7;1))32BF_5957*9nHUqI}K4LVOpBtk39`TdIcPsTNL7fkxH2M+^|l zi}Q}JM-1^gHFdjwsAfVlVBGe|_&eOAs*_Ll#XvPeCayl=S{}y;tq^j8XhbOsKp#f# zEPvWUw&a3bW)B;(bKR;mca78-x_tsjz0quUTtK_MKL!Y)OWM(%@l_!|4_G9Q_M(TF zM>o{HrD*)S#e|}|<)bSKO^Uz0p_0d992tM|l9&ZgmCIIM^}IK-Y&fJJHvWs;42*{NNt1yPgcpxT zyiB}$0whwk!5YpSTqxdRS%EseACnvrC;`}$MG-*(wUdVtBY(sY!U!ZffXfY?i*tP` zG3Ow+C--W}W3%pomk+}iT2wZwWCId^p|l6!HEc^*HbFw&A*tduoE}S0kK`c`y1}q2 zWkdc+*8wny3ZUQbx0SN8ve*u0R9QKDeNc@mWuQ_sJYqX8Z#s$&0~n52jmoGyV9sBm zo!8xQ{Tjj>!+)(i*RHA7e+IB+YfM1zshIVeb|P$(>BP*gX${H)CH0ZzDN(k z1!<$AA8G|kqLNcn4P8B6X*MfV<8EGqiaB}fnU>j_Sw6Dl+GQ=AZM5>_;9D--y|#UN z%anTW?pxc*Y!JEz=BSk{U%WVoSL+oJV1zDQ-#|a`mZ~Ce0$B0u#Xkto+rJ*Vp;ZT> zW02X1!GFLY`K61O+Ea}ZDU`RG`3v82L8CTOIePNEQOLmjKoQ{Cd$yIMEyC2;g>PxK zH}&J!@E?O6u4Nj6gYFj+8ZS=d22_$nexf5f_-lw91Q_?h_j850tyrk!w$n_o=k#dd z)5jj(b_~4G1+@!!5z2()6eiv;G=YKwa@drj1pR?5o*`0IYnajC#>0Lp9 zp8(%Pqbgd%0H<8&h&3#m*uvx&9s|*rw1s&Q!D~Bj7>Hm|M2|61xbGAD($1dAHGk&yp?DCEL zRD1rqP7wVgDy)rva8A!(2jlb=vQC|LO4>7a)yothP!lOMz<@IHC7pNe^hnm+ok2gz z95Fn*Si+J8N$z0;uc3*@K>!0n7c<3Iw0}iE@tS%xcm0`L_O?9{cuuXLOV1Y)RAmKb zn}XIhSjT8)AtFdM_l1(VE5Z=Yw9PEH-d^bRy+J%vZfd5-s`GV05z?)ES$}9C z#u%No#|4tlJ&=1y+T%q8oUIS7u?8k^0okZIXv+;r(S{igNgJY>vb~Nx!vap}Cz%^G zJuY{pR%l|KrO>Y8cY6`c0?j}u<7G6qMR(P|xb+b_Lc@BGyfxoQec0n)y|9|2yK@#+& z|7wto2IqbNl-@C)tfaKXRdwGCy#Q#7GYz!G++CS&EzW_huuNfwXgJsH!r$iytE+?U zn694O;X7?fQwZUzo(zGe_<2>&A8z=0+iKVat2TGHZ}&Ea=L@u2&@l) zBuJghT5g9dqJ}^({I3tA{eNix!!KTXDLQc}x$zaR7~l286X?ya7+-Y%{r6AmpIvL_ zyq8_w8G#n_pcO`)vp4z9CqFs!*0&!1u4gT#sv~lLl znI{e(2F{e>3w^?KZ76q?J_{JH=Qdmhw;xR&^NYT}g}dYF4}bVuzvz!>FMS=)-8?fh zGuV7jPfzD~CMSdeTWjT}!44eE9hcIQLgPqej;R!ePQ)aYkB+QnF9K`SOJ%5+Dn;0~ zWb|hTT1DL|2CFfPMt=bu!+o$Ah)lO`UEkf`J>O~+LS4hfs90}pfgR_)xjlpDV{KN8 z=VId6|1$)Yr3#IPENJ-Z4Kuc5Dj+N7mdMdHPN}9@nC@R&T~n07p(?_mpENGqaDB#- z!>fk2w&OfntG)e5w|iv2%T!Gm{=vPaf^9~XeZnbC6>V|D34fyGtG0eNz61UGd``>d zq3%AQHy1Dl(1L!tHN@Zk!0&$OQ|~_5Zk>5(eR%0H^y2MrMK5eruh{;P@n?^m!-i#2 zf;-5T%%z$%X~IYj{2GGMBYB<-e6^DXk~(zYB2s$utCf;ous&{FsRZBp+3E4ubzcd^ z9;~Y4AEM*W-hZNVbRF)z>D8*C9Or-H8r9%ir_o;lvD$;i^;>i|K&|m_pL;$9o7V!Q zby;TjvKJVuVK39EDR4SVGyz~lpsSPAxFu)Lmiz%aJ4$0G>9N!pWCuW50QOtl)-^&y zn2c&ouYX?q!q;B{8bFQBxos6|^i!QVRr6PW7k7(W8h>U%TRSkg?6^aT8GbRIOa9y5u{5zkhh7JqvVpUBw)O{qQ^h3P9m8Q2K%k z_Ry*b!ga-{MZkop5&>KD6idZMDFzGFDhsZLa?= z#Z!mBtoT5FlG$KSnL1WDXXblbO@l^ZU>KAw50_Slm+)4ltl(D!T7?CgqqaBlGo4x` zKkTjTTpA{xGu^s4!n9i6GtqHl{8iBSmW-nYA__W83_vgk(Q{8d@W4~pf9iejdw&Xt zpF45qohLp=;EUXO#b?jz$1jt^(8JTxqqiBx7a6S1ggPrU^d(8vM+ek$1=^qnrd>BF zZ&#Pvmvs8coMUGqW^fM@$=oQR0WoB@8cRB`K5`Fgdrc%YXBWOA`Uo;b|}^jRpgN?TE}nGU!VjB;!HRxgsA| z>|a`7xVx^cAFeG{oP4`p97I;RS}LD^xG`0opIVuB-g~w>S1I-{t4w2dzJYcPYg4`< zLOU)8-T_na^H&;kju~tz+4lHdn^n!64pU?bYRq!VWTb_;oQfKqXwX6~a(^3=6QZcm zaYqgG!uY#*?S?zXUx4pdj(>9dd*1V&@n779>TkPq{380^_`CQ1#{XGIwU@q#b|UM; z@45FqXz6Wl8-MrCbM|8%{}9lUn+tNeQ3l9^RGqa6;6E3T;Nz8TC!#B&GwX}}_%GkS zw)W!UmG7FmZ;Cv1>fpt*1bXGJ2*O_4|LCq8$xW+4<+iw}D~V`(YUy`@Kz|wC(u#!{2e@Gj zOoVSy+v=t0TN}G;q&@fa%xq|Fwbsg1bKZ&=SITyxAWV|AnzzqG_#<%1!pr?Zm9%%{ zs6uj7kx|e>K_r52A@$C?Pn`l2{iD0@#van%e%IUIj*L@JTm_qVzy0oW`e2^ykmwI6 zlK@OL>Wt6}r@r}t_J0?@*naS@$@V8}4}A0LtH1d`ZPT{l7sv@1$I{1MmOhn^Ob`f| zC-tHyO}lIek9Yv#@UFcZO_3MlpML4DRhR7k@66P7oqg|1U%CvN0$;lEwhMLrtN)7L z)YP~+zTuq&1M4JjM{gRx4R0}tan4uptHjIQpZj?3k8;oCzJH#2aYZuh06^qY6SvcF zWY+kZdp`=H9FuGYFmr>FOX(z1VUgu>QGQ&<=%IZ2AFrI=Ju*Fe0oCH-<_%f6+RC#+YvYXGQdKXTg#1v%F zhU8|7HLR^+t$#q2ic7RuE6#3hV{UClL+>lPZIVlnMhE9mUjL#U-R_ znL4h?*uARhPMMiyk~cM_(XtppEVipOSO}@I0Lail3Z;mfJF2e^b*^iTyjokBHw{In z4CIo7$rg;#@g3CnFj;88yOavQ?wRe7thRRofN2WLcpe+XU=AR7evFmu^#{ zN`9#rxVmfuRL{hi)DhRc-FGi6f+AtKJZJ(BIMmh+U zbAR8x_nvJRpA>T^Ko6xg!cjMnR#6Tb3HdFsfHL@=#-5)3>Lo~F{O|9iY zFS};B{G&?c2Oyk)#SG?^&ELmDMbE1W2Y-nF0fe_w20#rn_M4n5#ZulW6*c&EPgjQS zy@1v8PJqdPaJVN)<9Uh?H+{Zb{zsJv1gCmM`}dh*OxmGOw&bo!V?2}LEz`q>0K1bc z?vb?9Dt9LEGss|9lzQ`Jdc<-XgX|I=i{J?&wqNm!n!GP2O?_!`cjD@M#F*zxQ1;;7d3=tlyTD?$qOtszG z*PNnc*G;=Gy70goLf`kVIbfIsG{x0$rL#C(GYrK7c+$j_V$&e`JTNGrL6{iz22`U| zn-5)h;lZ0@rdbDl8_aZu zIc%`ocw)KzTv5i$T3+IjWLb(6BfU<$DN3lfEZx7%OQiilFTCzAUiZ59)-dJB`PqBd zu!H|7`#JvS?B|Ek?c=wi+iy`E_9JxrKd9*w;{+S#DoD=6dxqs!{k=&`= z&AGSceufkjq5;~AE<>+EuSdUv-i+Rf?nUpHJU8tV*?B?|){Y-`hkcoo(n%RSpZF1@ zewuXxurKPu?m!-cbDF7(A?Oi5jJh)YHASaT zphW7H&1BFF*tQpOR!-pQ(%~w&mxcuU8E>c}xjQ2c(zr!U>5Yw~e>UJ@cOtHG95i7R zw4*_Pf$#t>^urMpu>)Gv!XkC#NQbzrXhye2{iff8OET)!4Mi7@Nq<@!Mon2IXe*fm zH1eDMUJ!!z=))eU2i{&x81=2$zzc8`XghUV*M*|Lp`5pcVon3dQA-tKSO)xEZJ019 zc%ESxBMP=GlLrhy8Ay&y5;IK=D`0S$peiO!rh@4JjG}Rj%E0UxVkSc$<&+bKbyqYs zP)nX6RMnDBVXk7(RDUpj77LUWiieIa)fCNZ=goi^z`AXRF@>qXlw}kRQ?1IFqf(11 z+~k~6wy?f`l@J)n3VqJuUPyvu!SX3f-f34#s%RIcTt(RV?Is6%0k<%*W*L?Z*1*&i znOS5}6M;&QMOK*1N8kpGcdRlfe^EAztvA8izCUXV70zQ4fqz}E5fwAX;0B<8ML6Lj zK*7N%KUORheODEt?t3LcR#el>158`C%uc?_fE~sQVofy+&oI;)=vqQZUSpI{(2+cE z0;mvzQqttAhCydDVM3w1oGG9pOamh*l?HZ)Vk1vvMi0W{5@C#)mQE|nR$#>WeV#$# z91YCadTlssP=6v~BMuD4e5GXZqQW$@RfnTnM*G^7V28>_GERt^5A>!}iUtXlRzZa- znOv&5Xi@9E6xv^BvPV2>=RE1t2Cxlc^l=Gf+9_U<8jk zI4i7bDNH~^8z%ga7{-?ARDxsr#W3``3)F{r^j7tS2hDOCh2q~52L%Vv*in6eD;rZRfrSIA~H?8Q4h3zv3u2uB4 z+4B_pD#Qo%b&E%Zsi^_3VgqA?Ov^8w%n%(Dja&{^8{`x$d9gYwP7|Iu3NQP@aFpft zP7Q3>+Mey}meGu+*}tK7(%ZLIPlUN|?Zo;V41erDKtzy6Q?b^Ii@M{LWD|>t&uyGr z2ZVMccQSW-?l*Jy$!I9h^~_xPinNY)Z#yB%}khnSwf*00N4B5vAa6xhvBtTVd+a9<0H$qRg~3Ir{u;9TCL@ zD1Rmj#hT|^SAy_f1geO>5_kc-h%kDk>w2|fLD-Z_L9fr+re)ifc|5Gw=Nk>+6Y2N0 zLf0)_e*~_0ReC8yoSuTaUA*u7>#jS0-}sYAbA4AsK{a?RsD@!Rz%QvzQUbL_4DOiC zdM$sy%IGw2QO0PCPgADeAC~G((-As>(|>9;M=h2yF8_&q;Cy}Zsfr%gG~N^HyWkQS zq3t#l02s%#;;%RB_2#<&dd<~!SF?k_hBrKXGbZx}LV@vK2ffo-2D<>ZY?N9eUwT>6 zD3d=C?nz#2y1$o5Cn=c0Kqlc z4>*O+ZJPrMMte*k$L}0(6b(&gKU=D0eLZrl5QN=^QDshVZNqRAUeyQ)X^Dn)sii1} z4cH5`vQBCR(=fv$-Ak{$5fmM*bbmS($~V6S-Lbr_vax$KnBMdmTnk)eI(H~{lVl=k zM0HZf%tJhCyv+KikyQW`n_=i6+bf%Q^O9z#J4}-+ZQcUqAPj9Y@3BJywRH}7=gH3$DF8wyMN8HtpcDV z*mS_65xjdEtjUp=@`u1scMgN?@N7vr>FgFz_68Hoa7HDBK>CRaoJI9&LABIN3e8%3 zuAA&i3cX4*nlg3J&!E7NOLc}vmeO(aMrft7Yk9Wk6rORVET!5&1N^ z;Ky+LO>yKYv-CI9#M?rs)LfyeN=uxlb~%5fG{b_!SSbgE|(*06$Hp zW1$&N90S^LTwG?BJwW%9TYz=-)7T;Cj&v@Okog|6=h||XRc8=-a#oGMiCjAUG;>6bqe0jKE8NXUP?~#~ z`thw0c+^!3wi?JrEO~jQ}fq84s90Jn?fslcIYxh1Groh z1=i<@e*lx^EH6wwPo9#J)qFduApA6_&E`9%hzgU@de3Y?IY3BLNkY`7JFM zyz|or;sVF*CXkD*)`so9^uP5e$86-}5bvw|dCl_Bhv-k-9 zYU%pYMR%T0!V{tN)xsAcQ}<4#@9{3;R;&lf8?Pq$z4=hv*MhpF?l_`J?I9J&&$F_uT4Jk8c0r54Uf|ZSZ4Y z(=ha7EthKzA}P+oOsD>sfi2;ASDZX}{-M5o zg|_YSPhEzbLgG2&e?gjO*paDK&|Tx|p?}>M9@)8RKj#Tay=l2t8U+*5Z5-iy$4@=@ zAX<6w9}ge?;Nipn09w}?|Ky$W-{u&99{=wkdFIc>YdeGJ#mDct=W+DH=il?5&tvb= zBWKPWd6YbQ^vs!~k4|JH#9xC}oz88|9hEkFAPp@~9a(U|?EjjB%9ln0>s$F1)3Kg#G0pHph zt}XQ6$lRccfkg~kx;a*A3a^RQT#XY?d({mpHMmC(uHJTqg+FS7snym=fTG#gX*RDE zoS4|2rn@%P?x;=gZ5%pyzg-Me6@Ls}dt{$dXH=!Ew30cV7l$ossGeg6*Q%TGDhodk zJ-wJaFLxk!xg4o(Lne933iaEwsA*%Vs-dbGl8_OkNKUgz#|a;02UChD_lBL!116JV z`b`O8fQw3c(rFX(r7z>|k7}Tqzzi|E0hZsRxXCcr0os;1fGxTRCI#Q zW^j;foHRY#R1MPuki+px3~F$BA_o=x6X0n^&I38mp-dO~VN0fxHQbKq2xyg_+raOS z8jZz9WBkYH+v64VsT+6y=6}toJKnV&-GRPRhXafC-yfercLw86+;QZ-d(k7~M^SlG zA1#qBAZy*+{@lf>Es~mOHX|z)^s;0Qsgv3R)SRTzrm6GNfj(-=NeG<*YzMuRn$N=E zA9OZLOXk^p$j)z>E9bLS2q5CTr?eye4!SPf3c5L6hcYnwF{=K&*Q+WUO zHDD?!YdICyRLv_tesxr>&sFQ#*}r#rr=_YZ`(Io0y2rO4sryVjy7LO?mZnh0f742C zyZ5%Fh4*gXA-_)n%tB9Nf?`9v*Nfwq>a%tDds(a;^hcaq%I%ONER8Ho($&*C4>KEz z(yU=Q)g+R%0_d z0sAc^hKTpvx-VIsLd|V^<>xBCJ-=(o9L{cDkzRZ3!1yuLYTo)=zrGjEd1`Zsu58_R z>u&V+$yM9-?nov&X@P77{`nT!XHe4NWD>o6CCwlUh6GuVWPfr1c4zTjY5ciJQVb~Z z;bxFG#rQHFpC<9hFf1U%;XqDNO=nB5rOO~lur5jIBq}R+k$IJYJOeS|RA`(D%>#G{ zcw42KDTxfOJJS)qnDWpovQQRiO)ycc!Hm7TvbE>S>PcwRp#G7InrwFo61uZU7Ed8;$0A zDO3djYZ0H0je@W&Ko%vLi-w7APt}&aSwQXZQ0f@w@q->yG((aItwPOwbqd=G#adi3 zHZ*1f0TG1yP_MJRszDk#KgAKL}0Gmiys6@Stk7oSeS! z{K1^Z^M85}mMV@h-Cb)^M~eQ{kbx zC2g=Y-zRFPDH)Prr^)=%Wzx<@dX}#-$i^)E`SK1UnQbxxyC};7wz0#PmZjnw zruB`Qa+QnGpd)yDAX&5Il{qjK3}e+G z?Crb|2*+tFo^J@m92=41%z0W^oSQQu6~i!fx8h5MGW2Vr7tc#-0$ywWyxG+VP z6WDLO}T$Vfnz+nI&YH39gnd zpIJ#(;O3&FSTP}1Np)OIRh4zhD!`|_O`+{(p}d9&>TaVyqtOzazK24+34fFxC@3|o zy38CVNMQLCT(XSlE9DSqqzt(s0G5$wIT#xh#0|!3r2D8~5S26Hl?|0UBFM|M6r!k( zsVVc7m8n>Dp`MCkl)-2@0hLWfXmpJldoIE_l(T^)N_9+{&XA|l)9FSPuI8VY{%4X; zrdlBsO7`H}xem=@m& z*dgfJu1H;anePCe(CG#89fi_+AtTQN3CIVaR^h5N$pVbhpd;r;0>)aNq;s81xR%;+ z2z@n(dVb)`B#lI>nh_La9Ys*qWUz_?s7G5WH@KV}gu%Gw=o>CyD70%v5rmtW9EPb7 zo@Zjqwp9Vc520;z9)B0iP6(PrMTQ*2vb(axBy1M=H0zeCC|yN@DLRY-l{FdxseF(X z(0;ZdrkLU06~`CG^p%J@k*?Z0{s!F5n}34pVPX|^A`42HX3Cmy zBMm=8O*MWoG4d)>K@5e;-Ow%q$O0@y0e1<(4YV*bkLL{2RhdgoTQN0Dz#taPG>i0@ zdLr++PF~KVCl;tTyI>04auwSEojRCbnw7fDMrs~(itQT_VY*`amc?|!6@o7nswqH% zsa#`5jszjcrhhFMG+llCF@(>D3u05wA;)5HA5pkQstA33&-r-Q1Gd4CWzlm!X*?z( zH1DF0m2--xx@!BNr}7XPstC zNev|Uk=bDBe75v9z31vOmWNk*u^Dwkx4{5mnf&VZ0>Vk7hj-8@SC zz=*pmAP1m=fr;39peCN@ilv3sIOc|}a5Jy9VTa=QEj@1s^<=Y^Eb>J)QN8BL zLEJTgKDT^F5w6$P@+Nnd3S@O*DLo$O_Bn2X@V@}-izTx3{50#UzaS@fvX}qpy!^+r zjg^g+Mt}N&+UK_Kd)e+cG$vb($=}9Gy}t6phWt$Tp3Tq0Um;ZH!=k|J4SG?J3`WvZ zisI9Y@wE?r?vaO&#RNStzV*QeQRuw*n|GfrH<*$%?+Ae3NW~2myXbo;kDQmjjq>B? zskU4f?=2 z2Wgny%A#VO&D?u=8USTED?9t^O9_}@I>0?CJeD5u?63qyYc$IM0-D>F&?hCkUO$^p z2PXqZ0+EwKm1WH4u`!p8O#9noT=qe8}Fn0hl|AZFRRA1 zY=3IT&iRU8$cKTVRP$00 zF@aeEm8Wsb2eJh%uWA}mLrtdLaBOiEGzi6NTrXZ{0<@8S+=y6AB^2ub95f`uM_^AG zqjb7j*d|jDR(CV_3i&~>d*$kXLTNVfhJWSwFpmQKP|fK4@skGV70Uwhrlt$sQByDk_0lv>RE@#4+JE5# zJxiulboU+U5{22@?MH!fE`62z3^?aQWe1Gz@rf^Z3i$t_+?}}(Fb+eZ8-}Tj9?nF^?RdC)ns#xqE5KUHe`~b6y4Dp zPL=t_MM9xai)D;6?el;_gLl~z(bjOc~Vq_5^4U$l0F=N@)Mi_AJ3YqoFHr1 zk(});G;MaNIOW*{=()p9fK%lfrXInf(wZ)o!9c!C0!&qn^CiGmAieNYH=KEyx@Cg^ z5H;_rBo=vQf*k~93uNEhmtU;OHIU>3RVaiIRG4KhbD!iYb%X)D|A3MxukHl|d9q{- zP(DCcUO!(UV8|3kqBPW+cWBXo8?*W4stJ2F)r`SdGizYE``qwoNDM>ht^uYz54Der zyiQ4JaUp;k%E>>(FsqbmP>2;k=n4~YdD?HG%cPUxJ{Es?LS)K02`sr{k_y9-k6*bV z`3{jrorEepycTNV!?k#^j))-xFEBzy%-5U^yL63At~q(uU-c{kDFuzS2VDg0`D*kg z^fvT6=%eUk=n?cE(dW@0qd!H@pua+Yi~bvW7X1_Y31G{YQvL$00bAGuL>u7(j&T*& z@Dy(0HlBaQOL%~{;dQ(V@5hJmCHMrs3}234fnSMVgI|lU#W&)c@Eh@)@fmzO{te({ zZ^3utci`W`@4@%u2k?XV1Nb5QAMnTVC-9^A)A%#^5AoypkMR@u3-~|dC-Kwx8T?=H zm!&o7rW5vi!@)K*fbH%M)R#Fx71Z07#=d}^_T+y8fFn3EtVtehBM{vrbt@*ru8cp+ z1^)c(Dxc5dHtgu7LA$0Oz)|?yf+ibH9X4?f45JD^2Tye!!|q= z!0&(NQ(6Fz-PCo>gF!pHX$1QQ={`6m8Dw z-;R2nCX}mdBY8jG6j6c(LFkLDti4cl;8=fP%N3ABf7kI25d-B>}(SOCCM@1!ut>;IhtSM~8o> z-`HOs1f%8jgtKR3^a4&@8!0nTJ%Ll-Jwi=T7zv?@5Gqac5vqi!aAXc6(^JJkP+R;a z7?ACcQugG;Ju-P={2T!WF@8?!3D9B)aOn6!P7+Gc?@^4E@coOx?H&|)3 z57Y|XF2Lv;a!FSmMh84Nb}(uJPnLgb6N>SE4EOk=w9ez>GEeqXQcaG#GUrmPGU-hK zmGoSS+dY+1#LA$dE|(XXlGy(E$3R=@_(z1)E5|q@-3`1#v2EB1A%6{_Yw3cuAaF?J`JG_G6%53JTZU%Jt)?r zm{zASx**LD#^@f|Dj}2(xm~nXCc?rErKwTUC2OP>Jx0*HT(WBX^zoxd;HrxcNy2o2 zynH`}o*{eT=Xq=L_ix=Hmlw+B{2=st`61_P5EA62tw}p;auB8G4Pj#m-b32WE|Yrv z>|HYoRjrJD}PQ5z|jB$Zq|plm)rk5`7Y`fK$~k zaKgVRyZHhBeraz-D4T=Ek}?mVg5!sTe9rjxItZKs@c%B52NaSA0dh(S!Yyc---et0 z-G2?gO7XL)m!r(fM5g6DiRpUkp?LwG`2O9P9>EA$JenEL9oi>zUdJ^;6nCAF8A1%t z{t;Xq$mCx~UX)%bU4%rgo&sho6M!Wy`{LJO>r!Yt{Qdg)o5y9x96Nd>6#{f{U)ove zC*UP$wza3^_8(2n)y zPZ9>4PTooSa>bbi2@ETU^u`DGW4641y1!>X;`7x~3m3+Jhmw7U*4a_v>cxZo2<>O0 zQYUz}yzgc4Dmzz@vG!Ioi@~MA9N$7-HZ!8%?#Yz@EPWF_z5mSq18X}k+;>|twSWIi zr?IrKYiaRJV-YVTjfLgM_n-Og+mkj%`**D0cV<8O%cYIwmf!43S^TrEqyz}Z$6EhwgXn5Og<3C3)#7|DRyG1cXZwLHK2n5v$eKRJ(|$%VOMu9}-oBOWfBi!Ju# zG7;&zm8)owPL59}$Ty^qY=Q$kX}V?pleO*|G2#}{)AsTbeKO<_Rh_(y8xfa9hPfecCtw@ z;W>X>x(02!IEHM}wQLo$C}{W3Wv!>h)8S}oJC*BDYoQ7T>LrtgMmIon{Ul(VOyxSc z)!e?^>m{p`v5kt1Ck;}Fm$=z_jcHz-+m{@!5p@D@vfi6qFDqLLH<_$`FIPW7A{|t- zawUnA{YDsnaT1#38=fL!DiA8(eDf@j>fUa~TKi63wvDmfy;${H>+SaXf%R5v-EOI- zp|#|EnrTJXm1)=jod7#P#J@BT>hwOsPg2eo!~9Jz{Tk_uw9P5rtIlqFJJj zfnp#{DFng)W8?Qe0~<(JJ}19SV!|~Sq}Tk%VrzV#uFNYsK6ax9-v&B*)8zLST`Y+7 zf3kVin8@$7`0kv5TFBWd9T>yu+D(lCOXNaMgI<(=hN6-EPJgi^=$-k-!|0ZmzB4^@ z^x>}@JNht~s`m8E-1M;*$Df*+!FRtnH}le8-g@i956^wMd*iL}@8P*4=f=$DGs9dn z^*R3HXR=@Z$gdtd`cZDcR#$mWzSC_)huvDX0(Z7pLT_|7)EjLs`;tv9PD7?uxx{Fi8Y36>FO>6- z)71+kCk_3HyE>b0ntErt($N@gD%xImuz&`wkzCq1o3@%drFoRD_?f0|lgUXSEX>(l za7B89$ULPbHJxHN4va+pClQXK08CdDmj2HVzMgtkxI%PL2Bw@Qr;`;*CIRe|JW3}9 zd=kdd2jG)wN*pf|`ef}k^V}+fK>jsBLi&IIG$ z``*3pzI}=JUPNR>#+LCS_a)Y<%BoDMN+p$4o0b9Ex%Zs^*xC>(Eo|N>34+)?^$2>MSjIm*Wn>0>k7#A_`M0!<2yf?K^wG(!&9${AxxcwaS10djZh}^~ zarTMFnfibGtH-B5%v@_uZdX^{*Ib)~TNiF^9p3!PaCi#-dcE%qlVKTNSjBv;^gebO zXg-6&p~A(5YYI0P3gfmCm$+CC#NLlxhI9<14pgc9&WAf}I9R9MVP7pac4jSLvkx6$ zBQb@8UrftPew zPIj!yg;(El;LyufmyedonbFIZR!=OL<)bfr*`WisTzz51>YQBZzQpQoXW7AQ^7X~- zth;~RwGLi#@qsr+-qE#}x$z%gQ8kUNwA$a}p+(?3*F)dDy6`r{36657L|h{tPMznv z)8N5g?m3}isN!6p?g4w{I+sh(!Afm=WNp=m?W*k zt#ZVL$#jhi=Cg(7d;|Ug$!DR{LsK)Umuf*!Hn|<(G}aUCxEa27Zh>=VF-@>QuTH)| z$Xo?|JWNA#;`_m9lE7COnsqR9N!76hlwwC3J)T)B7F(&cmWnZxB9y*Uu7#~OTke0O zPXTC1Hcu=aw=&Q`v&@7!3eLl0+Tv2JWC2qO?eEz=C$dThVV)D5r-+BEU+2U;;tbSD zdyZUIEk_99VCc3lHG8HD6+v%&GmKrK)6QWBHpp-m41t?KU6rRrg=ytJNx-~k*%4+E zV;*<1+sbJOw=6+W;s(*oy-pqHF+6_}Irtz`bfJi1Fg}k}ipRj9`S1o*JU~xKD-ES9 z`?b{1cx1Lr(7?4yrRZ7IHVO5M3&lpWyOwY-5q`BE*~0Z~S1r`ggwkl(1qP{^6gPx? zssoF*HZ11wRF4kL0h86B*J`}+26#W}xKwii#G=oqHB_rCUal)Au#L+LrREIg1H zh)H5XS#HhWyvQhexOt;%tJfOU&C|-{N(dIB6Eey6!QA886Y#nm|R?`Rq9+W zTNUh9SNQ=?(MfZ>JGEPr_7Z@R1l zlrDZn?;_|WXHF>$eX6A*6Zv5XSBm zlscZ(N~b!_GJUG>p2CBLKg!Ww)rL_zgQ}ad1+}Au%w`5#JvH3YWQKHsHxBdc9vIY< zO!eA1qJU+5N2Vl zq9}lwkI1|9N)mBt0!x1|InW?Z$rQoq0_X;VqPq?wPW^zUcY~5$-d!I$aA>XW`a*@k z!$^Q6KeW(B4MF3hm7S$yWrS)&XE2TVRTS4C(}e|$i9#8|G=kPDn1&$s#e74fy6gQW zr@(@AhWn^Y)1?+VhAFA!CV5LkI-F^y6x@_PgMly>Qc8r52#J5pv|z~X^4dkmE~?pF z1ctS|FxMDJ9dYI`2A)RJGeN^z-@MqcbwmiUbiKCrBGc1}&NX6!WkW}EphDS!amuoF z5g9HoH$0oiz`KkntD~W-<5B-M7|f7i8UkR6&~k(Qs%}WAik!-sfJG{r>4L(gg_A1& zRQY@=WO)H%K-qt+iYK8%Ge$+K)JVfNt{NduJ&aqYH&0xu_NnBYN z@wH=z#9FULU(Q+q0`sl+CPresaxL0pPV6u{zd8wrrt8?DmLAj4_*B558`IcG3S3;wsV~~S1syR>P2&t)wwP_ zV(z-@_VoEX*{LZ`VIgY9O^nMy9M!w%{JFpXTa3OTNB-t-*vY><48MhG9v`2L3w6+@ z_7yHH+)(%zg; z|7)>Ugue#b+d_Zls2-SplGM|q$zuQ1fkT7MmPXF&;E>@x#_0KEpUIZx4TQq+hpk=+1-BP;Li5;HW_X|x{XB{ zQ$HERd0iu05DxD};h^i+#2evyWs^&Lr(AU$OT4T#Te6rU& zdDwO?IlUnd>cz;nDw9vuSL*QhME(zfLp(P$9mfn^&xuOqh1N>v!216F(cS$!qeEMV zj$E}gx9ys4vorb8;zIL+FdU77=z@QyA1$rTMfUc#9nF=vVVXv=u-NEy?d_A>&f-GW z4Gv#E`fsM&>NH=M)a$GB^GW`1ihKHp^ou}g^}>O|)rA)oK2-Qf;gf~W6#k^}#lqt- z2K$Ks@uyrk(pDZVF-zXj9%bV^JO;N@r&(?Cz?dD9bq0OZ^px9Aj@gx-Uw41ix73oV z%9(&(n<7z(BI2P->U(gU$UA@q?a*ExQ;l3`worj@_=HZ@OZSYy&Hd#3m%$+t7l)hi za74dy+%keFkY;^BdH0&N_SV9@sj2AA_TTHW+A0S%ru*wAu|UEYMEGvWr?p8HF0TdU zR@Jo~k9sA~=fp5HYEijP?!JE*1siKMgA*sHfewoPx2Z_Dx*|j;+xxmw11flUR=#Wa)@fScL6FL|mwyvGB(-oM~VKkt}pXom{`60Z03O{_K^=2X7+`5fk0dKd#7xJFXUx(if_*ldf zcTbNO3w!!DVMbxCFa`_uUtnpHVJ;Z;TM1fw-FuOqDrOLNp zpln?95C*-p1`}cGudx?*o{cdmWi-X~N_!*n{oQ6)R#zjBd;ZGjKw~1W5PG%l9~@Ld z3)H8DX7U!d=zFH+-m#Nk3dU=GDZTZ7b}7}m&3$H8FYhdE8q7zh@Wr-tnCWoGl9qFO zbAdfgv)v8s{&C?sC*R<~jNk;Nu&G{e=_2O>Tmodqd%F z(A0mw@NWvA&m+5kE(t}AG;@8`W~(pgYXk-FAY<5mYRxe#^$9@?<`L3?%pWz9Qr0a4$dR6M;E_aI|8jHrXNT;UQfb*FB&tv8p#xV5M z)?iem9_oL5Kj^-=hUU;ino!0;Jwlg16^RaJ?fd7_k~HUAYfhy&S|9H2tNQJrmX>?H zPWc5ZzEOmm$mA)biK~k}qTg?GUgc1F`STbS!hj7>7Lu!=HW8u@i1-;PSqRp2u@Pl3HTPm^&Kg zD0$PUo9X7S@{d;1Vv6J0wY{~q*?gGLPch%rS)K(;?S|^f@7-yX^kn&g^ z&FH5&D5-PBg%oj+5g}Hp<#L#U0oSWG>ggQlP_){&q9lpz za@hEs4l0WyffSk7(5zv#?AXGUj7irv!JdEP(R(9V-B(W%zwD8>fHDY!ZQJFhTPX)w zJbAEJrIdz^vXjJ>hOY2z_A^1Hmbm4TR}p?)o|-&CFu;au^-?jmQzL9}w(xeRoECc$ zBTh(hIYRe;Axu#Q!gr1z<7F=V*JwsyU_eP{pEmh#@Fu_E+EE`U9q3IY7&?lizud3O! zwq-KtkcwkjZn@~=?qjK!*vEo2==y;r-dF80)~j>Jlfkm@g|edyO3GPm28N8PQQ2$g zF!Sm`HTHpf`m)56Oqzekt*Vn2(@=kKR~AJ%<41!q5Z+c&@xi)gLYEx0KQc6`hA25d zE5Ls%S`%(H7>a|3&EiWBOQ+U%Iu58mGaLlA2J#J2*%r{U3!0c|0t0c!xvnkXwL-gU zw+S6<#Uu2NbN4>A9>sC=TYviISH0@aFE%f3Z|uBcbFjkJ&fWX$rBOTT_11s3$Xf5n zksqxtj*~r`A_e{4F0A4_bI+$Y?*v8Rs>Ex!s7e+3V=x%*FqlIuZSFggd&ajWpCUxY zw&BV7InzsG)N*7{46G)7S7TqRvF{?h@S4em)3fLjqOLb<zK#qc>6IJ}bRWtlwkGDfhh8c!JMI8v$q<{^Xt`yqbZvf=xWGT$#vvIB=6yt&8w7)|!J|09kXg>ls3}euV`tuBRfw+j$I*%qrXpbVCt&+W9SMkv_~s_j9$+sYK?7~Ho9ZZ?F&*#IuU+~xfdx|ZWo;SL@5 zal;Y{9c^Q@1tO;6zGojSrXgs87>70*Do1uqKAiT|zUEGj*9`jNF5Mw@+VABXvZ~#< zt%+Nz*mX${-QRyjXgxrcT6|+88r_V*=AWcM9H*t}^{W2g(s@SEh{$ah*VwhADcQ_OKBdc5HGU zCuRmx+nKHXMAPM>UOK}=pdV#Sj}*uO<5d&I2`;loAVGf&gxD#A#b#q3Od(CJ%xt(& z4>64)#id1zVbfGnUyAPN8SYxboQgCIMU~vKaRUc;au6|!p-U=e_tk7>QC}uZGK|aD zF(gx9kShEonsL5Dbx8$!-Vv-1Mov_P%{+ zn?B58V%x%@X4#HGN``1vY)^I|yw8)(9U>V5JUM`6@LF^j%OcY^EE^ zn-ufKTY?%GK8isOEW!0~4uTgqjdfBrH5do@3Z^r3Mj3>)BQe8{R!$h7N0LCjQ%2Z( zY9xQo*X9?-L4#pXG{(Tr22SO3C`%0vx`Ns&btF|)&@eJ1lAsyDP1Wa91XKcBmmu;i z)`y_)Co4mxaC8nHZ5UCKClFsE&@~+VH&RPdUJ-0{iw;%i6p4d4(`km zYRg)?N7+%_Q}>{&)o^+=e+cb|opI<{!v!$oc~mtcTPA@)asFt_^JN(e+gmzza=L;3P zIL=P3ATl3Oak`DImHNV_VJytAoq54VSvsJ!mhEj#qqeiL2xRz<<@rFTzSn<_E$D(T zTWM=)bzw!M;GHbzh7nB67<6`IbG0*oPB@S^vcXUw%*<- zb>}q?^U}NXyjS+O{iXf$jL+RVcWft+_T*C_6{2HNvsYSb0&6f}>Zc3MqwW=pR>+*Y z7;|;QV!#vlUCh>Wp$ik3iumYV+`@eIFji3dT^MF%h8#r7JMO~82VH1}f~UC)Hxx~2 z!KHh96^Mh>E9_$S1@wR0#`!-_(;Vowji~V|jM$^IN#8}A0d%SH^TTi-k?zqOM~zCU ze-#*7W~`#k-v_0YReXlHk6m;&Dv!=?@-u6vuRp!94sYwUQLdB*FX(}-dgO-jQncAc z_xblByo1BbAjZYLYdUu?xoMpfdmV4O4&F}9@(t4OgKd)(8ft&_?!v;U!daE67oorq z2o;g!go>OVf>PLN&YY6P^Dod9jSZV{K|9XZ@G|}9E;xU9?^)!Pm5kD?k8hu_&BaC2 zrq#(;Nzb(Hca--|O)jxb%+ZEt@fv&b4RzlDxOImZUxgF)xu`H7@}2_!Hkf9by6G7P z-ra^0LDhO(my}CK`VKcd-!wfJ?z0SGYQV~5*)~CuDG80}j-6;&v{&^F4|F`;fb*h^ zhswgj9RYvt%=xx3ea{rW6Q>fiL&M-^WD?7_4b*l5)3HIvhM~S)T+X{0jodHjy4-~FhA((P|G=7>{^Br;@ zXgNz|T`+UL7*B3j-`Evqu^EBl^#u9_w!)43E;)bkpLxU&R#wQWx??d2ncH=Z$?ZfV zt;Eyd8za})<&`2>lAV>}zU45oif4<>3-);GpRnVq&PUX0dNxZwqR@Tj_g2VgI}^$+ z9YMy@kwO=1t8B*V(6)-xgG1!m!-2uvCa&3NE3Lt;{f#9FDsZuBfYRlaSC5;LTK>J5 zv><;9mrhARP4C?iypgwX|B{2%rT)H!v6qr^{Niz=nRX8MJd?5S7?$IrE-uXo*ybHv zO5a3fOww83M{Evc!zqb(uO)w)#@Clx@oEShltqjE@=|l>h;5fKE3ei$ zoi>Wy;>C-l8gZ@pl2JEadf}y$PfN!S9gF_pitQ5@{mNxc7(DIsuf68ND`WR!pK3x+ zwNGDvs_2`(fC12}8bIvGb~oRY_n zAOFZ{N={EMhqqs{5x?Dj{yVwgg_~uX-bUi=+%qrSDwFiK$q%yMppUPOdSq^H_5Lk7 zS6Tar0hinMt7ZN#|3+@Q{6*J0lb1k~_pS9$pS{6_Pw!pZD4dsn?*N_TrWux5{u|Gh zc!yl2%9$;)^i;e%Fu6gd1kEm0$LGoO@@*s04`B4CsI=#kU}zu6wb|P^@RMZw^qE_F4a=d_HUixQtyvTGtwR^p zuiENr9JE~;MZx5;nv2^I0uwN3uHHIeUpcsH(-TryXws|#5psC=WxxC?J+VSxShN0$ z*dok}ThKQx+e6Al=jTBal`=bBT+DV^qFhY|duV*a>GZ*AMde zmy?-kG73-3E7UOmhDsBslhtV=e?X4wNi^%O&!{h(=DLr3nURig@V^P4LQ7AJ9%6qf4#Q6!`bmW+Th|xdcA8e=(L+N>CPG z2%m&!fcfp1xP_8=m6VhJKNkxgWre+YU12qq6B0NR0^?y!6MFxRn?aM@@`=@U_sq%l zN_uJiFu7xLFMV76(zLR6{LEr|{ieyi=k1l{0_JKOWW%W#)t>>F8k zo~2Wm2?f@oT#$3J&%F<)e;QXYM{2<}y(+;q%JcRiuH5DD8~r_SE44eRwy9KZ`y7dd zbq8b>^?X}1G&{oOfdup-yYAaPCriq9U8`!GVh%_l$~@D2?Px|dOv7e^y$71rkuBlt zi2|6&Bx5>o)-ZG*%SYGCl8Jm6xGltu}W8Si-HwRI_`go#E_uh3Pm;fU z^2u|Do_zATC!eHG{_@vu#TPjNZy$%(d0p&U`UpsWyd8|Ki`ub@J3xn(ezz_9IaAvv zQGT#S;52G93ZY3Y_jadgg~x4-W*DdH37Eb`O;@xl;ywsPyicXuhWU&uwLf0HX9@Hg zsdf0Xvcojplx63~!_W~l%Yf*^UK(qRcI4Cb`nT-G^aI}dhP>Sa-T8Qr0k5+C8-127 znRobquFfvpCr!`4i9Eiz_+`!Zj@{wcB#4&D<5yGD_wV$5lO8v1vvkwa6H8al6>mFw zwAG>7D~~b3^renA?5(a6{l+tK5WcR=6zRvq)9g88Y-~igi+M1|I%#8Y z$JD3*qTaKa6U`=9T zd^S`&!!FJ9_fiFsglfTr{Lm{Fy-5+?$>N_K9iR0&OP!i;hGk&Rjv*}HEF}ha-7qY6 z=KR`xVtUfjL1S3(m@~c8!>3H1+T8rBzk13LMsYq3N=2C1z!8B9Y8KO>;Zzpqlex?> zb;}Ws&@@+V$v>$WS2+*x>k;Uk657ykFc4+{UC z=%htPcTOvUjofu-{=1un)11u}`o+W)HD{KWBf< zzQw-Hp3~;FmbRo_qMg)UqPW+MjDrYk#YKSGV+a{gi%2 zKdb*M{R8?Z_0Q;E(!Z*IQ-4PPuKpv=z>aC~1-`^D;V1cv`1Smk`CIut`~m(T|0Mqt z{uTaz@E-^U>W(XFVnu9#eK`_;SBMviUlFeoZxz2O-X-o69~X~`zYtG}zZL(F_(!8+ zbc|)=pmD^w)VR{P!MMeEwed#dF5`EM4;lZ)_>A$V#(yxLG`?y47vtN;_lx{A`=*5mwWQ&)&(w0Dx7AVPMD2bMN@Q#RSR5K&iANB#U$?a;qt9)j77)rC_2Il1N+GYTFUA4dEFLbD+;_3>jumw zgQFP!oW>YJ(H_BBtoY`jj~yda;A7GqCHZ0=bR9+{q86JXrtCC->*^>|_Y9}K2Ti2? zgcmXN1w#VjE+YPPffwXYfie*!a-0sv&@5d2^=KFBF&rzKaR#xAC{`O_hqTr5`Dln3 zzBQ#LBX%#aSA{-(2>-CN)jKMbn_FYtCdMbi)6($(Rp&Sni0cL16=Nq`3R`CixQkBQ zNGCQTX+gA;4y;v;owT75n5}s@>L#jxdV}FOZbSdZ#TdZ|;5&X! zF%qqZ#OI=$QY-IlT+Ga^dANVvAA!OJ)$6C3K!KDYbZ3?#ZB_|B3sm%Y+=n)Hw~Aso zX!m0nP^j1lCm5F7-2~}2#^3`?!K7S~bLeCYW5w*1qYUH)v|MyCi6%~yB6L+d6J6-( z0a!}8V&Bhy)jgclF>b)(HcKnxNIhNXBGuBZ0gM;!7^~5U)s1nxi^HWTXROj%c40Ko zsOlqKserf)DKd?bj=IndV>Lu@YgZu5v1;Z)(uXoq==yFv=&FIjKE~)4+z*CnJ{%}3 z16q}LF_6MI-s<8EM_6QpIqi+(7Vl!mfUY{`qXcY!X5dEgbeKljIM4KjDy~q#>)mV% z>0Pa*1K9zwq-yI(#T2g`07%w)c~r92!wH|b%a4ki@32q z%9}qQ-pYL!uv?i<4AzZW`%5_L>&s6 ztr3t2-^jhIVHUJOmcn=eeW)EPOoNWkfW~A>ok<1O6ROz;S|4wNT}AuYS7{$P29Btr znLcL50}cR0T})ewRQsDMpE#xxN6iA$2ltV4LaAyBcl$tlz$UgpYHcaYe;=FE!byR2 zg$#Cc3*DE7Kp6;@3S^6P9rJFlkM#ki4g+kqdJ5fF4C$SO8df889fr$6WJJHzM5BFA zLI#>3g*?SDsbXqCIfh1Rh>WEWu5mM8Lr}3Cf3ys-@MD@MUe?ih z=q9+8g2JdFJiJBJrqgA(|4+Cw1ZAG-i(z*pDo`6vnK@=#m(XGkU50Vk2kL_}>@VsvL6jSYWY11n>b|0IbWK;Mze2#KZqUx(g$-h(g>*}heKC9w2{=iFH&Pf5i->i)lU~_rRr^9}2_Xue`?9pL*7#WQnrhg^w@BU`-f#h2_O` z3!00FaU(*K08Xd|7O`rYb}b=aB;Q7ajf8q5uhUgiJL{uj*=EA?j zSlXAd?m2>B4sZTh)k_qqmyx`m(Srwv2_Y#s7IZ%$@5-BvRHUA%C?A@26h0iH$~i<= z)n__;Kc%z5Q|SlPP9^pz)Z+656i|hpK@^UM`lnCF6NeXCME)b(`SodOl>8-j2M(+- z;Q%6(kPSkAe~*y&tMgc<>M^JdC)3Jd&I1+rj|oW;&UjiSL-h$dbec#3xfh7)2y~g# z5%wKtsn#P6LQHg;VfwW9q0%NMtJCt(D5y3@FyVxU68^I;;UfO$NQA0V*o};Rn2=8+ z@;|`~i9%$a6Kcj9DSVck{vmcaR{E>t`@pCabjhNke{38#q|wh~3xxuYqYtUZ!BGvm z@@~w;hHcgiEpG$fgoC?9$dgDy@Z5h1UrW>ZqCulnk0Ti?Y(`Dz^pEj*LaOm4RKP@i z&-7`8R0+8gIi8}C6gi=y67(hFO`4-3PA3Wx^tHgu)zqRU?#D=M3_}(`o+LRgADNx)Jh`J*+@>Ghqgj z!@IGuRJXb&;b<|;M~u#sK$I5FYF)Q+ zXiS~eh!11wx*%Bu_F&8+>SMv08bYLUe;Q%*Aqvzg#y1ntDk+zYOQG1S$8z6Ajd+X* z1BEQ6;1DiIer>KqF_8l;Vv+~X0Y*>E#J7PMpig8LL4~w>0~0X#NsN!nkOg5{7I=z? zQzQatysQN>^q^8Oqx(xhqKXo@ZAV4qy;f{XU(H2LQWKt`DNZ10P!kQD!A{X~f0>Ol z!DL3Dd7*?U3zyU@C0jFcau<&0Y7wT=!Racfu&K>FM`*|g(a8)C7{;=2#UczI zW>y!x41{EQ9D`0>AUTFJOi0X7a7osIJ8g7Xr7gwX;AYRe1Vo3X=068@f3w9s6B=m1 z_@WmrbbLwIB(M{Xq{>u^Qw>4-FsxNKm8ONx&%~KW)PB!3=UX@oHjWT#ZJLS92spa{ zBJwD1WNVhNfukZffl8a`hRsYPrkZOA7Z@`<6(x;Y?IxB&sb6^KLe{>{OsnrY%vzBODyn7~bOB1x zCHnPQIH@G&El0govtj62$UKgS@YoGNr3P0TB5I;KJ_zO&OiF`m!U2*jS-?FAb5T#m z6wN$m@i|kIFi3V}1W*9A$}lJnqJ+ShAT*i=m9dN>NHv)0k&W(Gm=cI7cLicPK)M8u zV9q9?M44h7Md&+VeCjrG&*`I;{m6?;lQ8@m*;i_MQCbdut>VBL4^UmNmbGu)SOY_P~Q3IZDFAp zFqCu$j*Hm=;6cElNTX<20Ss_aDFir`q`-uks0J$215{06f9Xn}hk2y4yuM0zM&eUw z+0Y#t4;6F^7K{YPxoH3&m1s5LEC*$b8Y+dAiE`UYJk*vPAQyyzVm7dLUGp&ijXpOQ z0M+Qqv=E?zXhh~loQJa-+6`(N@!?{eQpl_Y${Nf)%wRZe#H=PTH_!^ZHnfT( zx}An|)Fy^)c<4GtP-akzs$h9FSQ_@ct{|e&b`q#>f9j@}&o;wW)=H3c6*MrWfUDojgO>&~Qv8o;S_K&H{~y>B=TU?F|Gl zv<@T{f6om_q&=b*0Mx_LaS`cDrCm}ss1iSPraep4I29JOvUz{;XV2$if&Nj!Kwo0g z4&Zm#jMKrMkEqH{gz@oh<&Tl0vgJt`r`|&2zH-s%jYs7BuQ+upsXuE7ZW~48!mPE_ zdBbAom^6zA8#R%eiiZKHt~NTu2HMEcENA+&tY6qJoGjc}_|?MQxc{~XGUbt}b5{ytGNyWDk{5{>l#Yyr)Q;b zN^xph+!S8L)6riN^whv`6IqNLTiTeve_VI%LAR;t?#5d-uAE>fUK;?7P+$;sUd|2KAyv5%jne@~yh z@W`p-Z(hjo|7E|ikdY@Q|N238$3Cjf%K1HJ=Q*!Q)E3=#(;{uqmCyeEnd8ULkQ*O+ z>`rn7et$xWZ++|J)8z7VU#A;ChutO%r9wl+_6E(Mt&&o+emcfK(Z)sLHhgY(+wi|F z`cKo|Q;X}}{aF=%vW;d{cg*I0f7rLNY;MTq%T0HhraA3))5#OR0zZ$uzOvJLl2n$qX0aZa z!D@$MBq(MJQ9BIr9!73I0SX)nISUN5Yt8iz< z_Y$?$q+kZp&Hb3Yah_uOqp6F1?p&WgsfbJ7d*Hkyl|YI71E>`1e@O?kCkS6l$+$}O zdzg+vgG_sQ;^KOWX;igV1T15yY0jz0uc8_T>XT_1RlQjb9a@s0g|Rr|G3m80U9Tap z*Oy9*=f0M&-#IcYfxakNxhC6waM=;^oO@jYN`n#D>tfvUEzPPSn#}h^XfKO#Cw6J+ zLf@(A#vxZF*E|f8f5=ew6ll@KZQqW_1(x;N5?`wp5lk)yn*eno;Mzv(Kh~19T7cHv zXl}D)pe2EeQe>X(fXNortDyNxRw^5{_T0&>dE?H?@yM%cq5--tF)Yu@#CjukBV`fr zSELR|b$FFC(A+VS@3kd?NwfavX$~-Wi3!Df102U*=?7S9{S}IX$Z|_ zf$u`8$3m}6F4Z-Mg`gDZ&a!3Lz#t;fc664Y@)J6n#{LW#Ww>RVv2MJ;GmD3^oo)$r z$==+04Z46+&tldMgzYSrOy4kD_g`CM-NIJk&4n+Z=Pt%!U?@x5 zoq}8N6%x!N%z2{F-EnKBhtcIh+S8R7E4D#6p%c?I))+TWG5#9k{#uyZ3qwFuWCGlU z>E~2JAKXXUVY_62yD}}_DQ*n|1=>4HHl*C>M$^C$e>nC(Ii;#J?y)u3`tAy7m)5Ga z60Mex)@^3@%9vuXz9Ew;q2EEIO-3dkuj29$dZO}3WvPOjScEI|Ww6ph2L`*t&}_m) zTCA=im?6a|VM5D3DtL!~@X%+`B>3Dz{}y#5_?&BUdx^eFNxlhOxHBYK6_hvBJH;AKzd2 zy~6JoK8#r@FkEDX#5i~Y$13QJ`g@#YhaprVe}ci!{1kuj)fp(*0nBmQZUJj+p;yVe z;%6ACAFB;V1@4X51-(Iz{W}=ve34=bw*EXJaeEjH`#{9~cnF_y%Z;sRruQQ~(X5S9 zuZ)f635On?TeC}{VYCBlBi(S=|1f=8`tcd{bM@6{&ir`t?+CRvtVW0V95@&$M=+*U zf3#A$V{VvnWU>gJ#3F>SM$^o29kh7r7=+WlG5?{DPp$@y*Wba(FIhf#sA+!%ZpPIh zFm!N8>9zwbCmTlao4~8H4gG#L;X9a(htT0FXx|s%o=SW@ix%5h70eBj=#hrGZ7k|H z<}u8hk;B8`9_K);Oh1Y;~mHSGgW{Mg)OlJ5uQh98rf59!? znVY!^k?i+WSW=$4JIO(UMef0l+H*2Xz=3gL$`4XW-P|`USBQHf@@q!E<0UF7cVOTG z-8NDR)?ef&Fb09brO^;VT+}zf9(7I3%_=b-7>vZy7)^UEhA9uGmeHOwLeul4=}3R+ z>VsgT2}8F`<6t9nLQVIW@htFJe{O53uX8g5lg(>vrJ%*O%JZez0fjiO877D$K`p(3 zco`9KrGotG@$8OKT<)y3dUu{~{&o46FaPf3%a=cQdF{3% z&UZnh`=zgt-}#t!{LU{Oe}IdYvLo?L^pR_~o9pY}h%X`UoP6!tb#ncI*0tC6CeK|v z%Cgb5aIo|e^26itOD3=GEq^ULF!|bU4XYlj`d<$+@A;bMd3^Y7uq=-hE-4%@oGx5d zxLV;#6*{vd7gPUS(hu6vKg+_-zu5oBxnTNAGc?6oD)5!7;P`I4f3*%Xb8Fm#!D6Q- zMPV3%{E$my^3!5bKlBSf!<~Qf^EQexgwj8nIt|OC&#H5lE|Z8M8bz9 zr&n<)`MFPWNnbU28@V}Fo-6Q5mB$C!QXkUkVRAH{JXCyVxKL&${IK#u=mvxYD^7}Q zyQ%a40_ba_3wWGkU}Rum04CxCoA;K)^V@u7;AVaS6k)g*@=_X^X1>ha52QF4m_R%L zSbGZ^la`9-f7~K4B+e!}CdelUCnP6AD0(TtIJh}>I-okrI|4hLJg`1GK5#y^KS)2E zLV!aIL%2i$M1n-#MQ%n=Myf{+N18|iNSaABN$yGzEK2fA7E7Q^5KMeps9OeG)?5l) hC|qP*l3dtcP+oRlYG6iShG8~gYGKM^bh9XoC; + private _telemetryReporter: ExperimentTelemetryReporter; + + constructor(private readonly _extensionContext: vscode.ExtensionContext) { + this._telemetryReporter = new ExperimentTelemetryReporter(_extensionContext); + this._experimentationServicePromise = this.createExperimentationService(); + } + + public async getTreatmentVariable(name: K, defaultValue: ExperimentTypes[K]): Promise { + const experimentationService = await this._experimentationServicePromise; + try { + const treatmentVariable = experimentationService.getTreatmentVariableAsync('vscode', name, /*checkCache*/ true) as ExperimentTypes[K]; + return treatmentVariable; + } catch { + return defaultValue; + } + } + + private async createExperimentationService(): Promise { + let targetPopulation: tas.TargetPopulation; + switch (vscode.env.uriScheme) { + case 'vscode': + targetPopulation = tas.TargetPopulation.Public; + case 'vscode-insiders': + targetPopulation = tas.TargetPopulation.Insiders; + case 'vscode-exploration': + targetPopulation = tas.TargetPopulation.Internal; + case 'code-oss': + targetPopulation = tas.TargetPopulation.Team; + default: + targetPopulation = tas.TargetPopulation.Public; + } + + const id = this._extensionContext.extension.id; + const version = this._extensionContext.extension.packageJSON.version || ''; + const experimentationService = tas.getExperimentationService(id, version, targetPopulation, this._telemetryReporter, this._extensionContext.globalState); + await experimentationService.initialFetch; + return experimentationService; + } + + + /** + * @inheritdoc + */ + public dispose() { + this._telemetryReporter.dispose(); + } +} + +export class ExperimentTelemetryReporter + implements tas.IExperimentationTelemetry, vscode.Disposable { + private _sharedProperties: Record = {}; + private _reporter: VsCodeTelemetryReporter; + constructor(ctxt: vscode.ExtensionContext) { + const extension = ctxt.extension; + const packageJSON = extension.packageJSON; + this._reporter = new VsCodeTelemetryReporter( + extension.id, + packageJSON.version || '', + packageJSON.aiKey || ''); + + } + + setSharedProperty(name: string, value: string): void { + this._sharedProperties[name] = value; + } + + postEvent(eventName: string, props: Map): void { + const propsObject = { + ...this._sharedProperties, + ...Object.fromEntries(props), + }; + this._reporter.sendTelemetryEvent(eventName, propsObject); + } + + dispose() { + this._reporter.dispose(); + } +} diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts new file mode 100644 index 0000000000..2b9effc8ff --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils'; +import { Kernel, saveAllFilesAndCloseAll } from './notebook.test'; + +export type INativeInteractiveWindow = { notebookUri: vscode.Uri; inputUri: vscode.Uri; notebookEditor: vscode.NotebookEditor }; + +async function createInteractiveWindow(kernel: Kernel) { + const { notebookEditor } = (await vscode.commands.executeCommand( + 'interactive.open', + // Keep focus on the owning file if there is one + { viewColumn: vscode.ViewColumn.Beside, preserveFocus: false }, + undefined, + `vscode.vscode-api-tests/${kernel.controller.id}`, + undefined + )) as unknown as INativeInteractiveWindow; + + return notebookEditor; +} + +async function addCell(code: string, notebook: vscode.NotebookDocument) { + const cell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, code, 'typescript'); + const edit = vscode.NotebookEdit.insertCells(notebook.cellCount, [cell]); + const workspaceEdit = new vscode.WorkspaceEdit(); + workspaceEdit.set(notebook.uri, [edit]); + await vscode.workspace.applyEdit(workspaceEdit); + return notebook.cellAt(notebook.cellCount - 1); +} + +async function addCellAndRun(code: string, notebook: vscode.NotebookDocument, i: number) { + const cell = await addCell(code, notebook); + await vscode.commands.executeCommand('notebook.cell.execute', { start: i, end: i + 1 }); + assert.strictEqual(cell.outputs.length, 1, 'execute failed'); + return cell; +} + + +(vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('Interactive Window', function () { + + const testDisposables: vscode.Disposable[] = []; + let defaultKernel: Kernel; + let secondKernel: Kernel; + + setup(async function () { + defaultKernel = new Kernel('mainKernel', 'Notebook Default Kernel', 'interactive'); + secondKernel = new Kernel('secondKernel', 'Notebook Secondary Kernel', 'interactive'); + testDisposables.push(defaultKernel.controller); + testDisposables.push(secondKernel.controller); + await saveAllFilesAndCloseAll(); + }); + + teardown(async function () { + disposeAll(testDisposables); + testDisposables.length = 0; + await saveAllFilesAndCloseAll(); + }); + + test('Can open an interactive window', async () => { + assert.ok(vscode.workspace.workspaceFolders); + const notebookEditor = await createInteractiveWindow(defaultKernel); + assert.ok(notebookEditor); + + // Try adding a cell and running it. + await addCell('print foo', notebookEditor.notebook); + + assert.strictEqual(notebookEditor.notebook.cellCount, 1); + assert.strictEqual(notebookEditor.notebook.cellAt(0).kind, vscode.NotebookCellKind.Code); + }); + + test('Interactive window scrolls after execute', async () => { + assert.ok(vscode.workspace.workspaceFolders); + const notebookEditor = await createInteractiveWindow(defaultKernel); + assert.ok(notebookEditor); + + // Run and add a bunch of cells + for (let i = 0; i < 10; i++) { + await addCellAndRun(`print ${i}`, notebookEditor.notebook, i); + } + + // Verify visible range has the last cell + assert.strictEqual(notebookEditor.visibleRanges[notebookEditor.visibleRanges.length - 1].end, notebookEditor.notebook.cellCount, `Last cell is not visible`); + + }); + + test('Interactive window has the correct kernel', async () => { + assert.ok(vscode.workspace.workspaceFolders); + const notebookEditor = await createInteractiveWindow(defaultKernel); + assert.ok(notebookEditor); + + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + + // Create a new interactive window with a different kernel + const notebookEditor2 = await createInteractiveWindow(secondKernel); + assert.ok(notebookEditor2); + + // Verify the kernel is the secondary one + await addCellAndRun(`print`, notebookEditor2.notebook, 0); + + assert.strictEqual(secondKernel.associatedNotebooks.has(notebookEditor2.notebook.uri.toString()), true, `Secondary kernel was not set as the kernel for the interactive window`); + + }); +}); diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 84a9d6ba89..fd6f483a48 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -25,7 +25,7 @@ let outputChannel: vscode.OutputChannel; export function activate(context: vscode.ExtensionContext) { let connectionPaused = false; - let connectionPausedEvent = new vscode.EventEmitter(); + const connectionPausedEvent = new vscode.EventEmitter(); function doResolve(_authority: string, progress: vscode.Progress<{ message?: string; increment?: number }>): Promise { if (connectionPaused) { @@ -159,7 +159,7 @@ export function activate(context: vscode.ExtensionContext) { let isDisconnected = false; const handleConnectionPause = () => { - let newIsDisconnected = connectionPaused; + const newIsDisconnected = connectionPaused; if (isDisconnected !== newIsDisconnected) { outputChannel.appendLine(`Connection state: ${newIsDisconnected ? 'open' : 'paused'}`); isDisconnected = newIsDisconnected; diff --git a/extensions/xml-language-features/tsconfig.json b/extensions/xml-language-features/tsconfig.json index 662cf2ddf2..ac03ca408b 100644 --- a/extensions/xml-language-features/tsconfig.json +++ b/extensions/xml-language-features/tsconfig.json @@ -10,7 +10,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.d.ts", - "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts" + "../../src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts" ] } diff --git a/extensions/xml/xml.language-configuration.json b/extensions/xml/xml.language-configuration.json index d04db08380..4706ceec5f 100644 --- a/extensions/xml/xml.language-configuration.json +++ b/extensions/xml/xml.language-configuration.json @@ -34,7 +34,6 @@ } }, "wordPattern": { - "pattern": "[:A-Z_a-z\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2FF}\\u{370}-\\u{37D}\\u{37F}-\\u{1FFF}\\u{200C}-\\u{200D}\\u{2070}-\\u{218F}\\u{2C00}-\\u{2FEF}\\u{3001}-\\u{D7FF}\\u{F900}-\\u{FDCF}\\u{FDF0}-\\u{FFFD}\\u{10000}-\\u{EFFFF}][-:A-Z_a-z\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2FF}\\u{370}-\\u{37D}\\u{37F}-\\u{1FFF}\\u{200C}-\\u{200D}\\u{2070}-\\u{218F}\\u{2C00}-\\u{2FEF}\\u{3001}-\\u{D7FF}\\u{F900}-\\u{FDCF}\\u{FDF0}-\\u{FFFD}\\u{10000}-\\u{EFFFF}.0-9\\u{B7}\\u{0300}-\\u{036F}\\u{203F}-\\u{2040}]*", - "flags": "u" + "pattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)" } } diff --git a/extensions/xml/xsl.language-configuration.json b/extensions/xml/xsl.language-configuration.json index 5abe96006b..63c5c75d5c 100644 --- a/extensions/xml/xsl.language-configuration.json +++ b/extensions/xml/xsl.language-configuration.json @@ -11,8 +11,7 @@ ["[", "]"] ], "wordPattern": { - "pattern": "[:A-Z_a-z\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2FF}\\u{370}-\\u{37D}\\u{37F}-\\u{1FFF}\\u{200C}-\\u{200D}\\u{2070}-\\u{218F}\\u{2C00}-\\u{2FEF}\\u{3001}-\\u{D7FF}\\u{F900}-\\u{FDCF}\\u{FDF0}-\\u{FFFD}\\u{10000}-\\u{EFFFF}][-:A-Z_a-z\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2FF}\\u{370}-\\u{37D}\\u{37F}-\\u{1FFF}\\u{200C}-\\u{200D}\\u{2070}-\\u{218F}\\u{2C00}-\\u{2FEF}\\u{3001}-\\u{D7FF}\\u{F900}-\\u{FDCF}\\u{FDF0}-\\u{FFFD}\\u{10000}-\\u{EFFFF}.0-9\\u{B7}\\u{0300}-\\u{036F}\\u{203F}-\\u{2040}]*", - "flags": "u" + "pattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)" } // enhancedBrackets: [{ diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 13a2f75ef0..7b4407130a 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -10,17 +10,17 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -coffee-script@^1.10.0: +coffeescript@1.12.7: version "1.12.7" - resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" - integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw== + resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.12.7.tgz#e57ee4c4867cf7f606bfc4a0f2d550c0981ddd27" + integrity sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA== -cson-parser@^1.3.3: - version "1.3.5" - resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-1.3.5.tgz#7ec675e039145533bf2a6a856073f1599d9c2d24" - integrity sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ= +cson-parser@^4.0.9: + version "4.0.9" + resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-4.0.9.tgz#eef0cf77edd057f97861ef800300c8239224eedb" + integrity sha512-I79SAcCYquWnEfXYj8hBqOOWKj6eH6zX1hhX3yqmS4K3bYp7jME3UFpHPzu3rUew0oyfc0s8T6IlWGXRAheHag== dependencies: - coffee-script "^1.10.0" + coffeescript "1.12.7" esbuild@^0.11.12: version "0.11.23" @@ -47,10 +47,10 @@ typescript@^4.8.0-dev.20220614: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.0-dev.20220714.tgz#9c1be002c44c3566e789aa404b2f94b0b96468cc" integrity sha512-wKK9FMpdvwI68PZiQdNTNmX4rpVXJBDOG9aylV9O6nJUO5YX8yv3bQNcyLc5tbI7J3+u7AU48LVY9SF9lYwy5g== -vscode-grammar-updater@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vscode-grammar-updater/-/vscode-grammar-updater-1.0.4.tgz#f0b8bd106a499a15f3e6b199055908ed8e860984" - integrity sha512-WjmpFo+jlnxOfHNeSrO3nJx8S2u3f926UL0AHJhDMQghCwEfkMvf37aafF83xvtLW2G9ywhifLbq4caxDQm+wQ== +vscode-grammar-updater@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vscode-grammar-updater/-/vscode-grammar-updater-1.1.0.tgz#030eacd8b8ba8f3f2fe43c9032601f839ba811c4" + integrity sha512-rWcJXyEFK27Mh9bxfBTLaul0KiGQk0GMXj2qTDH9cy3UZVx5MrF035B03os1w4oIXwl/QDhdLnsBK0j2SNiL1A== dependencies: - cson-parser "^1.3.3" + cson-parser "^4.0.9" fast-plist "0.1.2" diff --git a/package.json b/package.json index 9224aa94bf..23e1b5d64f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "azuredatastudio", "version": "1.44.0", - "distro": "2cd50bd144845cd70df1cb0ed301c64d3e585ded", + "distro": "5bd81cccd1b758c671648c7770966b250e3edbc2", "author": { "name": "Microsoft Corporation" }, @@ -41,7 +41,7 @@ "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", "tsec-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.tsec.json", "vscode-dts-compile-check": "tsc -p src/tsconfig.vscode-dts.json && tsc -p src/tsconfig.vscode-proposed-dts.json", - "valid-layers-check": "node build/lib/layersChecker.js", + "valid-layers-check": "node --max_old_space_size=8192 build/lib/layersChecker.js", "update-distro": "node build/npm/update-distro.mjs", "web": "echo 'yarn web' is replaced by './scripts/code-server' or './scripts/code-web'", "compile-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-web", @@ -57,7 +57,8 @@ "core-ci": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js core-ci", "extensions-ci": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js extensions-ci", "sqllint": "node --max_old_space_size=4095 ./node_modules/eslint/bin/eslint.js --no-eslintrc -c .eslintrc.sql.ts.json --rulesdir ./build/lib/eslint --ext .ts ./src/sql", - "extensions-lint": "node --max_old_space_size=4095 ./node_modules/eslint/bin/eslint.js --rulesdir ./build/lib/eslint --ext .ts ./extensions" + "extensions-lint": "node --max_old_space_size=4095 ./node_modules/eslint/bin/eslint.js --rulesdir ./build/lib/eslint --ext .ts ./extensions", + "webview-generate-csp-hash": "npx github:apaatsio/csp-hash-from-html csp-hash ./src/vs/workbench/contrib/webview/browser/pre/index.html" }, "dependencies": { "@angular/animations": "~4.1.3", @@ -70,7 +71,6 @@ "@angular/router": "~4.1.3", "@microsoft/1ds-core-js": "^3.2.2", "@microsoft/1ds-post-js": "^3.2.2", - "@microsoft/applicationinsights-web": "^2.8.4", "@parcel/watcher": "2.0.5", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/ripgrep": "^1.14.2", @@ -105,19 +105,20 @@ "semver-umd": "^5.5.7", "slickgrid": "github:Microsoft/SlickGrid.ADS#2.3.43", "spdlog": "^0.13.0", - "tas-client-umd": "0.1.4", + "tas-client-umd": "0.1.6", "turndown": "^7.0.0", "turndown-plugin-gfm": "^1.0.2", "v8-inspect-profiler": "^0.0.22", "vscode-oniguruma": "1.6.1", + "vscode-policy-watcher": "^1.1.1", "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.25", - "xterm-addon-search": "0.9.0-beta.25", - "xterm-addon-serialize": "0.7.0-beta.12", + "xterm": "4.20.0-beta.20", + "xterm-addon-search": "0.10.0-beta.3", + "xterm-addon-serialize": "0.8.0-beta.3", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.29", + "xterm-addon-webgl": "0.13.0-beta.9", "xterm-headless": "4.19.0-beta.25", "yauzl": "^2.9.2", "yazl": "^2.4.3", @@ -147,7 +148,7 @@ "@types/sinon": "^10.0.2", "@types/sinon-test": "^2.4.2", "@types/trusted-types": "^1.0.6", - "@types/vscode-notebook-renderer": "^1.60.0", + "@types/vscode-notebook-renderer": "1.60.0", "@types/webpack": "^4.41.25", "@types/wicg-file-system-access": "^2020.9.5", "@types/windows-foreground-love": "^0.3.0", @@ -159,7 +160,7 @@ "@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/parser": "^5.10.0", "@vscode/telemetry-extractor": "^1.9.6", - "@vscode/test-web": "^0.0.22", + "@vscode/test-web": "^0.0.29", "ansi-colors": "^3.2.3", "asar": "^3.0.3", "chromium-pickle-js": "^0.2.0", @@ -167,18 +168,18 @@ "cookie": "^0.4.0", "copy-webpack-plugin": "^6.0.3", "cson-parser": "^1.3.3", - "css-loader": "^3.2.0", + "css-loader": "^3.6.0", "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", "electron": "19.1.8", "eslint": "8.7.0", "eslint-plugin-header": "3.1.1", - "eslint-plugin-jsdoc": "^19.1.0", + "eslint-plugin-jsdoc": "^39.3.2", "event-stream": "3.3.4", "fancy-log": "^1.3.3", "fast-plist": "0.1.2", - "file-loader": "^4.2.0", + "file-loader": "^5.1.0", "glob": "^5.0.13", "gulp": "^4.0.0", "gulp-atom-electron": "^1.33.0", @@ -200,7 +201,6 @@ "gulp-shell": "^0.6.5", "gulp-sourcemaps": "^3.0.0", "gulp-svgmin": "^4.1.0", - "gulp-tsb": "4.0.6", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "husky": "^0.13.1", @@ -231,7 +231,7 @@ "request": "^2.85.0", "rimraf": "^2.2.8", "sinon": "^11.1.1", - "sinon-test": "^3.1.0", + "sinon-test": "^3.1.3", "source-map": "0.6.1", "source-map-support": "^0.3.2", "style-loader": "^1.0.0", @@ -239,7 +239,7 @@ "ts-loader": "^9.2.7", "tsec": "0.1.4", "typemoq": "^0.3.2", - "typescript": "^4.7.0-dev.20220502", + "typescript": "^4.8.0-dev.20220518", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/remote/.yarnrc b/remote/.yarnrc index 290849a6e6..3a3fbdc335 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,4 +1,4 @@ disturl "http://nodejs.org/dist" -target "16.13.2" +target "16.14.2" runtime "node" build_from_source "true" diff --git a/remote/package.json b/remote/package.json index 5c75e805c5..51d4337d86 100755 --- a/remote/package.json +++ b/remote/package.json @@ -13,7 +13,6 @@ "@angular/router": "~4.1.3", "@microsoft/1ds-core-js": "^3.2.2", "@microsoft/1ds-post-js": "^3.2.2", - "@microsoft/applicationinsights-web": "^2.8.4", "@parcel/watcher": "2.0.5", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/ripgrep": "^1.14.2", @@ -46,16 +45,16 @@ "slickgrid": "github:Microsoft/SlickGrid.ADS#2.3.43", "turndown": "^7.0.0", "turndown-plugin-gfm": "^1.0.2", - "tas-client-umd": "0.1.4", + "tas-client-umd": "0.1.6", "vscode-oniguruma": "1.6.1", "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.25", - "xterm-addon-search": "0.9.0-beta.25", - "xterm-addon-serialize": "0.7.0-beta.12", + "xterm": "4.20.0-beta.20", + "xterm-addon-search": "0.10.0-beta.3", + "xterm-addon-serialize": "0.8.0-beta.3", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.29", + "xterm-addon-webgl": "0.13.0-beta.9", "xterm-headless": "4.19.0-beta.25", "yauzl": "^2.9.2", "yazl": "^2.4.3", diff --git a/remote/web/package.json b/remote/web/package.json index e63da3f37f..f1b07fbf72 100755 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -13,7 +13,6 @@ "@angular/router": "~4.1.3", "@microsoft/1ds-core-js": "^3.2.2", "@microsoft/1ds-post-js": "^3.2.2", - "@microsoft/applicationinsights-web": "^2.8.4", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/vscode-languagedetection": "1.0.21", "angular2-grid": "2.0.6", @@ -34,12 +33,12 @@ "slickgrid": "github:Microsoft/SlickGrid.ADS#2.3.43", "turndown": "^7.0.0", "turndown-plugin-gfm": "^1.0.2", - "tas-client-umd": "0.1.4", + "tas-client-umd": "0.1.6", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.25", - "xterm-addon-search": "0.9.0-beta.25", + "xterm": "4.20.0-beta.20", + "xterm-addon-search": "0.10.0-beta.3", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.29" + "xterm-addon-webgl": "0.13.0-beta.9" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index ce1a2f713b..804792c54d 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -42,112 +42,41 @@ resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.1.3.tgz#ddafd46ae7ccc8b1f74904ffb45f394e44625216" integrity sha512-i+1GMIvfM3OC6XX2kZf+tL36Nc4jhcMNOY6bOrmwlN8APl59I9KqdvywC5HnutkDctb8expiWTIuSKZQAc4AIA== -"@microsoft/1ds-core-js@3.2.6", "@microsoft/1ds-core-js@^3.2.2": - version "3.2.6" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.6.tgz#8a77909f89f991aa2f0b4ae8825c75042962e68e" - integrity sha512-6OpppYCEA+rXjcs2w0KnWji3Y6ZDx0wykY7ZL3QF68NS323C45GHSpkDpVRT/lDU6Xbau/PvQm2zTYAzLcperA== +"@microsoft/1ds-core-js@3.2.9", "@microsoft/1ds-core-js@^3.2.2": + version "3.2.9" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.9.tgz#8a26935966e4871d1f1e40d992828bdd52bba84e" + integrity sha512-3pCfM2TzHn3gU9pxHztduKcVRdb/nzruvPFfHPZD0IM0mb0h6TGo2isELF3CTMahTx50RAC51ojNIw2/7VRkOg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.6" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-core-js" "2.8.10" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" "@microsoft/1ds-post-js@^3.2.2": - version "3.2.6" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.6.tgz#cdfa74acfc3205c0a5b79925284d6d166aa43901" - integrity sha512-Zdyl3FU6kU/a7TlVVSTBZg+hSECTT65iI99FsjMOx88HudyVyk9M/0lVbA+FVXvGaxzmtBW6Lw0qRHYp4tBMSA== + version "3.2.9" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.9.tgz#07030f7455cb4ac8993e9b0bfa6c78ebfe25b499" + integrity sha512-D/RtqkQ2Nr4cuoGqmhi5QTmi3cBlxehIThJ1u3BaH9H/YkLNTKEcHZRWTXy14bXheCefNHciLuadg37G2Kekcg== dependencies: - "@microsoft/1ds-core-js" "3.2.6" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/1ds-core-js" "3.2.9" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-analytics-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.8.7.tgz#dea44f5bbfb150e12f256b0c45ba6756276df9b6" - integrity sha512-WEsbh3yOzjo1DnH6NyVS/W9uXEWJkb0mgOHTnq3An2nvyT7HPzd56hqRfyJsWR2KAU6hvZ09l7OK85AjkfEqJA== +"@microsoft/applicationinsights-core-js@2.8.10": + version "2.8.10" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.10.tgz#beb96a97a046ddb031d6adecf0d3143b635edf42" + integrity sha512-jQrufDW0+sV8fBhRvzIPNGiCC6dELH+Ug0DM5CfN9757TBqZJz8CSWyDjex39as8+jD0F/8HRU9QdmrVgq5vFg== dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-shims" "2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.7.tgz#365b29842229ba615badc5f9c3f3ebc5c6ce7ef1" - integrity sha512-Ee2zp6Ij/Btnc9P8bVBXUIa4/X8HH1hNYpL2ALGfhwbWOUxoR+1+0FZpj+/H9vf2PP/8eS5r28MCoksbGUH+8A== - dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" +"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" + integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-common@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.7.tgz#8ac4d63450e8108ac1ce96750d32e286935388f7" - integrity sha512-bODgacISioOVZIYho1QUADVZh4jjFRQhfVokHbFUgDsJ1NgfpDdNlv+idxH9oriBEuf9odWnOM9jfr3OMcVicA== - dependencies: - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-core-js@2.8.6": - version "2.8.6" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.6.tgz#4f0f9ad809aacfc96cb882139b69b3625519c51a" - integrity sha512-rL+ceda1Y6HaHBe1vIbNT/f5JGuHiD5Ydq+DoAfu56o13wyJu4sao3QKaabgaIM59pPO+3BMeGsK8NNUGYaT3w== - dependencies: - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-core-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.7.tgz#3c43517bc6fdf2da59aa90708cefcf936db75488" - integrity sha512-y7USumU+a/kbDeORr/dnDFAxcS5+KOFJSM5oUkS8tBu7CYcX1QAGF4X11gdH2NjrGgHjgxhERhYRa/YhwY1NOQ== - dependencies: - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-dependencies-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.8.7.tgz#fe7e52ec0a3f18d66cb6c43af529c05858e0cbde" - integrity sha512-lLRZPCi4Aq+sJThiO6QeFfZguuvZkH8VbceMz3jNF8S1KvPC0174eVcihMPDrjErPXGcquH1fKvJn3PionhZuw== - dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-properties-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.8.7.tgz#6ec96789c3add4e93a1511bfcd946859622355f3" - integrity sha512-wEa2XJ3PAymFLcOajl7KwhLS2JGFW3SbF2lYsGeP/efIJewTRbB1l+6QgbNWiOCvPd4cpiq2jRdrgKaQpJN37Q== - dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" - integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== - -"@microsoft/applicationinsights-web@^2.8.4": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.8.7.tgz#23f5891768b6da6e4cdcc1a8d0cdf1b7b1376b1e" - integrity sha512-m0GufymksDAOarHnUzQ72d0RIAS2faY9xNnwAb7J08qOuDRqgnKamDH5U6af6Tjla4N1Ql2nOGLqQYg88/QjrQ== - dependencies: - "@microsoft/applicationinsights-analytics-js" "2.8.7" - "@microsoft/applicationinsights-channel-js" "2.8.7" - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-dependencies-js" "2.8.7" - "@microsoft/applicationinsights-properties-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/dynamicproto-js@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" - integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== +"@microsoft/dynamicproto-js@^1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" + integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== "@vscode/iconv-lite-umd@0.7.0": version "0.7.0" @@ -298,9 +227,9 @@ has-flag@^3.0.0: integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== html-to-image@^1.6.2: - version "1.9.0" - resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-1.9.0.tgz#cb49bf9f4b37376771c85cfdd65863ae9420b268" - integrity sha512-9gaDCIYg62Ek07F2pBk76AHgYZ2gxq2YALU7rK3gNCqXuhu6cWzsOQqM7qGbjZiOzxGzrU1deDqZpAod2NEwbA== + version "1.11.11" + resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea" + integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA== htmlparser2@^3.9.0: version "3.10.1" @@ -391,9 +320,9 @@ postcss@^6.0.14: supports-color "^5.4.0" readable-stream@^3.1.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.1" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" + integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -473,10 +402,10 @@ symbol-observable@^1.0.1: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -tas-client-umd@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.4.tgz#49db4130dd63a8342fabf77185a740fc6a7bea80" - integrity sha512-1hFqJeLD3ryNikniIaO7TItlXhS5vx7bJ+wbPDf8o+IifgwwOWK2ARisdEM9SnJd0ccfcwNPG6Po+RiKn5L2hg== +tas-client-umd@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.6.tgz#a0cf70a68f50d406773457630666224f0eb545a6" + integrity sha512-eOz5IK4cuNmSZI9QlqlT0FdvgfnnHDB6rjqleFaYAbzYE4RdJzYNiM28zFIXgmOVEgESvfabMFxG8WX5M4z3HA== turndown-plugin-gfm@^1.0.2: version "1.0.2" @@ -510,22 +439,22 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-search@0.9.0-beta.25: - version "0.9.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.25.tgz#c0923197f64793821ae8b4dfd30e19b411c8e7a7" - integrity sha512-Z6Gd6JN1jcUyQ1iB9yBtPBzNsnPv6DXAxNnJXqFvIznfx0FmXx85FL5SunsH0/uoXre5UwqI+SWc/ON3CkKeUQ== +xterm-addon-search@0.10.0-beta.3: + version "0.10.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.10.0-beta.3.tgz#5194434d86105637c71f6f20139a9d0b5c1a956a" + integrity sha512-UeGm/ymnp7HUYJJtsP0D+bljOWbdk3MctcLJ+0jv8AmU6YlAzJFtouvYSQrD5SAMyht5CRsvjzFgqic9X02JYg== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.29: - version "0.12.0-beta.29" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.29.tgz#7a508595c4521d14d7ed4315a121f9e3f230a0f0" - integrity sha512-NcZBsD0ar3ZpQX070hDIsyEBl/StRMNu6U+9crNpiD2rQVfkM1vcWkOv31Zlj3eu6/f8z5aStyZLRMCGFwiRbA== +xterm-addon-webgl@0.13.0-beta.9: + version "0.13.0-beta.9" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.9.tgz#66a9ac142ae347d0548abbf4e66bb2f35f415adb" + integrity sha512-x1o1tpCqIsICvhcRsZs+BLcwUIdizYS2G4TIH0KBnUDiSN+oSqpVBQNG8qKg56xbK8WtpdbQ9dLB7JR2W5cX0g== -xterm@4.19.0-beta.25: - version "4.19.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.25.tgz#38f92d0fef1cfdb290ef8994449a04fa1a8c90a7" - integrity sha512-pDiMWKN1Cj4+X/K9Xegp0SA0ZDEGVqiq7RPSy8oZO2wo2rze1BF20PAZb3/RSp30eY5WyOKilKnck4yNOsPzHw== +xterm@4.20.0-beta.20: + version "4.20.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.20.tgz#2979a31839f7b8ee3ffe4f063b40c02facdb0fed" + integrity sha512-ltDtTquH+33tXQPFSDqenbgz6LkvIob6l6Rac85L4aX5Ve7P3ubVLrq+lTFJGQn3iiwGqNmnE1t1EUuGhxsXcQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index 314ec16f45..ab7aed8c7a 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -42,112 +42,41 @@ resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.1.3.tgz#ddafd46ae7ccc8b1f74904ffb45f394e44625216" integrity sha512-i+1GMIvfM3OC6XX2kZf+tL36Nc4jhcMNOY6bOrmwlN8APl59I9KqdvywC5HnutkDctb8expiWTIuSKZQAc4AIA== -"@microsoft/1ds-core-js@3.2.6", "@microsoft/1ds-core-js@^3.2.2": - version "3.2.6" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.6.tgz#8a77909f89f991aa2f0b4ae8825c75042962e68e" - integrity sha512-6OpppYCEA+rXjcs2w0KnWji3Y6ZDx0wykY7ZL3QF68NS323C45GHSpkDpVRT/lDU6Xbau/PvQm2zTYAzLcperA== +"@microsoft/1ds-core-js@3.2.9", "@microsoft/1ds-core-js@^3.2.2": + version "3.2.9" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.9.tgz#8a26935966e4871d1f1e40d992828bdd52bba84e" + integrity sha512-3pCfM2TzHn3gU9pxHztduKcVRdb/nzruvPFfHPZD0IM0mb0h6TGo2isELF3CTMahTx50RAC51ojNIw2/7VRkOg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.6" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-core-js" "2.8.10" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" "@microsoft/1ds-post-js@^3.2.2": - version "3.2.6" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.6.tgz#cdfa74acfc3205c0a5b79925284d6d166aa43901" - integrity sha512-Zdyl3FU6kU/a7TlVVSTBZg+hSECTT65iI99FsjMOx88HudyVyk9M/0lVbA+FVXvGaxzmtBW6Lw0qRHYp4tBMSA== + version "3.2.9" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.9.tgz#07030f7455cb4ac8993e9b0bfa6c78ebfe25b499" + integrity sha512-D/RtqkQ2Nr4cuoGqmhi5QTmi3cBlxehIThJ1u3BaH9H/YkLNTKEcHZRWTXy14bXheCefNHciLuadg37G2Kekcg== dependencies: - "@microsoft/1ds-core-js" "3.2.6" - "@microsoft/applicationinsights-shims" "^2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/1ds-core-js" "3.2.9" + "@microsoft/applicationinsights-shims" "^2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-analytics-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.8.7.tgz#dea44f5bbfb150e12f256b0c45ba6756276df9b6" - integrity sha512-WEsbh3yOzjo1DnH6NyVS/W9uXEWJkb0mgOHTnq3An2nvyT7HPzd56hqRfyJsWR2KAU6hvZ09l7OK85AjkfEqJA== +"@microsoft/applicationinsights-core-js@2.8.10": + version "2.8.10" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.10.tgz#beb96a97a046ddb031d6adecf0d3143b635edf42" + integrity sha512-jQrufDW0+sV8fBhRvzIPNGiCC6dELH+Ug0DM5CfN9757TBqZJz8CSWyDjex39as8+jD0F/8HRU9QdmrVgq5vFg== dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" + "@microsoft/applicationinsights-shims" "2.0.2" + "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.7.tgz#365b29842229ba615badc5f9c3f3ebc5c6ce7ef1" - integrity sha512-Ee2zp6Ij/Btnc9P8bVBXUIa4/X8HH1hNYpL2ALGfhwbWOUxoR+1+0FZpj+/H9vf2PP/8eS5r28MCoksbGUH+8A== - dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" +"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" + integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-common@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.7.tgz#8ac4d63450e8108ac1ce96750d32e286935388f7" - integrity sha512-bODgacISioOVZIYho1QUADVZh4jjFRQhfVokHbFUgDsJ1NgfpDdNlv+idxH9oriBEuf9odWnOM9jfr3OMcVicA== - dependencies: - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-core-js@2.8.6": - version "2.8.6" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.6.tgz#4f0f9ad809aacfc96cb882139b69b3625519c51a" - integrity sha512-rL+ceda1Y6HaHBe1vIbNT/f5JGuHiD5Ydq+DoAfu56o13wyJu4sao3QKaabgaIM59pPO+3BMeGsK8NNUGYaT3w== - dependencies: - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-core-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.7.tgz#3c43517bc6fdf2da59aa90708cefcf936db75488" - integrity sha512-y7USumU+a/kbDeORr/dnDFAxcS5+KOFJSM5oUkS8tBu7CYcX1QAGF4X11gdH2NjrGgHjgxhERhYRa/YhwY1NOQ== - dependencies: - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-dependencies-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.8.7.tgz#fe7e52ec0a3f18d66cb6c43af529c05858e0cbde" - integrity sha512-lLRZPCi4Aq+sJThiO6QeFfZguuvZkH8VbceMz3jNF8S1KvPC0174eVcihMPDrjErPXGcquH1fKvJn3PionhZuw== - dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-properties-js@2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.8.7.tgz#6ec96789c3add4e93a1511bfcd946859622355f3" - integrity sha512-wEa2XJ3PAymFLcOajl7KwhLS2JGFW3SbF2lYsGeP/efIJewTRbB1l+6QgbNWiOCvPd4cpiq2jRdrgKaQpJN37Q== - dependencies: - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/applicationinsights-shims@2.0.1", "@microsoft/applicationinsights-shims@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd" - integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ== - -"@microsoft/applicationinsights-web@^2.8.4": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.8.7.tgz#23f5891768b6da6e4cdcc1a8d0cdf1b7b1376b1e" - integrity sha512-m0GufymksDAOarHnUzQ72d0RIAS2faY9xNnwAb7J08qOuDRqgnKamDH5U6af6Tjla4N1Ql2nOGLqQYg88/QjrQ== - dependencies: - "@microsoft/applicationinsights-analytics-js" "2.8.7" - "@microsoft/applicationinsights-channel-js" "2.8.7" - "@microsoft/applicationinsights-common" "2.8.7" - "@microsoft/applicationinsights-core-js" "2.8.7" - "@microsoft/applicationinsights-dependencies-js" "2.8.7" - "@microsoft/applicationinsights-properties-js" "2.8.7" - "@microsoft/applicationinsights-shims" "2.0.1" - "@microsoft/dynamicproto-js" "^1.1.6" - -"@microsoft/dynamicproto-js@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc" - integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg== +"@microsoft/dynamicproto-js@^1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" + integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== "@parcel/watcher@2.0.5": version "2.0.5" @@ -504,9 +433,9 @@ has-flag@^3.0.0: integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== html-to-image@^1.6.2: - version "1.9.0" - resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-1.9.0.tgz#cb49bf9f4b37376771c85cfdd65863ae9420b268" - integrity sha512-9gaDCIYg62Ek07F2pBk76AHgYZ2gxq2YALU7rK3gNCqXuhu6cWzsOQqM7qGbjZiOzxGzrU1deDqZpAod2NEwbA== + version "1.11.11" + resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea" + integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA== htmlparser2@^3.9.0: version "3.10.1" @@ -561,10 +490,10 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -ip@^1.1.5: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" - integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== jquery@3.5.0: version "3.5.0" @@ -627,9 +556,9 @@ mimic-response@^3.1.0: integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" @@ -653,10 +582,10 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -nan@^2.13.2, nan@^2.14.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== +nan@^2.13.2, nan@^2.14.0, nan@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== napi-build-utils@^1.0.1: version "1.0.2" @@ -676,9 +605,9 @@ ng2-charts@^1.6.0: chart.js "^2.6.0" node-abi@^3.3.0: - version "3.22.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.22.0.tgz#00b8250e86a0816576258227edbce7bbe0039362" - integrity sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w== + version "3.33.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.33.0.tgz#8b23a0cec84e1c5f5411836de6a9b84bccf26e7f" + integrity sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog== dependencies: semver "^7.3.5" @@ -693,9 +622,9 @@ node-addon-api@^4.3.0: integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-pty@0.11.0-beta11: version "0.11.0-beta11" @@ -777,9 +706,9 @@ rc@^1.2.7: strip-json-comments "~2.0.1" readable-stream@^3.1.1, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.1" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" + integrity sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -829,9 +758,9 @@ semver@^5.3.0, semver@^5.4.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -873,11 +802,11 @@ socks-proxy-agent@^5.0.0: socks "^2.3.3" socks@^2.3.3: - version "2.6.2" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" - integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== dependencies: - ip "^1.1.5" + ip "^2.0.0" smart-buffer "^4.2.0" source-map@^0.6.1: @@ -886,13 +815,13 @@ source-map@^0.6.1: integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdlog@^0.13.0: - version "0.13.6" - resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.13.6.tgz#26b2e13d46cbf8f2334c12ba2a8cc82de5a28f02" - integrity sha512-iGqDoA88G3Rv3lkbVQglTulp3nv12FzND6LDC7cOZ+OoFvWnXVb3+Ebhed60oZ6+IWWGwDtjXK6ympwr7C1XmQ== + version "0.13.7" + resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.13.7.tgz#719a972be103e473770202e37dca1c9d80502180" + integrity sha512-DiWxvyHuDJKfNuanSnizY2pmqZGaSHej3xpOD4LQ+kkT3oLWpCXI6VRFDnziyXBQKCl8kmyaYnOu9QBhf1WEXQ== dependencies: bindings "^1.5.0" mkdirp "^0.5.5" - nan "^2.14.0" + nan "^2.17.0" srcset@^1.0.0: version "1.0.0" @@ -905,7 +834,7 @@ srcset@^1.0.0: stack-chain@^1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" - integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== + integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= string_decoder@^1.1.1: version "1.3.0" @@ -952,15 +881,15 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tas-client-umd@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.4.tgz#49db4130dd63a8342fabf77185a740fc6a7bea80" - integrity sha512-1hFqJeLD3ryNikniIaO7TItlXhS5vx7bJ+wbPDf8o+IifgwwOWK2ARisdEM9SnJd0ccfcwNPG6Po+RiKn5L2hg== +tas-client-umd@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.6.tgz#a0cf70a68f50d406773457630666224f0eb545a6" + integrity sha512-eOz5IK4cuNmSZI9QlqlT0FdvgfnnHDB6rjqleFaYAbzYE4RdJzYNiM28zFIXgmOVEgESvfabMFxG8WX5M4z3HA== tslib@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== tunnel-agent@^0.6.0: version "0.6.0" @@ -1039,35 +968,35 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-search@0.9.0-beta.25: - version "0.9.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.25.tgz#c0923197f64793821ae8b4dfd30e19b411c8e7a7" - integrity sha512-Z6Gd6JN1jcUyQ1iB9yBtPBzNsnPv6DXAxNnJXqFvIznfx0FmXx85FL5SunsH0/uoXre5UwqI+SWc/ON3CkKeUQ== +xterm-addon-search@0.10.0-beta.3: + version "0.10.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.10.0-beta.3.tgz#5194434d86105637c71f6f20139a9d0b5c1a956a" + integrity sha512-UeGm/ymnp7HUYJJtsP0D+bljOWbdk3MctcLJ+0jv8AmU6YlAzJFtouvYSQrD5SAMyht5CRsvjzFgqic9X02JYg== -xterm-addon-serialize@0.7.0-beta.12: - version "0.7.0-beta.12" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.12.tgz#4f845d8b1a9f9b7ae3f910455ce8c58b041babc7" - integrity sha512-b4Ug0B/RSJMux+KAcp+PXVqubVyXjN1yCQw1FOkgVYTpmd9AH/X+EcxKml5Lz8DsKmsXqfD9AlV3WpEeT+OtMw== +xterm-addon-serialize@0.8.0-beta.3: + version "0.8.0-beta.3" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.8.0-beta.3.tgz#47ade3fedacbb75bd26e63cfe0120586623e0e4f" + integrity sha512-gvfempZCYuAhLqN4O6fA2TuoavPjOxFKlh8hLcOzPackiLUhwKr1jQpDXcnq8VgqUiGgb+XNZpPEbI0Q7EhTgA== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.29: - version "0.12.0-beta.29" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.29.tgz#7a508595c4521d14d7ed4315a121f9e3f230a0f0" - integrity sha512-NcZBsD0ar3ZpQX070hDIsyEBl/StRMNu6U+9crNpiD2rQVfkM1vcWkOv31Zlj3eu6/f8z5aStyZLRMCGFwiRbA== +xterm-addon-webgl@0.13.0-beta.9: + version "0.13.0-beta.9" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.9.tgz#66a9ac142ae347d0548abbf4e66bb2f35f415adb" + integrity sha512-x1o1tpCqIsICvhcRsZs+BLcwUIdizYS2G4TIH0KBnUDiSN+oSqpVBQNG8qKg56xbK8WtpdbQ9dLB7JR2W5cX0g== xterm-headless@4.19.0-beta.25: version "4.19.0-beta.25" resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.25.tgz#a0a1b59f386c44458f06b8ced64e3567371cc983" integrity sha512-UswSgymk3g9i6XTpFAasnqqIvWhi+AEWT+iO3kkjII6ll+dYEQgeZAv92EnCmeRHp11u5TP+IBAo8jy+aTYbtA== -xterm@4.19.0-beta.25: - version "4.19.0-beta.25" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.25.tgz#38f92d0fef1cfdb290ef8994449a04fa1a8c90a7" - integrity sha512-pDiMWKN1Cj4+X/K9Xegp0SA0ZDEGVqiq7RPSy8oZO2wo2rze1BF20PAZb3/RSp30eY5WyOKilKnck4yNOsPzHw== +xterm@4.20.0-beta.20: + version "4.20.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.20.tgz#2979a31839f7b8ee3ffe4f063b40c02facdb0fed" + integrity sha512-ltDtTquH+33tXQPFSDqenbgz6LkvIob6l6Rac85L4aX5Ve7P3ubVLrq+lTFJGQn3iiwGqNmnE1t1EUuGhxsXcQ== yallist@^4.0.0: version "4.0.0" @@ -1090,8 +1019,8 @@ yazl@^2.4.3: buffer-crc32 "~0.2.3" zone.js@^0.11.4: - version "0.11.6" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.6.tgz#c7cacfc298fe24bb585329ca04a44d9e2e840e74" - integrity sha512-umJqFtKyZlPli669gB1gOrRE9hxUUGkZr7mo878z+NEBJZZixJkKeVYfnoLa7g25SseUDc92OZrMKKHySyJrFg== + version "0.11.8" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.8.tgz#40dea9adc1ad007b5effb2bfed17f350f1f46a21" + integrity sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA== dependencies: tslib "^2.3.0" diff --git a/resources/darwin/bin/code.sh b/resources/darwin/bin/code.sh index e439786a19..7fc9d3917a 100755 --- a/resources/darwin/bin/code.sh +++ b/resources/darwin/bin/code.sh @@ -3,6 +3,15 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Source EULA. See License.txt in the project root for license information. +# when run in remote terminal, use the remote cli +if [ -n "$VSCODE_IPC_HOOK_CLI" ]; then + REMOTE_CLI="$(which -a '@@APPNAME@@' | grep /remote-cli/)" + if [ -n "$REMOTE_CLI" ]; then + "$REMOTE_CLI" "$@" + exit $? + fi +fi + function app_realpath() { SOURCE=$1 while [ -h "$SOURCE" ]; do diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index a9641ca948..5ac0698cca 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -3,9 +3,18 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the Source EULA. See License.txt in the project root for license information. +# when run in remote terminal, use the remote cli +if [ -n "$VSCODE_IPC_HOOK_CLI" ]; then + REMOTE_CLI="$(which -a '@@APPNAME@@' | grep /remote-cli/)" + if [ -n "$REMOTE_CLI" ]; then + "$REMOTE_CLI" "$@" + exit $? + fi +fi + # test that VSCode wasn't installed inside WSL if grep -qi Microsoft /proc/version && [ -z "$DONT_PROMPT_WSL_INSTALL" ]; then - echo "To use @@PRODNAME@@ with the Windows Subsystem for Linux, please install @@PRODNAME@@ in Windows and uninstall the Linux version in WSL. You can then use the \`@@NAME@@\` command in a WSL terminal just as you would in a normal command prompt." 1>&2 + echo "To use @@PRODNAME@@ with the Windows Subsystem for Linux, please install @@PRODNAME@@ in Windows and uninstall the Linux version in WSL. You can then use the \`@@APPNAME@@\` command in a WSL terminal just as you would in a normal command prompt." 1>&2 printf "Do you want to continue anyway? [y/N] " 1>&2 read -r YN YN=$(printf '%s' "$YN" | tr '[:upper:]' '[:lower:]') @@ -44,11 +53,11 @@ else VSCODE_PATH="$(dirname "$(readlink -f "$0")")/.." else # else use the standard install location - VSCODE_PATH="/usr/share/@@NAME@@" + VSCODE_PATH="/usr/share/@@APPNAME@@" fi fi -ELECTRON="$VSCODE_PATH/@@NAME@@" +ELECTRON="$VSCODE_PATH/@@APPNAME@@" CLI="$VSCODE_PATH/resources/app/out/cli.js" ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --ms-enable-electron-run-as-node "$@" exit $? diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template index 642b5fe439..96d269162a 100644 --- a/resources/linux/debian/control.template +++ b/resources/linux/debian/control.template @@ -1,7 +1,8 @@ Package: @@NAME@@ Version: @@VERSION@@ Section: devel -Depends: libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1, libgbm1 +Depends: @@DEPENDS@@ +Recommends: @@RECOMMENDS@@ Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation diff --git a/resources/linux/rpm/code.spec.template b/resources/linux/rpm/code.spec.template index 5b7eadb8c9..00ddb6fdf0 100644 --- a/resources/linux/rpm/code.spec.template +++ b/resources/linux/rpm/code.spec.template @@ -11,6 +11,8 @@ Icon: @@NAME@@.xpm Requires: @@DEPENDENCIES@@ AutoReq: 0 +%global __provides_exclude_from ^%{_datadir}/@@NAME@@/.*\\.so.*$ + %description Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle. See https://code.visualstudio.com/docs/setup/linux for installation instructions and FAQ. @@ -19,6 +21,7 @@ Visual Studio Code is a new choice of tool that combines the simplicity of a cod %define _build_id_links none %install +mkdir -p %{buildroot}/usr/bin mkdir -p %{buildroot}/usr/share/@@NAME@@ mkdir -p %{buildroot}/usr/share/applications mkdir -p %{buildroot}/usr/share/pixmaps @@ -32,6 +35,7 @@ cp -r usr/share/mime/packages/@@NAME@@-workspace.xml %{buildroot}/usr/share/mime cp -r usr/share/pixmaps/@@ICON@@.png %{buildroot}/usr/share/pixmaps cp usr/share/bash-completion/completions/@@NAME@@ %{buildroot}/usr/share/bash-completion/completions/@@NAME@@ cp usr/share/zsh/site-functions/_@@NAME@@ %{buildroot}/usr/share/zsh/site-functions/_@@NAME@@ +ln -s ../share/@@NAME@@/bin/@@NAME@@ %{buildroot}/usr/bin/@@NAME@@ %post # Remove the legacy bin command if this is the stable build @@ -39,9 +43,6 @@ if [ "@@NAME@@" = "code" ]; then rm -f /usr/local/bin/code fi -# Symlink bin command to /usr/bin -ln -sf /usr/share/@@NAME@@/bin/@@NAME@@ %{_bindir}/@@NAME@@ - # Register yum repository # TODO: #229: Enable once the yum repository is signed #if [ "@@NAME@@" != "code-oss" ]; then @@ -56,10 +57,6 @@ ln -sf /usr/share/@@NAME@@/bin/@@NAME@@ %{_bindir}/@@NAME@@ update-mime-database /usr/share/mime &> /dev/null || : %postun -if [ $1 = 0 ]; then - rm -f /usr/bin/@@NAME@@ -fi - # Update mimetype database for removed workspace mimetype update-mime-database /usr/share/mime &> /dev/null || : @@ -67,6 +64,7 @@ update-mime-database /usr/share/mime &> /dev/null || : %defattr(-,root,root) %attr(4755, root, root) /usr/share/@@NAME@@/chrome-sandbox +/usr/bin/@@NAME@@ /usr/share/@@NAME@@/ /usr/share/applications/@@NAME@@.desktop /usr/share/applications/@@NAME@@-url-handler.desktop diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index 8bbad497ac..fc775b6554 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -58,11 +58,9 @@ apps: command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ --no-sandbox common-id: @@NAME@@.desktop environment: - DISABLE_WAYLAND: 1 GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas url-handler: command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ --open-url --no-sandbox environment: - DISABLE_WAYLAND: 1 GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas diff --git a/resources/win32/policies/Code.admx b/resources/win32/policies/Code.admx deleted file mode 100644 index 916f503b78..0000000000 --- a/resources/win32/policies/Code.admx +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - none - - - - - manual - - - - - start - - - - - default - - - - - - - diff --git a/resources/win32/policies/en-US/Code.adml b/resources/win32/policies/en-US/Code.adml deleted file mode 100644 index f79b1778ec..0000000000 --- a/resources/win32/policies/en-US/Code.adml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - Code - OSS 1.67 or later - Code - OSS - Update - Update Mode - Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service. - Disable updates. - Disable automatic background update checks. Updates will be available if you manually check for updates. - Check for updates only on startup. Disable automatic background update checks. - Enable automatic update checks. Code will check for updates automatically and periodically. - - - - - - - - diff --git a/scripts/test-remote-integration.bat b/scripts/test-remote-integration.bat index 1c04b2e392..a7d0719205 100644 --- a/scripts/test-remote-integration.bat +++ b/scripts/test-remote-integration.bat @@ -53,10 +53,10 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( :: Run from a built: need to compile all test extensions :: because we run extension tests from their source folders :: and the build bundles extensions into .build webpacked - call yarn gulp compile-extension:vscode-api-tests^ - compile-extension:microsoft-authentication^ - compile-extension:github-authentication^ - compile-extension:vscode-test-resolver + :: call yarn gulp compile-extension:vscode-api-tests^ + :: compile-extension:microsoft-authentication^ + :: compile-extension:github-authentication^ + :: compile-extension:vscode-test-resolver :: Configuration for more verbose output set VSCODE_CLI=1 diff --git a/scripts/test-remote-integration.sh b/scripts/test-remote-integration.sh index e2212317d3..6d883f5321 100755 --- a/scripts/test-remote-integration.sh +++ b/scripts/test-remote-integration.sh @@ -47,16 +47,17 @@ else # Run from a built: need to compile all test extensions # because we run extension tests from their source folders # and the build bundles extensions into .build webpacked - yarn gulp compile-extension:vscode-api-tests \ - compile-extension:vscode-test-resolver \ - compile-extension:markdown-language-features \ - compile-extension:typescript-language-features \ - compile-extension:emmet \ - compile-extension:git \ - compile-extension:ipynb \ - compile-extension:microsoft-authentication \ - compile-extension:github-authentication \ - compile-extension-media + # yarn gulp compile-extension:vscode-api-tests \ + # compile-extension:vscode-test-resolver \ + # compile-extension:markdown-language-features \ + # compile-extension:typescript-language-features \ + # compile-extension:emmet \ + # compile-extension:git \ + # compile-extension:ipynb \ + # compile-extension:configuration-editing \ + # compile-extension:microsoft-authentication \ + # compile-extension:github-authentication \ + # compile-extension-media # Configuration for more verbose output export VSCODE_CLI=1 @@ -132,6 +133,11 @@ echo "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS --folder-uri=$AUTHORITY$(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$REMOTE_VSCODE/ipynb --extensionTestsPath=$REMOTE_VSCODE/ipynb/out/test $API_TESTS_EXTRA_ARGS $EXTRA_INTEGRATION_TEST_ARGUMENTS kill_app +echo +echo "### Configuration editing tests" +echo +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS --folder-uri=$AUTHORITY$(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$REMOTE_VSCODE/configuration-editing --extensionTestsPath=$REMOTE_VSCODE/configuration-editing/out/test $API_TESTS_EXTRA_ARGS $EXTRA_INTEGRATION_TEST_ARGUMENTS +kill_app # Cleanup diff --git a/scripts/test-web-integration.bat b/scripts/test-web-integration.bat index 99bb16b7d5..7be27c00b3 100644 --- a/scripts/test-web-integration.bat +++ b/scripts/test-web-integration.bat @@ -25,12 +25,13 @@ if "%VSCODE_REMOTE_SERVER_PATH%"=="" ( :: Run from a built: need to compile all test extensions :: because we run extension tests from their source folders :: and the build bundles extensions into .build webpacked - call yarn gulp compile-extension:vscode-api-tests^ - compile-extension:markdown-language-features^ - compile-extension:typescript-language-features^ - compile-extension:emmet^ - compile-extension:git^ - compile-extension-media + :: call yarn gulp compile-extension:vscode-api-tests^ + :: compile-extension:markdown-language-features^ + :: compile-extension:typescript-language-features^ + :: compile-extension:emmet^ + :: compile-extension:configuration-editing^ + :: compile-extension:git^ + :: compile-extension-media ) if not exist ".\test\integration\browser\out\index.js" ( @@ -70,3 +71,10 @@ set GITWORKSPACE=%TEMPDIR%\git-%RANDOM% mkdir %GITWORKSPACE% call node .\test\integration\browser\out\index.js --workspacePath=%GITWORKSPACE% --extensionDevelopmentPath=.\extensions\git --extensionTestsPath=.\extensions\git\out\test %* if %errorlevel% neq 0 exit /b %errorlevel% + +echo. +echo ### Configuration editing tests +set CFWORKSPACE=%TEMPDIR%\git-%RANDOM% +mkdir %CFWORKSPACE% +call node .\test\integration\browser\out\index.js --workspacePath=%CFWORKSPACE% --extensionDevelopmentPath=.\extensions\configuration-editing --extensionTestsPath=.\extensions\configuration-editing\out\test %* +if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/scripts/test-web-integration.sh b/scripts/test-web-integration.sh index 8f05929fdc..95278eec0e 100755 --- a/scripts/test-web-integration.sh +++ b/scripts/test-web-integration.sh @@ -19,13 +19,14 @@ else # Run from a built: need to compile all test extensions # because we run extension tests from their source folders # and the build bundles extensions into .build webpacked - yarn gulp compile-extension:vscode-api-tests \ - compile-extension:markdown-language-features \ - compile-extension:typescript-language-features \ - compile-extension:emmet \ - compile-extension:git \ - compile-extension:ipynb \ - compile-extension-media + # yarn gulp compile-extension:vscode-api-tests \ + # compile-extension:markdown-language-features \ + # compile-extension:typescript-language-features \ + # compile-extension:emmet \ + # compile-extension:git \ + # compile-extension:ipynb \ + # compile-extension:configuration-editing \ + # compile-extension-media fi if [ ! -e 'test/integration/browser/out/index.js' ];then @@ -70,3 +71,8 @@ echo "### Ipynb tests" echo node test/integration/browser/out/index.js --workspacePath $(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/ipynb --extensionTestsPath=$ROOT/extensions/ipynb/out/test "$@" +echo +echo "### Configuration editing tests" +echo +node test/integration/browser/out/index.js --workspacePath $(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/configuration-editing --extensionTestsPath=$ROOT/extensions/configuration-editing/out/test "$@" + diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 6d083053df..2844cde913 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -88,15 +88,6 @@ function pipeLoggingToParent() { } } - // Add the stack trace as payload if we are told so. We remove the message and the 2 top frames - // to start the stacktrace where the console message was being written - if (process.env['VSCODE_LOG_STACK'] === 'true') { - const stack = new Error().stack; - if (stack) { - argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); - } - } - try { const res = JSON.stringify(argsArray, function (key, value) { @@ -155,13 +146,8 @@ function pipeLoggingToParent() { safeSend({ type: '__$console', severity, arguments: args }); } - let isMakingConsoleCall = false; - /** - * Wraps a console message so that it is transmitted to the renderer. If - * native logging is turned on, the original console message will be written - * as well. This is needed since the console methods are "magic" in V8 and - * are the only methods that allow later introspection of logged variables. + * Wraps a console message so that it is transmitted to the renderer. * * The wrapped property is not defined with `writable: false` to avoid * throwing errors, but rather a no-op setting. See https://github.com/microsoft/vscode-extension-telemetry/issues/88 @@ -170,26 +156,10 @@ function pipeLoggingToParent() { * @param {'log' | 'warn' | 'error'} severity */ function wrapConsoleMethod(method, severity) { - if (process.env['VSCODE_LOG_NATIVE'] === 'true') { - const original = console[method]; - const stream = method === 'error' || method === 'warn' ? process.stderr : process.stdout; - Object.defineProperty(console, method, { - set: () => { }, - get: () => function () { - safeSendConsoleMessage(severity, safeToArray(arguments)); - isMakingConsoleCall = true; - stream.write('\nSTART_NATIVE_LOG\n'); - original.apply(console, arguments); - stream.write('\nEND_NATIVE_LOG\n'); - isMakingConsoleCall = false; - }, - }); - } else { - Object.defineProperty(console, method, { - set: () => { }, - get: () => function () { safeSendConsoleMessage(severity, safeToArray(arguments)); }, - }); - } + Object.defineProperty(console, method, { + set: () => { }, + get: () => function () { safeSendConsoleMessage(severity, safeToArray(arguments)); }, + }); } /** @@ -211,13 +181,11 @@ function pipeLoggingToParent() { Object.defineProperty(stream, 'write', { set: () => { }, get: () => (chunk, encoding, callback) => { - if (!isMakingConsoleCall) { - buf += chunk.toString(encoding); - const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n'); - if (eol !== -1) { - console[severity](buf.slice(0, eol)); - buf = buf.slice(eol + 1); - } + buf += chunk.toString(encoding); + const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n'); + if (eol !== -1) { + console[severity](buf.slice(0, eol)); + buf = buf.slice(eol + 1); } original.call(stream, chunk, encoding, callback); @@ -231,7 +199,7 @@ function pipeLoggingToParent() { wrapConsoleMethod('log', 'log'); wrapConsoleMethod('warn', 'warn'); wrapConsoleMethod('error', 'error'); - } else if (process.env['VSCODE_LOG_NATIVE'] !== 'true') { + } else { console.log = function () { /* ignore */ }; console.warn = function () { /* ignore */ }; console.info = function () { /* ignore */ }; @@ -273,17 +241,13 @@ function listenForMessagePort() { // We need to listen for the 'port' event as soon as possible, // otherwise we might miss the event. But we should also be // prepared in case the event arrives late. - // @ts-ignore - if (process.parentPort) { - // @ts-ignore - process.parentPort.on('message', (e) => { - if (global.vscodePortsCallback) { - global.vscodePortsCallback(e.ports); - } else { - global.vscodePorts = e.ports; - } - }); - } + process.on('port', (e) => { + if (global.vscodePortsCallback) { + global.vscodePortsCallback(e.ports); + } else { + global.vscodePorts = e.ports; + } + }); } //#endregion diff --git a/src/bootstrap.js b/src/bootstrap.js index 215994c403..31ff7e5b8d 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -205,7 +205,7 @@ } /** - * @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process} + * @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process | undefined} */ function safeProcess() { const sandboxGlobals = safeSandboxGlobals(); diff --git a/src/buildfile.js b/src/buildfile.js index d0ee3c0464..e444470d78 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -32,16 +32,17 @@ exports.base = [ { name: 'vs/editor/common/services/editorSimpleWorker', include: ['vs/base/common/worker/simpleWorker'], - prepend: ['vs/loader.js', 'vs/nls.js'], - append: ['vs/base/worker/workerMain'], + exclude: ['vs/nls'], + prepend: [ + { path: 'vs/loader.js' }, + { path: 'vs/nls.js', amdModuleId: 'vs/nls' }, + { path: 'vs/base/worker/workerMain.js' } + ], dest: 'vs/base/worker/workerMain.js' }, { name: 'vs/base/common/worker/simpleWorker', - }, - { - name: 'vs/platform/extensions/node/extensionHostStarterWorker', - exclude: ['vs/base/common/worker/simpleWorker'] + exclude: ['vs/nls'], } ]; diff --git a/src/main.js b/src/main.js index 9877718224..b5647f8ad4 100644 --- a/src/main.js +++ b/src/main.js @@ -160,9 +160,6 @@ function configureCommandlineSwitchesSync(cliArgs) { // alias from us for --disable-gpu 'disable-hardware-acceleration', - // provided by Electron - 'disable-color-correct-rendering', - // override for the color profile to use 'force-color-profile' ]; @@ -254,9 +251,7 @@ function readArgvConfigSync() { // Fallback to default if (!argvConfig) { - argvConfig = { - 'disable-color-correct-rendering': true // Force pre-Chrome-60 color profile handling (for https://github.com/microsoft/vscode/issues/51791) - }; + argvConfig = {}; } return argvConfig; @@ -286,11 +281,7 @@ function createDefaultArgvConfigSync(argvConfigPath) { '{', ' // Use software rendering instead of hardware accelerated rendering.', ' // This can help in cases where you see rendering issues in VS Code.', - ' // "disable-hardware-acceleration": true,', - '', - ' // Enabled by default by VS Code to resolve color issues in the renderer', - ' // See https://github.com/microsoft/vscode/issues/51791 for details', - ' "disable-color-correct-rendering": true', + ' // "disable-hardware-acceleration": true', '}' ]; @@ -329,7 +320,7 @@ function configureCrashReporter() { if (!fs.existsSync(crashReporterDirectory)) { try { - fs.mkdirSync(crashReporterDirectory); + fs.mkdirSync(crashReporterDirectory, { recursive: true }); } catch (error) { console.error(`The path '${crashReporterDirectory}' specified for --crash-reporter-directory does not seem to exist or cannot be created.`); app.exit(1); diff --git a/src/server-main.js b/src/server-main.js index 6f6f66e7b9..91abf7114c 100644 --- a/src/server-main.js +++ b/src/server-main.js @@ -55,7 +55,7 @@ async function start() { * @typedef { import('./vs/server/node/remoteExtensionHostAgentServer').IServerAPI } IServerAPI */ /** @type {IServerAPI | null} */ - let _remoteExtensionHostAgentServer = null; + const _remoteExtensionHostAgentServer = null; /** @type {Promise | null} */ let _remoteExtensionHostAgentServerPromise = null; /** @returns {Promise} */ diff --git a/src/sql/azdata.d.ts b/src/sql/azdata.d.ts index cc5d58384c..93cdde60cf 100644 --- a/src/sql/azdata.d.ts +++ b/src/sql/azdata.d.ts @@ -4761,6 +4761,7 @@ declare module 'azdata' { * @deprecated please use the method createModelViewDialog(title: string, dialogName?: string, width?: DialogWidth) instead. * Create a dialog with the given title * @param title The title of the dialog, displayed at the top + * @param dialogName Name of the dialog. * @param isWide Indicates whether the dialog is wide or normal */ export function createModelViewDialog(title: string, dialogName?: string, isWide?: boolean): Dialog; @@ -5391,6 +5392,7 @@ declare module 'azdata' { */ export function getQueryDocument(fileUri: string): Thenable; + /* eslint-disable */ /** * Opens an untitled text document. The editor will prompt the user for a file * path when the document is to be saved. The `options` parameter allows to @@ -5401,6 +5403,7 @@ declare module 'azdata' { * @return A promise that resolves to a {@link QueryDocument}. */ export function openQueryDocument(options?: { content?: string; }, providerId?: string): Thenable; + /* eslint-enable */ } /** @@ -5655,6 +5658,7 @@ declare module 'azdata' { */ export const onDidChangeActiveNotebookEditor: vscode.Event; + /* eslint-disable */ /** * Show the given document in a notebook editor. A {@link vscode.ViewColumn} can be provided * to control where the editor is being shown. Might change the {@link nb.activeNotebookEditor}. @@ -5674,6 +5678,7 @@ declare module 'azdata' { * @return A promise that resolves to a {@link NotebookEditor}. */ export function showNotebookDocument(uri: vscode.Uri, showOptions?: NotebookShowOptions): Thenable; + /* eslint-enable */ export interface NotebookDocument { /** diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 182e1b0abd..5a2c26b7d1 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -679,13 +679,13 @@ declare module 'azdata' { /** * Enters the workspace with the provided path - * @param workspacefile + * @param workspaceFile */ export function enterWorkspace(workspaceFile: vscode.Uri): Promise; /** * Saves and enters the workspace with the provided path - * @param workspacefile + * @param workspaceFile */ export function saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise; } @@ -848,7 +848,7 @@ declare module 'azdata' { * Open a table designer window. * @param providerId The table designer provider Id. * @param tableInfo The table information. The object will be passed back to the table designer provider as the unique identifier for the table. - * @param telemetryInfo: Optional Key-value pair containing any extra information that needs to be sent via telemetry + * @param telemetryInfo Optional Key-value pair containing any extra information that needs to be sent via telemetry */ export function openTableDesigner(providerId: string, tableInfo: TableInfo, telemetryInfo?: { [key: string]: string }): Thenable; diff --git a/src/sql/base/browser/globalPointerMoveMonitor.ts b/src/sql/base/browser/globalPointerMoveMonitor.ts new file mode 100644 index 0000000000..95e6f3d16a --- /dev/null +++ b/src/sql/base/browser/globalPointerMoveMonitor.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from 'vs/base/browser/dom'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; + +export interface IPointerMoveEventData { + leftButton: boolean; + buttons: number; + pageX: number; + pageY: number; +} + +export interface IEventMerger { + (lastEvent: R | null, currentEvent: PointerEvent): R; +} + +export interface IPointerMoveCallback { + (pointerMoveData: R): void; +} + +export interface IOnStopCallback { + (browserEvent?: PointerEvent | KeyboardEvent): void; +} + +export function standardPointerMoveMerger(lastEvent: IPointerMoveEventData | null, currentEvent: PointerEvent): IPointerMoveEventData { + currentEvent.preventDefault(); + return { + leftButton: (currentEvent.button === 0), + buttons: currentEvent.buttons, + pageX: currentEvent.pageX, + pageY: currentEvent.pageY + }; +} + +export class GlobalPointerMoveMonitor implements IDisposable { + + private readonly _hooks = new DisposableStore(); + private _pointerMoveEventMerger: IEventMerger | null = null; + private _pointerMoveCallback: IPointerMoveCallback | null = null; + private _onStopCallback: IOnStopCallback | null = null; + + public dispose(): void { + this.stopMonitoring(false); + this._hooks.dispose(); + } + + public stopMonitoring(invokeStopCallback: boolean, browserEvent?: PointerEvent | KeyboardEvent): void { + if (!this.isMonitoring()) { + // Not monitoring + return; + } + + // Unhook + this._hooks.clear(); + this._pointerMoveEventMerger = null; + this._pointerMoveCallback = null; + const onStopCallback = this._onStopCallback; + this._onStopCallback = null; + + if (invokeStopCallback && onStopCallback) { + onStopCallback(browserEvent); + } + } + + public isMonitoring(): boolean { + return !!this._pointerMoveEventMerger; + } + + public startMonitoring( + initialElement: Element, + pointerId: number, + initialButtons: number, + pointerMoveEventMerger: IEventMerger, + pointerMoveCallback: IPointerMoveCallback, + onStopCallback: IOnStopCallback + ): void { + if (this.isMonitoring()) { + this.stopMonitoring(false); + } + this._pointerMoveEventMerger = pointerMoveEventMerger; + this._pointerMoveCallback = pointerMoveCallback; + this._onStopCallback = onStopCallback; + + let eventSource: Element | Window = initialElement; + + try { + initialElement.setPointerCapture(pointerId); + this._hooks.add(toDisposable(() => { + initialElement.releasePointerCapture(pointerId); + })); + } catch (err) { + // See https://github.com/microsoft/vscode/issues/144584 + // See https://github.com/microsoft/vscode/issues/146947 + // `setPointerCapture` sometimes fails when being invoked + // from a `mousedown` listener on macOS and Windows + // and it always fails on Linux with the exception: + // DOMException: Failed to execute 'setPointerCapture' on 'Element': + // No active pointer with the given id is found. + // In case of failure, we bind the listeners on the window + eventSource = window; + } + + this._hooks.add(dom.addDisposableThrottledListener( + eventSource, + dom.EventType.POINTER_MOVE, + (data: R) => { + if (data.buttons !== initialButtons) { + // Buttons state has changed in the meantime + this.stopMonitoring(true); + return; + } + this._pointerMoveCallback!(data); + }, + (lastEvent: R | null, currentEvent) => this._pointerMoveEventMerger!(lastEvent, currentEvent) + )); + + this._hooks.add(dom.addDisposableListener( + eventSource, + dom.EventType.POINTER_UP, + (e: PointerEvent) => this.stopMonitoring(true) + )); + } +} diff --git a/src/sql/base/browser/ui/panel/panel.component.ts b/src/sql/base/browser/ui/panel/panel.component.ts index 9534820890..db0bc65cce 100644 --- a/src/sql/base/browser/ui/panel/panel.component.ts +++ b/src/sql/base/browser/ui/panel/panel.component.ts @@ -261,6 +261,7 @@ export class PanelComponent extends Disposable implements IThemable { this.selectTab(nextTabIndex); } + /* eslint-disable */ /** * Updates the specified tab with new config values * @param tabId The id of the tab to update @@ -285,6 +286,7 @@ export class PanelComponent extends Disposable implements IThemable { tabHeader?.refresh(); } } + /* eslint-enable */ private findAndRemoveTabFromMRU(tab: TabComponent): void { let mruIndex = this._mru.findIndex(i => i === tab); diff --git a/src/sql/platform/actions/browser/menuEntryActionViewItem.ts b/src/sql/platform/actions/browser/menuEntryActionViewItem.ts index 67a3c22c95..2b6bfc71db 100644 --- a/src/sql/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/sql/platform/actions/browser/menuEntryActionViewItem.ts @@ -10,9 +10,10 @@ import { MenuItemAction } from 'vs/platform/actions/common/actions'; import { ICommandAction } from 'vs/platform/action/common/action'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; const ids = new IdGenerator('menu-item-action-item-icon-'); @@ -32,8 +33,10 @@ export class LabeledMenuItemActionItem extends MenuEntryActionViewItem { @IKeybindingService labeledkeybindingService: IKeybindingService, @INotificationService _notificationService: INotificationService, @IContextKeyService _contextKeyService: IContextKeyService, + @IThemeService _themeService: IThemeService, + @IContextMenuService _contextMenuService: IContextMenuService ) { - super(_action, undefined, labeledkeybindingService, _notificationService, _contextKeyService); + super(_action, undefined, labeledkeybindingService, _notificationService, _contextKeyService, _themeService, _contextMenuService); } override updateLabel(): void { @@ -104,9 +107,11 @@ export class MaskedLabeledMenuItemActionItem extends MenuEntryActionViewItem { action: MenuItemAction, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService _themeService: IThemeService, + @IContextMenuService _contextMenuService: IContextMenuService ) { - super(action, undefined, keybindingService, notificationService, contextKeyService); + super(action, undefined, keybindingService, notificationService, contextKeyService, _themeService, _contextMenuService); } override updateLabel(): void { diff --git a/src/sql/platform/connection/common/connectionStore.ts b/src/sql/platform/connection/common/connectionStore.ts index 09c4ee7cbc..28ff48ed85 100644 --- a/src/sql/platform/connection/common/connectionStore.ts +++ b/src/sql/platform/connection/common/connectionStore.ts @@ -42,14 +42,14 @@ export class ConnectionStore { @ICapabilitiesService private capabilitiesService: ICapabilitiesService ) { try { - const configRaw = this.storageService.get(RECENT_CONNECTIONS_STATE_KEY, StorageScope.GLOBAL, '[]'); + const configRaw = this.storageService.get(RECENT_CONNECTIONS_STATE_KEY, StorageScope.APPLICATION, '[]'); this.mru = JSON.parse(configRaw); } catch (e) { this.mru = []; } this.storageService.onWillSaveState(() => - this.storageService.store(RECENT_CONNECTIONS_STATE_KEY, JSON.stringify(this.mru), StorageScope.GLOBAL, StorageTarget.MACHINE)); + this.storageService.store(RECENT_CONNECTIONS_STATE_KEY, JSON.stringify(this.mru), StorageScope.APPLICATION, StorageTarget.MACHINE)); } /** diff --git a/src/sql/workbench/api/common/extHostModelViewTree.ts b/src/sql/workbench/api/common/extHostModelViewTree.ts index 7568e0c8b9..5aea9d8f91 100644 --- a/src/sql/workbench/api/common/extHostModelViewTree.ts +++ b/src/sql/workbench/api/common/extHostModelViewTree.ts @@ -15,8 +15,7 @@ import * as vsTreeExt from 'vs/workbench/api/common/extHostTreeViews'; import { Emitter } from 'vs/base/common/event'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer'; -import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { SqlMainContext, DataTransferDTO } from 'vs/workbench/api/common/extHost.protocol'; export class ExtHostModelViewTreeViews implements ExtHostModelViewTreeViewsShape { private _proxy: MainThreadModelViewShape; @@ -86,7 +85,7 @@ export class ExtHostModelViewTreeViews implements ExtHostModelViewTreeViewsShape return Promise.resolve(undefined); } - $handleDrop(destinationViewId: string, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise { + $handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise { return Promise.resolve(undefined); } diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index f55206e1f7..c501aaab89 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -26,7 +26,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemetry'; import { IQueryEvent } from 'sql/workbench/services/query/common/queryModel'; -import { DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer'; +import { DataTransferDTO } from 'vs/workbench/api/common/extHost.protocol'; export abstract class ExtHostAzureBlobShape { public $createSas(connectionUri: string, blobContainerUri: string, blobStorageKey: string, storageAccountName: string, expirationDate: string): Thenable { throw ni(); } @@ -794,7 +794,7 @@ export interface ExtHostModelViewShape { export interface ExtHostModelViewTreeViewsShape { $getChildren(treeViewId: string, treeItemHandle?: string): Promise; - $handleDrop(destinationViewId: string, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise; + $handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: vscode.CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise; $handleDrag(sourceViewId: string, sourceTreeItemHandles: string[], operationUuid: string, token: vscode.CancellationToken): Promise; $createTreeView(handle: number, componentId: string, options: { treeDataProvider: vscode.TreeDataProvider }, extension: IExtensionDescription): azdata.TreeComponentView; diff --git a/src/sql/workbench/browser/designer/designerScriptEditor.ts b/src/sql/workbench/browser/designer/designerScriptEditor.ts index affff83ed3..14ad8b4997 100644 --- a/src/sql/workbench/browser/designer/designerScriptEditor.ts +++ b/src/sql/workbench/browser/designer/designerScriptEditor.ts @@ -16,7 +16,7 @@ import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { TextResourceEditorModel } from 'vs/workbench/common/editor/textResourceEditorModel'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -30,13 +30,14 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IModelService } from 'vs/editor/common/services/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; +import { IFileService } from 'vs/platform/files/common/files'; class DesignerCodeEditor extends CodeEditorWidget { } let DesignerScriptEditorInstanceId = 0; -export class DesignerScriptEditor extends BaseTextEditor implements DesignerTextEditor { +export class DesignerScriptEditor extends AbstractTextCodeEditor implements DesignerTextEditor { private _content: string; private _contentChangeEventEmitter: Emitter = new Emitter(); readonly onDidContentChange: Event = this._contentChangeEventEmitter.event; @@ -55,9 +56,10 @@ export class DesignerScriptEditor extends BaseTextEditor { +export class QueryTextEditor extends AbstractTextCodeEditor { public static ID = 'modelview.editors.textEditor'; private _dimension: DOM.Dimension; @@ -47,15 +49,18 @@ export class QueryTextEditor extends BaseTextEditor { - storageService.store(storageKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store(storageKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE); } }]); } diff --git a/src/sql/workbench/contrib/configuration/common/configurationUpgrader.ts b/src/sql/workbench/contrib/configuration/common/configurationUpgrader.ts index cccd4d57dd..c927667db9 100644 --- a/src/sql/workbench/contrib/configuration/common/configurationUpgrader.ts +++ b/src/sql/workbench/contrib/configuration/common/configurationUpgrader.ts @@ -31,7 +31,7 @@ const settingsToMove: { [key: string]: string } = deepFreeze({ export class ConfigurationUpgraderContribution implements IWorkbenchContribution { private static readonly STORAGE_KEY = 'configurationUpgrader'; - private readonly globalStorage: { [key: string]: boolean } = JSON.parse(this.storageService.get(ConfigurationUpgraderContribution.STORAGE_KEY, StorageScope.GLOBAL, '{}')); + private readonly globalStorage: { [key: string]: boolean } = JSON.parse(this.storageService.get(ConfigurationUpgraderContribution.STORAGE_KEY, StorageScope.APPLICATION, '{}')); private readonly workspaceStorage: { [key: string]: boolean } = JSON.parse(this.storageService.get(ConfigurationUpgraderContribution.STORAGE_KEY, StorageScope.WORKSPACE, '{}')); public readonly processingPromise: Promise; @@ -43,7 +43,7 @@ export class ConfigurationUpgraderContribution implements IWorkbenchContribution ) { this.processingPromise = (async () => { await this.processSettings(); - this.storageService.store(ConfigurationUpgraderContribution.STORAGE_KEY, JSON.stringify(this.globalStorage), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(ConfigurationUpgraderContribution.STORAGE_KEY, JSON.stringify(this.globalStorage), StorageScope.APPLICATION, StorageTarget.MACHINE); this.storageService.store(ConfigurationUpgraderContribution.STORAGE_KEY, JSON.stringify(this.workspaceStorage), StorageScope.WORKSPACE, StorageTarget.MACHINE); })(); } diff --git a/src/sql/workbench/contrib/dashboard/browser/contents/webviewContent.component.ts b/src/sql/workbench/contrib/dashboard/browser/contents/webviewContent.component.ts index 4dd48edc5f..1fbec76f09 100644 --- a/src/sql/workbench/contrib/dashboard/browser/contents/webviewContent.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/contents/webviewContent.component.ts @@ -98,11 +98,14 @@ export class WebviewContent extends AngularDisposable implements OnInit, IDashbo this._onMessageDisposable.dispose(); } - this._webview = this.webviewService.createWebviewElement(this.id, - {}, - { - allowScripts: true - }, undefined); + this._webview = this.webviewService.createWebviewElement({ + id: this.id, + contentOptions: { + allowScripts: true, + }, + options: {}, + extension: undefined + }); this._webview.mountTo(this._el.nativeElement); diff --git a/src/sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.component.ts b/src/sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.component.ts index 1cb2d45136..aec22b57d8 100644 --- a/src/sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/widgets/insights/insightsWidget.component.ts @@ -190,14 +190,14 @@ export class InsightsWidget extends DashboardWidget implements IDashboardWidget, }; this.lastUpdated = nls.localize('insights.lastUpdated', "Last Updated: {0} {1}", currentTime.toLocaleTimeString(), currentTime.toLocaleDateString()); this._changeRef.detectChanges(); - this.storageService.store(this._getStorageKey(), JSON.stringify(store), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(this._getStorageKey(), JSON.stringify(store), StorageScope.APPLICATION, StorageTarget.MACHINE); } return result; } private _checkStorage(): boolean { if (this.insightConfig.cacheId) { - const storage = this.storageService.get(this._getStorageKey(), StorageScope.GLOBAL); + const storage = this.storageService.get(this._getStorageKey(), StorageScope.APPLICATION); if (storage) { const storedResult: IStorageResult = JSON.parse(storage); const date = new Date(storedResult.date); diff --git a/src/sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.component.ts b/src/sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.component.ts index b965ec65e3..d70e62c6c4 100644 --- a/src/sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/widgets/webview/webviewWidget.component.ts @@ -99,11 +99,14 @@ export class WebviewWidget extends DashboardWidget implements IDashboardWidget, this._onMessageDisposable.dispose(); } - this._webview = this.webviewService.createWebviewElement(this.id, - {}, - { + this._webview = this.webviewService.createWebviewElement({ + id: this.id, + contentOptions: { allowScripts: true, - }, undefined); + }, + options: {}, + extension: undefined + }); this._webview.mountTo(this._el.nativeElement); this._onMessageDisposable = this._webview.onMessage(e => { diff --git a/src/sql/workbench/contrib/editData/browser/editDataEditor.ts b/src/sql/workbench/contrib/editData/browser/editDataEditor.ts index e88789ce73..da9b30aade 100644 --- a/src/sql/workbench/contrib/editData/browser/editDataEditor.ts +++ b/src/sql/workbench/contrib/editData/browser/editDataEditor.ts @@ -41,6 +41,7 @@ import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ICodeEditorViewState } from 'vs/editor/common/editorCommon'; /** * Editor that hosts an action bar and a resultSetInput for an edit data session @@ -583,7 +584,7 @@ export class EditDataEditor extends EditorPane { newInput.results.onRestoreViewStateEmitter.fire(); } if (newInput.savedViewState) { - this._sqlEditor.getControl().restoreViewState(newInput.savedViewState); + this._sqlEditor.getControl().restoreViewState(newInput.savedViewState); } }); } diff --git a/src/sql/workbench/contrib/executionPlan/browser/executionPlanComparisonEditorView.ts b/src/sql/workbench/contrib/executionPlan/browser/executionPlanComparisonEditorView.ts index 72fca50162..53080c6591 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/executionPlanComparisonEditorView.ts +++ b/src/sql/workbench/contrib/executionPlan/browser/executionPlanComparisonEditorView.ts @@ -798,11 +798,10 @@ class SearchNodeAction extends Action { public static LABEL_FOR_ADDED_PLAN = localize('epCompare.searchNodeActionAddedPlan', 'Find Node - Added Plan'); constructor(private readonly _planIdentifier: PlanIdentifier, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IAdsTelemetryService private readonly _telemetryService: IAdsTelemetryService) { - const getLabelForAction = () => { + super(SearchNodeAction.ID, undefined, searchIconClassNames, true, () => { return _planIdentifier === PlanIdentifier.Added ? SearchNodeAction.LABEL_FOR_ADDED_PLAN : SearchNodeAction.LABEL; - }; + }); - super(SearchNodeAction.ID, getLabelForAction(), searchIconClassNames); this.enabled = false; } diff --git a/src/sql/workbench/contrib/executionPlan/browser/executionPlanContribution.ts b/src/sql/workbench/contrib/executionPlan/browser/executionPlanContribution.ts index ca5de09e5c..9963299476 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/executionPlanContribution.ts +++ b/src/sql/workbench/contrib/executionPlan/browser/executionPlanContribution.ts @@ -70,14 +70,16 @@ export class ExecutionPlanEditorOverrideContribution extends Disposable implemen priority: RegisteredEditorPriority.builtin }, {}, - (editorInput, group) => { - const executionPlanGraphInfo = { - graphFileContent: undefined, - graphFileType: undefined - }; - const executionPlanInput = this._register(this._instantiationService.createInstance(ExecutionPlanInput, editorInput.resource, executionPlanGraphInfo)); + { + createEditorInput: (editorInput, group) => { + const executionPlanGraphInfo = { + graphFileContent: undefined, + graphFileType: undefined + }; + const executionPlanInput = this._register(this._instantiationService.createInstance(ExecutionPlanInput, editorInput.resource, executionPlanGraphInfo)); - return { editor: executionPlanInput, options: editorInput.options, group: group }; + return { editor: executionPlanInput, options: editorInput.options, group: group }; + } } )); } diff --git a/src/sql/workbench/contrib/executionPlan/browser/executionPlanView.ts b/src/sql/workbench/contrib/executionPlan/browser/executionPlanView.ts index d3721b9f78..2ed39bab53 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/executionPlanView.ts +++ b/src/sql/workbench/contrib/executionPlan/browser/executionPlanView.ts @@ -449,7 +449,7 @@ export class SavePlanFile extends Action { const fileExtension = 'sqlplan'; //TODO: Get this extension from provider let defaultUri: URI; - const lastUsedSavePath = this.storageService.get(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, StorageScope.GLOBAL); + const lastUsedSavePath = this.storageService.get(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, StorageScope.APPLICATION); if (lastUsedSavePath) { defaultUri = joinPath(URI.file(lastUsedSavePath), `${defaultFileName}.${fileExtension}`); @@ -479,7 +479,7 @@ export class SavePlanFile extends Action { if (destination) { // Remember as last used save folder - this.storageService.store(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, dirname(destination).fsPath, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(SavePlanFile.LAST_USED_EXECUTION_PLAN_SAVE_PATH_STORAGE_KEY, dirname(destination).fsPath, StorageScope.APPLICATION, StorageTarget.MACHINE); // Perform save await context.fileService.writeFile(destination, VSBuffer.fromString(context.model.graphFile.graphFileContent)); diff --git a/src/sql/workbench/contrib/executionPlan/browser/widgets/highlightExpensiveNodeWidget.ts b/src/sql/workbench/contrib/executionPlan/browser/widgets/highlightExpensiveNodeWidget.ts index 135d8d6be9..e4f1c5596d 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/widgets/highlightExpensiveNodeWidget.ts +++ b/src/sql/workbench/contrib/executionPlan/browser/widgets/highlightExpensiveNodeWidget.ts @@ -181,7 +181,7 @@ export class HighlightExpensiveOperationWidget extends ExecutionPlanWidgetBase { public showStoreDefaultMetricPrompt(): void { const currentDefaultExpensiveOperationMetric = this.getDefaultExpensiveOperationMetric(); - if (this._selectedExpensiveOperationType === currentDefaultExpensiveOperationMetric || !this._storageService.getBoolean('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', StorageScope.GLOBAL, true)) { + if (this._selectedExpensiveOperationType === currentDefaultExpensiveOperationMetric || !this._storageService.getBoolean('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', StorageScope.APPLICATION, true)) { return; } @@ -197,7 +197,7 @@ export class HighlightExpensiveOperationWidget extends ExecutionPlanWidgetBase { }, { label: localize('qp.expensiveOperationMetric.dontShowAgain', "Don't Show Again"), - run: () => this._storageService.store('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', false, StorageScope.GLOBAL, StorageTarget.USER) + run: () => this._storageService.store('qp.expensiveOperationMetric.showChangeDefaultExpensiveMetricPrompt', false, StorageScope.APPLICATION, StorageTarget.USER) } ]; diff --git a/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts b/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts index 5354326773..31a31198cf 100644 --- a/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts +++ b/src/sql/workbench/contrib/extensions/browser/scenarioRecommendations.ts @@ -52,7 +52,7 @@ export class ScenarioRecommendations extends ExtensionRecommendations { promptRecommendedExtensionsByScenario(scenarioType: string): void { const storageKey = 'extensionAssistant/RecommendationsIgnore/' + scenarioType; - if (this.storageService.getBoolean(storageKey, StorageScope.GLOBAL, false) || this.ignoreRecommendations()) { + if (this.storageService.getBoolean(storageKey, StorageScope.APPLICATION, false) || this.ignoreRecommendations()) { return; } @@ -104,7 +104,7 @@ export class ScenarioRecommendations extends ExtensionRecommendations { 'NeverShowAgainButton', visualizerExtensionNotificationService ); - this.storageService.store(storageKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE); } }], { diff --git a/src/sql/workbench/contrib/modelView/browser/webview.component.ts b/src/sql/workbench/contrib/modelView/browser/webview.component.ts index 89c8acd0f5..f380f45c98 100644 --- a/src/sql/workbench/contrib/modelView/browser/webview.component.ts +++ b/src/sql/workbench/contrib/modelView/browser/webview.component.ts @@ -73,11 +73,14 @@ export default class WebViewComponent extends ComponentBase i } private _createWebview(): void { - this._webview = this.webviewService.createWebviewElement(this.id, - {}, - { - allowScripts: true - }, undefined); + this._webview = this.webviewService.createWebviewElement({ + id: this.id, + contentOptions: { + allowScripts: true, + }, + options: {}, + extension: undefined + }); this._webview.mountTo(this._el.nativeElement); diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/interfaces.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/interfaces.ts index e39c2f7648..b93827804e 100644 --- a/src/sql/workbench/contrib/notebook/browser/cellViews/interfaces.ts +++ b/src/sql/workbench/contrib/notebook/browser/cellViews/interfaces.ts @@ -10,7 +10,7 @@ import { AngularDisposable } from 'sql/base/browser/lifecycle'; import { ICellEditorProvider, INotebookService, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService'; import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor'; import { nb } from 'azdata'; import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput'; @@ -34,7 +34,7 @@ export abstract class CellView extends AngularDisposable implements OnDestroy, I public abstract layout(): void; - public getEditor(): BaseTextEditor | undefined { + public getEditor(): AbstractTextCodeEditor | undefined { return undefined; } diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts index 82aa3e3a53..f6e74cd77e 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -803,18 +803,19 @@ export class NotebookEditorOverrideContribution extends Disposable implements IW priority: RegisteredEditorPriority.builtin }, {}, - async (editorInput, group) => { - const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput; - // Try to convert the input, falling back to just a plain file input if we're unable to - const newInput = this.convertInput(fileInput); - return { editor: newInput, options: editorInput.options, group: group }; - }, - undefined, - async (diffEditorInput, group) => { - const diffEditorInputImpl = await this._editorService.createEditorInput(diffEditorInput) as DiffEditorInput; - // Try to convert the input, falling back to the original input if we're unable to - const newInput = this.convertInput(diffEditorInputImpl); - return { editor: newInput, options: diffEditorInput.options, group: group }; + { + createEditorInput: async (editorInput, group) => { + const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput; + // Try to convert the input, falling back to just a plain file input if we're unable to + const newInput = this.convertInput(fileInput); + return { editor: newInput, options: editorInput.options, group: group }; + }, + createDiffEditorInput: async (diffEditorInput, group) => { + const diffEditorInputImpl = await this._editorService.createEditorInput(diffEditorInput) as DiffEditorInput; + // Try to convert the input, falling back to the original input if we're unable to + const newInput = this.convertInput(diffEditorInputImpl); + return { editor: newInput, options: diffEditorInput.options, group: group }; + } } )); } diff --git a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts index 38d6274408..f827803d73 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts @@ -32,7 +32,7 @@ import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model'; import { NotebookFindDecorations } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations'; import { TimeoutTimer } from 'vs/base/common/async'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState'; @@ -94,7 +94,7 @@ export class NotebookEditor extends EditorPane implements IFindNotebookControlle public getLastPosition(): NotebookRange { return this._previousMatch; } - public getCellEditor(cellGuid: string): BaseTextEditor | undefined { + public getCellEditor(cellGuid: string): AbstractTextCodeEditor | undefined { let editorImpl = this._notebookService.findNotebookEditor(this.notebookInput.notebookUri); if (editorImpl) { let cellEditorProvider = editorImpl.cellEditors.filter(c => c.cellGuid() === cellGuid)[0]; diff --git a/src/sql/workbench/contrib/notebook/test/browser/markdownTextTransformer.test.ts b/src/sql/workbench/contrib/notebook/test/browser/markdownTextTransformer.test.ts index 3763dd88cb..1d1f88254c 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/markdownTextTransformer.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/markdownTextTransformer.test.ts @@ -33,7 +33,7 @@ import { NotebookEditorStub } from 'sql/workbench/contrib/notebook/test/testComm import { Range } from 'vs/editor/common/core/range'; import { IProductService } from 'vs/platform/product/common/productService'; import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService'; -import { LanguageId } from 'vs/editor/common/languages'; +import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; suite.skip('MarkdownTextTransformer', () => { let markdownTextTransformer: MarkdownTextTransformer; diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookEditor.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookEditor.test.ts index f7c084b485..82876639c8 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/notebookEditor.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/notebookEditor.test.ts @@ -730,7 +730,8 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins instantiationService.get(ITextResourceConfigurationService), instantiationService.get(IThemeService), instantiationService.get(IEditorGroupsService), - instantiationService.get(IEditorService) + instantiationService.get(IEditorService), + instantiationService.get(IFileService) ); const notebookEditorStub = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: new NotebookModelStub(), notebookParams: { notebookUri: untitledNotebookInput.notebookUri } }); notebookService.addNotebookEditor(notebookEditorStub); diff --git a/src/sql/workbench/contrib/notebook/test/testCommon.ts b/src/sql/workbench/contrib/notebook/test/testCommon.ts index 4c4256c814..837f9080e4 100644 --- a/src/sql/workbench/contrib/notebook/test/testCommon.ts +++ b/src/sql/workbench/contrib/notebook/test/testCommon.ts @@ -11,7 +11,7 @@ import * as dom from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { TestEditorGroupsService, TestEditorService, TestTextResourceConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestEditorGroupsService, TestEditorService, TestFileService, TestTextResourceConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension'; @@ -48,7 +48,8 @@ class CellEditorProviderStub extends stubs.CellEditorProviderStub { new TestTextResourceConfigurationService(), new TestThemeService(), new TestEditorGroupsService(), - new TestEditorService() + new TestEditorService(), + new TestFileService() ); } if (this._editor) { diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts index eca0a98c41..78749649af 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts @@ -44,6 +44,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; +import { workbenchTreeDataPreamble } from 'vs/platform/list/browser/listService'; suite('SQL Connection Tree Action tests', () => { let errorMessageService: TypeMoq.Mock; @@ -706,7 +707,16 @@ suite('SQL Connection Tree Action tests', () => { }); }); - test('RefreshConnectionAction - AsyncServerTree - refresh should not be called if connection status is not connect', () => { + /* + TypeError: instantiationService.invokeFunction is not a function + at new WorkbenchAsyncDataTree (file:///C:/Users/lewissanchez/GitProjects/azuredatastudio-merge/out/vs/platform/list/browser/listService.js:643:102) + at new AsyncServerTree (file:///C:/Users/lewissanchez/GitProjects/azuredatastudio-merge/out/sql/workbench/services/objectExplorer/browser/asyncServerTree.js:9:5) + at Utils.conthunktor (C:\Users\lewissanchez\GitProjects\azuredatastudio-merge\node_modules\typemoq\typemoq.js:227:23) + at Mock.ofType2 (C:\Users\lewissanchez\GitProjects\azuredatastudio-merge\node_modules\typemoq\typemoq.js:1248:48) + at Mock.ofType (C:\Users\lewissanchez\GitProjects\azuredatastudio-merge\node_modules\typemoq\typemoq.js:1243:29) + at Context. (file:///C:/Users/lewissanchez/GitProjects/azuredatastudio-merge/out/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.js:612: + */ + test.skip('RefreshConnectionAction - AsyncServerTree - refresh should not be called if connection status is not connect', (done) => { // {{SQL CARBON TODO}} 3/17/23 Not sure why this is failing after mokcing the instantiation service's invoke function. let isConnectedReturnValue: boolean = false; let sqlProvider = { providerId: mssqlProviderName, @@ -770,6 +780,24 @@ suite('SQL Connection Tree Action tests', () => { objectExplorerService.callBase = true; objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode); objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node])); + + let testDisposable = { + dispose() { + // + } + }; + let testInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); + testInstantiationService.setup(x => x.invokeFunction(workbenchTreeDataPreamble, TypeMoq.It.isAny())).returns((): any => { + return { + getTypeNavigationMode: undefined, + disposable: testDisposable, + treeOptions: {} + }; + }); + + let mockContextKeyService = new MockContextKeyService(); + let testListService = new TestListService(); + let testConfigurationService = new TestConfigurationService(); let tree = TypeMoq.Mock.ofType(AsyncServerTree, TypeMoq.MockBehavior.Loose, 'ConnectionTreeActionsTest', // user $('div'), // container @@ -777,12 +805,11 @@ suite('SQL Connection Tree Action tests', () => { [], // renderers {}, // data source {}, // options - new MockContextKeyService(), // IContextKeyService - new TestListService(), // IListService, + testInstantiationService.object, + mockContextKeyService, // IContextKeyService + testListService, // IListService, undefined, // IThemeService, - new TestConfigurationService(), // IConfigurationService, - undefined, // IKeybindingService, - new TestAccessibilityService()); // IAccessibilityService + testConfigurationService); // IConfigurationService, tree.callBase = true; tree.setup(x => x.updateChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve()); @@ -802,6 +829,7 @@ suite('SQL Connection Tree Action tests', () => { objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); tree.verify(x => x.updateChildren(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0)); + done(); }); }); diff --git a/src/sql/workbench/contrib/profiler/browser/profilerResourceEditor.ts b/src/sql/workbench/contrib/profiler/browser/profilerResourceEditor.ts index 9450b7c4f0..4b3c1b3d5b 100644 --- a/src/sql/workbench/contrib/profiler/browser/profilerResourceEditor.ts +++ b/src/sql/workbench/contrib/profiler/browser/profilerResourceEditor.ts @@ -9,7 +9,7 @@ import * as DOM from 'vs/base/browser/dom'; import { TextResourceEditorModel } from 'vs/workbench/common/editor/textResourceEditorModel'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -23,6 +23,7 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; +import { IFileService } from 'vs/platform/files/common/files'; class ProfilerResourceCodeEditor extends CodeEditorWidget { @@ -38,7 +39,7 @@ class ProfilerResourceCodeEditor extends CodeEditorWidget { /** * Extension of TextResourceEditor that is always readonly rather than only with non UntitledInputs */ -export class ProfilerResourceEditor extends BaseTextEditor { +export class ProfilerResourceEditor extends AbstractTextCodeEditor { public static ID = 'profiler.editors.textEditor'; constructor( @@ -48,14 +49,16 @@ export class ProfilerResourceEditor extends BaseTextEditor false + // canHandleDiff: () => false }, - async (editorInput, group) => { - const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput; - const langAssociation = languageAssociationRegistry.getAssociationForLanguage(lang); - const queryEditorInput = langAssociation?.syncConvertInput?.(fileInput); - if (!queryEditorInput) { - this._logService.warn('Unable to create input for resolving editor ', editorInput.resource); - return undefined; + { + createEditorInput: async (editorInput, group) => { + const fileInput = await this._editorService.createEditorInput(editorInput) as FileEditorInput; + const langAssociation = languageAssociationRegistry.getAssociationForLanguage(lang); + const queryEditorInput = langAssociation?.syncConvertInput?.(fileInput); + if (!queryEditorInput) { + this._logService.warn('Unable to create input for resolving editor ', editorInput.resource); + return undefined; + } + return { editor: queryEditorInput, options: editorInput.options, group: group }; } - return { editor: queryEditorInput, options: editorInput.options, group: group }; } )); }); diff --git a/src/sql/workbench/contrib/query/browser/queryEditor.ts b/src/sql/workbench/contrib/query/browser/queryEditor.ts index 6659c1ca15..ea5da92e97 100644 --- a/src/sql/workbench/contrib/query/browser/queryEditor.ts +++ b/src/sql/workbench/contrib/query/browser/queryEditor.ts @@ -27,7 +27,7 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor'; import { FileEditorInput } from 'vs/workbench/contrib/files/browser/editors/fileEditorInput'; import { URI } from 'vs/base/common/uri'; import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files'; @@ -71,7 +71,7 @@ export class QueryEditor extends EditorPane { private textResourceEditor: TextResourceEditor; private textFileEditor: TextFileEditor; - private currentTextEditor: BaseTextEditor; + private currentTextEditor: AbstractTextCodeEditor; private textResourceEditorContainer: HTMLElement; private textFileEditorContainer: HTMLElement; diff --git a/src/sql/workbench/contrib/webview/browser/webViewDialog.ts b/src/sql/workbench/contrib/webview/browser/webViewDialog.ts index 630b790de6..e90c993af8 100644 --- a/src/sql/workbench/contrib/webview/browser/webViewDialog.ts +++ b/src/sql/workbench/contrib/webview/browser/webViewDialog.ts @@ -87,11 +87,14 @@ export class WebViewDialog extends Modal { protected renderBody(container: HTMLElement) { this._body = DOM.append(container, DOM.$('div.webview-dialog')); - this._webview = this.webviewService.createWebviewElement(this.id, - {}, - { - allowScripts: true - }, undefined); + this._webview = this.webviewService.createWebviewElement({ + id: this.id, + contentOptions: { + allowScripts: true, + }, + options: {}, + extension: undefined + }); this._webview.mountTo(this._body); diff --git a/src/sql/workbench/contrib/welcome/gettingStarted/browser/abstractEnablePreviewFeatures.ts b/src/sql/workbench/contrib/welcome/gettingStarted/browser/abstractEnablePreviewFeatures.ts index ae249dd819..bc992ba145 100644 --- a/src/sql/workbench/contrib/welcome/gettingStarted/browser/abstractEnablePreviewFeatures.ts +++ b/src/sql/workbench/contrib/welcome/gettingStarted/browser/abstractEnablePreviewFeatures.ts @@ -25,7 +25,7 @@ export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContrib protected handlePreviewFeatures(): void { let previewFeaturesEnabled = this.configurationService.getValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES); - if (previewFeaturesEnabled || this.storageService.get(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, StorageScope.GLOBAL)) { + if (previewFeaturesEnabled || this.storageService.get(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, StorageScope.APPLICATION)) { return; } Promise.all([ @@ -45,7 +45,7 @@ export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContrib label: localize('enablePreviewFeatures.yes', "Yes (recommended)"), run: () => { this.configurationService.updateValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, true).catch(e => onUnexpectedError(e)); - this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.APPLICATION, StorageTarget.MACHINE); } }, { label: localize('enablePreviewFeatures.no', "No"), @@ -56,7 +56,7 @@ export abstract class AbstractEnablePreviewFeatures implements IWorkbenchContrib label: localize('enablePreviewFeatures.never', "No, don't show again"), run: () => { this.configurationService.updateValue(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES, false).catch(e => onUnexpectedError(e)); - this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(AbstractEnablePreviewFeatures.ENABLE_PREVIEW_FEATURES_SHOWN, true, StorageScope.APPLICATION, StorageTarget.MACHINE); }, isSecondary: true }] diff --git a/src/sql/workbench/contrib/welcome/notifyEncryption/notifyEncryptionDialog.ts b/src/sql/workbench/contrib/welcome/notifyEncryption/notifyEncryptionDialog.ts index f1e7f75339..fde9d9e687 100644 --- a/src/sql/workbench/contrib/welcome/notifyEncryption/notifyEncryptionDialog.ts +++ b/src/sql/workbench/contrib/welcome/notifyEncryption/notifyEncryptionDialog.ts @@ -43,11 +43,11 @@ export class NotifyEncryptionDialog extends ErrorMessageDialog { } public override open(): void { - if (this._storageService.get(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, StorageScope.GLOBAL)) { + if (this._storageService.get(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, StorageScope.APPLICATION)) { return; } - this._storageService.store(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + this._storageService.store(NotifyEncryptionDialog.NOTIFY_ENCRYPT_SHOWN, true, StorageScope.APPLICATION, StorageTarget.MACHINE); if (!this._connectionManagementService.getConnections()?.some(conn => conn.providerName === mssqlProviderName)) { return; diff --git a/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts index a691f74360..90ed289be2 100644 --- a/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -382,7 +382,7 @@ class WelcomePage extends Disposable { newButton.onDidClick(() => { this.contextMenuService.showContextMenu({ getAnchor: () => newButtonHtmlElement, - getActions: () => NewActionItems.map(command => new MenuItemAction(command, undefined, {}, this.contextKeyService, this.commandService)) + getActions: () => NewActionItems.map(command => new MenuItemAction(command, undefined, {}, undefined, this.contextKeyService, this.commandService)) }); }); diff --git a/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts b/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts index b33afc03dc..cddb288b03 100644 --- a/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts +++ b/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts @@ -67,7 +67,7 @@ export class AccountManagementService implements IAccountManagementService { @IAdsTelemetryService private _telemetryService: IAdsTelemetryService ) { this._mementoContext = new Memento(AccountManagementService.ACCOUNT_MEMENTO, this._storageService); - const mementoObj = this._mementoContext.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + const mementoObj = this._mementoContext.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); this._accountStore = this._instantiationService.createInstance(AccountStore, mementoObj); // Setup the event emitters diff --git a/src/sql/workbench/services/connection/browser/connectionManagementService.ts b/src/sql/workbench/services/connection/browser/connectionManagementService.ts index 1e7d23224a..1f8ecb6e8a 100644 --- a/src/sql/workbench/services/connection/browser/connectionManagementService.ts +++ b/src/sql/workbench/services/connection/browser/connectionManagementService.ts @@ -123,7 +123,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti this._connectionStatusManager = _instantiationService.createInstance(ConnectionStatusManager); if (this._storageService) { this._mementoContext = new Memento(ConnectionManagementService.CONNECTION_MEMENTO, this._storageService); - this._mementoObj = this._mementoContext.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + this._mementoObj = this._mementoContext.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); } this.initializeConnectionProvidersMap(); diff --git a/src/sql/workbench/services/insights/common/insightsUtils.ts b/src/sql/workbench/services/insights/common/insightsUtils.ts index ccb4abada4..d1c3f13924 100644 --- a/src/sql/workbench/services/insights/common/insightsUtils.ts +++ b/src/sql/workbench/services/insights/common/insightsUtils.ts @@ -12,6 +12,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +/* eslint-disable */ /** * Resolves the given file path using the VS ConfigurationResolver service, replacing macros such as * ${workspaceRoot} with their expected values and then testing each path to see if it exists. It will @@ -58,3 +59,4 @@ export async function resolveQueryFilePath(services: ServicesAccessor, filePath? throw Error(localize('insightsDidNotFindResolvedFile', "Could not find query file at any of the following paths :\n {0}", resolvedFileUris.map(uri => uri.fsPath).join('\n'))); } +/* eslint-enable */ diff --git a/src/sql/workbench/services/notebook/browser/notebookService.ts b/src/sql/workbench/services/notebook/browser/notebookService.ts index 018bc278e3..41ed71afc3 100644 --- a/src/sql/workbench/services/notebook/browser/notebookService.ts +++ b/src/sql/workbench/services/notebook/browser/notebookService.ts @@ -15,7 +15,7 @@ import { INotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes import { ICellModel, INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { NotebookChangeType, CellType } from 'sql/workbench/services/notebook/common/contracts'; import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; +import { AbstractTextCodeEditor } from 'vs/workbench/browser/parts/editor/textCodeEditor'; import { Range } from 'vs/editor/common/core/range'; import { IEditorPane } from 'vs/workbench/common/editor'; import { INotebookInput } from 'sql/workbench/services/notebook/browser/interface'; @@ -196,7 +196,7 @@ export interface INotebookSection { export interface ICellEditorProvider { isCellOutput: boolean; cellGuid(): string; - getEditor(): BaseTextEditor | undefined; + getEditor(): AbstractTextCodeEditor | undefined; deltaDecorations(newDecorationsRange: NotebookRange | NotebookRange[], oldDecorationsRange: NotebookRange | NotebookRange[]): void; } diff --git a/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts b/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts index f4c0245d95..1e61ef918f 100644 --- a/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts +++ b/src/sql/workbench/services/notebook/browser/notebookServiceImpl.ts @@ -866,11 +866,11 @@ export class NotebookService extends Disposable implements INotebookService { } private get providersMemento(): NotebookProvidersMemento { - return this._providersMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE) as NotebookProvidersMemento; + return this._providersMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE) as NotebookProvidersMemento; } private get trustedNotebooksMemento(): TrustedNotebooksMemento { - let cache = this._trustedNotebooksMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE) as TrustedNotebooksMemento; + let cache = this._trustedNotebooksMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE) as TrustedNotebooksMemento; if (!cache.trustedNotebooksCache) { cache.trustedNotebooksCache = {}; } diff --git a/src/sql/workbench/services/objectExplorer/browser/asyncServerTree.ts b/src/sql/workbench/services/objectExplorer/browser/asyncServerTree.ts index b1a91d8a6b..da45e66b8f 100644 --- a/src/sql/workbench/services/objectExplorer/browser/asyncServerTree.ts +++ b/src/sql/workbench/services/objectExplorer/browser/asyncServerTree.ts @@ -12,12 +12,11 @@ import { IAsyncDataTreeNode, IAsyncDataTreeUpdateChildrenOptions } from 'vs/base import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAsyncDataSource, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class AsyncServerTree extends WorkbenchAsyncDataTree { @@ -32,14 +31,13 @@ export class AsyncServerTree extends WorkbenchAsyncDataTree { diff --git a/src/sql/workbench/services/profiler/browser/profilerService.ts b/src/sql/workbench/services/profiler/browser/profilerService.ts index f4ca075a31..4482b10a8d 100644 --- a/src/sql/workbench/services/profiler/browser/profilerService.ts +++ b/src/sql/workbench/services/profiler/browser/profilerService.ts @@ -72,7 +72,7 @@ export class ProfilerService implements IProfilerService { @IStorageService private _storageService: IStorageService ) { this._context = new Memento('ProfilerEditor', this._storageService); - this._memento = this._context.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + this._memento = this._context.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); } public registerProvider(providerId: string, provider: azdata.ProfilerProvider): void { diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index f9f0c874eb..057aa8748a 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -2,7 +2,10 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true, - "types": ["trusted-types"], + "types": [ + "trusted-types", + "wicg-file-system-access" + ], "paths": {}, "module": "amd", "moduleResolution": "classic", @@ -15,9 +18,8 @@ "include": [ "typings/require.d.ts", "typings/thenable.d.ts", - "vs/css.d.ts", + "vs/loader.d.ts", "vs/monaco.d.ts", - "vs/nls.d.ts", "vs/editor/*", "vs/base/common/*", "vs/base/browser/*", @@ -28,6 +30,7 @@ "node_modules/*", "vs/platform/files/browser/htmlFileSystemProvider.ts", "vs/platform/files/browser/webFileSystemAccess.ts", + "vs/platform/telemetry/*", "vs/platform/assignment/*" ] } diff --git a/src/tsconfig.vscode-dts.json b/src/tsconfig.vscode-dts.json index 4ae9bc7643..b860765839 100644 --- a/src/tsconfig.vscode-dts.json +++ b/src/tsconfig.vscode-dts.json @@ -14,6 +14,7 @@ "types": [], "lib": [ "es5", + "ES2015.Iterable" ], }, "include": [ diff --git a/src/tsec.exemptions.json b/src/tsec.exemptions.json index 7bad0d38d2..c2479eb2c7 100644 --- a/src/tsec.exemptions.json +++ b/src/tsec.exemptions.json @@ -7,7 +7,7 @@ "vs/workbench/api/worker/extHostExtensionService.ts", "vs/base/worker/workerMain", "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts", - "vs/workbench/services/keybinding/test/electron-browser/keyboardMapperTestUtils.ts" + "vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts" ], "ban-trustedtypes-createpolicy": [ "vs/base/browser/dom.ts", @@ -15,6 +15,7 @@ "vs/base/browser/defaultWorkerFactory.ts", "vs/base/worker/workerMain.ts", "vs/editor/contrib/markdownRenderer/browser/markdownRenderer.ts", + "vs/editor/contrib/stickyScroll/browser/stickyScroll.ts", "vs/editor/browser/view/domLineBreaksComputer.ts", "vs/editor/browser/view/viewLayer.ts", "vs/editor/browser/widget/diffEditorWidget.ts", diff --git a/src/vs/base/browser/broadcast.ts b/src/vs/base/browser/broadcast.ts new file mode 100644 index 0000000000..31b5d06141 --- /dev/null +++ b/src/vs/base/browser/broadcast.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getErrorMessage } from 'vs/base/common/errors'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; + +export class BroadcastDataChannel extends Disposable { + + private broadcastChannel: BroadcastChannel | undefined; + + private readonly _onDidReceiveData = this._register(new Emitter()); + readonly onDidReceiveData = this._onDidReceiveData.event; + + constructor(private readonly channelName: string) { + super(); + + // Use BroadcastChannel + if ('BroadcastChannel' in window) { + try { + this.broadcastChannel = new BroadcastChannel(channelName); + const listener = (event: MessageEvent) => { + this._onDidReceiveData.fire(event.data); + }; + this.broadcastChannel.addEventListener('message', listener); + this._register(toDisposable(() => { + if (this.broadcastChannel) { + this.broadcastChannel.removeEventListener('message', listener); + this.broadcastChannel.close(); + } + })); + } catch (error) { + console.warn('Error while creating broadcast channel. Falling back to localStorage.', getErrorMessage(error)); + } + } + + // BroadcastChannel is not supported. Use storage. + if (!this.broadcastChannel) { + this.channelName = `BroadcastDataChannel.${channelName}`; + this.createBroadcastChannel(); + } + } + + private createBroadcastChannel(): void { + const listener = (event: StorageEvent) => { + if (event.key === this.channelName && event.newValue) { + this._onDidReceiveData.fire(JSON.parse(event.newValue)); + } + }; + window.addEventListener('storage', listener); + this._register(toDisposable(() => window.removeEventListener('storage', listener))); + } + + /** + * Sends the data to other BroadcastChannel objects set up for this channel. Data can be structured objects, e.g. nested objects and arrays. + * @param data data to broadcast + */ + postData(data: T): void { + if (this.broadcastChannel) { + this.broadcastChannel.postMessage(data); + } else { + // remove previous changes so that event is triggered even if new changes are same as old changes + window.localStorage.removeItem(this.channelName); + window.localStorage.setItem(this.channelName, JSON.stringify(data)); + } + } +} diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 4fc3109271..23ff961ef5 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -71,9 +71,7 @@ class DevicePixelRatioMonitor extends Disposable { } private _handleChange(fireEvent: boolean): void { - if (this._mediaQueryList) { - this._mediaQueryList.removeEventListener('change', this._listener); - } + this._mediaQueryList?.removeEventListener('change', this._listener); this._mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`); this._mediaQueryList.addEventListener('change', this._listener); diff --git a/src/vs/base/browser/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index 5a755f7e3c..a7e79f1790 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -86,15 +86,11 @@ class WebWorker implements IWorker { } public postMessage(message: any, transfer: Transferable[]): void { - if (this.worker) { - this.worker.then(w => w.postMessage(message, transfer)); - } + this.worker?.then(w => w.postMessage(message, transfer)); } public dispose(): void { - if (this.worker) { - this.worker.then(w => w.terminate()); - } + this.worker?.then(w => w.terminate()); this.worker = null; } } @@ -112,7 +108,7 @@ export class DefaultWorkerFactory implements IWorkerFactory { } public create(moduleId: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker { - let workerId = (++DefaultWorkerFactory.LAST_WORKER_ID); + const workerId = (++DefaultWorkerFactory.LAST_WORKER_ID); if (this._webWorkerFailedBeforeError) { throw this._webWorkerFailedBeforeError; diff --git a/src/vs/base/browser/deviceAccess.ts b/src/vs/base/browser/deviceAccess.ts new file mode 100644 index 0000000000..4c27962098 --- /dev/null +++ b/src/vs/base/browser/deviceAccess.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// https://wicg.github.io/webusb/ + +export interface UsbDeviceData { + readonly deviceClass: number; + readonly deviceProtocol: number; + readonly deviceSubclass: number; + readonly deviceVersionMajor: number; + readonly deviceVersionMinor: number; + readonly deviceVersionSubminor: number; + readonly manufacturerName?: string; + readonly productId: number; + readonly productName?: string; + readonly serialNumber?: string; + readonly usbVersionMajor: number; + readonly usbVersionMinor: number; + readonly usbVersionSubminor: number; + readonly vendorId: number; +} + +export async function requestUsbDevice(options?: { filters?: unknown[] }): Promise { + const usb = (navigator as any).usb; + if (!usb) { + return undefined; + } + + const device = await usb.requestDevice({ filters: options?.filters ?? [] }); + if (!device) { + return undefined; + } + + return { + deviceClass: device.deviceClass, + deviceProtocol: device.deviceProtocol, + deviceSubclass: device.deviceSubclass, + deviceVersionMajor: device.deviceVersionMajor, + deviceVersionMinor: device.deviceVersionMinor, + deviceVersionSubminor: device.deviceVersionSubminor, + manufacturerName: device.manufacturerName, + productId: device.productId, + productName: device.productName, + serialNumber: device.serialNumber, + usbVersionMajor: device.usbVersionMajor, + usbVersionMinor: device.usbVersionMinor, + usbVersionSubminor: device.usbVersionSubminor, + vendorId: device.vendorId, + }; +} + +// https://wicg.github.io/serial/ + +export interface SerialPortData { + readonly usbVendorId?: number | undefined; + readonly usbProductId?: number | undefined; +} + +export async function requestSerialPort(options?: { filters?: unknown[] }): Promise { + const serial = (navigator as any).serial; + if (!serial) { + return undefined; + } + + const port = await serial.requestPort({ filters: options?.filters ?? [] }); + if (!port) { + return undefined; + } + + const info = port.getInfo(); + return { + usbVendorId: info.usbVendorId, + usbProductId: info.usbProductId + }; +} + +// https://wicg.github.io/webhid/ + +export interface HidDeviceData { + readonly opened: boolean; + readonly vendorId: number; + readonly productId: number; + readonly productName: string; + readonly collections: []; +} + +export async function requestHidDevice(options?: { filters?: unknown[] }): Promise { + const hid = (navigator as any).hid; + if (!hid) { + return undefined; + } + + const devices = await hid.requestDevice({ filters: options?.filters ?? [] }); + if (!devices.length) { + return undefined; + } + + const device = devices[0]; + return { + opened: device.opened, + vendorId: device.vendorId, + productId: device.productId, + productName: device.productName, + collections: device.collections + }; +} diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index c99a802668..c17d37d6f7 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -88,7 +88,7 @@ function _wrapAsStandardKeyboardEvent(handler: (e: IKeyboardEvent) => void): (e: return handler(new StandardKeyboardEvent(e)); }; } -export let addStandardDisposableListener: IAddStandardDisposableListenerSignature = function addStandardDisposableListener(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable { +export const addStandardDisposableListener: IAddStandardDisposableListenerSignature = function addStandardDisposableListener(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable { let wrapHandler = handler; if (type === 'click' || type === 'mousedown') { @@ -100,14 +100,14 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur return addDisposableListener(node, type, wrapHandler, useCapture); }; -export let addStandardDisposableGenericMouseDownListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { - let wrapHandler = _wrapAsStandardMouseEvent(handler); +export const addStandardDisposableGenericMouseDownListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { + const wrapHandler = _wrapAsStandardMouseEvent(handler); return addDisposableGenericMouseDownListener(node, wrapHandler, useCapture); }; -export let addStandardDisposableGenericMouseUpListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { - let wrapHandler = _wrapAsStandardMouseEvent(handler); +export const addStandardDisposableGenericMouseUpListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { + const wrapHandler = _wrapAsStandardMouseEvent(handler); return addDisposableGenericMouseUpListener(node, wrapHandler, useCapture); }; @@ -122,35 +122,6 @@ export function addDisposableGenericMouseMoveListener(node: EventTarget, handler export function addDisposableGenericMouseUpListener(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_UP : EventType.MOUSE_UP, handler, useCapture); } -export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { - return addDisposableListener(node, 'mouseout', (e: MouseEvent) => { - // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget); - while (toElement && toElement !== node) { - toElement = toElement.parentNode; - } - if (toElement === node) { - return; - } - - handler(e); - }); -} - -export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { - return addDisposableListener(node, 'pointerout', (e: MouseEvent) => { - // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget); - while (toElement && toElement !== node) { - toElement = toElement.parentNode; - } - if (toElement === node) { - return; - } - - handler(e); - }); -} export function createEventEmitter(target: HTMLElement, type: K, options?: boolean | AddEventListenerOptions): event.Emitter { let domListener: DomListener | null = null; @@ -258,7 +229,7 @@ class AnimationFrameQueueItem implements IDisposable { */ let inAnimationFrameRunner = false; - let animationFrameRunner = () => { + const animationFrameRunner = () => { animFrameRequested = false; CURRENT_QUEUE = NEXT_QUEUE; @@ -267,14 +238,14 @@ class AnimationFrameQueueItem implements IDisposable { inAnimationFrameRunner = true; while (CURRENT_QUEUE.length > 0) { CURRENT_QUEUE.sort(AnimationFrameQueueItem.sort); - let top = CURRENT_QUEUE.shift()!; + const top = CURRENT_QUEUE.shift()!; top.execute(); } inAnimationFrameRunner = false; }; scheduleAtNextAnimationFrame = (runner: () => void, priority: number = 0) => { - let item = new AnimationFrameQueueItem(runner, priority); + const item = new AnimationFrameQueueItem(runner, priority); NEXT_QUEUE.push(item); if (!animFrameRequested) { @@ -287,7 +258,7 @@ class AnimationFrameQueueItem implements IDisposable { runAtThisOrScheduleAtNextAnimationFrame = (runner: () => void, priority?: number) => { if (inAnimationFrameRunner) { - let item = new AnimationFrameQueueItem(runner, priority); + const item = new AnimationFrameQueueItem(runner, priority); CURRENT_QUEUE!.push(item); return item; } else { @@ -323,9 +294,9 @@ class TimeoutThrottledDomListener extends Disposable { let lastEvent: R | null = null; let lastHandlerTime = 0; - let timeout = this._register(new TimeoutTimer()); + const timeout = this._register(new TimeoutTimer()); - let invokeHandler = () => { + const invokeHandler = () => { lastHandlerTime = (new Date()).getTime(); handler(lastEvent); lastEvent = null; @@ -334,7 +305,7 @@ class TimeoutThrottledDomListener extends Disposable { this._register(addDisposableListener(node, type, (e) => { lastEvent = eventMerger(lastEvent, e); - let elapsedTime = (new Date()).getTime() - lastHandlerTime; + const elapsedTime = (new Date()).getTime() - lastHandlerTime; if (elapsedTime >= minimumTimeMs) { timeout.cancel(); @@ -392,7 +363,7 @@ class SizeUtils { } private static getDimension(element: HTMLElement, cssPropertyName: string, jsPropertyName: string): number { - let computedStyle: CSSStyleDeclaration = getComputedStyle(element); + const computedStyle: CSSStyleDeclaration = getComputedStyle(element); let value = '0'; if (computedStyle) { if (computedStyle.getPropertyValue) { @@ -568,7 +539,7 @@ export function position(element: HTMLElement, top: number, right?: number, bott * Returns the position of a dom node relative to the entire page. */ export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePosition { - let bb = domNode.getBoundingClientRect(); + const bb = domNode.getBoundingClientRect(); return { left: bb.left + StandardWindow.scrollX, top: bb.top + StandardWindow.scrollY, @@ -577,6 +548,24 @@ export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePositi }; } +/** + * Returns the effective zoom on a given element before window zoom level is applied + */ +export function getDomNodeZoomLevel(domNode: HTMLElement): number { + let testElement: HTMLElement | null = domNode; + let zoom = 1.0; + do { + const elementZoomLevel = (getComputedStyle(testElement) as any).zoom; + if (elementZoomLevel !== null && elementZoomLevel !== undefined && elementZoomLevel !== '1') { + zoom *= elementZoomLevel; + } + + testElement = testElement.parentElement; + } while (testElement !== null && testElement !== document.documentElement); + + return zoom; +} + export interface IStandardWindow { readonly scrollX: number; readonly scrollY: number; @@ -605,33 +594,33 @@ export const StandardWindow: IStandardWindow = new class implements IStandardWin // Adapted from WinJS // Gets the width of the element, including margins. export function getTotalWidth(element: HTMLElement): number { - let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element); + const margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element); return element.offsetWidth + margin; } export function getContentWidth(element: HTMLElement): number { - let border = SizeUtils.getBorderLeftWidth(element) + SizeUtils.getBorderRightWidth(element); - let padding = SizeUtils.getPaddingLeft(element) + SizeUtils.getPaddingRight(element); + const border = SizeUtils.getBorderLeftWidth(element) + SizeUtils.getBorderRightWidth(element); + const padding = SizeUtils.getPaddingLeft(element) + SizeUtils.getPaddingRight(element); return element.offsetWidth - border - padding; } export function getTotalScrollWidth(element: HTMLElement): number { - let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element); + const margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element); return element.scrollWidth + margin; } // Adapted from WinJS // Gets the height of the content of the specified element. The content height does not include borders or padding. export function getContentHeight(element: HTMLElement): number { - let border = SizeUtils.getBorderTopWidth(element) + SizeUtils.getBorderBottomWidth(element); - let padding = SizeUtils.getPaddingTop(element) + SizeUtils.getPaddingBottom(element); + const border = SizeUtils.getBorderTopWidth(element) + SizeUtils.getBorderBottomWidth(element); + const padding = SizeUtils.getPaddingTop(element) + SizeUtils.getPaddingBottom(element); return element.offsetHeight - border - padding; } // Adapted from WinJS // Gets the height of the element, including its margins. export function getTotalHeight(element: HTMLElement): number { - let margin = SizeUtils.getMarginTop(element) + SizeUtils.getMarginBottom(element); + const margin = SizeUtils.getMarginTop(element) + SizeUtils.getMarginBottom(element); return element.offsetHeight + margin; } @@ -641,16 +630,16 @@ function getRelativeLeft(element: HTMLElement, parent: HTMLElement): number { return 0; } - let elementPosition = getTopLeftOffset(element); - let parentPosition = getTopLeftOffset(parent); + const elementPosition = getTopLeftOffset(element); + const parentPosition = getTopLeftOffset(parent); return elementPosition.left - parentPosition.left; } export function getLargestChildWidth(parent: HTMLElement, children: HTMLElement[]): number { - let childWidths = children.map((child) => { + const childWidths = children.map((child) => { return Math.max(getTotalScrollWidth(child), getTotalWidth(child)) + getRelativeLeft(child, parent) || 0; }); - let maxWidth = Math.max(...childWidths); + const maxWidth = Math.max(...childWidths); return maxWidth; } @@ -769,7 +758,7 @@ export function getActiveElement(): Element | null { } export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement { - let style = document.createElement('style'); + const style = document.createElement('style'); style.type = 'text/css'; style.media = 'screen'; container.appendChild(style); @@ -777,7 +766,7 @@ export function createStyleSheet(container: HTMLElement = document.getElementsBy } export function createMetaElement(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLMetaElement { - let meta = document.createElement('meta'); + const meta = document.createElement('meta'); container.appendChild(meta); return meta; } @@ -815,10 +804,10 @@ export function removeCSSRulesContainingSelector(ruleName: string, style: HTMLSt return; } - let rules = getDynamicStyleSheetRules(style); - let toDelete: number[] = []; + const rules = getDynamicStyleSheetRules(style); + const toDelete: number[] = []; for (let i = 0; i < rules.length; i++) { - let rule = rules[i]; + const rule = rules[i]; if (rule.selectorText.indexOf(ruleName) !== -1) { toDelete.push(i); } @@ -852,6 +841,7 @@ export const EventType = { POINTER_UP: 'pointerup', POINTER_DOWN: 'pointerdown', POINTER_MOVE: 'pointermove', + POINTER_LEAVE: 'pointerleave', CONTEXT_MENU: 'contextmenu', WHEEL: 'wheel', // Keyboard @@ -928,7 +918,7 @@ export interface IFocusTracker extends Disposable { } export function saveParentsScrollTop(node: Element): number[] { - let r: number[] = []; + const r: number[] = []; for (let i = 0; node && node.nodeType === node.ELEMENT_NODE; i++) { r[i] = node.scrollTop; node = node.parentNode; @@ -988,7 +978,7 @@ class FocusTracker extends Disposable implements IFocusTracker { }; this._refreshStateHandler = () => { - let currentNodeHasFocus = FocusTracker.hasFocusWithin(element); + const currentNodeHasFocus = FocusTracker.hasFocusWithin(element); if (currentNodeHasFocus !== hasFocus) { if (hasFocus) { onBlur(); @@ -1048,7 +1038,7 @@ export enum Namespace { } function _$(namespace: Namespace, description: string, attrs?: { [key: string]: any }, ...children: Array): T { - let match = SELECTOR_REGEX.exec(description); + const match = SELECTOR_REGEX.exec(description); if (!match) { throw new Error('Bad use of emmet'); @@ -1056,7 +1046,7 @@ function _$(namespace: Namespace, description: string, attrs? attrs = { ...(attrs || {}) }; - let tagName = match[1] || 'div'; + const tagName = match[1] || 'div'; let result: T; if (namespace !== Namespace.HTML) { @@ -1123,14 +1113,14 @@ export function join(nodes: Node[], separator: Node | string): Node[] { } export function show(...elements: HTMLElement[]): void { - for (let element of elements) { + for (const element of elements) { element.style.display = ''; element.removeAttribute('aria-hidden'); } } export function hide(...elements: HTMLElement[]): void { - for (let element of elements) { + for (const element of elements) { element.style.display = 'none'; element.setAttribute('aria-hidden', 'true'); } @@ -1158,7 +1148,7 @@ export function removeTabIndexAndUpdateFocus(node: HTMLElement): void { // typically never want that, rather put focus to the closest element // in the hierarchy of the parent DOM nodes. if (document.activeElement === node) { - let parentFocusable = findParentWithAttribute(node.parentElement, 'tabIndex'); + const parentFocusable = findParentWithAttribute(node.parentElement, 'tabIndex'); if (parentFocusable) { parentFocusable.focus(); } @@ -1289,7 +1279,7 @@ RemoteAuthorities.setPreferredWebSchema(/^https:/.test(window.location.href) ? ' /** * returns url('...') */ -export function asCSSUrl(uri: URI): string { +export function asCSSUrl(uri: URI | null | undefined): string { if (!uri) { return `url('')`; } @@ -1669,25 +1659,12 @@ export function getCookieValue(name: string): string | undefined { return match ? match.pop() : undefined; } -export const enum ZIndex { - SASH = 35, - SuggestWidget = 40, - Hover = 50, - DragImage = 1000, - MenubarMenuItemsHolder = 2000, // quick-input-widget - ContextView = 2500, - ModalDialog = 2600, - PaneDropOverlay = 10000 -} - - export interface IDragAndDropObserverCallbacks { readonly onDragEnter: (e: DragEvent) => void; readonly onDragLeave: (e: DragEvent) => void; readonly onDrop: (e: DragEvent) => void; readonly onDragEnd: (e: DragEvent) => void; - - readonly onDragOver?: (e: DragEvent) => void; + readonly onDragOver?: (e: DragEvent, dragDuration: number) => void; } export class DragAndDropObserver extends Disposable { @@ -1698,6 +1675,9 @@ export class DragAndDropObserver extends Disposable { // repeadedly. private counter: number = 0; + // Allows to measure the duration of the drag operation. + private dragStartTime = 0; + constructor(private readonly element: HTMLElement, private readonly callbacks: IDragAndDropObserverCallbacks) { super(); @@ -1707,6 +1687,7 @@ export class DragAndDropObserver extends Disposable { private registerListeners(): void { this._register(addDisposableListener(this.element, EventType.DRAG_ENTER, (e: DragEvent) => { this.counter++; + this.dragStartTime = e.timeStamp; this.callbacks.onDragEnter(e); })); @@ -1714,27 +1695,194 @@ export class DragAndDropObserver extends Disposable { this._register(addDisposableListener(this.element, EventType.DRAG_OVER, (e: DragEvent) => { e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - if (this.callbacks.onDragOver) { - this.callbacks.onDragOver(e); - } + this.callbacks.onDragOver?.(e, e.timeStamp - this.dragStartTime); })); this._register(addDisposableListener(this.element, EventType.DRAG_LEAVE, (e: DragEvent) => { this.counter--; if (this.counter === 0) { + this.dragStartTime = 0; + this.callbacks.onDragLeave(e); } })); this._register(addDisposableListener(this.element, EventType.DRAG_END, (e: DragEvent) => { this.counter = 0; + this.dragStartTime = 0; + this.callbacks.onDragEnd(e); })); this._register(addDisposableListener(this.element, EventType.DROP, (e: DragEvent) => { this.counter = 0; + this.dragStartTime = 0; + this.callbacks.onDrop(e); })); } } + +export function computeClippingRect(elementOrRect: HTMLElement | DOMRectReadOnly, clipper: HTMLElement) { + const frameRect = (elementOrRect instanceof HTMLElement ? elementOrRect.getBoundingClientRect() : elementOrRect); + const rootRect = clipper.getBoundingClientRect(); + + const top = Math.max(rootRect.top - frameRect.top, 0); + const right = Math.max(frameRect.width - (frameRect.right - rootRect.right), 0); + const bottom = Math.max(frameRect.height - (frameRect.bottom - rootRect.bottom), 0); + const left = Math.max(rootRect.left - frameRect.left, 0); + + return { top, right, bottom, left }; +} + +type HTMLElementAttributeKeys = Partial<{ [K in keyof T]: T[K] extends Function ? never : T[K] extends object ? HTMLElementAttributeKeys : T[K] }>; +type ElementAttributes = HTMLElementAttributeKeys & Record; +type RemoveHTMLElement = T extends HTMLElement ? never : T; +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; +type ArrayToObj = UnionToIntersection>; +type HHTMLElementTagNameMap = HTMLElementTagNameMap & { '': HTMLDivElement }; + +type TagToElement = T extends `${infer TStart}#${string}` + ? TStart extends keyof HHTMLElementTagNameMap + ? HHTMLElementTagNameMap[TStart] + : HTMLElement + : T extends `${infer TStart}.${string}` + ? TStart extends keyof HHTMLElementTagNameMap + ? HHTMLElementTagNameMap[TStart] + : HTMLElement + : T extends keyof HTMLElementTagNameMap + ? HTMLElementTagNameMap[T] + : HTMLElement; + +type TagToElementAndId = TTag extends `${infer TTag}@${infer TId}` + ? { element: TagToElement; id: TId } + : { element: TagToElement; id: 'root' }; + +type TagToRecord = TagToElementAndId extends { element: infer TElement; id: infer TId } + ? Record<(TId extends string ? TId : never) | 'root', TElement> + : never; + +type Child = HTMLElement | string | Record; +type Children = [] + | [Child] + | [Child, Child] + | [Child, Child, Child] + | [Child, Child, Child, Child] + | [Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child] + | [Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child, Child]; + +const H_REGEX = /(?[\w\-]+)?(?:#(?[\w\-]+))?(?(?:\.(?:[\w\-]+))*)(?:@(?(?:[\w\_])+))?/; + +/** + * A helper function to create nested dom nodes. + * + * + * ```ts + * const elements = h('div.code-view', [ + * h('div.title@title'), + * h('div.container', [ + * h('div.gutter@gutterDiv'), + * h('div@editor'), + * ]), + * ]); + * const editor = createEditor(elements.editor); + * ``` +*/ +export function h + (tag: TTag): + TagToRecord extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; + +export function h + (tag: TTag, children: T): + (ArrayToObj & TagToRecord) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; + +export function h + (tag: TTag, attributes: Partial>>): + TagToRecord extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; + +export function h + (tag: TTag, attributes: Partial>>, children: T): + (ArrayToObj & TagToRecord) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; + +export function h(tag: string, ...args: [] | [attributes: { $: string } & Partial> | Record, children?: any[]] | [children: any[]]): Record { + let attributes: { $?: string } & Partial>; + let children: (Record | HTMLElement)[] | undefined; + + if (Array.isArray(args[0])) { + attributes = {}; + children = args[0]; + } else { + attributes = args[0] as any || {}; + children = args[1]; + } + + const match = H_REGEX.exec(tag); + + if (!match || !match.groups) { + throw new Error('Bad use of h'); + } + + const tagName = match.groups['tag'] || 'div'; + const el = document.createElement(tagName); + + if (match.groups['id']) { + el.id = match.groups['id']; + } + + if (match.groups['class']) { + el.className = match.groups['class'].replace(/\./g, ' ').trim(); + } + + const result: Record = {}; + + if (match.groups['name']) { + result[match.groups['name']] = el; + } + + if (children) { + for (const c of children) { + if (c instanceof HTMLElement) { + el.appendChild(c); + } else if (typeof c === 'string') { + el.append(c); + } else { + Object.assign(result, c); + el.appendChild(c.root); + } + } + } + + for (const [key, value] of Object.entries(attributes)) { + if (key === 'style') { + for (const [cssKey, cssValue] of Object.entries(value)) { + el.style.setProperty( + camelCaseToHyphenCase(cssKey), + typeof cssValue === 'number' ? cssValue + 'px' : '' + cssValue + ); + } + } else if (key === 'tabIndex') { + el.tabIndex = value; + } else { + el.setAttribute(camelCaseToHyphenCase(key), value.toString()); + } + } + + result['root'] = el; + + return result; +} + +function camelCaseToHyphenCase(str: string) { + return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); +} diff --git a/src/vs/base/browser/formattedTextRenderer.ts b/src/vs/base/browser/formattedTextRenderer.ts index c8a8b668d2..7a37d820c4 100644 --- a/src/vs/base/browser/formattedTextRenderer.ts +++ b/src/vs/base/browser/formattedTextRenderer.ts @@ -8,7 +8,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { DisposableStore } from 'vs/base/common/lifecycle'; export interface IContentActionHandler { - callback: (content: string, event?: IMouseEvent) => void; + callback: (content: string, event: IMouseEvent) => void; readonly disposables: DisposableStore; } diff --git a/src/vs/base/browser/globalPointerMoveMonitor.ts b/src/vs/base/browser/globalPointerMoveMonitor.ts index 95e6f3d16a..9acd7ece52 100644 --- a/src/vs/base/browser/globalPointerMoveMonitor.ts +++ b/src/vs/base/browser/globalPointerMoveMonitor.ts @@ -6,40 +6,18 @@ import * as dom from 'vs/base/browser/dom'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -export interface IPointerMoveEventData { - leftButton: boolean; - buttons: number; - pageX: number; - pageY: number; -} - -export interface IEventMerger { - (lastEvent: R | null, currentEvent: PointerEvent): R; -} - -export interface IPointerMoveCallback { - (pointerMoveData: R): void; +export interface IPointerMoveCallback { + (event: PointerEvent): void; } export interface IOnStopCallback { (browserEvent?: PointerEvent | KeyboardEvent): void; } -export function standardPointerMoveMerger(lastEvent: IPointerMoveEventData | null, currentEvent: PointerEvent): IPointerMoveEventData { - currentEvent.preventDefault(); - return { - leftButton: (currentEvent.button === 0), - buttons: currentEvent.buttons, - pageX: currentEvent.pageX, - pageY: currentEvent.pageY - }; -} - -export class GlobalPointerMoveMonitor implements IDisposable { +export class GlobalPointerMoveMonitor implements IDisposable { private readonly _hooks = new DisposableStore(); - private _pointerMoveEventMerger: IEventMerger | null = null; - private _pointerMoveCallback: IPointerMoveCallback | null = null; + private _pointerMoveCallback: IPointerMoveCallback | null = null; private _onStopCallback: IOnStopCallback | null = null; public dispose(): void { @@ -55,7 +33,6 @@ export class GlobalPointerMoveMonitor, - pointerMoveCallback: IPointerMoveCallback, + pointerMoveCallback: IPointerMoveCallback, onStopCallback: IOnStopCallback ): void { if (this.isMonitoring()) { this.stopMonitoring(false); } - this._pointerMoveEventMerger = pointerMoveEventMerger; this._pointerMoveCallback = pointerMoveCallback; this._onStopCallback = onStopCallback; @@ -103,18 +78,19 @@ export class GlobalPointerMoveMonitor( + this._hooks.add(dom.addDisposableListener( eventSource, dom.EventType.POINTER_MOVE, - (data: R) => { - if (data.buttons !== initialButtons) { + (e) => { + if (e.buttons !== initialButtons) { // Buttons state has changed in the meantime this.stopMonitoring(true); return; } - this._pointerMoveCallback!(data); - }, - (lastEvent: R | null, currentEvent) => this._pointerMoveEventMerger!(lastEvent, currentEvent) + + e.preventDefault(); + this._pointerMoveCallback!(e); + } )); this._hooks.add(dom.addDisposableListener( diff --git a/src/vs/base/browser/history.ts b/src/vs/base/browser/history.ts index 48fbb8218f..a78482e249 100644 --- a/src/vs/base/browser/history.ts +++ b/src/vs/base/browser/history.ts @@ -3,10 +3,18 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; + export interface IHistoryNavigationWidget { + readonly element: HTMLElement; + showPreviousValue(): void; showNextValue(): void; + onDidFocus: Event; + + onDidBlur: Event; + } diff --git a/src/vs/base/browser/iframe.ts b/src/vs/base/browser/iframe.ts index baa78fd7b1..5564597f44 100644 --- a/src/vs/base/browser/iframe.ts +++ b/src/vs/base/browser/iframe.ts @@ -27,8 +27,8 @@ function getParentWindowIfSameOrigin(w: Window): Window | null { // Cannot really tell if we have access to the parent window unless we try to access something in it try { - let location = w.location; - let parentLocation = w.parent.location; + const location = w.location; + const parentLocation = w.parent.location; if (location.origin !== 'null' && parentLocation.origin !== 'null' && location.origin !== parentLocation.origin) { hasDifferentOriginAncestorFlag = true; return null; @@ -97,7 +97,7 @@ export class IframeUtils { let top = 0, left = 0; - let windowChain = this.getSameOriginWindowChain(); + const windowChain = this.getSameOriginWindowChain(); for (const windowChainEl of windowChain) { @@ -112,7 +112,7 @@ export class IframeUtils { break; } - let boundingRect = windowChainEl.iframeElement.getBoundingClientRect(); + const boundingRect = windowChainEl.iframeElement.getBoundingClientRect(); top += boundingRect.top; left += boundingRect.left; } diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index 5cb39b4191..7081ff88af 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -14,6 +14,13 @@ class MissingStoresError extends Error { } } +export class DBClosedError extends Error { + readonly code = 'DBClosed'; + constructor(dbName: string) { + super(`IndexedDB database '${dbName}' is closed.`); + } +} + export class IndexedDB { static async create(name: string, version: number | undefined, stores: string[]): Promise { @@ -21,7 +28,7 @@ export class IndexedDB { return new IndexedDB(database, name); } - static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise { + private static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise { mark(`code/willOpenDatabase/${name}`); try { return await IndexedDB.doOpenDatabase(name, version, stores); @@ -109,7 +116,7 @@ export class IndexedDB { runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest): Promise; async runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest | IDBRequest[]): Promise { if (!this.database) { - throw new Error(`IndexedDB database '${this.name}' is not opened.`); + throw new DBClosedError(this.name); } const transaction = this.database.transaction(store, transactionMode); this.pendingTransactions.push(transaction); @@ -128,7 +135,7 @@ export class IndexedDB { async getKeyValues(store: string, isValid: (value: unknown) => value is V): Promise> { if (!this.database) { - throw new Error(`IndexedDB database '${this.name}' is not opened.`); + throw new DBClosedError(this.name); } const transaction = this.database.transaction(store, 'readonly'); this.pendingTransactions.push(transaction); diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 9bb731a64c..dd6441db7e 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -13,7 +13,7 @@ import * as platform from 'vs/base/common/platform'; function extractKeyCode(e: KeyboardEvent): KeyCode { if (e.charCode) { // "keypress" events mostly - let char = String.fromCharCode(e.charCode).toUpperCase(); + const char = String.fromCharCode(e.charCode).toUpperCase(); return KeyCodeUtils.fromString(char); } @@ -77,7 +77,7 @@ const shiftKeyMod = KeyMod.Shift; const metaKeyMod = (platform.isMacintosh ? KeyMod.CtrlCmd : KeyMod.WinCtrl); export function printKeyboardEvent(e: KeyboardEvent): string { - let modifiers: string[] = []; + const modifiers: string[] = []; if (e.ctrlKey) { modifiers.push(`ctrl`); } @@ -94,7 +94,7 @@ export function printKeyboardEvent(e: KeyboardEvent): string { } export function printStandardKeyboardEvent(e: StandardKeyboardEvent): string { - let modifiers: string[] = []; + const modifiers: string[] = []; if (e.ctrlKey) { modifiers.push(`ctrl`); } @@ -128,7 +128,7 @@ export class StandardKeyboardEvent implements IKeyboardEvent { private _asRuntimeKeybinding: SimpleKeybinding; constructor(source: KeyboardEvent) { - let e = source; + const e = source; this.browserEvent = e; this.target = e.target; diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 24957ad6fe..86cac17c99 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -9,11 +9,9 @@ import { DomEmitter } from 'vs/base/browser/event'; import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { raceCancellation } from 'vs/base/common/async'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; -import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; +import { IMarkdownString, escapeDoubleQuotes, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; import { markdownEscapeEscapedIcons } from 'vs/base/common/iconLabels'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -44,8 +42,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const disposables = new DisposableStore(); let isDisposed = false; - const cts = disposables.add(new CancellationTokenSource()); - const element = createElement(options); const _uriMassage = function (part: string): string { @@ -96,11 +92,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende return uri.toString(); }; - // signal to code-block render that the - // element has been created - let signalInnerHTML: () => void; - const withInnerHTML = new Promise(c => signalInnerHTML = c); - const renderer = new marked.Renderer(); renderer.image = (href: string, title: string, text: string) => { @@ -108,13 +99,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende let attributes: string[] = []; if (href) { ({ href, dimensions } = parseHrefAndDimensions(href)); - attributes.push(`src="${href}"`); + attributes.push(`src="${escapeDoubleQuotes(href)}"`); } if (text) { - attributes.push(`alt="${text}"`); + attributes.push(`alt="${escapeDoubleQuotes(text)}"`); } if (title) { - attributes.push(`title="${title}"`); + attributes.push(`title="${escapeDoubleQuotes(title)}"`); } if (dimensions.length) { attributes = attributes.concat(dimensions); @@ -130,53 +121,30 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende if (href === text) { // raw link case text = removeMarkdownEscapes(text); } - href = _href(href, false); - if (markdown.baseUri) { - href = resolveWithBaseUri(URI.from(markdown.baseUri), href); - } - title = typeof title === 'string' ? removeMarkdownEscapes(title) : ''; - href = removeMarkdownEscapes(href); - if ( - !href - || /^data:|javascript:/i.test(href) - || (/^command:/i.test(href) && !markdown.isTrusted) - || /^command:(\/\/\/)?_workbench\.downloadResource/i.test(href) - ) { - // drop the link - return text; - } else { - // HTML Encode href - href = href.replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); - return `${text}`; - } + title = typeof title === 'string' ? escapeDoubleQuotes(removeMarkdownEscapes(title)) : ''; + href = removeMarkdownEscapes(href); + + // HTML Encode href + href = href.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + return `${text}`; }; renderer.paragraph = (text): string => { return `

${text}

`; }; + // Will collect [id, renderedElement] tuples + const codeBlocks: Promise<[string, HTMLElement]>[] = []; + if (options.codeBlockRenderer) { renderer.code = (code, lang) => { - const value = options.codeBlockRenderer!(lang ?? '', code); - // when code-block rendering is async we return sync - // but update the node with the real result later. const id = defaultGenerator.nextId(); - raceCancellation(Promise.all([value, withInnerHTML]), cts.token).then(values => { - if (!isDisposed && values) { - const span = element.querySelector(`div[data-code="${id}"]`); - if (span) { - DOM.reset(span, values[0]); - } - options.asyncRenderCallback?.(); - } - }).catch(() => { - // ignore - }); - + const value = options.codeBlockRenderer!(lang ?? '', code); + codeBlocks.push(value.then(element => [id, element])); return `
${escape(code)}
`; }; } @@ -268,10 +236,45 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende } }); + markdownHtmlDoc.body.querySelectorAll('a') + .forEach(a => { + const href = a.getAttribute('href'); // Get the raw 'href' attribute value as text, not the resolved 'href' + a.setAttribute('href', ''); // Clear out href. We use the `data-href` for handling clicks instead + if ( + !href + || /^data:|javascript:/i.test(href) + || (/^command:/i.test(href) && !markdown.isTrusted) + || /^command:(\/\/\/)?_workbench\.downloadResource/i.test(href) + ) { + // drop the link + a.replaceWith(...a.childNodes); + } else { + let resolvedHref = _href(href, false); + if (markdown.baseUri) { + resolvedHref = resolveWithBaseUri(URI.from(markdown.baseUri), href); + } + a.dataset.href = resolvedHref; + } + }); + element.innerHTML = sanitizeRenderedMarkdown(markdown, markdownHtmlDoc.body.innerHTML) as unknown as string; - // signal that async code blocks can be now be inserted - signalInnerHTML!(); + if (codeBlocks.length > 0) { + Promise.all(codeBlocks).then((tuples) => { + if (isDisposed) { + return; + } + const renderedElements = new Map(tuples); + const placeholderElements = element.querySelectorAll(`div[data-code]`); + for (const placeholderElement of placeholderElements) { + const renderedElement = renderedElements.get(placeholderElement.dataset['code'] ?? ''); + if (renderedElement) { + DOM.reset(placeholderElement, renderedElement); + } + } + options.asyncRenderCallback?.(); + }); + } // signal size changes for image tags if (options.asyncRenderCallback) { @@ -287,7 +290,6 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende element, dispose: () => { isDisposed = true; - cts.cancel(); disposables.dispose(); } }; diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index 95f0d67d27..c18f6fbe5f 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -74,7 +74,7 @@ export class StandardMouseEvent implements IMouseEvent { } // Find the position of the iframe this code is executing in relative to the iframe where the event was captured. - let iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(self, e.view); + const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(self, e.view); this.posx -= iframeOffsets.left; this.posy -= iframeOffsets.top; } @@ -152,8 +152,8 @@ export class StandardWheelEvent { if (e) { // Old (deprecated) wheel events - let e1 = e; - let e2 = e; + const e1 = e; + const e2 = e; // vertical delta scroll if (typeof e1.wheelDeltaY !== 'undefined') { diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 7862280764..f30c6dc8e2 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -146,7 +146,7 @@ export class Gesture extends Disposable { } private onTouchStart(e: TouchEvent): void { - let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. + const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. if (this.handle) { this.handle.dispose(); @@ -154,7 +154,7 @@ export class Gesture extends Disposable { } for (let i = 0, len = e.targetTouches.length; i < len; i++) { - let touch = e.targetTouches.item(i); + const touch = e.targetTouches.item(i); this.activeTouches[touch.identifier] = { id: touch.identifier, @@ -167,7 +167,7 @@ export class Gesture extends Disposable { rollingPageY: [touch.pageY] }; - let evt = this.newGestureEvent(EventType.Start, touch.target); + const evt = this.newGestureEvent(EventType.Start, touch.target); evt.pageX = touch.pageX; evt.pageY = touch.pageY; this.dispatchEvent(evt); @@ -181,27 +181,27 @@ export class Gesture extends Disposable { } private onTouchEnd(e: TouchEvent): void { - let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. + const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. - let activeTouchCount = Object.keys(this.activeTouches).length; + const activeTouchCount = Object.keys(this.activeTouches).length; for (let i = 0, len = e.changedTouches.length; i < len; i++) { - let touch = e.changedTouches.item(i); + const touch = e.changedTouches.item(i); if (!this.activeTouches.hasOwnProperty(String(touch.identifier))) { console.warn('move of an UNKNOWN touch', touch); continue; } - let data = this.activeTouches[touch.identifier], + const data = this.activeTouches[touch.identifier], holdTime = Date.now() - data.initialTimeStamp; if (holdTime < Gesture.HOLD_DELAY && Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)) < 30 && Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)) < 30) { - let evt = this.newGestureEvent(EventType.Tap, data.initialTarget); + const evt = this.newGestureEvent(EventType.Tap, data.initialTarget); evt.pageX = arrays.tail(data.rollingPageX); evt.pageY = arrays.tail(data.rollingPageY); this.dispatchEvent(evt); @@ -210,18 +210,18 @@ export class Gesture extends Disposable { && Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)) < 30 && Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)) < 30) { - let evt = this.newGestureEvent(EventType.Contextmenu, data.initialTarget); + const evt = this.newGestureEvent(EventType.Contextmenu, data.initialTarget); evt.pageX = arrays.tail(data.rollingPageX); evt.pageY = arrays.tail(data.rollingPageY); this.dispatchEvent(evt); } else if (activeTouchCount === 1) { - let finalX = arrays.tail(data.rollingPageX); - let finalY = arrays.tail(data.rollingPageY); + const finalX = arrays.tail(data.rollingPageX); + const finalY = arrays.tail(data.rollingPageY); - let deltaT = arrays.tail(data.rollingTimestamps) - data.rollingTimestamps[0]; - let deltaX = finalX - data.rollingPageX[0]; - let deltaY = finalY - data.rollingPageY[0]; + const deltaT = arrays.tail(data.rollingTimestamps) - data.rollingTimestamps[0]; + const deltaX = finalX - data.rollingPageX[0]; + const deltaY = finalY - data.rollingPageY[0]; // We need to get all the dispatch targets on the start of the inertia event const dispatchTo = this.targets.filter(t => data.initialTarget instanceof Node && t.contains(data.initialTarget)); @@ -249,7 +249,7 @@ export class Gesture extends Disposable { } private newGestureEvent(type: string, initialTarget?: EventTarget): GestureEvent { - let event = document.createEvent('CustomEvent') as unknown as GestureEvent; + const event = document.createEvent('CustomEvent') as unknown as GestureEvent; event.initEvent(type, false, true); event.initialTarget = initialTarget; event.tapCount = 0; @@ -289,12 +289,12 @@ export class Gesture extends Disposable { private inertia(dispatchTo: EventTarget[], t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void { this.handle = DomUtils.scheduleAtNextAnimationFrame(() => { - let now = Date.now(); + const now = Date.now(); // velocity: old speed + accel_over_time - let deltaT = now - t1, - delta_pos_x = 0, delta_pos_y = 0, - stopped = true; + const deltaT = now - t1; + let delta_pos_x = 0, delta_pos_y = 0; + let stopped = true; vX += Gesture.SCROLL_FRICTION * deltaT; vY += Gesture.SCROLL_FRICTION * deltaT; @@ -310,7 +310,7 @@ export class Gesture extends Disposable { } // dispatch translation event - let evt = this.newGestureEvent(EventType.Change); + const evt = this.newGestureEvent(EventType.Change); evt.translationX = delta_pos_x; evt.translationY = delta_pos_y; dispatchTo.forEach(d => d.dispatchEvent(evt)); @@ -322,20 +322,20 @@ export class Gesture extends Disposable { } private onTouchMove(e: TouchEvent): void { - let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. + const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. for (let i = 0, len = e.changedTouches.length; i < len; i++) { - let touch = e.changedTouches.item(i); + const touch = e.changedTouches.item(i); if (!this.activeTouches.hasOwnProperty(String(touch.identifier))) { console.warn('end of an UNKNOWN touch', touch); continue; } - let data = this.activeTouches[touch.identifier]; + const data = this.activeTouches[touch.identifier]; - let evt = this.newGestureEvent(EventType.Change, data.initialTarget); + const evt = this.newGestureEvent(EventType.Change, data.initialTarget); evt.translationX = touch.pageX - arrays.tail(data.rollingPageX); evt.translationY = touch.pageY - arrays.tail(data.rollingPageY); evt.pageX = touch.pageX; diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index af5ca1f46d..a4fe9f3df5 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -9,6 +9,8 @@ import { $, addDisposableListener, append, EventHelper, EventLike, EventType } f import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { ISelectBoxOptions, ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { Action, ActionRunner, IAction, IActionChangeEvent, IActionRunner, Separator } from 'vs/base/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -21,6 +23,7 @@ export interface IBaseActionViewItemOptions { draggable?: boolean; isMenu?: boolean; useEventAsContext?: boolean; + hoverDelegate?: IHoverDelegate; } export class BaseActionViewItem extends Disposable implements IActionViewItem { @@ -28,7 +31,9 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { element: HTMLElement | undefined; _context: unknown; - _action: IAction; + readonly _action: IAction; + + private customHover?: ICustomHover; get action() { return this._action; @@ -213,8 +218,27 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { // implement in subclass } + protected getTooltip(): string | undefined { + return this.getAction().tooltip; + } + protected updateTooltip(): void { - // implement in subclass + if (!this.element) { + return; + } + const title = this.getTooltip() ?? ''; + this.element.setAttribute('aria-label', title); + if (!this.options.hoverDelegate) { + this.element.title = title; + } else { + this.element.title = ''; + if (!this.customHover) { + this.customHover = setupCustomHover(this.options.hoverDelegate, this.element, title); + this._store.add(this.customHover); + } else { + this.customHover.update(title); + } + } } protected updateClass(): void { @@ -323,7 +347,7 @@ export class ActionViewItem extends BaseActionViewItem { } } - override updateTooltip(): void { + override getTooltip() { let title: string | null = null; if (this.getAction().tooltip) { @@ -336,11 +360,7 @@ export class ActionViewItem extends BaseActionViewItem { title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding); } } - - if (title && this.label) { - this.label.title = title; - this.label.setAttribute('aria-label', title); - } + return title ?? undefined; } override updateClass(): void { @@ -372,9 +392,7 @@ export class ActionViewItem extends BaseActionViewItem { this.updateEnabled(); } else { - if (this.label) { - this.label.classList.remove('codicon'); - } + this.label?.classList.remove('codicon'); } } @@ -385,18 +403,14 @@ export class ActionViewItem extends BaseActionViewItem { this.label.classList.remove('disabled'); } - if (this.element) { - this.element.classList.remove('disabled'); - } + this.element?.classList.remove('disabled'); } else { if (this.label) { this.label.setAttribute('aria-disabled', 'true'); this.label.classList.add('disabled'); } - if (this.element) { - this.element.classList.add('disabled'); - } + this.element?.classList.add('disabled'); } } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 3fd79799a4..d1a3bff9a8 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -6,6 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { ActionRunner, IAction, IActionRunner, IRunEvent, Separator } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -49,6 +50,7 @@ export interface IActionBarOptions { readonly allowContextMenu?: boolean; readonly preventLoopNavigation?: boolean; readonly focusOnlyEnabledItems?: boolean; + readonly hoverDelegate?: IHoverDelegate; } export interface IActionOptions extends IActionViewItemOptions { @@ -327,7 +329,7 @@ export class ActionBar extends Disposable implements IActionRunner { } if (!item) { - item = new ActionViewItem(this.context, action, options); + item = new ActionViewItem(this.context, action, { hoverDelegate: this.options.hoverDelegate, ...options }); } // Prevent native context menu on actions diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 60d1ed117a..642145c356 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -170,7 +170,7 @@ export class BreadcrumbsWidget { } domFocus(): void { - let idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1; + const idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1; if (idx >= 0 && idx < this._items.length) { this._focus(idx, undefined); } else { @@ -226,7 +226,7 @@ export class BreadcrumbsWidget { } reveal(item: BreadcrumbsItem): void { - let idx = this._items.indexOf(item); + const idx = this._items.indexOf(item); if (idx >= 0) { this._reveal(idx, false); } @@ -281,7 +281,7 @@ export class BreadcrumbsWidget { dispose(removed); this._focus(-1, undefined); } catch (e) { - let newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`); + const newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`); newError.name = e.name; newError.stack = e.stack; throw newError; @@ -291,8 +291,8 @@ export class BreadcrumbsWidget { private _render(start: number): void { let didChange = false; for (; start < this._items.length && start < this._nodes.length; start++) { - let item = this._items[start]; - let node = this._nodes[start]; + const item = this._items[start]; + const node = this._nodes[start]; this._renderItem(item, node); didChange = true; } @@ -308,8 +308,8 @@ export class BreadcrumbsWidget { // case b: more items -> render them for (; start < this._items.length; start++) { - let item = this._items[start]; - let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div'); + const item = this._items[start]; + const node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div'); if (node) { this._renderItem(item, node); this._domNode.appendChild(node); @@ -343,7 +343,7 @@ export class BreadcrumbsWidget { return; } for (let el: HTMLElement | null = event.target; el; el = el.parentElement) { - let idx = this._nodes.indexOf(el as HTMLDivElement); + const idx = this._nodes.indexOf(el as HTMLDivElement); if (idx >= 0) { this._focus(idx, event); this._select(idx, event); diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css index 71a2f6a8af..3781de7daa 100644 --- a/src/vs/base/browser/ui/button/button.css +++ b/src/vs/base/browser/ui/button/button.css @@ -38,8 +38,36 @@ cursor: pointer; } -.monaco-button-dropdown > .monaco-dropdown-button { - margin-left: 1px; +.monaco-button-dropdown.disabled { + cursor: default; +} + +.monaco-button-dropdown > .monaco-button:focus { + outline-offset: -1px !important; +} + +.monaco-button-dropdown.disabled > .monaco-button.disabled, +.monaco-button-dropdown.disabled > .monaco-button.disabled:focus, +.monaco-button-dropdown.disabled > .monaco-button-dropdown-separator { + opacity: 0.4 !important; +} + +.monaco-button-dropdown > .monaco-button.monaco-text-button { + border-right-width: 0 !important; +} + +.monaco-button-dropdown .monaco-button-dropdown-separator { + padding: 4px 0; + cursor: default; +} + +.monaco-button-dropdown .monaco-button-dropdown-separator > div { + height: 100%; + width: 1px; +} + +.monaco-button-dropdown > .monaco-button.monaco-dropdown-button { + border-left-width: 0 !important; } .monaco-description-button { diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 7786d3ef55..cfaa963689 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -15,6 +15,7 @@ import { Emitter, Event as BaseEvent } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { mixin } from 'vs/base/common/objects'; +import { localize } from 'vs/nls'; import 'vs/css!./button'; export interface IButtonOptions extends IButtonStyles { @@ -27,6 +28,7 @@ export interface IButtonStyles { buttonBackground?: Color; buttonHoverBackground?: Color; buttonForeground?: Color; + buttonSeparator?: Color; buttonSecondaryBackground?: Color; buttonSecondaryHoverBackground?: Color; buttonSecondaryForeground?: Color; @@ -41,6 +43,7 @@ export interface IButtonStyles { const defaultOptions: IButtonStyles = { buttonBackground: Color.fromHex('#0E639C'), buttonHoverBackground: Color.fromHex('#006BB3'), + buttonSeparator: Color.white, buttonForeground: Color.white }; @@ -154,8 +157,8 @@ export class Button extends Disposable implements IButton { // Also set hover background when button is focused for feedback this.focusTracker = this._register(trackFocus(this._element)); - this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground())); - this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles + this._register(this.focusTracker.onDidFocus(() => { if (this.enabled) { this.setHoverBackground(); } })); + this._register(this.focusTracker.onDidBlur(() => { if (this.enabled) { this.applyStyles(); } })); this.applyStyles(); } @@ -317,6 +320,7 @@ export interface IButtonWithDropdownOptions extends IButtonOptions { readonly contextMenuProvider: IContextMenuProvider; readonly actions: IAction[]; readonly actionRunner?: IActionRunner; + readonly addPrimaryActionToDropdown?: boolean; } export class ButtonWithDropdown extends Disposable implements IButton { @@ -324,6 +328,8 @@ export class ButtonWithDropdown extends Disposable implements IButton { private readonly button: Button; private readonly action: Action; private readonly dropdownButton: Button; + private readonly separatorContainer: HTMLDivElement; + private readonly separator: HTMLDivElement; readonly element: HTMLElement; private readonly _onDidClick = this._register(new Emitter()); @@ -340,13 +346,23 @@ export class ButtonWithDropdown extends Disposable implements IButton { this._register(this.button.onDidClick(e => this._onDidClick.fire(e))); this.action = this._register(new Action('primaryAction', this.button.label, undefined, true, async () => this._onDidClick.fire(undefined))); + this.separatorContainer = document.createElement('div'); + this.separatorContainer.classList.add('monaco-button-dropdown-separator'); + + this.separator = document.createElement('div'); + this.separatorContainer.appendChild(this.separator); + this.element.appendChild(this.separatorContainer); + this.dropdownButton = this._register(new Button(this.element, { ...options, title: false, supportIcons: true })); + this.dropdownButton.element.title = localize("button dropdown more actions", 'More Actions...'); + this.dropdownButton.element.setAttribute('aria-haspopup', 'true'); + this.dropdownButton.element.setAttribute('aria-expanded', 'false'); this.dropdownButton.element.classList.add('monaco-dropdown-button'); this.dropdownButton.icon = Codicon.dropDownButton; this._register(this.dropdownButton.onDidClick(e => { options.contextMenuProvider.showContextMenu({ getAnchor: () => this.dropdownButton.element, - getActions: () => [this.action, ...options.actions], + getActions: () => options.addPrimaryActionToDropdown === false ? [...options.actions] : [this.action, ...options.actions], actionRunner: options.actionRunner, onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false') }); @@ -366,6 +382,8 @@ export class ButtonWithDropdown extends Disposable implements IButton { set enabled(enabled: boolean) { this.button.enabled = enabled; this.dropdownButton.enabled = enabled; + + this.element.classList.toggle('disabled', !enabled); } get enabled(): boolean { @@ -375,6 +393,20 @@ export class ButtonWithDropdown extends Disposable implements IButton { style(styles: IButtonStyles): void { this.button.style(styles); this.dropdownButton.style(styles); + + // Separator + const border = styles.buttonBorder ? styles.buttonBorder.toString() : ''; + + this.separatorContainer.style.borderTopWidth = border ? '1px' : ''; + this.separatorContainer.style.borderTopStyle = border ? 'solid' : ''; + this.separatorContainer.style.borderTopColor = border; + + this.separatorContainer.style.borderBottomWidth = border ? '1px' : ''; + this.separatorContainer.style.borderBottomStyle = border ? 'solid' : ''; + this.separatorContainer.style.borderBottomColor = border; + + this.separatorContainer.style.backgroundColor = styles.buttonBackground?.toString() ?? ''; + this.separator.style.backgroundColor = styles.buttonSeparator?.toString() ?? ''; } focus(): void { @@ -398,12 +430,10 @@ export class ButtonWithDescription extends Button implements IButtonWithDescript this._labelElement = document.createElement('div'); this._labelElement.classList.add('monaco-button-label'); - this._labelElement.tabIndex = -1; this._element.appendChild(this._labelElement); this._descriptionElement = document.createElement('div'); this._descriptionElement.classList.add('monaco-button-description'); - this._descriptionElement.tabIndex = -1; this._element.appendChild(this._descriptionElement); } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 2f5dbfcc76de9fe983e8eb2b5aaf163e1aa32740..5abfa748fb56c712c19d127f550727acc3fabe59 100644 GIT binary patch delta 8457 zcmZYF34B!5*#_|Eog_1vnd~!}$wpR^up|MpvqM1EfXF7YNytKgkVFWJXdMJZML-}3 z)D;maP--buuof*^#ibT4F10QQR;|4^Qna{K5x?i;#m|1f5BT5r&di;ebI!f@ocEkz z^ZxKBj)rf}2~`5J0l=KvMK!G%PYl}xM6LpocFt?MvEler?>+}~tpnaa)L36rH*LqR zNj&x}l{fMU#|+17+&@lT8yB^$=qUc-dwyOA;M~^KQd^TcbMhxZRuT|ay{KkItK+2H z%GdcAnawqe>R+Fp@=-X%;pdZIwze#38)DDs4+)O~f(2WS=R`jW;j!dcGkyG)jz02e zd%7HQbVMC_O%6NSFH2={gN&>NVJT@H*yU|%e_)1V`Mb$K&b@rGyDM^ z7%VMVjDYOKR)nO7IPpC$%WC`%58zjFv&_V5StL`WPUgx~sh1|1Cv)&?nIZGrd%J_7 z6r`aG(lG^7aV@4{24*4)*~mdxG7H^v3`U#2^g8Pz=Lx zT!oPskE<~O*I*9jq7L)05R1@^7PR69EI}KVVi}fW1#ZMj+=QEP3s!}&2J3J;*5fX0 zz(#Ds-PnvRXvaO+jvd&EpW|NKj|cG(cJY{pu^W%z0Dg(ba0tJ}<9Gs3;`co7PvKeo z5zpayoWu)w5ij8kua0y02>-yR_$Mr!$G`9e{*8CYK+Ag`0)((U@s=3M*8DET#rN?#u15`j0|MLhEKNO2fT+r;}l-UUvL_4;w`+5 zcknJ=!>h6wPfG@Fg%d7B!i~S-4g3|UIEK%00sWASB#go@u#dN>vt6Ptx1W%R_8+30 z?pJuD@egm;nCM{W1Kw_PmGow;RlM0?v2QW3pRry^I%9*98yM#)iDqn6yggyfSNMi; zf#NL-YoWqu#wH+SHo|+~TBM|kv02F&#uml999FC1{SM1~IiZwsv0@(pYl&iS0IN;0 zUx2k#v4?=QOkprWu$J=yvD<*PLa_sZb)!OnaiwCX0_!Hl?giG(iXQ{Zd?85>##vkn`7`G|>n(-bbGZ?ok zcBr?+-pvjb)+35tE37?=oh+NLX(u4wbOp zR2(#6y`?yO!uqS?Knm+^#UT~e-xLQ|SnsG1$5~kKDvr3Y-cuZVVZE<73d1_1I3C0L zKyhS-byjhVhILMHw1)Me;QKxLV11^TCBQnbm@mNkm%%wu3(R?KK%{h*lJa5z-Va$t*Mz60B#nEAjCQ_O*2hbv}72-y*QKunBaI~7wT z*e=Cn33jA1>9XxsOrT(U6;mnLQHn_w>}bXG3U-WQq6IrvG3A0C*NN@_0(asS^Dx*6 ziWwPfpJHwX+plnzF;OvJgB?)J++YV4b2!*ZirF0OWW~G=c8X$#2RoJDZ>O^Myg2MM z#k3E07sUh+cDiCJ2s=YDDTJM=m>$B;QcM(KXDg)VxB8sd=RmQ}ez`OwIc#$>aTR_vZr=Q}Y2zOw9)> zF*P5g#ME}M5>wkDN=yxhDls*zQerAKOo^$~a3!WvR~c-7F|`_@#MEk}5>u;DN=&Ur zD=~!}qr?<)tP)emY9*$SHpcl%e#U5yBe{ifp)$9S-K1m{ z<056|>vpq}HHc%zbSj4PFFXS|6*+{g~@+^pn2MpILg`x#A5NgiM{g(Z2A(bSaWAx2YEl1CU< zE6(>|n_3fRey~lgiE}{Mw<*pBdH@AA(P}rvVh%-{yrum3-Q`n~Y zh_h7K_bAR+VVkBT&Rk)emLwcy+^J+k`}EYD&}=?3?MZl_(X=Oti_x?viK&ulPZD$G z>{6Wn!hTrcAmeT&!x==I@l43*<>9<|6vNVJ=&9f{rVg=6Omd{&7$6@OG>PSJBpmN7oBWI1E#BsV1H#(Y7^jf^iUF*oK*O3aP< zvf?rY>^~_kSipWoVI1S16&EpJpHeVa>8pwh8?awfQ@O?g`!5QnR;Lx$Jz&4CxB>$E z4aKz(*l#MXiokwLaeV~#Ulqo*k53PVOuM|JxM~9XUB&ei*zYN>q`)?pJ)ws2jN<<*zSzw=4TycSYPI2u8_J@kAFtGowU@n}GfF5QeF?{f`;u;O?PZTm4|Dkv*$7BRU zTlmOQT*HBFE3W3i?oeFUf&H1{3J>h_ifcWv|E0L<1N(yF`VZ{S6<2~_f1$W01pD8L zt3$9a0@KZg3q`QMR9rHG{gvY45$vxO%xU~aaX|_8e-zBM`mN$36YTF4Zf(DuQ5<3w z%qj}F=7bJWTzx_ZFK2`6Q0U;zX>dgf9pQ>=Q|O3LT%|&XQ*pft9qgqHu3VubQgICn z9d5Uq-hir4SW^HzGtcxQT-dGGLU^B(q|iHeG9joKM?D(b^%PxOlD1JU2a z6vtG@?2fqyn+x<;gRX4l-LOZjLW}VJDpFJRZW%gs)@8yioIoUO# z>wvDubE|US%A1(?PJY+?#{5g&`gMD+`_%3idMxg7sK;|XJw1zhPVaed&l3gd1>*}2 z6?7D)6wWC;QFx&!rf6o-s-p9~hWA?4>rk)Dy^r?(w9oiHM~gkh)x|4|w-z5LezEvM ziNB=0WOB*klFgx#eI+ML&Xim#^_LcuHk9rzy;xRSHm&T*veRYf%h#6QU;cFYg^I}) ziz}Y1xKNp1Sy#Ef@<8RYmFN5V`ws8Br|;AK{QU;?TiNe$zc2dd^`F*%UH=pP&kV>O zFlNAO13d%R4m>sJ@E~h&+ThiL_YS@|Wa^N|hFlyvYUqh7e^o=(%~c1hLSGE)F|2CX zf??~2S;NzZ@4L!*RpV9nUS*94jJSEk!4aoN_82*0N4+_Z69$DJPc&G>@x4db61|JBvSS3fBrAaB1BPaKpJYn*($tOaS&rV60GIh!)QzNHVO$E7EF9PW_IA&%(OuKw7+MUXYA$P4~h z^4%U+$nA*@Mu(?4U2$FV{8QW>htst(Axewxoal`8=E_p1%NxCVTl77y60L+JMS8*_ z64T?l$H#Z^1p?juT~nhSTf)Sb;P+`A;;Wsk!}~6mCwksJ(f2szr^>J&{HZb_)~1*> z`LU^8KQ@0L=qkz28z>d#q_}eYtVBKwZd%?YL3uSXwX>O%#WcG=ZeWuu(&zHJ+31Oq z*V*DXi97o8*AfvIex-pElihA@_OPgf{^XQ^uX`Y&v(*!06HJS{JKu5SgPvz%+viUx z9K&^xR&mA5gNZb$+ z?uhhyT;7;ik2fw5ofwD@cON<4|2}6;&@8(8N`i@b`DHnYjDw}bnV93^qx`a9=VuNQ zetg>xd|i>*YQbpBC**%Fgg*iOaLf?QGoc$?6v7_LRGNx%aR1Wn?Eg zqtoJpSx2g?y2KpWGU1N&Q1}lVbNRbY3Sv3>WSC5^A~9G}QP#`R`PDgP6*(*f_Y+IX zN?aw0ITfz_iX3x6_m{++?7Y(Q{=zG!(zib$qB15ox9|QnxxQTfn>A~euUm9dWmH^T zl-uo3P4!nMMR&Vltk1VErZOU-@4m5Pb8|z!yu7i#pYu2l6~5H`G#mcXGX-0k-LpAnvxQPPGe~1GiX^{C zvYoLScvtz;B||C(Iry8Z!|n2Z(7&VK4o134-Cd9 zto`w|i{?O}{kwI=?E`P?7QeoyF(_kFf*Hr{JGc#rUDg-6=lJ){KQ~c zMPlAicH~vhZ|uJD(8e78?Y{9!^T%;v${e3)G7IyL5+2Wbm+@*0Rs<7c*$493UX{Me zp(A4=#=1w2Fx!~Q6U%d!^Iu&-%rlJmlErc-XPq@rYxO<59=nV|&H?cp5XezNx8jNmI>|#=^Rm zHqTEF!_98Q+@_l5g~wiX@W;_CU+w(GwN3SfOBU7eJq`1ln(FJK{`th1FQK+& z(W3h1w!)>&i|d!PG%c&IJN9_EOmZCS;*vi(2BgoM-&WYVlpnFU{)VOXOWF$OwG_6s z6xKG@G|#JFlJ@^zZmwVcxp delta 6977 zcmXBZ3w+OI`v>sPwZm_-vz<50n1(U7vCUy-4mqwl&1sZvW`;S$2+2>9a!6w}d;0fC zQc03bNGeHEkED`R(n+fOmgpdPJo%O;tuV3wRezhsy z4uGr$P&#>L*_^mH+w24a3xT+Ar&cejeBx@(6F^D@aOPUol(LF(8#j;QXTPSLDt^Id zg3nugeUREz&8%5?_K&}B@_ZKoPigh+$z`28{J8)~&H#L~W|l3Sa|#-9C+*{Gxs9Hu>$XTNW7AV)s9vh3^jd{`0CMV-VoC8-Bja>UIUj^2h3q1?GBB zNs1(6i!_xAffT?>fAo`wBwKn(9_~Y?1Ysfe;T!aja?C`mtieNAEG_UCF5`;K$4h9y zGcsF-;T|cKK{8fG%U~HNlcYpOVVe}mMEJlD{=9%-gdh}Q2uB1W5sw5f644C9FdQQ= z5~DE&Noa{=v_fm#g0^Uf_DDkqbVNEjp))ej71_u^F1n#RdLj?`=#2vO#UKpE5DY~L zCgKj1p%PV?hUu7rYRtlH%)y9ZGh1FPtwOEH* ztj7k_;SoHFjrbQH!~gL!kK^Cigr~3-&*C|}fSuTd-Dt!fyo>{Qm0kZDUdJ0agg5aq zKEY{xhO_t*=kOK2!+Cs<3-|#B7x6PL;TQad-@W(~*YIE55FhbKfCNet36>BEl`sjH z2#JwciIaFqkVI)F%_T`%N-JqCZR8e7mA2AO+Dn>rmJG?1Ea@U$rJHn@9@10trML8v zzS2)_l>stDhDxE_Cc|ZfjFe&-BjaU)+%DcqnJRPSPMIt7q(&CVU9wOX$zoY5%j8~J zF89kyc|calYFQ@_ORcPz4N@-~@hQ&Wb9{kOD8^(|U<$^{9Vo|Cl%fbJXoDZ&#R|MA zUJk%GjKfE`E*+&z{P48oN)B$yK=JoV6mF9T(F;Fe8MfnFT$N~~iZA|$EObF1nJo9o zJ#x1!!Di_o>G%Vaq)r}{M`W5zml;wmGi8=kp(zI9Z#>NB_jc^Z5xj-N7=T;RAJKRP zPvA)uVuEx+J;oyj&tr!Onj^ObAK)lifnV_<-o{b<2k+uN9LM`Ofe&yJ$MB9!!Cr~P z5_k}RKs4bq=&2hKa-*&^B)}WOM-owdsNFwl08FXk0|!&4aEh^9Nebg+CCQAOP#kPw zoT8*DW2F-J{i(`5|E5Yw0pm2UJMgQPak}CI3^PNaA7izWhZtun$!46Tq!;6C#YY}y zj^e`)bEiTk<6OlF0cM_pcOhTYD9#cv^A+a{7*2N%dNAIlIETQvpCQgBFpCso85b+g zFfey3&NVPg6x{dkQJjBZmI7XP;8XmcSoGOU{)wR z!+5_E*CHzwr#6@e6x;Vhme1p>?^#Zm$07lpO#e{)%}et@~6SV_SA zs#sIN{H9o4!2GUQXTbcSSaHByRjfT={#2|&V6G|FBQSp{Rwgjl$;$(a6qx_2f9~uC zUlR)#nExr3Ffcb2iy4@|70VizME=17hjpN0sRQeySoFa9DwaR6eu{+=j7{yWzHde7{gN;)x-(cev3pv;X#gYy-QL(s#ZN}ekoAK^B zLTz)!iVwDhV(kZ;q*w*Qwp6SKVUrarL)cb|H6mmlMhznnsA5`ww@iT#I<0d64!#aDRC`Wq{Ox0a3!vFyd!uZam_PQiEExwN?hv{ zD{-wbT8V3gF-qK>k5%ICe4G+@=i`;QyDd@TZgzt5io27EO5B~?uEgER9ZKAtOj6>m zzEp|3_%bE#;>(q|3!bdRU1Wt4cQI3xxC^Ls{a?wucfWM1627-pO5E3`DVf7KUCEt{ zGnC9_tX49Qai)?Q##u@hFwRzT7o$5D$wKzObtfZP#OO{&vY64GjASXJI~mC`#`(%^ z16X%rlI4u<#3c7KE>yCT(Vds%0Y-OTl2wd%la~is%@<3QtYf@K$-|89my^^ox{D=Q z&*(0MWCNqS5R!UEcOk^39_)RJi$2&DipxJ(cY(x(AfJDCg~T->th++u>Jaup#dRXA zyISIk5!PKSaqS4ZMsXDhyH;T=<3oxoOIUY9#5E?YyCLFg6V}~Otvh2WU${FWEc)R&yfsde8#mi58}@^&nhmUVYex` z=j%DeMK$bp#pN~Z^NI^?*c}SX7++9ae8cWkT!zECTPIv)+@-iQ=X3TF-w;w6cPp;f zVH*`!?y!3l*YL0}E3W2Y_bT*Z+^1wR<9;RgF}|Y2jot%F+z@}&al_UP@q-GkXsR8Q-9C3RC_G873 z4%kzQTOP2VC~khho>trjf&J7C9v-+O0((YrZv^%;#a$9uKFJREOJLoDOx!tvJ*&8f z0{f-H1jciU`zo+sDekbqeyzCI0{e~Pt_uf`8(G8+7})O=b}*h-+?0X+UU7Q{_JTqS z#vc^i$=${J+-MrubaB%Yk=-I2BCkhfL={I>M=gn38`TiCBkF)R>Rj~1 z=mpWW(dS}{Viw1&jX4?{5nB*j61zOMK6ZEPvDoWz&EpE@-rq-TiADdzmR_U{nqt6(BGTfe|Y}|{ZI5a z{jc4+dqDhvya9U$+#EP-;Hg2$gBA{YX3*KeIfFM0zBnX$NVg#qhb$SgdB}~SIYSo= zeSGN2!l1&c!p90v-&TIx@uC4m>xTIa%Nw>}*va9s!Ea1!9pDKFlK@W(i4O3DOP98(oh2(Ei{H#niH=K>+-Ohh)UG*MvA_HJg#`79 z3l9wQ3+~$_*iU@I!$V`jBK!gZ{lh~2gMBbe!+d0AEk4$2VYQt4^)> z|Nf}ZB5Nq%}`{iyqr8nfqBH))i6lEUgHtSPCxvL>T$ z)vDHw-nBiNH1?@~qgrw|im$9;c*s{aOT)fSev;w4Z(#ucNZ{{RKBc;P-fhKq zNLKCpB1lI0#6(w2DW5+zeO^u3$I)%!Ad^ToB$fkW?NAQmb8#_cw oa&%O6*`nF=YtrXU;}4b1O|PC(S+lV=Qm*W)jgoBvzMRhf9|a4pfdBvi diff --git a/src/vs/base/browser/ui/contextview/contextview.css b/src/vs/base/browser/ui/contextview/contextview.css index 73e0627590..22d8b86cb7 100644 --- a/src/vs/base/browser/ui/contextview/contextview.css +++ b/src/vs/base/browser/ui/contextview/contextview.css @@ -5,7 +5,6 @@ .context-view { position: absolute; - z-index: 2500; } .context-view.fixed { @@ -13,6 +12,5 @@ font-family: inherit; font-size: 13px; position: fixed; - z-index: 2500; color: inherit; } diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index acc51d4685..9e8449d5d8 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -206,7 +206,7 @@ export class ContextView extends Disposable { this.view.className = 'context-view'; this.view.style.top = '0px'; this.view.style.left = '0px'; - this.view.style.zIndex = '2500'; + this.view.style.zIndex = '2575'; this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute'; DOM.show(this.view); @@ -220,9 +220,7 @@ export class ContextView extends Disposable { this.doLayout(); // Focus - if (this.delegate.focus) { - this.delegate.focus(); - } + this.delegate.focus?.(); } getViewElement(): HTMLElement { @@ -253,20 +251,25 @@ export class ContextView extends Disposable { } // Get anchor - let anchor = this.delegate!.getAnchor(); + const anchor = this.delegate!.getAnchor(); // Compute around let around: IView; // Get the element's position and size (to anchor the view) if (DOM.isHTMLElement(anchor)) { - let elementPosition = DOM.getDomNodePagePosition(anchor); + const elementPosition = DOM.getDomNodePagePosition(anchor); + + // In areas where zoom is applied to the element or its ancestors, we need to adjust the size of the element + // e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level. + // Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Size Multiplier: 1.5 + const zoom = DOM.getDomNodeZoomLevel(anchor); around = { - top: elementPosition.top, - left: elementPosition.left, - width: elementPosition.width, - height: elementPosition.height + top: elementPosition.top * zoom, + left: elementPosition.left * zoom, + width: elementPosition.width * zoom, + height: elementPosition.height * zoom }; } else { around = { @@ -359,7 +362,7 @@ export class ContextView extends Disposable { } } -let SHADOW_ROOT_CSS = /* css */ ` +const SHADOW_ROOT_CSS = /* css */ ` :host { all: initial; /* 1st rule so subsequent properties are reset. */ } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index b18a349545..2dc0b2c077 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -165,7 +165,7 @@ export class Dialog extends Disposable { } private getIconAriaLabel(): string { - let typeLabel = nls.localize('dialogInfoMessage', 'Info'); + const typeLabel = nls.localize('dialogInfoMessage', 'Info'); switch (this.options.type) { case 'error': nls.localize('dialogErrorMessage', 'Error'); @@ -427,13 +427,9 @@ export class Dialog extends Disposable { this.element.style.backgroundColor = bgColor?.toString() ?? ''; this.element.style.border = border; - if (this.buttonBar) { - this.buttonBar.buttons.forEach(button => button.style(style)); - } + this.buttonBar?.buttons.forEach(button => button.style(style)); - if (this.checkbox) { - this.checkbox.style(style); - } + this.checkbox?.style(style); if (fgColor && bgColor) { const messageDetailColor = fgColor.transparent(.9); diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 1ef15a03b8..b35fd37dff 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -56,8 +56,10 @@ export class BaseDropdown extends ActionRunner { for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) { this._register(addDisposableListener(this._label, event, e => { - if (e instanceof MouseEvent && e.detail > 1) { - return; // prevent multiple clicks to open multiple context menus (https://github.com/microsoft/vscode/issues/41363) + if (e instanceof MouseEvent && (e.detail > 1 || e.button !== 0)) { + // prevent right click trigger to allow separate context menu (https://github.com/microsoft/vscode/issues/151064) + // prevent multiple clicks to open multiple context menus (https://github.com/microsoft/vscode/issues/41363) + return; } if (this.visible) { diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 0c2051287b..7b4bffa10d 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -126,9 +126,22 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { }; } + this.updateTooltip(); this.updateEnabled(); } + override getTooltip(): string | undefined { + let title: string | null = null; + + if (this.getAction().tooltip) { + title = this.getAction().tooltip; + } else if (this.getAction().label) { + title = this.getAction().label; + } + + return title ?? undefined; + } + override setActionContext(newContext: unknown): void { super.setActionContext(newContext); diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 24410b29fb..1fed42abf6 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle'; +import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { CaseSensitiveToggle, RegexToggle, WholeWordsToggle } from 'vs/base/browser/ui/findinput/findInputToggles'; import { HistoryInputBox, IInputBoxStyles, IInputValidator, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; @@ -31,6 +31,7 @@ export interface IFindInputOptions extends IFindInputStyles { readonly appendWholeWordsLabel?: string; readonly appendRegexLabel?: string; readonly history?: string[]; + readonly additionalToggles?: Toggle[]; readonly showHistoryHint?: () => boolean; } @@ -74,6 +75,7 @@ export class FindInput extends Widget { protected regex: RegexToggle; protected wholeWords: WholeWordsToggle; protected caseSensitive: CaseSensitiveToggle; + protected additionalToggles: Toggle[] = []; public domNode: HTMLElement; public inputBox: HistoryInputBox; @@ -209,15 +211,11 @@ export class FindInput extends Widget { this._onCaseSensitiveKeyDown.fire(e); })); - if (this._showOptionButtons) { - this.inputBox.paddingRight = this.caseSensitive.width() + this.wholeWords.width() + this.regex.width(); - } - // Arrow-Key support to navigate between options - let indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode]; + const indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode]; this.onkeydown(this.domNode, (event: IKeyboardEvent) => { if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { - let index = indexes.indexOf(document.activeElement); + const index = indexes.indexOf(document.activeElement); if (index >= 0) { let newIndex: number = -1; if (event.equals(KeyCode.RightArrow)) { @@ -250,11 +248,37 @@ export class FindInput extends Widget { this.controls.appendChild(this.wholeWords.domNode); this.controls.appendChild(this.regex.domNode); + if (!this._showOptionButtons) { + this.caseSensitive.domNode.style.display = 'none'; + this.wholeWords.domNode.style.display = 'none'; + this.regex.domNode.style.display = 'none'; + } + + for (const toggle of options?.additionalToggles ?? []) { + this._register(toggle); + this.controls.appendChild(toggle.domNode); + + this._register(toggle.onChange(viaKeyboard => { + this._onDidOptionChange.fire(viaKeyboard); + if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { + this.inputBox.focus(); + } + })); + + this.additionalToggles.push(toggle); + } + + if (this.additionalToggles.length > 0) { + this.controls.style.display = 'block'; + } + + this.inputBox.paddingRight = + (this._showOptionButtons ? this.caseSensitive.width() + this.wholeWords.width() + this.regex.width() : 0) + + this.additionalToggles.reduce((r, t) => r + t.width(), 0); + this.domNode.appendChild(this.controls); - if (parent) { - parent.appendChild(this.domNode); - } + parent?.appendChild(this.domNode); this._register(dom.addDisposableListener(this.inputBox.inputElement, 'compositionstart', (e: CompositionEvent) => { this.imeSessionInProgress = true; @@ -284,6 +308,10 @@ export class FindInput extends Widget { this.regex.enable(); this.wholeWords.enable(); this.caseSensitive.enable(); + + for (const toggle of this.additionalToggles) { + toggle.enable(); + } } public disable(): void { @@ -292,6 +320,10 @@ export class FindInput extends Widget { this.regex.disable(); this.wholeWords.disable(); this.caseSensitive.disable(); + + for (const toggle of this.additionalToggles) { + toggle.disable(); + } } public setFocusInputOnOptionClick(value: boolean): void { @@ -358,6 +390,10 @@ export class FindInput extends Widget { this.wholeWords.style(toggleStyles); this.caseSensitive.style(toggleStyles); + for (const toggle of this.additionalToggles) { + toggle.style(toggleStyles); + } + const inputBoxStyles: IInputBoxStyles = { inputBackground: this.inputBackground, inputForeground: this.inputForeground, diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 62b38bbae3..ae34d42d37 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -189,10 +189,10 @@ export class ReplaceInput extends Widget { } // Arrow-Key support to navigate between options - let indexes = [this.preserveCase.domNode]; + const indexes = [this.preserveCase.domNode]; this.onkeydown(this.domNode, (event: IKeyboardEvent) => { if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { - let index = indexes.indexOf(document.activeElement); + const index = indexes.indexOf(document.activeElement); if (index >= 0) { let newIndex: number = -1; if (event.equals(KeyCode.RightArrow)) { @@ -218,16 +218,14 @@ export class ReplaceInput extends Widget { }); - let controls = document.createElement('div'); + const controls = document.createElement('div'); controls.className = 'controls'; controls.style.display = this._showOptionButtons ? 'block' : 'none'; controls.appendChild(this.preserveCase.domNode); this.domNode.appendChild(controls); - if (parent) { - parent.appendChild(this.domNode); - } + parent?.appendChild(this.domNode); this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e)); this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e)); @@ -361,9 +359,7 @@ export class ReplaceInput extends Widget { } public showMessage(message: InputBoxMessage): void { - if (this.inputBox) { - this.inputBox.showMessage(message); - } + this.inputBox?.showMessage(message); } public clearMessage(): void { diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 726b29e1bb..5c00b23928 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -527,6 +527,16 @@ export class Grid extends Disposable { return this.gridview.resizeView(location, size); } + /** + * Returns whether all other {@link IView views} are at their minimum size. + * + * @param view The reference {@link IView view}. + */ + isViewSizeMaximized(view: T): boolean { + const location = this.getViewLocation(view); + return this.gridview.isViewSizeMaximized(location); + } + /** * Get the size of a {@link IView view}. * @@ -748,6 +758,16 @@ export class SerializableGrid extends Grid { return result; } + /** + * Construct a new {@link SerializableGrid} from a grid descriptor. + * + * @param gridDescriptor A grid descriptor in which leaf nodes point to actual views. + * @returns A new {@link SerializableGrid} instance. + */ + static from(gridDescriptor: GridDescriptor, options: IGridOptions = {}): SerializableGrid { + return SerializableGrid.deserialize(createSerializedGrid(gridDescriptor), { fromJSON: view => view }, options); + } + /** * Useful information in order to proportionally restore view sizes * upon the very first layout call. @@ -776,15 +796,21 @@ export class SerializableGrid extends Grid { } } -export type GridNodeDescriptor = { size?: number; groups?: GridNodeDescriptor[] }; -export type GridDescriptor = { orientation: Orientation; groups?: GridNodeDescriptor[] }; +export type GridLeafNodeDescriptor = { size?: number; data?: any }; +export type GridBranchNodeDescriptor = { size?: number; groups: GridNodeDescriptor[] }; +export type GridNodeDescriptor = GridBranchNodeDescriptor | GridLeafNodeDescriptor; +export type GridDescriptor = { orientation: Orientation } & GridBranchNodeDescriptor; -export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor, rootNode: boolean): void { - if (!rootNode && nodeDescriptor.groups && nodeDescriptor.groups.length <= 1) { - nodeDescriptor.groups = undefined; +function isGridBranchNodeDescriptor(nodeDescriptor: GridNodeDescriptor): nodeDescriptor is GridBranchNodeDescriptor { + return !!(nodeDescriptor as GridBranchNodeDescriptor).groups; +} + +export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor, rootNode: boolean): void { + if (!rootNode && (nodeDescriptor as any).groups && (nodeDescriptor as any).groups.length <= 1) { + (nodeDescriptor as any).groups = undefined; } - if (!nodeDescriptor.groups) { + if (!isGridBranchNodeDescriptor(nodeDescriptor)) { return; } @@ -811,11 +837,11 @@ export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor, r } } -function createSerializedNode(nodeDescriptor: GridNodeDescriptor): ISerializedNode { - if (nodeDescriptor.groups) { +function createSerializedNode(nodeDescriptor: GridNodeDescriptor): ISerializedNode { + if (isGridBranchNodeDescriptor(nodeDescriptor)) { return { type: 'branch', data: nodeDescriptor.groups.map(c => createSerializedNode(c)), size: nodeDescriptor.size! }; } else { - return { type: 'leaf', data: null, size: nodeDescriptor.size! }; + return { type: 'leaf', data: nodeDescriptor.data, size: nodeDescriptor.size! }; } } @@ -843,7 +869,7 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width * Creates a new JSON object from a {@link GridDescriptor}, which can * be deserialized by {@link SerializableGrid.deserialize}. */ -export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerializedGrid { +export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerializedGrid { sanitizeGridNodeDescriptor(gridDescriptor, true); const root = createSerializedNode(gridDescriptor); diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index e807aaade3..47d83d510c 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -592,6 +592,10 @@ class BranchNode implements ISplitView, IDisposable { this.splitview.resizeView(index, size); } + isChildSizeMaximized(index: number): boolean { + return this.splitview.isViewSizeMaximized(index); + } + distributeViewSizes(recursive = false): void { this.splitview.distributeViewSizes(); @@ -857,9 +861,7 @@ class LeafNode implements ISplitView, IDisposable { set boundarySashes(boundarySashes: IRelativeBoundarySashes) { this._boundarySashes = boundarySashes; - if (this.view.setBoundarySashes) { - this.view.setBoundarySashes(toAbsoluteBoundarySashes(boundarySashes, this.orientation)); - } + this.view.setBoundarySashes?.(toAbsoluteBoundarySashes(boundarySashes, this.orientation)); } layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { @@ -897,9 +899,7 @@ class LeafNode implements ISplitView, IDisposable { } setVisible(visible: boolean): void { - if (this.view.setVisible) { - this.view.setVisible(visible); - } + this.view.setVisible?.(visible); } dispose(): void { @@ -1435,6 +1435,27 @@ export class GridView implements IDisposable { } } + /** + * Returns whether all other {@link IView views} are at their minimum size. + * + * @param location The {@link GridLocation location} of the view. + */ + isViewSizeMaximized(location: GridLocation): boolean { + const [ancestors, node] = this.getNode(location); + + if (!(node instanceof LeafNode)) { + throw new Error('Invalid location'); + } + + for (let i = 0; i < ancestors.length; i++) { + if (!ancestors[i].isChildSizeMaximized(location[i])) { + return false; + } + } + + return true; + } + /** * Distribute the size among all {@link IView views} within the entire * grid or within a single {@link SplitView}. diff --git a/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts b/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts index b5415c0f7d..738047d69d 100644 --- a/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts +++ b/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; +import { IUpdatableHoverOptions } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -12,7 +13,7 @@ export interface IHoverDelegateTarget extends IDisposable { x?: number; } -export interface IHoverDelegateOptions { +export interface IHoverDelegateOptions extends IUpdatableHoverOptions { content: IMarkdownString | string | HTMLElement; target: IHoverDelegateTarget | HTMLElement; hoverPosition?: HoverPosition; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts b/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts index ad6238db67..3079fa99f6 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts @@ -33,6 +33,21 @@ export function setupNativeHover(htmlElement: HTMLElement, tooltip: string | ITo export type IHoverContent = string | ITooltipMarkdownString | HTMLElement | undefined; type IResolvedHoverContent = IMarkdownString | string | HTMLElement | undefined; +/** + * Copied from src\vs\workbench\services\hover\browser\hover.ts + * @deprecated Use IHoverService + */ +export interface IHoverAction { + label: string; + commandId: string; + iconClass?: string; + run(target: HTMLElement): void; +} + +export interface IUpdatableHoverOptions { + actions?: IHoverAction[]; + linkHandler?(url: string): void; +} export interface ICustomHover extends IDisposable { @@ -49,7 +64,7 @@ export interface ICustomHover extends IDisposable { /** * Updates the contents of the hover. */ - update(tooltip: IHoverContent): void; + update(tooltip: IHoverContent, options?: IUpdatableHoverOptions): void; } @@ -61,7 +76,7 @@ class UpdatableHoverWidget implements IDisposable { constructor(private hoverDelegate: IHoverDelegate, private target: IHoverDelegateTarget | HTMLElement, private fadeInAnimation: boolean) { } - async update(content: IHoverContent, focus?: boolean): Promise { + async update(content: IHoverContent, focus?: boolean, options?: IUpdatableHoverOptions): Promise { if (this._cancellationTokenSource) { // there's an computation ongoing, cancel it this._cancellationTokenSource.dispose(true); @@ -99,10 +114,10 @@ class UpdatableHoverWidget implements IDisposable { } } - this.show(resolvedContent, focus); + this.show(resolvedContent, focus, options); } - private show(content: IResolvedHoverContent, focus?: boolean): void { + private show(content: IResolvedHoverContent, focus?: boolean, options?: IUpdatableHoverOptions): void { const oldHoverWidget = this._hoverWidget; if (this.hasContent(content)) { @@ -111,7 +126,8 @@ class UpdatableHoverWidget implements IDisposable { target: this.target, showPointer: this.hoverDelegate.placement === 'element', hoverPosition: HoverPosition.BELOW, - skipFadeInAnimation: !this.fadeInAnimation || !!oldHoverWidget // do not fade in if the hover is already showing + skipFadeInAnimation: !this.fadeInAnimation || !!oldHoverWidget, // do not fade in if the hover is already showing + ...options }; this._hoverWidget = this.hoverDelegate.showHover(hoverOptions, focus); @@ -142,7 +158,7 @@ class UpdatableHoverWidget implements IDisposable { } } -export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, content: IHoverContent): ICustomHover { +export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, content: IHoverContent, options?: IUpdatableHoverOptions): ICustomHover { let hoverPreparation: IDisposable | undefined; let hoverWidget: UpdatableHoverWidget | undefined; @@ -163,7 +179,7 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM return new TimeoutTimer(async () => { if (!hoverWidget || hoverWidget.isDisposed) { hoverWidget = new UpdatableHoverWidget(hoverDelegate, target || htmlElement, delay > 0); - await hoverWidget.update(content, focus); + await hoverWidget.update(content, focus, options); } }, delay); }; @@ -208,9 +224,9 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM hide: () => { hideHover(true, true); }, - update: async newContent => { + update: async (newContent, hoverOptions) => { content = newContent; - await hoverWidget?.update(content); + await hoverWidget?.update(content, undefined, hoverOptions); }, dispose: () => { mouseOverDomEmitter.dispose(); diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 5384199ba4..60d2591d4d 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -171,9 +171,9 @@ export class InputBox extends Widget { this.element = dom.append(container, $('.monaco-inputbox.idle')); - let tagName = this.options.flexibleHeight ? 'textarea' : 'input'; + const tagName = this.options.flexibleHeight ? 'textarea' : 'input'; - let wrapper = dom.append(this.element, $('.ibwrapper')); + const wrapper = dom.append(this.element, $('.ibwrapper')); this.input = dom.append(wrapper, $(tagName + '.input.empty')); this.input.setAttribute('autocorrect', 'off'); this.input.setAttribute('autocapitalize', 'off'); @@ -257,14 +257,14 @@ export class InputBox extends Widget { this.applyStyles(); } - private onBlur(): void { + protected onBlur(): void { this._hideMessage(); if (this.options.showPlaceholderOnFocus) { this.input.setAttribute('placeholder', ''); } } - private onFocus(): void { + protected onFocus(): void { this._showMessage(); if (this.options.showPlaceholderOnFocus) { this.input.setAttribute('placeholder', this.placeholder || ''); @@ -491,7 +491,7 @@ export class InputBox extends Widget { } let div: HTMLElement; - let layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px'; + const layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px'; this.contextViewProvider.showContextView({ getAnchor: () => this.element, @@ -672,11 +672,19 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge private readonly history: HistoryNavigator; private observer: MutationObserver | undefined; + private readonly _onDidFocus = this._register(new Emitter()); + readonly onDidFocus = this._onDidFocus.event; + + private readonly _onDidBlur = this._register(new Emitter()); + readonly onDidBlur = this._onDidBlur.event; + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options: IHistoryInputOptions) { + super(container, contextViewProvider, options); + const NLS_PLACEHOLDER_HISTORY_HINT = nls.localize({ key: 'history.inputbox.hint', comment: ['Text will be prefixed with \u21C5 plus a single space, then used as a hint where input field keeps history'] }, "for history"); const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX = ` or \u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT}`; const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS = ` (\u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT})`; - super(container, contextViewProvider, options); + this.history = new HistoryNavigator(options.history, 100); // Function to append the history suffix to the placeholder if necessary @@ -781,6 +789,16 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge this.history.clear(); } + protected override onBlur(): void { + super.onBlur(); + this._onDidBlur.fire(); + } + + protected override onFocus(): void { + super.onFocus(); + this._onDidFocus.fire(); + } + private getCurrentValue(): string | null { let currentValue = this.history.current(); if (!currentValue) { diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts index d8b8c251c4..0eb2c54792 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts @@ -89,7 +89,7 @@ export class KeybindingLabel implements IThemable { this.clear(); if (this.keybinding) { - let [firstPart, chordPart] = this.keybinding.getParts(); + const [firstPart, chordPart] = this.keybinding.getParts(); if (firstPart) { this.renderPart(this.domNode, firstPart, this.matches ? this.matches.firstPart : null); } diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 4c14ad6644..1eee599f26 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -65,72 +65,7 @@ z-index: 1000; } -/* Type filter */ - -.monaco-list-type-filter { - display: flex; - align-items: center; - position: absolute; - border-radius: 2px; - padding: 0px 3px; - max-width: calc(100% - 10px); - text-overflow: ellipsis; - overflow: hidden; - text-align: right; - box-sizing: border-box; - cursor: all-scroll; - font-size: 13px; - line-height: 18px; - height: 20px; - z-index: 1; - top: 4px; -} - -.monaco-list-type-filter.dragging { - transition: top 0.2s, left 0.2s; -} - -.monaco-list-type-filter.ne { - right: 4px; -} - -.monaco-list-type-filter.nw { - left: 4px; -} - -.monaco-list-type-filter > .controls { - display: flex; - align-items: center; - box-sizing: border-box; - transition: width 0.2s; - width: 0; -} - -.monaco-list-type-filter.dragging > .controls, -.monaco-list-type-filter:hover > .controls { - width: 36px; -} - -.monaco-list-type-filter > .controls > * { - border: none; - box-sizing: border-box; - -webkit-appearance: none; - -moz-appearance: none; - background: none; - width: 16px; - height: 16px; - flex-shrink: 0; - margin: 0; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; -} - -.monaco-list-type-filter > .controls > .filter { - margin-left: 4px; -} +/* Filter */ .monaco-list-type-filter-message { position: absolute; @@ -149,13 +84,3 @@ .monaco-list-type-filter-message:empty { display: none; } - -/* Electron */ - -.monaco-list-type-filter { - cursor: grab; -} - -.monaco-list-type-filter.dragging { - cursor: grabbing; -} diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index ff1823b2ce..cd1498ef3d 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -12,7 +12,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IThemable } from 'vs/base/common/styler'; import 'vs/css!./list'; import { IListContextMenuEvent, IListEvent, IListMouseEvent, IListRenderer, IListVirtualDelegate } from './list'; -import { IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IListStyles, List } from './listWidget'; +import { IListAccessibilityProvider, IListOptions, IListOptionsUpdate, IListStyles, List, TypeNavigationMode } from './listWidget'; export interface IPagedRenderer extends IListRenderer { renderPlaceholder(index: number, templateData: TTemplateData): void; @@ -95,8 +95,8 @@ class PagedAccessibilityProvider implements IListAccessibilityProvider { - readonly enableKeyboardNavigation?: boolean; - readonly automaticKeyboardNavigation?: boolean; + readonly typeNavigationEnabled?: boolean; + readonly typeNavigationMode?: TypeNavigationMode; readonly ariaLabel?: string; readonly keyboardSupport?: boolean; readonly multipleSelectionSupport?: boolean; @@ -282,8 +282,8 @@ export class PagedList implements IThemable, IDisposable { this.list.layout(height, width); } - toggleKeyboardNavigation(): void { - this.list.toggleKeyboardNavigation(); + triggerTypeNavigation(): void { + this.list.triggerTypeNavigation(); } reveal(index: number, relativeTop?: number): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index df961e66d4..decc1896e5 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -15,7 +15,6 @@ import { Delayer, disposableTimeout } from 'vs/base/common/async'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { getOrDefault } from 'vs/base/common/objects'; import { IRange, Range } from 'vs/base/common/range'; import { INewScrollDimensions, Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; @@ -257,7 +256,7 @@ export class ListView implements ISpliceable, IDisposable { private readonly disposables: DisposableStore = new DisposableStore(); private readonly _onDidChangeContentHeight = new Emitter(); - readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event); + readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event, undefined, this.disposables); get contentHeight(): number { return this.rangeMap.size; } get onDidScroll(): Event { return this.scrollableElement.onScroll; } @@ -325,7 +324,7 @@ export class ListView implements ISpliceable, IDisposable { this.domNode.classList.toggle('mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); - this._horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + this._horizontalScrolling = options.horizontalScrolling ?? DefaultOptions.horizontalScrolling; this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; @@ -335,7 +334,7 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; - const transformOptimization = getOrDefault(options, o => o.transformOptimization, DefaultOptions.transformOptimization); + const transformOptimization = options.transformOptimization ?? DefaultOptions.transformOptimization; if (transformOptimization) { this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)'; } @@ -344,14 +343,14 @@ export class ListView implements ISpliceable, IDisposable { this.scrollable = new Scrollable({ forceIntegerValues: true, - smoothScrollDuration: getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, + smoothScrollDuration: (options.smoothScrolling ?? false) ? 125 : 0, scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb) }); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { - alwaysConsumeMouseWheel: getOrDefault(options, o => o.alwaysConsumeMouseWheel, DefaultOptions.alwaysConsumeMouseWheel), + alwaysConsumeMouseWheel: options.alwaysConsumeMouseWheel ?? DefaultOptions.alwaysConsumeMouseWheel, horizontal: ScrollbarVisibility.Auto, - vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), + vertical: options.verticalScrollMode ?? DefaultOptions.verticalScrollMode, + useShadows: options.useShadows ?? DefaultOptions.useShadows, mouseWheelScrollSensitivity: options.mouseWheelScrollSensitivity, fastScrollSensitivity: options.fastScrollSensitivity }, this.scrollable)); @@ -371,10 +370,10 @@ export class ListView implements ISpliceable, IDisposable { this.disposables.add(addDisposableListener(this.domNode, 'dragleave', e => this.onDragLeave(this.toDragEvent(e)))); this.disposables.add(addDisposableListener(this.domNode, 'dragend', e => this.onDragEnd(e))); - this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); - this.setRowHeight = getOrDefault(options, o => o.setRowHeight, DefaultOptions.setRowHeight); - this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); - this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); + this.setRowLineHeight = options.setRowLineHeight ?? DefaultOptions.setRowLineHeight; + this.setRowHeight = options.setRowHeight ?? DefaultOptions.setRowHeight; + this.supportDynamicHeights = options.supportDynamicHeights ?? DefaultOptions.supportDynamicHeights; + this.dnd = options.dnd ?? DefaultOptions.dnd; this.layout(); } @@ -705,7 +704,7 @@ export class ListView implements ISpliceable, IDisposable { } layout(height?: number, width?: number): void { - let scrollDimensions: INewScrollDimensions = { + const scrollDimensions: INewScrollDimensions = { height: typeof height === 'number' ? height : getContentHeight(this.domNode) }; @@ -813,9 +812,7 @@ export class ListView implements ISpliceable, IDisposable { throw new Error(`No renderer found for template id ${item.templateId}`); } - if (renderer) { - renderer.renderElement(item.element, index, item.row.templateData, item.size); - } + renderer?.renderElement(item.element, index, item.row.templateData, item.size); const uri = this.dnd.getDragURI(item.element); item.dragStartDisposable.dispose(); @@ -938,17 +935,17 @@ export class ListView implements ISpliceable, IDisposable { // Events - @memoize get onMouseClick(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'click')).event, e => this.toMouseEvent(e)); } - @memoize get onMouseDblClick(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'dblclick')).event, e => this.toMouseEvent(e)); } - @memoize get onMouseMiddleClick(): Event> { return Event.filter(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'auxclick')).event, e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } - @memoize get onMouseUp(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseup')).event, e => this.toMouseEvent(e)); } - @memoize get onMouseDown(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousedown')).event, e => this.toMouseEvent(e)); } - @memoize get onMouseOver(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseover')).event, e => this.toMouseEvent(e)); } - @memoize get onMouseMove(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousemove')).event, e => this.toMouseEvent(e)); } - @memoize get onMouseOut(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseout')).event, e => this.toMouseEvent(e)); } - @memoize get onContextMenu(): Event | IListGestureEvent> { return Event.any(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'contextmenu')).event, e => this.toMouseEvent(e)), Event.map(this.disposables.add(new DomEmitter(this.domNode, TouchEventType.Contextmenu)).event as Event, e => this.toGestureEvent(e))); } - @memoize get onTouchStart(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'touchstart')).event, e => this.toTouchEvent(e)); } - @memoize get onTap(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.rowsContainer, TouchEventType.Tap)).event, e => this.toGestureEvent(e as GestureEvent)); } + @memoize get onMouseClick(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'click')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onMouseDblClick(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'dblclick')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onMouseMiddleClick(): Event> { return Event.filter(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'auxclick')).event, e => this.toMouseEvent(e as MouseEvent), this.disposables), e => e.browserEvent.button === 1, this.disposables); } + @memoize get onMouseUp(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseup')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onMouseDown(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousedown')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onMouseOver(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseover')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onMouseMove(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mousemove')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onMouseOut(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'mouseout')).event, e => this.toMouseEvent(e), this.disposables); } + @memoize get onContextMenu(): Event | IListGestureEvent> { return Event.any(Event.map(this.disposables.add(new DomEmitter(this.domNode, 'contextmenu')).event, e => this.toMouseEvent(e), this.disposables), Event.map(this.disposables.add(new DomEmitter(this.domNode, TouchEventType.Contextmenu)).event as Event, e => this.toGestureEvent(e), this.disposables)); } + @memoize get onTouchStart(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.domNode, 'touchstart')).event, e => this.toTouchEvent(e), this.disposables); } + @memoize get onTap(): Event> { return Event.map(this.disposables.add(new DomEmitter(this.rowsContainer, TouchEventType.Tap)).event, e => this.toGestureEvent(e as GestureEvent), this.disposables); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); @@ -1032,9 +1029,7 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragData = new ElementsDragAndDropData(elements); StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); - if (this.dnd.onDragStart) { - this.dnd.onDragStart(this.currentDragData, event); - } + this.dnd.onDragStart?.(this.currentDragData, event); } private onDragOver(event: IListDragEvent): boolean { @@ -1114,9 +1109,7 @@ export class ListView implements ISpliceable, IDisposable { const item = this.items[index]!; item.dropTarget = true; - if (item.row) { - item.row.domNode.classList.add('drop-target'); - } + item.row?.domNode.classList.add('drop-target'); } this.currentDragFeedbackDisposable = toDisposable(() => { @@ -1124,9 +1117,7 @@ export class ListView implements ISpliceable, IDisposable { const item = this.items[index]!; item.dropTarget = false; - if (item.row) { - item.row.domNode.classList.remove('drop-target'); - } + item.row?.domNode.classList.remove('drop-target'); } }); } @@ -1169,9 +1160,7 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragData = undefined; StaticDND.CurrentDragAndDropData = undefined; - if (this.dnd.onDragEnd) { - this.dnd.onDragEnd(event); - } + this.dnd.onDragEnd?.(event); } private clearDragOverFeedback(): void { @@ -1364,7 +1353,7 @@ export class ListView implements ISpliceable, IDisposable { const size = item.size; if (!this.setRowHeight && item.row) { - let newSize = item.row.domNode.offsetHeight; + const newSize = item.row.domNode.offsetHeight; item.size = newSize; item.lastDynamicHeightWidth = this.renderWidth; return newSize - size; @@ -1379,16 +1368,12 @@ export class ListView implements ISpliceable, IDisposable { if (renderer) { renderer.renderElement(item.element, index, row.templateData, undefined); - if (renderer.disposeElement) { - renderer.disposeElement(item.element, index, row.templateData, undefined); - } + renderer.disposeElement?.(item.element, index, row.templateData, undefined); } item.size = row.domNode.offsetHeight; - if (this.virtualDelegate.setDynamicHeight) { - this.virtualDelegate.setDynamicHeight(item.element, item.size); - } + this.virtualDelegate.setDynamicHeight?.(item.element, item.size); item.lastDynamicHeightWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode); @@ -1429,9 +1414,7 @@ export class ListView implements ISpliceable, IDisposable { if (item.row) { const renderer = this.renderers.get(item.row.templateId); if (renderer) { - if (renderer.disposeElement) { - renderer.disposeElement(item.element, -1, item.row.templateData, undefined); - } + renderer.disposeElement?.(item.element, -1, item.row.templateData, undefined); renderer.disposeTemplate(item.row.templateData); } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index c9fb17f0e8..0e356c596f 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -9,6 +9,7 @@ import { DomEmitter, stopEvent } from 'vs/base/browser/event'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Gesture } from 'vs/base/browser/touch'; import { alert } from 'vs/base/browser/ui/aria/aria'; +import { IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { binarySearch, firstOrDefault, range } from 'vs/base/common/arrays'; @@ -258,6 +259,23 @@ export function isMonacoEditor(e: HTMLElement): boolean { return isMonacoEditor(e.parentElement); } +export function isButton(e: HTMLElement): boolean { + if ((e.tagName === 'A' && e.classList.contains('monaco-button')) || + (e.tagName === 'DIV' && e.classList.contains('monaco-button-dropdown'))) { + return true; + } + + if (e.classList.contains('monaco-list')) { + return false; + } + + if (!e.parentElement) { + return false; + } + + return isButton(e.parentElement); +} + class KeyboardController implements IDisposable { private readonly disposables = new DisposableStore(); @@ -265,9 +283,9 @@ class KeyboardController implements IDisposable { @memoize private get onKeyDown(): Event.IChainableEvent { - return Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) + return this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) .filter(e => !isInputElement(e.target as HTMLElement)) - .map(e => new StandardKeyboardEvent(e)); + .map(e => new StandardKeyboardEvent(e))); } constructor( @@ -367,7 +385,12 @@ class KeyboardController implements IDisposable { } } -enum TypeLabelControllerState { +export enum TypeNavigationMode { + Automatic, + Trigger +} + +enum TypeNavigationControllerState { Idle, Typing } @@ -385,12 +408,12 @@ export const DefaultKeyboardNavigationDelegate = new class implements IKeyboardN } }; -class TypeLabelController implements IDisposable { +class TypeNavigationController implements IDisposable { private enabled = false; - private state: TypeLabelControllerState = TypeLabelControllerState.Idle; + private state: TypeNavigationControllerState = TypeNavigationControllerState.Idle; - private automaticKeyboardNavigation = true; + private mode = TypeNavigationMode.Automatic; private triggered = false; private previouslyFocused = -1; @@ -401,26 +424,23 @@ class TypeLabelController implements IDisposable { private list: List, private view: ListView, private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider, + private keyboardNavigationEventFilter: IKeyboardNavigationEventFilter, private delegate: IKeyboardNavigationDelegate ) { this.updateOptions(list.options); } updateOptions(options: IListOptions): void { - const enableKeyboardNavigation = typeof options.enableKeyboardNavigation === 'undefined' ? true : !!options.enableKeyboardNavigation; - - if (enableKeyboardNavigation) { + if (options.typeNavigationEnabled ?? true) { this.enable(); } else { this.disable(); } - if (typeof options.automaticKeyboardNavigation !== 'undefined') { - this.automaticKeyboardNavigation = options.automaticKeyboardNavigation; - } + this.mode = options.typeNavigationMode ?? TypeNavigationMode.Automatic; } - toggle(): void { + trigger(): void { this.triggered = !this.triggered; } @@ -429,21 +449,27 @@ class TypeLabelController implements IDisposable { return; } - const onChar = Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) + let typing = false; + + const onChar = this.enabledDisposables.add(Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)) .filter(e => !isInputElement(e.target as HTMLElement)) - .filter(() => this.automaticKeyboardNavigation || this.triggered) + .filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered) .map(event => new StandardKeyboardEvent(event)) + .filter(e => typing || this.keyboardNavigationEventFilter(e)) .filter(e => this.delegate.mightProducePrintableCharacter(e)) - .forEach(e => e.preventDefault()) + .forEach(stopEvent) .map(event => event.browserEvent.key) .event; - const onClear = Event.debounce(onChar, () => null, 800); - const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i)); + const onClear = Event.debounce(onChar, () => null, 800, undefined, undefined, this.enabledDisposables); + const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i), undefined, this.enabledDisposables); onInput(this.onInput, this, this.enabledDisposables); onClear(this.onClear, this, this.enabledDisposables); + onChar(() => typing = true, undefined, this.enabledDisposables); + onClear(() => typing = false, undefined, this.enabledDisposables); + this.enabled = true; this.triggered = false; } @@ -473,15 +499,15 @@ class TypeLabelController implements IDisposable { private onInput(word: string | null): void { if (!word) { - this.state = TypeLabelControllerState.Idle; + this.state = TypeNavigationControllerState.Idle; this.triggered = false; return; } const focus = this.list.getFocus(); const start = focus.length > 0 ? focus[0] : 0; - const delta = this.state === TypeLabelControllerState.Idle ? 1 : 0; - this.state = TypeLabelControllerState.Typing; + const delta = this.state === TypeNavigationControllerState.Idle ? 1 : 0; + this.state = TypeNavigationControllerState.Typing; for (let i = 0; i < this.list.length; i++) { const index = (start + i + delta) % this.list.length; @@ -512,7 +538,7 @@ class DOMFocusController implements IDisposable { private list: List, private view: ListView ) { - const onKeyDown = Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event) + const onKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event)) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -801,6 +827,10 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected .codicon { color: ${styles.listActiveSelectionIconForeground}; }`); } + if (styles.listFocusAndSelectionOutline) { + content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected { outline-color: ${styles.listFocusAndSelectionOutline} !important; }`); + } + if (styles.listFocusAndSelectionBackground) { content.push(` .monaco-drag-image, @@ -874,22 +904,6 @@ export class DefaultStyleController implements IStyleController { `); } - if (styles.listFilterWidgetBackground) { - content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`); - } - - if (styles.listFilterWidgetOutline) { - content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`); - } - - if (styles.listFilterWidgetNoMatchesOutline) { - content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`); - } - - if (styles.listMatchesShadow) { - content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); - } - if (styles.tableColumnsBorder) { content.push(` .monaco-table:hover > .monaco-split-view2, @@ -912,9 +926,13 @@ export class DefaultStyleController implements IStyleController { } } +export interface IKeyboardNavigationEventFilter { + (e: StandardKeyboardEvent): boolean; +} + export interface IListOptionsUpdate extends IListViewOptionsUpdate { - readonly enableKeyboardNavigation?: boolean; - readonly automaticKeyboardNavigation?: boolean; + readonly typeNavigationEnabled?: boolean; + readonly typeNavigationMode?: TypeNavigationMode; readonly multipleSelectionSupport?: boolean; } @@ -927,6 +945,7 @@ export interface IListOptions extends IListOptionsUpdate { readonly multipleSelectionController?: IMultipleSelectionController; readonly styleController?: (suffix: string) => IStyleController; readonly accessibilityProvider?: IListAccessibilityProvider; + readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; // list view options readonly useShadows?: boolean; @@ -943,13 +962,14 @@ export interface IListOptions extends IListOptionsUpdate { readonly alwaysConsumeMouseWheel?: boolean; } -export interface IListStyles { +export interface IListStyles extends IFindInputStyles { listBackground?: Color; listFocusBackground?: Color; listFocusForeground?: Color; listActiveSelectionBackground?: Color; listActiveSelectionForeground?: Color; listActiveSelectionIconForeground?: Color; + listFocusAndSelectionOutline?: Color; listFocusAndSelectionBackground?: Color; listFocusAndSelectionForeground?: Color; listInactiveSelectionBackground?: Color; @@ -967,7 +987,8 @@ export interface IListStyles { listFilterWidgetBackground?: Color; listFilterWidgetOutline?: Color; listFilterWidgetNoMatchesOutline?: Color; - listMatchesShadow?: Color; + listFilterWidgetShadow?: Color; + listMatchesShadow?: Color; // {{SQL CARBON EDIT}} treeIndentGuidesStroke?: Color; tableColumnsBorder?: Color; tableOddRowsBackgroundColor?: Color; @@ -978,6 +999,7 @@ const defaultStyles: IListStyles = { listActiveSelectionBackground: Color.fromHex('#0E639C'), listActiveSelectionForeground: Color.fromHex('#FFFFFF'), listActiveSelectionIconForeground: Color.fromHex('#FFFFFF'), + listFocusAndSelectionOutline: Color.fromHex('#90C2F9'), listFocusAndSelectionBackground: Color.fromHex('#094771'), listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'), listInactiveSelectionBackground: Color.fromHex('#3F3F46'), @@ -1109,9 +1131,7 @@ class PipelineRenderer implements IListRenderer { let i = 0; for (const renderer of this.renderers) { - if (renderer.disposeElement) { - renderer.disposeElement(element, index, templateData[i], height); - } + renderer.disposeElement?.(element, index, templateData[i], height); i += 1; } @@ -1182,9 +1202,7 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - if (this.dnd.onDragStart) { - this.dnd.onDragStart(data, originalEvent); - } + this.dnd.onDragStart?.(data, originalEvent); } onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { @@ -1196,9 +1214,7 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } onDragEnd(originalEvent: DragEvent): void { - if (this.dnd.onDragEnd) { - this.dnd.onDragEnd(originalEvent); - } + this.dnd.onDragEnd?.(originalEvent); } drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void { @@ -1230,7 +1246,7 @@ export class List implements ISpliceable, IThemable, IDisposable { protected view: ListView; private spliceable: ISpliceable; private styleController: IStyleController; - private typeLabelController?: TypeLabelController; + private typeNavigationController?: TypeNavigationController; private accessibilityProvider?: IListAccessibilityProvider; private keyboardController: KeyboardController | undefined; private mouseController: MouseController; @@ -1239,11 +1255,11 @@ export class List implements ISpliceable, IThemable, IDisposable { protected readonly disposables = new DisposableStore(); @memoize get onDidChangeFocus(): Event> { - return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); + return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e), this.disposables); } @memoize get onDidChangeSelection(): Event> { - return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e)); + return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e), this.disposables); } get domId(): string { return this.view.domId; } @@ -1270,14 +1286,14 @@ export class List implements ISpliceable, IThemable, IDisposable { @memoize get onContextMenu(): Event> { let didJustPressContextMenuKey = false; - const fromKeyDown = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) + const fromKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)) .map(e => new StandardKeyboardEvent(e)) .filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .map(stopEvent) .filter(() => false) .event as Event; - const fromKeyUp = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event) + const fromKeyUp = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event)) .forEach(() => didJustPressContextMenuKey = false) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) @@ -1291,7 +1307,7 @@ export class List implements ISpliceable, IThemable, IDisposable { }) .event; - const fromMouse = Event.chain(this.view.onContextMenu) + const fromMouse = this.disposables.add(Event.chain(this.view.onContextMenu)) .filter(_ => !didJustPressContextMenuKey) .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.pageX + 1, y: browserEvent.pageY }, browserEvent })) .event; @@ -1329,9 +1345,7 @@ export class List implements ISpliceable, IThemable, IDisposable { if (this.accessibilityProvider) { baseRenderers.push(new AccessibiltyRenderer(this.accessibilityProvider)); - if (this.accessibilityProvider.onDidChangeActiveDescendant) { - this.accessibilityProvider.onDidChangeActiveDescendant(this.onDidChangeActiveDescendant, this, this.disposables); - } + this.accessibilityProvider.onDidChangeActiveDescendant?.(this.onDidChangeActiveDescendant, this, this.disposables); } renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); @@ -1373,8 +1387,8 @@ export class List implements ISpliceable, IThemable, IDisposable { if (_options.keyboardNavigationLabelProvider) { const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate; - this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider, delegate); - this.disposables.add(this.typeLabelController); + this.typeNavigationController = new TypeNavigationController(this, this.view, _options.keyboardNavigationLabelProvider, _options.keyboardNavigationEventFilter ?? (() => true), delegate); + this.disposables.add(this.typeNavigationController); } this.mouseController = this.createMouseController(_options); @@ -1399,9 +1413,7 @@ export class List implements ISpliceable, IThemable, IDisposable { updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { this._options = { ...this._options, ...optionsUpdate }; - if (this.typeLabelController) { - this.typeLabelController.updateOptions(this._options); - } + this.typeNavigationController?.updateOptions(this._options); if (this._options.multipleSelectionController !== undefined) { if (this._options.multipleSelectionSupport) { @@ -1517,9 +1529,9 @@ export class List implements ISpliceable, IThemable, IDisposable { this.view.layout(height, width); } - toggleKeyboardNavigation(): void { - if (this.typeLabelController) { - this.typeLabelController.toggle(); + triggerTypeNavigation(): void { + if (this.typeNavigationController) { + this.typeNavigationController.trigger(); } } @@ -1598,20 +1610,25 @@ export class List implements ISpliceable, IThemable, IDisposable { async focusNextPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): Promise { let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; - const lastPageElement = this.view.element(lastPageIndex); - const currentlyFocusedElement = this.getFocusedElements()[0]; + const currentlyFocusedElementIndex = this.getFocus()[0]; - if (currentlyFocusedElement !== lastPageElement) { + if (currentlyFocusedElementIndex !== lastPageIndex && (currentlyFocusedElementIndex === undefined || lastPageIndex > currentlyFocusedElementIndex)) { const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter); - if (lastGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(lastGoodPageIndex)) { + if (lastGoodPageIndex > -1 && currentlyFocusedElementIndex !== lastGoodPageIndex) { this.setFocus([lastGoodPageIndex], browserEvent); } else { this.setFocus([lastPageIndex], browserEvent); } } else { const previousScrollTop = this.view.getScrollTop(); - this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); + let nextpageScrollTop = previousScrollTop + this.view.renderHeight; + if (lastPageIndex > currentlyFocusedElementIndex) { + // scroll last page element to the top only if the last page element is below the focused element + nextpageScrollTop -= this.view.elementHeight(lastPageIndex); + } + + this.view.setScrollTop(nextpageScrollTop); if (this.view.getScrollTop() !== previousScrollTop) { this.setFocus([]); @@ -1633,13 +1650,12 @@ export class List implements ISpliceable, IThemable, IDisposable { firstPageIndex = this.view.indexAfter(scrollTop - 1); } - const firstPageElement = this.view.element(firstPageIndex); - const currentlyFocusedElement = this.getFocusedElements()[0]; + const currentlyFocusedElementIndex = this.getFocus()[0]; - if (currentlyFocusedElement !== firstPageElement) { + if (currentlyFocusedElementIndex !== firstPageIndex && (currentlyFocusedElementIndex === undefined || currentlyFocusedElementIndex >= firstPageIndex)) { const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter); - if (firstGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(firstGoodPageIndex)) { + if (firstGoodPageIndex > -1 && currentlyFocusedElementIndex !== firstGoodPageIndex) { this.setFocus([firstGoodPageIndex], browserEvent); } else { this.setFocus([firstPageIndex], browserEvent); @@ -1783,6 +1799,10 @@ export class List implements ISpliceable, IThemable, IDisposable { return this.view.domNode; } + getElementID(index: number): string { + return this.view.getElementDomId(index); + } + style(styles: IListStyles): void { this.styleController.style(styles); } diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 450d0e3ccb..e8aaa5faaa 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -21,7 +21,7 @@ export interface IRangedGroup { export function groupIntersect(range: IRange, groups: IRangedGroup[]): IRangedGroup[] { const result: IRangedGroup[] = []; - for (let r of groups) { + for (const r of groups) { if (range.start >= r.range.end) { continue; } @@ -62,7 +62,7 @@ export function consolidate(groups: IRangedGroup[]): IRangedGroup[] { const result: IRangedGroup[] = []; let previousGroup: IRangedGroup | null = null; - for (let group of groups) { + for (const group of groups) { const start = group.range.start; const end = group.range.end; const size = group.size; @@ -138,7 +138,7 @@ export class RangeMap { let index = 0; let size = 0; - for (let group of this.groups) { + for (const group of this.groups) { const count = group.range.end - group.range.start; const newSize = size + (count * group.size); @@ -172,7 +172,7 @@ export class RangeMap { let position = 0; let count = 0; - for (let group of this.groups) { + for (const group of this.groups) { const groupCount = group.range.end - group.range.start; const newCount = count + groupCount; diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 70019a71ee..f6f4f39ce3 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -15,9 +15,7 @@ export interface IRow { function removeFromParent(element: HTMLElement): void { try { - if (element.parentElement) { - element.parentElement.removeChild(element); - } + element.parentElement?.removeChild(element); } catch (e) { // this will throw if this happens due to a blur event, nasty business } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index cbe14eede2..f8879a6c05 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -90,14 +90,13 @@ export class Menu extends ActionBar { context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, + ariaRole: 'menu', focusOnlyEnabledItems: true, triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true } }); this.menuElement = menuElement; - this.actionsList.setAttribute('role', 'menu'); - this.actionsList.tabIndex = 0; this.menuDisposables = this._register(new DisposableStore()); @@ -160,7 +159,7 @@ export class Menu extends ActionBar { } this._register(addDisposableListener(this.domNode, EventType.MOUSE_OUT, e => { - let relatedTarget = e.relatedTarget as HTMLElement; + const relatedTarget = e.relatedTarget as HTMLElement; if (!isAncestor(relatedTarget, this.domNode)) { this.focusedItem = undefined; this.updateFocus(); @@ -211,7 +210,7 @@ export class Menu extends ActionBar { })); - let parentData: ISubMenuData = { + const parentData: ISubMenuData = { parent: this }; @@ -287,11 +286,13 @@ export class Menu extends ActionBar { const fgColor = style.foregroundColor ? `${style.foregroundColor}` : ''; const bgColor = style.backgroundColor ? `${style.backgroundColor}` : ''; const border = style.borderColor ? `1px solid ${style.borderColor}` : ''; - const shadow = style.shadowColor ? `0 2px 4px ${style.shadowColor}` : ''; + const borderRadius = '5px'; + const shadow = style.shadowColor ? `0 2px 8px ${style.shadowColor}` : ''; - container.style.border = border; - this.domNode.style.color = fgColor; - this.domNode.style.backgroundColor = bgColor; + container.style.outline = border; + container.style.borderRadius = borderRadius; + container.style.color = fgColor; + container.style.backgroundColor = bgColor; container.style.boxShadow = shadow; if (this.viewItems) { @@ -340,7 +341,7 @@ export class Menu extends ActionBar { private setFocusedItem(element: HTMLElement): void { for (let i = 0; i < this.actionsList.children.length; i++) { - let elem = this.actionsList.children[i]; + const elem = this.actionsList.children[i]; if (element === elem) { this.focusedItem = i; break; @@ -445,9 +446,9 @@ class BaseMenuActionViewItem extends BaseActionViewItem { // Set mnemonic if (this.options.label && options.enableMnemonics) { - let label = this.getAction().label; + const label = this.getAction().label; if (label) { - let matches = MENU_MNEMONIC_REGEX.exec(label); + const matches = MENU_MNEMONIC_REGEX.exec(label); if (matches) { this.mnemonic = (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase(); } @@ -608,9 +609,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { this.label.innerText = replaceDoubleEscapes(label).trim(); } - if (this.item) { - this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); - } + this.item?.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); } else { this.label.innerText = label.replace(/&&/g, '&').trim(); } @@ -691,20 +690,19 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const isSelected = this.element && this.element.classList.contains('focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : undefined; - const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; + const outline = isSelected && this.menuStyle.selectionBorderColor ? `1px solid ${this.menuStyle.selectionBorderColor}` : ''; + const outlineOffset = isSelected && this.menuStyle.selectionBorderColor ? `-1px` : ''; if (this.item) { this.item.style.color = fgColor ? fgColor.toString() : ''; this.item.style.backgroundColor = bgColor ? bgColor.toString() : ''; + this.item.style.outline = outline; + this.item.style.outlineOffset = outlineOffset; } if (this.check) { this.check.style.color = fgColor ? fgColor.toString() : ''; } - - if (this.container) { - this.container.style.border = border; - } } style(style: IMenuStyles): void { @@ -765,7 +763,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { } this._register(addDisposableListener(this.element, EventType.KEY_UP, e => { - let event = new StandardKeyboardEvent(e); + const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { EventHelper.stop(e, true); @@ -774,7 +772,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { })); this._register(addDisposableListener(this.element, EventType.KEY_DOWN, e => { - let event = new StandardKeyboardEvent(e); + const event = new StandardKeyboardEvent(e); if (getActiveElement() === this.item) { if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { @@ -914,7 +912,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.submenuContainer.style.top = `${top - viewBox.top}px`; this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { - let event = new StandardKeyboardEvent(e); + const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.LeftArrow)) { EventHelper.stop(e, true); @@ -925,7 +923,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { })); this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, e => { - let event = new StandardKeyboardEvent(e); + const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.LeftArrow)) { EventHelper.stop(e, true); } @@ -966,9 +964,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.submenuIndicator.style.color = fgColor ? `${fgColor}` : ''; } - if (this.parentData.submenu) { - this.parentData.submenu.style(this.menuStyle); - } + this.parentData.submenu?.style(this.menuStyle); } override dispose(): void { @@ -1012,7 +1008,8 @@ function getMenuWidgetCSS(style: IMenuStyles, isForShadowDom: boolean): string { let result = /* css */` .monaco-menu { font-size: 13px; - + border-radius: 5px; + min-width: 160px; } ${formatRule(Codicon.menuSelection)} @@ -1087,10 +1084,9 @@ ${formatRule(Codicon.menuSubmenu)} .monaco-menu .monaco-action-bar.vertical .action-label.separator { display: block; - border-bottom: 1px solid #bbb; + border-bottom: 1px solid var(--vscode-menu-separatorBackground); padding-top: 1px; - margin-left: .8em; - margin-right: .8em; + padding: 30px; } .monaco-menu .secondary-actions .monaco-action-bar .action-label { @@ -1136,6 +1132,11 @@ ${formatRule(Codicon.menuSubmenu)} position: relative; } +.monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .keybinding, +.monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .keybinding { + opacity: unset; +} + .monaco-menu .monaco-action-bar.vertical .action-label { flex: 1 1 auto; text-decoration: none; @@ -1191,12 +1192,9 @@ ${formatRule(Codicon.menuSubmenu)} } .monaco-menu .monaco-action-bar.vertical .action-label.separator { - padding: 0.5em 0 0 0; - margin-bottom: 0.5em; width: 100%; height: 0px !important; - margin-left: .8em !important; - margin-right: .8em !important; + opacity: 1; } .monaco-menu .monaco-action-bar.vertical .action-label.separator.text { @@ -1238,17 +1236,15 @@ ${formatRule(Codicon.menuSubmenu)} outline: 0; } -.monaco-menu .monaco-action-bar.vertical .action-item { - border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ -} - - -/* High Contrast Theming */ +.hc-black .context-view.monaco-menu-container, +.hc-light .context-view.monaco-menu-container, :host-context(.hc-black) .context-view.monaco-menu-container, :host-context(.hc-light) .context-view.monaco-menu-container { box-shadow: none; } +.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused, +.hc-light .monaco-menu .monaco-action-bar.vertical .action-item.focused, :host-context(.hc-black) .monaco-menu .monaco-action-bar.vertical .action-item.focused, :host-context(.hc-light) .monaco-menu .monaco-action-bar.vertical .action-item.focused { background: none; @@ -1257,11 +1253,11 @@ ${formatRule(Codicon.menuSubmenu)} /* Vertical Action Bar Styles */ .monaco-menu .monaco-action-bar.vertical { - padding: .5em 0; + padding: .6em 0; } .monaco-menu .monaco-action-bar.vertical .action-menu-item { - height: 1.8em; + height: 2em; } .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), @@ -1277,10 +1273,12 @@ ${formatRule(Codicon.menuSubmenu)} .monaco-menu .monaco-action-bar.vertical .action-label.separator { font-size: inherit; - padding: 0.2em 0 0 0; - margin-bottom: 0.2em; + margin: 5px 0 !important; + padding: 0; + border-radius: 0; } +.linux .monaco-menu .monaco-action-bar.vertical .action-label.separator, :host-context(.linux) .monaco-menu .monaco-action-bar.vertical .action-label.separator { margin-left: 0; margin-right: 0; @@ -1291,6 +1289,7 @@ ${formatRule(Codicon.menuSubmenu)} padding: 0 1.8em; } +.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator { :host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator { height: 100%; mask-size: 10px 10px; diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index cb21994180..2ea1400009 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -9,25 +9,35 @@ display: flex; flex-shrink: 1; box-sizing: border-box; - height: 30px; + height: 100%; overflow: hidden; - flex-wrap: wrap; +} + +.menubar.overflow-menu-only { + width: 38px; } .fullscreen .menubar:not(.compact) { margin: 0px; - padding: 0px 5px; + padding: 4px 5px; } .menubar > .menubar-menu-button { + display: flex; align-items: center; box-sizing: border-box; - padding: 0px 8px; cursor: default; -webkit-app-region: no-drag; zoom: 1; white-space: nowrap; - outline: 0; + outline: 0 !important; +} + +.monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .menubar-menu-title { + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; + outline-color: var(--vscode-focusBorder); } .menubar.compact { @@ -41,6 +51,11 @@ padding: 0px; } +.menubar-menu-title { + padding: 0px 8px; + border-radius: 5px; +} + .menubar .menubar-menu-items-holder { position: fixed; left: 0px; @@ -62,8 +77,13 @@ } .menubar .toolbar-toggle-more { - width: 20px; - height: 100%; + width: 22px; + height: 22px; + padding: 0 8px; + display: flex; + align-items: center; + justify-content: center; + vertical-align: sub; } .menubar.compact .toolbar-toggle-more { @@ -77,19 +97,15 @@ justify-content: center; } -.menubar .toolbar-toggle-more { - padding: 0; - vertical-align: sub; -} - +.menubar:not(.compact) .menubar-menu-button:first-child .toolbar-toggle-more::before, .menubar.compact .toolbar-toggle-more::before { content: "\eb94" !important; } /* Match behavior of outline for activity bar icons */ -.menubar.compact > .menubar-menu-button.open, -.menubar.compact > .menubar-menu-button:focus, -.menubar.compact > .menubar-menu-button:hover { +.menubar.compact > .menubar-menu-button.open .menubar-menu-title, +.menubar.compact > .menubar-menu-button:focus .menubar-menu-title, +.menubar.compact > .menubar-menu-button:hover .menubar-menu-title{ outline-width: 1px !important; outline-offset: -8px !important; } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index c708528c42..c8719d01cc 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -116,7 +116,7 @@ export class MenuBar extends Disposable { this._register(DOM.ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this)); this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, (e) => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; const key = !!e.key ? e.key.toLocaleLowerCase() : ''; @@ -154,7 +154,7 @@ export class MenuBar extends Disposable { })); this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, (e) => { - let event = e as FocusEvent; + const event = e as FocusEvent; if (event.relatedTarget) { if (!this.container.contains(event.relatedTarget as HTMLElement)) { @@ -164,7 +164,7 @@ export class MenuBar extends Disposable { })); this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_OUT, (e) => { - let event = e as FocusEvent; + const event = e as FocusEvent; // We are losing focus and there is no related target, e.g. webview case if (!event.relatedTarget) { @@ -204,11 +204,11 @@ export class MenuBar extends Disposable { const menuIndex = this.menus.length; const cleanMenuLabel = cleanMnemonic(menuBarMenu.label); - let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(menuBarMenu.label); + const mnemonicMatches = MENU_MNEMONIC_REGEX.exec(menuBarMenu.label); // Register mnemonics if (mnemonicMatches) { - let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3]; + const mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3]; this.registerMnemonic(this.menus.length, mnemonic); } @@ -225,7 +225,7 @@ export class MenuBar extends Disposable { this.updateLabels(titleElement, buttonElement, menuBarMenu.label); this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.KEY_UP, (e) => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) { @@ -313,8 +313,7 @@ export class MenuBar extends Disposable { createOverflowMenu(): void { const label = this.isCompact ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', 'More'); - const title = this.isCompact ? label : undefined; - const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.isCompact ? 0 : -1, 'aria-label': label, 'title': title, 'aria-haspopup': true }); + const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': this.isCompact ? 0 : -1, 'aria-label': label, 'aria-haspopup': true }); const titleElement = $('div.menubar-menu-title.toolbar-toggle-more' + Codicon.menuBarMore.cssSelector, { 'role': 'none', 'aria-hidden': true }); buttonElement.appendChild(titleElement); @@ -322,7 +321,7 @@ export class MenuBar extends Disposable { buttonElement.style.visibility = 'hidden'; this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.KEY_UP, (e) => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; const triggerKeys = [KeyCode.Enter]; @@ -330,7 +329,12 @@ export class MenuBar extends Disposable { triggerKeys.push(KeyCode.DownArrow); } else { triggerKeys.push(KeyCode.Space); - triggerKeys.push(this.options.compactMode === Direction.Right ? KeyCode.RightArrow : KeyCode.LeftArrow); + + if (this.options.compactMode === Direction.Right) { + triggerKeys.push(KeyCode.RightArrow); + } else if (this.options.compactMode === Direction.Left) { + triggerKeys.push(KeyCode.LeftArrow); + } } if ((triggerKeys.some(k => event.equals(k)) && !this.isOpen)) { @@ -469,6 +473,11 @@ export class MenuBar extends Disposable { return; } + const overflowMenuOnlyClass = 'overflow-menu-only'; + + // Remove overflow only restriction to allow the most space + this.container.classList.toggle(overflowMenuOnlyClass, false); + const sizeAvailable = this.container.offsetWidth; let currentSize = 0; let full = this.isCompact; @@ -476,7 +485,7 @@ export class MenuBar extends Disposable { this.numMenusShown = 0; const showableMenus = this.menus.filter(menu => menu.buttonElement !== undefined && menu.titleElement !== undefined) as (MenuBarMenuWithElements & { titleElement: HTMLElement; buttonElement: HTMLElement })[]; - for (let menuBarMenu of showableMenus) { + for (const menuBarMenu of showableMenus) { if (!full) { const size = menuBarMenu.buttonElement.offsetWidth; if (currentSize + size > sizeAvailable) { @@ -495,6 +504,18 @@ export class MenuBar extends Disposable { } } + + // If below minimium menu threshold, show the overflow menu only as hamburger menu + if (this.numMenusShown - 1 <= showableMenus.length / 2) { + for (const menuBarMenu of showableMenus) { + menuBarMenu.buttonElement.style.visibility = 'hidden'; + } + + full = true; + this.numMenusShown = 0; + currentSize = 0; + } + // Overflow if (this.isCompact) { this.overflowMenu.actions = []; @@ -534,6 +555,9 @@ export class MenuBar extends Disposable { this.container.appendChild(this.overflowMenu.buttonElement); this.overflowMenu.buttonElement.style.visibility = 'hidden'; } + + // If we are only showing the overflow, add this class to avoid taking up space + this.container.classList.toggle(overflowMenuOnlyClass, this.numMenusShown === 0); } private updateLabels(titleElement: HTMLElement, buttonElement: HTMLElement, label: string): void { @@ -542,7 +566,7 @@ export class MenuBar extends Disposable { // Update the button label to reflect mnemonics if (this.options.enableMnemonics) { - let cleanLabel = strings.escape(label); + const cleanLabel = strings.escape(label); // This is global so reset it MENU_ESCAPED_MNEMONIC_REGEX.lastIndex = 0; @@ -569,11 +593,11 @@ export class MenuBar extends Disposable { titleElement.innerText = cleanMenuLabel.replace(/&&/g, '&'); } - let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); + const mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); // Register mnemonics if (mnemonicMatches) { - let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3]; + const mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[3]; if (this.options.enableMnemonics) { buttonElement.setAttribute('aria-keyshortcuts', 'Alt+' + mnemonic.toLocaleLowerCase()); @@ -740,7 +764,7 @@ export class MenuBar extends Disposable { this._onFocusStateChange.fire(this.focusState >= MenubarState.FOCUSED); } - private get isVisible(): boolean { + get isVisible(): boolean { return this.focusState >= MenubarState.VISIBLE; } @@ -838,7 +862,7 @@ export class MenuBar extends Disposable { if (this.menus) { this.menus.forEach(menuBarMenu => { if (menuBarMenu.titleElement && menuBarMenu.titleElement.children.length) { - let child = menuBarMenu.titleElement.children.item(0) as HTMLElement; + const child = menuBarMenu.titleElement.children.item(0) as HTMLElement; if (child) { child.style.textDecoration = (this.options.alwaysOnMnemonics || visible) ? 'underline' : ''; } @@ -956,9 +980,7 @@ export class MenuBar extends Disposable { } if (this.focusedMenu.holder) { - if (this.focusedMenu.holder.parentElement) { - this.focusedMenu.holder.parentElement.classList.remove('open'); - } + this.focusedMenu.holder.parentElement?.classList.remove('open'); this.focusedMenu.holder.remove(); } @@ -975,7 +997,7 @@ export class MenuBar extends Disposable { const actualMenuIndex = menuIndex >= this.numMenusShown ? MenuBar.OVERFLOW_INDEX : menuIndex; const customMenu = actualMenuIndex === MenuBar.OVERFLOW_INDEX ? this.overflowMenu : this.menus[actualMenuIndex]; - if (!customMenu.actions || !customMenu.buttonElement) { + if (!customMenu.actions || !customMenu.buttonElement || !customMenu.titleElement) { return; } @@ -983,23 +1005,24 @@ export class MenuBar extends Disposable { customMenu.buttonElement.classList.add('open'); - const buttonBoundingRect = customMenu.buttonElement.getBoundingClientRect(); + const titleBoundingRect = customMenu.titleElement.getBoundingClientRect(); + const titleBoundingRectZoom = DOM.getDomNodeZoomLevel(customMenu.titleElement); if (this.options.compactMode === Direction.Right) { - menuHolder.style.top = `${buttonBoundingRect.top}px`; - menuHolder.style.left = `${buttonBoundingRect.left + this.container.clientWidth}px`; + menuHolder.style.top = `${titleBoundingRect.top}px`; + menuHolder.style.left = `${titleBoundingRect.left + this.container.clientWidth}px`; } else if (this.options.compactMode === Direction.Left) { - menuHolder.style.top = `${buttonBoundingRect.top}px`; + menuHolder.style.top = `${titleBoundingRect.top}px`; menuHolder.style.right = `${this.container.clientWidth}px`; menuHolder.style.left = 'auto'; } else { - menuHolder.style.top = `${buttonBoundingRect.bottom}px`; - menuHolder.style.left = `${buttonBoundingRect.left}px`; + menuHolder.style.top = `${titleBoundingRect.bottom * titleBoundingRectZoom}px`; + menuHolder.style.left = `${titleBoundingRect.left * titleBoundingRectZoom}px`; } customMenu.buttonElement.appendChild(menuHolder); - let menuOptions: IMenuOptions = { + const menuOptions: IMenuOptions = { getKeyBinding: this.options.getKeybinding, actionRunner: this.actionRunner, enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics), @@ -1008,7 +1031,7 @@ export class MenuBar extends Disposable { useEventAsContext: true }; - let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); + const menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); if (this.menuStyle) { menuWidget.style(this.menuStyle); } diff --git a/src/vs/editor/contrib/suggest/browser/resizable.ts b/src/vs/base/browser/ui/resizable/resizable.ts similarity index 100% rename from src/vs/editor/contrib/suggest/browser/resizable.ts rename to src/vs/base/browser/ui/resizable/resizable.ts diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index fc6be8e6e7..d194e90990 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -17,7 +17,7 @@ import 'vs/css!./sash'; * Allow the sashes to be visible at runtime. * @remark Use for development purposes only. */ -let DEBUG = false; +const DEBUG = false; // DEBUG = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this /** diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 93749bf6f1..69dded3ba3 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode'; -import { GlobalPointerMoveMonitor, IPointerMoveEventData, standardPointerMoveMerger } from 'vs/base/browser/globalPointerMoveMonitor'; +import { GlobalPointerMoveMonitor } from 'vs/base/browser/globalPointerMoveMonitor'; import { StandardWheelEvent } from 'vs/base/browser/mouseEvent'; import { ScrollbarArrow, ScrollbarArrowOptions } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; @@ -245,8 +245,7 @@ export abstract class AbstractScrollbar extends Widget { e.target, e.pointerId, e.buttons, - standardPointerMoveMerger, - (pointerMoveData: IPointerMoveEventData) => { + (pointerMoveData: PointerEvent) => { const pointerOrthogonalPosition = this._sliderOrthogonalPointerPosition(pointerMoveData); const pointerOrthogonalDelta = Math.abs(pointerOrthogonalPosition - initialPointerOrthogonalPosition); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index dc61139d2a..0cbf432d7b 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -232,7 +232,7 @@ export abstract class AbstractScrollableElement extends Widget { this._setListeningToMouseWheel(this._options.handleMouseWheel); this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e)); - this.onnonbubblingmouseout(this._listenOnDomNode, (e) => this._onMouseOut(e)); + this.onmouseleave(this._listenOnDomNode, (e) => this._onMouseLeave(e)); this._hideTimeout = this._register(new TimeoutTimer()); this._isDragging = false; @@ -525,7 +525,7 @@ export abstract class AbstractScrollableElement extends Widget { this._hide(); } - private _onMouseOut(e: IMouseEvent): void { + private _onMouseLeave(e: IMouseEvent): void { this._mouseIsOver = false; this._hide(); } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts index c594768f78..bbcd1c27fa 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { GlobalPointerMoveMonitor, standardPointerMoveMerger } from 'vs/base/browser/globalPointerMoveMonitor'; +import { GlobalPointerMoveMonitor } from 'vs/base/browser/globalPointerMoveMonitor'; import { Widget } from 'vs/base/browser/ui/widget'; import { IntervalTimer, TimeoutTimer } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; @@ -103,7 +103,6 @@ export class ScrollbarArrow extends Widget { e.target, e.pointerId, e.buttons, - standardPointerMoveMerger, (pointerMoveData) => { /* Intentional empty */ }, () => { this._pointerdownRepeatTimer.cancel(); diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts index 563ee0f42a..291f05dd45 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts @@ -103,9 +103,7 @@ export class ScrollbarVisibilityController extends Disposable { // The CSS animation doesn't play otherwise this._revealTimer.setIfNotSet(() => { - if (this._domNode) { - this._domNode.setClassName(this._visibleClassName); - } + this._domNode?.setClassName(this._visibleClassName); }, 0); } @@ -115,8 +113,6 @@ export class ScrollbarVisibilityController extends Disposable { return; } this._isVisible = false; - if (this._domNode) { - this._domNode.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : '')); - } + this._domNode?.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : '')); } } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index cf1872bf40..4cf69a9606 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -131,6 +131,10 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); } + if (typeof this.selectBoxOptions.ariaDescription === 'string') { + this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); + } + this._onDidSelect = new Emitter(); this._register(this._onDidSelect); @@ -169,8 +173,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectionDetailsPane = dom.append(this.selectDropDownContainer, $('.select-box-details-pane')); // Create span flex box item/div we can measure and control - let widthControlOuterDiv = dom.append(this.selectDropDownContainer, $('.select-box-dropdown-container-width-control')); - let widthControlInnerDiv = dom.append(widthControlOuterDiv, $('.width-control-div')); + const widthControlOuterDiv = dom.append(this.selectDropDownContainer, $('.select-box-dropdown-container-width-control')); + const widthControlInnerDiv = dom.append(widthControlOuterDiv, $('.width-control-div')); this.widthControlElement = document.createElement('span'); this.widthControlElement.className = 'option-text-width-control'; dom.append(widthControlInnerDiv, this.widthControlElement); @@ -291,10 +295,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Mirror options in drop-down // Populate select list for non-native select mode - if (this.selectList) { - this.selectList.splice(0, this.selectList.length, this.options); - this.selectList?.setSelection(this.selected !== -1 ? [this.selected] : []); // {{SQL CARBON EDIT}} - Set the selected indexes. - } + this.selectList?.splice(0, this.selectList.length, this.options); + this.selectList?.setSelection(this.selected !== -1 ? [this.selected] : []); // {{SQL CARBON EDIT}} - Set the selected indexes. } public select(index: number): void { @@ -449,7 +451,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } private createOption(value: string, index: number, disabled?: boolean): HTMLOptionElement { - let option = document.createElement('option'); + const option = document.createElement('option'); option.value = value; option.text = value; option.disabled = !!disabled; diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css index 306c73c056..1351e2119b 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -24,7 +24,6 @@ height: 22px; font-size: 11px; font-weight: bold; - text-transform: uppercase; overflow: hidden; display: flex; cursor: pointer; @@ -32,6 +31,10 @@ box-sizing: border-box; } +.monaco-pane-view .pane > .pane-header > .title { + text-transform: uppercase; +} + .monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header { flex-direction: column; height: 100%; diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index fc70855a07..a9a56b0975 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -143,9 +143,7 @@ export abstract class Pane extends Disposable implements IView { return false; } - if (this.element) { - this.element.classList.toggle('expanded', expanded); - } + this.element?.classList.toggle('expanded', expanded); this._expanded = !!expanded; this.updateHeader(); diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 63ae7c6ce9..55ec3417a0 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -231,9 +231,7 @@ abstract class ViewItem { this.container.classList.toggle('visible', visible); - if (this.view.setVisible) { - this.view.setVisible(visible); - } + this.view.setVisible?.(visible); } get minimumSize(): number { return this.visible ? this.view.minimumSize : 0; } @@ -933,6 +931,23 @@ export class SplitView extends Disposable { this.state = State.Idle; } + /** + * Returns whether all other {@link IView views} are at their minimum size. + */ + isViewSizeMaximized(index: number): boolean { + if (index < 0 || index >= this.viewItems.length) { + return false; + } + + for (const item of this.viewItems) { + if (item !== this.viewItems[index] && item.size > item.minimumSize) { + return false; + } + } + + return true; + } + /** * Distribute the entire {@link SplitView} size among all {@link IView views}. */ @@ -1011,7 +1026,7 @@ export class SplitView extends Disposable { // Add sash if (this.viewItems.length > 1) { - let opts = { orthogonalStartSash: this.orthogonalStartSash, orthogonalEndSash: this.orthogonalEndSash }; + const opts = { orthogonalStartSash: this.orthogonalStartSash, orthogonalEndSash: this.orthogonalEndSash }; const sash = this.orientation === Orientation.VERTICAL ? new Sash(this.sashContainer, { getHorizontalSashTop: s => this.getSashPosition(s), getHorizontalSashWidth: this.getSashOrthogonalSize }, { ...opts, orientation: Orientation.HORIZONTAL }) diff --git a/src/vs/base/browser/ui/table/tableWidget.ts b/src/vs/base/browser/ui/table/tableWidget.ts index 4b27576872..1d08ab0f25 100644 --- a/src/vs/base/browser/ui/table/tableWidget.ts +++ b/src/vs/base/browser/ui/table/tableWidget.ts @@ -265,8 +265,8 @@ export class Table implements ISpliceable, IThemable, IDisposable { this.list.layout(listHeight, width); } - toggleKeyboardNavigation(): void { - this.list.toggleKeyboardNavigation(); + triggerTypeNavigation(): void { + this.list.triggerTypeNavigation(); } style(styles: ITableStyles): void { @@ -341,6 +341,10 @@ export class Table implements ISpliceable, IThemable, IDisposable { return this.list.getFocusedElements(); } + getRelativeTop(index: number): number | null { + return this.list.getRelativeTop(index); + } + reveal(index: number, relativeTop?: number): void { this.list.reveal(index, relativeTop); } diff --git a/src/vs/base/browser/ui/toggle/toggle.ts b/src/vs/base/browser/ui/toggle/toggle.ts index 55f1b48a0b..64a8303d7c 100644 --- a/src/vs/base/browser/ui/toggle/toggle.ts +++ b/src/vs/base/browser/ui/toggle/toggle.ts @@ -98,6 +98,7 @@ export class Toggle extends Widget { readonly onKeyDown: Event = this._onKeyDown.event; private readonly _opts: IToggleOpts; + private _icon: CSSIcon | undefined; readonly domNode: HTMLElement; private _checked: boolean; @@ -110,7 +111,8 @@ export class Toggle extends Widget { const classes = ['monaco-custom-toggle']; if (this._opts.icon) { - classes.push(...CSSIcon.asClassNameArray(this._opts.icon)); + this._icon = this._opts.icon; + classes.push(...CSSIcon.asClassNameArray(this._icon)); } if (this._opts.actionClassName) { classes.push(...this._opts.actionClassName.split(' ')); @@ -146,6 +148,7 @@ export class Toggle extends Widget { this.checked = !this._checked; this._onChange.fire(true); keyboardEvent.preventDefault(); + keyboardEvent.stopPropagation(); return; } @@ -174,6 +177,16 @@ export class Toggle extends Widget { this.applyStyles(); } + setIcon(icon: CSSIcon | undefined): void { + if (this._icon) { + this.domNode.classList.remove(...CSSIcon.asClassNameArray(this._icon)); + } + this._icon = icon; + if (this._icon) { + this.domNode.classList.add(...CSSIcon.asClassNameArray(this._icon)); + } + } + width(): number { return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */; } diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 57284c0c48..9967f74100 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -130,9 +130,7 @@ export class ToolBar extends Disposable { set context(context: unknown) { this.actionBar.context = context; - if (this.toggleMenuActionViewItem) { - this.toggleMenuActionViewItem.setActionContext(context); - } + this.toggleMenuActionViewItem?.setActionContext(context); for (const actionViewItem of this.submenuActionViewItems) { actionViewItem.setActionContext(context); } @@ -169,7 +167,7 @@ export class ToolBar extends Disposable { setActions(primaryActions: ReadonlyArray, secondaryActions?: ReadonlyArray): void { this.clear(); - let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; + const primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; // Inject additional action to open secondary actions if present this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index ed5bf4bb14..f858d8a369 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -3,27 +3,34 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DragAndDropData, IDragAndDropData, StaticDND } from 'vs/base/browser/dnd'; -import { $, addDisposableListener, append, clearNode, createStyleSheet, getDomNodePagePosition, hasParentWithClass } from 'vs/base/browser/dom'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { $, append, clearNode, createStyleSheet, h, hasParentWithClass } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IIdentityProvider, IKeyboardNavigationDelegate, IKeyboardNavigationLabelProvider, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IListMouseEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; +import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IListMouseEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; -import { DefaultKeyboardNavigationDelegate, IListOptions, IListStyles, isInputElement, isMonacoEditor, List, MouseController } from 'vs/base/browser/ui/list/listWidget'; +import { IListOptions, IListStyles, isButton, isInputElement, isMonacoEditor, List, MouseController, TypeNavigationMode } from 'vs/base/browser/ui/list/listWidget'; +import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; import { ICollapseStateChangeEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeEvent, ITreeFilter, ITreeModel, ITreeModelSpliceEvent, ITreeMouseEvent, ITreeNavigator, ITreeNode, ITreeRenderer, TreeDragOverBubble, TreeError, TreeFilterResult, TreeMouseEventTarget, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; +import { Action } from 'vs/base/common/actions'; import { distinct, equals, firstOrDefault, range } from 'vs/base/common/arrays'; -import { disposableTimeout } from 'vs/base/common/async'; +import { disposableTimeout, timeout } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; import { SetMap } from 'vs/base/common/collections'; +import { Color } from 'vs/base/common/color'; import { Emitter, Event, EventBufferer, Relay } from 'vs/base/common/event'; import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; -import { isMacintosh } from 'vs/base/common/platform'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; +import { isNumber } from 'vs/base/common/types'; import 'vs/css!./media/tree'; import { localize } from 'vs/nls'; @@ -70,9 +77,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - if (this.dnd.onDragStart) { - this.dnd.onDragStart(asTreeDragAndDropData(data), originalEvent); - } + this.dnd.onDragStart?.(asTreeDragAndDropData(data), originalEvent); } onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { @@ -137,9 +142,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } onDragEnd(originalEvent: DragEvent): void { - if (this.dnd.onDragEnd) { - this.dnd.onDragEnd(originalEvent); - } + this.dnd.onDragEnd?.(originalEvent); } } @@ -198,8 +201,7 @@ function asListOptions(modelProvider: () => ITreeModel implements IListV } setDynamicHeight(element: N, height: number): void { - if (this.delegate.setDynamicHeight) { - this.delegate.setDynamicHeight(element.element, height); - } + this.delegate.setDynamicHeight?.(element.element, height); } } @@ -308,8 +308,9 @@ interface Collection { readonly onDidChange: Event; } -class EventCollection implements Collection { +class EventCollection implements Collection, IDisposable { + private readonly disposables = new DisposableStore(); readonly onDidChange: Event; get elements(): T[] { @@ -317,7 +318,11 @@ class EventCollection implements Collection { } constructor(onDidChange: Event, private _elements: T[] = []) { - this.onDidChange = Event.forEach(onDidChange, elements => this._elements = elements); + this.onDidChange = Event.forEach(onDidChange, elements => this._elements = elements, this.disposables); + } + + dispose(): void { + this.disposables.dispose(); } } @@ -350,9 +355,7 @@ class TreeRenderer implements IListRenderer Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); - if (renderer.onDidChangeTwistieState) { - renderer.onDidChangeTwistieState(this.onDidChangeTwistieState, this, this.disposables); - } + renderer.onDidChangeTwistieState?.(this.onDidChangeTwistieState, this, this.disposables); } updateOptions(options: ITreeRendererOptions = {}): void { @@ -414,9 +417,7 @@ class TreeRenderer implements IListRenderer disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, height: number | undefined): void { templateData.indentGuidesDisposable.dispose(); - if (this.renderer.disposeElement) { - this.renderer.disposeElement(node, index, templateData.templateData, height); - } + this.renderer.disposeElement?.(node, index, templateData.templateData, height); if (typeof height === 'number') { this.renderedNodes.delete(node); @@ -568,7 +569,7 @@ class TreeRenderer implements IListRenderer export type LabelFuzzyScore = { label: string; score: FuzzyScore }; -class TypeFilter implements ITreeFilter, IDisposable { +class FindFilter implements ITreeFilter, IDisposable { private _totalCount = 0; get totalCount(): number { return this._totalCount; } private _matchCount = 0; @@ -592,15 +593,11 @@ class TypeFilter implements ITreeFilter, IDi } filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { + let visibility = TreeVisibility.Visible; + if (this._filter) { const result = this._filter.filter(element, parentVisibility); - if (this.tree.options.simpleKeyboardNavigation) { - return result; - } - - let visibility: TreeVisibility; - if (typeof result === 'boolean') { visibility = result ? TreeVisibility.Visible : TreeVisibility.Hidden; } else if (isFilterResult(result)) { @@ -616,9 +613,9 @@ class TypeFilter implements ITreeFilter, IDi this._totalCount++; - if (this.tree.options.simpleKeyboardNavigation || !this._pattern) { + if (!this._pattern) { this._matchCount++; - return { data: FuzzyScore.Default, visibility: true }; + return { data: FuzzyScore.Default, visibility }; } const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element); @@ -627,22 +624,22 @@ class TypeFilter implements ITreeFilter, IDi for (const l of labels) { const labelStr = l && l.toString(); if (typeof labelStr === 'undefined') { - return { data: FuzzyScore.Default, visibility: true }; + return { data: FuzzyScore.Default, visibility }; } - const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0); + const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0, { firstMatchCanBeWeak: true, boostFullMatch: true }); if (score) { this._matchCount++; return labels.length === 1 ? - { data: score, visibility: true } : - { data: { label: labelStr, score: score }, visibility: true }; + { data: score, visibility } : + { data: { label: labelStr, score: score }, visibility }; } } - if (this.tree.options.filterOnType) { + if (this.tree.findMode === TreeFindMode.Filter) { return TreeVisibility.Recurse; } else { - return { data: FuzzyScore.Default, visibility: true }; + return { data: FuzzyScore.Default, visibility }; } } @@ -656,170 +653,282 @@ class TypeFilter implements ITreeFilter, IDi } } -class TypeFilterController implements IDisposable { +export interface ICaseSensitiveToggleOpts { + readonly isChecked: boolean; + readonly inputActiveOptionBorder?: Color; + readonly inputActiveOptionForeground?: Color; + readonly inputActiveOptionBackground?: Color; +} - private _enabled = false; - get enabled(): boolean { return this._enabled; } +export class ModeToggle extends Toggle { + constructor(opts?: ICaseSensitiveToggleOpts) { + super({ + icon: Codicon.filter, + title: localize('filter', "Filter"), + isChecked: opts?.isChecked ?? false, + inputActiveOptionBorder: opts?.inputActiveOptionBorder, + inputActiveOptionForeground: opts?.inputActiveOptionForeground, + inputActiveOptionBackground: opts?.inputActiveOptionBackground + }); + } +} + +export interface IFindWidgetStyles extends IFindInputStyles, IListStyles { } + +export interface IFindWidgetOpts extends IFindWidgetStyles { } + +export enum TreeFindMode { + Highlight, + Filter +} + +class FindWidget extends Disposable { + + private readonly elements = h('.monaco-tree-type-filter', [ + h('.monaco-tree-type-filter-grab.codicon.codicon-debug-gripper@grab', { tabIndex: 0 }), + h('.monaco-tree-type-filter-input@findInput'), + h('.monaco-tree-type-filter-actionbar@actionbar'), + ]); + + set mode(mode: TreeFindMode) { + this.modeToggle.checked = mode === TreeFindMode.Filter; + this.findInput.inputBox.setPlaceHolder(mode === TreeFindMode.Filter ? localize('type to filter', "Type to filter") : localize('type to search', "Type to search")); + } + + private readonly modeToggle: ModeToggle; + private readonly findInput: FindInput; + private readonly actionbar: ActionBar; + private width = 0; + private right = 0; + + readonly _onDidDisable = new Emitter(); + readonly onDidDisable = this._onDidDisable.event; + readonly onDidChangeValue: Event; + readonly onDidChangeMode: Event; + + constructor( + container: HTMLElement, + private tree: AbstractTree, + contextViewProvider: IContextViewProvider, + mode: TreeFindMode, + options?: IFindWidgetOpts + ) { + super(); + + container.appendChild(this.elements.root); + this._register(toDisposable(() => container.removeChild(this.elements.root))); + + this.modeToggle = this._register(new ModeToggle({ ...options, isChecked: mode === TreeFindMode.Filter })); + this.onDidChangeMode = Event.map(this.modeToggle.onChange, () => this.modeToggle.checked ? TreeFindMode.Filter : TreeFindMode.Highlight, this._store); + + this.findInput = this._register(new FindInput(this.elements.findInput, contextViewProvider, false, { + label: localize('type to search', "Type to search"), + additionalToggles: [this.modeToggle] + })); + + this.actionbar = this._register(new ActionBar(this.elements.actionbar)); + this.mode = mode; + + const emitter = this._register(new DomEmitter(this.findInput.inputBox.inputElement, 'keydown')); + const onKeyDown = this._register(Event.chain(emitter.event)) + .map(e => new StandardKeyboardEvent(e)) + .event; + + this._register(onKeyDown((e): any => { + switch (e.keyCode) { + case KeyCode.DownArrow: + e.preventDefault(); + e.stopPropagation(); + this.tree.domFocus(); + return; + } + })); + + const closeAction = this._register(new Action('close', localize('close', "Close"), 'codicon codicon-close', true, () => this.dispose())); + this.actionbar.push(closeAction, { icon: true, label: false }); + + const onGrabMouseDown = this._register(new DomEmitter(this.elements.grab, 'mousedown')); + + this._register(onGrabMouseDown.event(e => { + const disposables = new DisposableStore(); + const onWindowMouseMove = disposables.add(new DomEmitter(window, 'mousemove')); + const onWindowMouseUp = disposables.add(new DomEmitter(window, 'mouseup')); + + const startRight = this.right; + const startX = e.pageX; + this.elements.grab.classList.add('grabbing'); + + const update = (e: MouseEvent) => { + const deltaX = e.pageX - startX; + this.right = startRight - deltaX; + this.layout(); + }; + + disposables.add(onWindowMouseMove.event(update)); + disposables.add(onWindowMouseUp.event(e => { + update(e); + this.elements.grab.classList.remove('grabbing'); + disposables.dispose(); + })); + })); + + const onGrabKeyDown = this._register(Event.chain(this._register(new DomEmitter(this.elements.grab, 'keydown')).event)) + .map(e => new StandardKeyboardEvent(e)) + .event; + + this._register(onGrabKeyDown((e): any => { + let right: number | undefined; + + if (e.keyCode === KeyCode.LeftArrow) { + right = Number.POSITIVE_INFINITY; + } else if (e.keyCode === KeyCode.RightArrow) { + right = 0; + } else if (e.keyCode === KeyCode.Space) { + right = this.right === 0 ? Number.POSITIVE_INFINITY : 0; + } + + if (right !== undefined) { + e.preventDefault(); + e.stopPropagation(); + this.right = right; + this.layout(); + } + })); + + this.onDidChangeValue = this.findInput.onDidChange; + this.style(options ?? {}); + } + + style(styles: IFindWidgetStyles): void { + this.findInput.style(styles); + + if (styles.listFilterWidgetBackground) { + this.elements.root.style.backgroundColor = styles.listFilterWidgetBackground.toString(); + } + + if (styles.listFilterWidgetShadow) { + this.elements.root.style.boxShadow = `0 0 8px 2px ${styles.listFilterWidgetShadow}`; + } + } + + focus() { + this.findInput.focus(); + } + + select() { + this.findInput.select(); + } + + layout(width: number = this.width): void { + this.width = width; + this.right = clamp(this.right, 0, Math.max(0, width - 212)); + this.elements.root.style.right = `${this.right}px`; + } + + showMessage(message: IMessage): void { + this.findInput.showMessage(message); + } + + clearMessage(): void { + this.findInput.clearMessage(); + } + + override async dispose(): Promise { + this._onDidDisable.fire(); + this.elements.root.classList.add('disabled'); + await timeout(300); + super.dispose(); + } +} + +class FindController implements IDisposable { private _pattern = ''; get pattern(): string { return this._pattern; } - private _filterOnType: boolean; - get filterOnType(): boolean { return this._filterOnType; } + private _mode: TreeFindMode; + get mode(): TreeFindMode { return this._mode; } + set mode(mode: TreeFindMode) { + if (mode === this._mode) { + return; + } - private _empty: boolean = false; - get empty(): boolean { return this._empty; } + this._mode = mode; - private readonly _onDidChangeEmptyState = new Emitter(); - readonly onDidChangeEmptyState: Event = Event.latch(this._onDidChangeEmptyState.event); + if (this.widget) { + this.widget.mode = this._mode; + } - private positionClassName = 'ne'; - private domNode: HTMLElement; - private messageDomNode: HTMLElement; - private labelDomNode: HTMLElement; - private filterOnTypeDomNode: HTMLInputElement; - private clearDomNode: HTMLElement; - private keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; + this.tree.refilter(); + this.render(); + this._onDidChangeMode.fire(mode); + } - private automaticKeyboardNavigation = true; - private triggered = false; + private widget: FindWidget | undefined; + private styles: IFindWidgetStyles | undefined; + private width = 0; + + private readonly _onDidChangeMode = new Emitter(); + readonly onDidChangeMode = this._onDidChangeMode.event; private readonly _onDidChangePattern = new Emitter(); readonly onDidChangePattern = this._onDidChangePattern.event; - private readonly enabledDisposables = new DisposableStore(); + private readonly _onDidChangeOpenState = new Emitter(); + readonly onDidChangeOpenState = this._onDidChangeOpenState.event; + + private enabledDisposables = new DisposableStore(); private readonly disposables = new DisposableStore(); constructor( private tree: AbstractTree, model: ITreeModel, private view: List>, - private filter: TypeFilter, - private keyboardNavigationDelegate: IKeyboardNavigationDelegate + private filter: FindFilter, + private readonly contextViewProvider: IContextViewProvider ) { - this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`); - this.domNode.draggable = true; - this.disposables.add(addDisposableListener(this.domNode, 'dragstart', () => this.onDragStart())); - - this.messageDomNode = append(view.getHTMLElement(), $(`.monaco-list-type-filter-message`)); - - this.labelDomNode = append(this.domNode, $('span.label')); - const controls = append(this.domNode, $('.controls')); - - this._filterOnType = !!tree.options.filterOnType; - this.filterOnTypeDomNode = append(controls, $('input.filter')); - this.filterOnTypeDomNode.type = 'checkbox'; - this.filterOnTypeDomNode.checked = this._filterOnType; - this.filterOnTypeDomNode.tabIndex = -1; - this.updateFilterOnTypeTitleAndIcon(); - this.disposables.add(addDisposableListener(this.filterOnTypeDomNode, 'input', () => this.onDidChangeFilterOnType())); - - this.clearDomNode = append(controls, $('button.clear' + Codicon.treeFilterClear.cssSelector)); - this.clearDomNode.tabIndex = -1; - this.clearDomNode.title = localize('clear', "Clear"); - - this.keyboardNavigationEventFilter = tree.options.keyboardNavigationEventFilter; - + this._mode = tree.options.defaultFindMode ?? TreeFindMode.Highlight; model.onDidSplice(this.onDidSpliceModel, this, this.disposables); - this.updateOptions(tree.options); } - updateOptions(options: IAbstractTreeOptions): void { - if (options.simpleKeyboardNavigation) { - this.disable(); - } else { - this.enable(); - } - - if (typeof options.filterOnType !== 'undefined') { - this._filterOnType = !!options.filterOnType; - this.filterOnTypeDomNode.checked = this._filterOnType; - this.updateFilterOnTypeTitleAndIcon(); - } - - if (typeof options.automaticKeyboardNavigation !== 'undefined') { - this.automaticKeyboardNavigation = options.automaticKeyboardNavigation; - } - - this.tree.refilter(); - this.render(); - - if (!this.automaticKeyboardNavigation) { - this.onEventOrInput(''); - } - } - - toggle(): void { - this.triggered = !this.triggered; - - if (!this.triggered) { - this.onEventOrInput(''); - } - } - - private enable(): void { - if (this._enabled) { + open(): void { + if (this.widget) { + this.widget.focus(); + this.widget.select(); return; } - const onRawKeyDown = this.enabledDisposables.add(new DomEmitter(this.view.getHTMLElement(), 'keydown')); - const onKeyDown = Event.chain(onRawKeyDown.event) - .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) - .filter(e => e.key !== 'Dead' && !/^Media/.test(e.key)) - .map(e => new StandardKeyboardEvent(e)) - .filter(this.keyboardNavigationEventFilter || (() => true)) - .filter(() => this.automaticKeyboardNavigation || this.triggered) - .filter(e => (this.keyboardNavigationDelegate.mightProducePrintableCharacter(e) && !(e.keyCode === KeyCode.DownArrow || e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.LeftArrow || e.keyCode === KeyCode.RightArrow)) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey))) - .forEach(e => { e.stopPropagation(); e.preventDefault(); }) - .event; + this.mode = this.tree.options.defaultFindMode ?? TreeFindMode.Highlight; + this.widget = new FindWidget(this.view.getHTMLElement(), this.tree, this.contextViewProvider, this.mode, this.styles); + this.enabledDisposables.add(this.widget); - const onClearClick = this.enabledDisposables.add(new DomEmitter(this.clearDomNode, 'click')); + this.widget.onDidChangeValue(this.onDidChangeValue, this, this.enabledDisposables); + this.widget.onDidChangeMode(mode => this.mode = mode, undefined, this.enabledDisposables); + this.widget.onDidDisable(this.close, this, this.enabledDisposables); - Event.chain(Event.any(onKeyDown, onClearClick.event)) - .event(this.onEventOrInput, this, this.enabledDisposables); + this.widget.layout(this.width); + this.widget.focus(); - this.filter.pattern = ''; - this.tree.refilter(); - this.render(); - this._enabled = true; - this.triggered = false; + this._onDidChangeOpenState.fire(true); } - private disable(): void { - if (!this._enabled) { + close(): void { + if (!this.widget) { return; } - this.domNode.remove(); - this.enabledDisposables.clear(); - this.tree.refilter(); - this.render(); - this._enabled = false; - this.triggered = false; + this.widget = undefined; + + this.enabledDisposables.dispose(); + this.enabledDisposables = new DisposableStore(); + + this.onDidChangeValue(''); + this.tree.domFocus(); + + this._onDidChangeOpenState.fire(false); } - private onEventOrInput(e: MouseEvent | StandardKeyboardEvent | string): void { - if (typeof e === 'string') { - this.onInput(e); - } else if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) { - this.onInput(''); - } else if (e.keyCode === KeyCode.Backspace) { - this.onInput(this.pattern.length === 0 ? '' : this.pattern.substr(0, this.pattern.length - 1)); - } else { - this.onInput(this.pattern + e.browserEvent.key); - } - } - - private onInput(pattern: string): void { - const container = this.view.getHTMLElement(); - - if (pattern && !this.domNode.parentElement) { - container.append(this.domNode); - } else if (!pattern && this.domNode.parentElement) { - this.domNode.remove(); - this.tree.domFocus(); - } - + private onDidChangeValue(pattern: string): void { this._pattern = pattern; this._onDidChangePattern.fire(pattern); @@ -841,75 +950,10 @@ class TypeFilterController implements IDisposable { } this.render(); - - if (!pattern) { - this.triggered = false; - } - } - - private onDragStart(): void { - const container = this.view.getHTMLElement(); - const { left } = getDomNodePagePosition(container); - const containerWidth = container.clientWidth; - const midContainerWidth = containerWidth / 2; - const width = this.domNode.clientWidth; - const disposables = new DisposableStore(); - let positionClassName = this.positionClassName; - - const updatePosition = () => { - switch (positionClassName) { - case 'nw': - this.domNode.style.top = `4px`; - this.domNode.style.left = `4px`; - break; - case 'ne': - this.domNode.style.top = `4px`; - this.domNode.style.left = `${containerWidth - width - 6}px`; - break; - } - }; - - const onDragOver = (event: DragEvent) => { - event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - - const x = event.clientX - left; - if (event.dataTransfer) { - event.dataTransfer.dropEffect = 'none'; - } - - if (x < midContainerWidth) { - positionClassName = 'nw'; - } else { - positionClassName = 'ne'; - } - - updatePosition(); - }; - - const onDragEnd = () => { - this.positionClassName = positionClassName; - this.domNode.className = `monaco-list-type-filter ${this.positionClassName}`; - this.domNode.style.top = ''; - this.domNode.style.left = ''; - - dispose(disposables); - }; - - updatePosition(); - this.domNode.classList.remove(positionClassName); - - this.domNode.classList.add('dragging'); - disposables.add(toDisposable(() => this.domNode.classList.remove('dragging'))); - - disposables.add(addDisposableListener(document, 'dragover', e => onDragOver(e))); - disposables.add(addDisposableListener(this.domNode, 'dragend', () => onDragEnd())); - - StaticDND.CurrentDragAndDropData = new DragAndDropData('vscode-ui'); - disposables.add(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); } private onDidSpliceModel(): void { - if (!this._enabled || this.pattern.length === 0) { + if (!this.widget || this.pattern.length === 0) { return; } @@ -917,46 +961,18 @@ class TypeFilterController implements IDisposable { this.render(); } - private onDidChangeFilterOnType(): void { - this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); - this.tree.refilter(); - this.tree.domFocus(); - this.render(); - this.updateFilterOnTypeTitleAndIcon(); - } - - private updateFilterOnTypeTitleAndIcon(): void { - if (this.filterOnType) { - this.filterOnTypeDomNode.classList.remove(...Codicon.treeFilterOnTypeOff.classNamesArray); - this.filterOnTypeDomNode.classList.add(...Codicon.treeFilterOnTypeOn.classNamesArray); - this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); - } else { - this.filterOnTypeDomNode.classList.remove(...Codicon.treeFilterOnTypeOn.classNamesArray); - this.filterOnTypeDomNode.classList.add(...Codicon.treeFilterOnTypeOff.classNamesArray); - this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); - } - } - private render(): void { const noMatches = this.filter.totalCount > 0 && this.filter.matchCount === 0; - if (this.pattern && this.tree.options.filterOnType && noMatches) { - this.messageDomNode.textContent = localize('empty', "No elements found"); - this._empty = true; + if (this.pattern && noMatches) { + this.widget?.showMessage({ type: MessageType.WARNING, content: localize('not found', "No elements found.") }); } else { - this.messageDomNode.innerText = ''; - this._empty = false; + this.widget?.clearMessage(); } - - this.domNode.classList.toggle('no-matches', noMatches); - this.domNode.title = localize('found', "Matched {0} out of {1} elements", this.filter.matchCount, this.filter.totalCount); - this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern; - - this._onDidChangeEmptyState.fire(this._empty); } shouldAllowFocus(node: ITreeNode): boolean { - if (!this.enabled || !this.pattern || this.filterOnType) { + if (!this.widget || !this.pattern || this._mode === TreeFindMode.Filter) { return true; } @@ -967,16 +983,20 @@ class TypeFilterController implements IDisposable { return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); } - dispose() { - if (this._enabled) { - this.domNode.remove(); - this.enabledDisposables.dispose(); - this._enabled = false; - this.triggered = false; - } + style(styles: IFindWidgetStyles): void { + this.styles = styles; + this.widget?.style(styles); + } + layout(width: number): void { + this.width = width; + this.widget?.layout(width); + } + + dispose() { this._onDidChangePattern.dispose(); - dispose(this.disposables); + this.enabledDisposables.dispose(); + this.disposables.dispose(); } } @@ -987,6 +1007,8 @@ function asTreeMouseEvent(event: IListMouseEvent>): ITreeMo target = TreeMouseEventTarget.Twistie; } else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-contents', 'monaco-tl-row')) { target = TreeMouseEventTarget.Element; + } else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tree-type-filter', 'monaco-list')) { + target = TreeMouseEventTarget.Filter; } return { @@ -1004,15 +1026,11 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IAbstractTreeOptionsUpdate, IListOptions { + readonly contextViewProvider?: IContextViewProvider; readonly collapseByDefault?: boolean; // defaults to false readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; - readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; readonly additionalScrollHeight?: number; + readonly findWidgetEnabled?: boolean; } function dfs(node: ITreeNode, fn: (node: ITreeNode) => void): void { @@ -1158,7 +1177,9 @@ class TreeNodeListMouseController extends MouseController< } protected override onViewPointer(e: IListMouseEvent>): void { - if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) { + if (isButton(e.browserEvent.target as HTMLElement) || + isInputElement(e.browserEvent.target as HTMLElement) || + isMonacoEditor(e.browserEvent.target as HTMLElement)) { return; } @@ -1321,7 +1342,8 @@ export abstract class AbstractTree implements IDisposable private selection: Trait; private anchor: Trait; private eventBufferer = new EventBufferer(); - private typeFilterController?: TypeFilterController; + private findController?: FindController; + readonly onDidChangeFindOpenState: Event = Event.None; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; private styleElement: HTMLStyleElement; protected readonly disposables = new DisposableStore(); @@ -1332,7 +1354,7 @@ export abstract class AbstractTree implements IDisposable get onDidChangeSelection(): Event> { return this.eventBufferer.wrapEvent(this.selection.onDidChange); } get onMouseClick(): Event> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); } - get onMouseDblClick(): Event> { return Event.map(this.view.onMouseDblClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return Event.filter(Event.map(this.view.onMouseDblClick, asTreeMouseEvent), e => e.target !== TreeMouseEventTarget.Filter); } get onContextMenu(): Event> { return Event.map(this.view.onContextMenu, asTreeContextMenuEvent); } get onTap(): Event> { return Event.map(this.view.onTap, asTreeMouseEvent); } get onPointer(): Event> { return Event.map(this.view.onPointer, asTreeMouseEvent); } @@ -1351,8 +1373,11 @@ export abstract class AbstractTree implements IDisposable private readonly _onWillRefilter = new Emitter(); readonly onWillRefilter: Event = this._onWillRefilter.event; - get filterOnType(): boolean { return !!this._options.filterOnType; } - get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } + get findMode(): TreeFindMode { return this.findController?.mode ?? TreeFindMode.Highlight; } + set findMode(findMode: TreeFindMode) { if (this.findController) { this.findController.mode = findMode; } } + readonly onDidChangeFindMode: Event; + + get onDidChangeFindPattern(): Event { return this.findController ? this.findController.onDidChangePattern : Event.None; } get expandOnDoubleClick(): boolean { return typeof this._options.expandOnDoubleClick === 'undefined' ? true : this._options.expandOnDoubleClick; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? true : this._options.expandOnlyOnTwistieClick; } @@ -1373,16 +1398,16 @@ export abstract class AbstractTree implements IDisposable const onDidChangeCollapseStateRelay = new Relay>(); const onDidChangeActiveNodes = new Relay[]>(); - const activeNodes = new EventCollection(onDidChangeActiveNodes.event); + const activeNodes = this.disposables.add(new EventCollection(onDidChangeActiveNodes.event)); this.renderers = renderers.map(r => new TreeRenderer(r, () => this.model, onDidChangeCollapseStateRelay.event, activeNodes, _options)); - for (let r of this.renderers) { + for (const r of this.renderers) { this.disposables.add(r); } - let filter: TypeFilter | undefined; + let filter: FindFilter | undefined; if (_options.keyboardNavigationLabelProvider) { - filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as any as ITreeFilter); + filter = new FindFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as any as ITreeFilter); _options = { ..._options, filter: filter as ITreeFilter }; // TODO need typescript help here this.disposables.add(filter); } @@ -1400,7 +1425,7 @@ export abstract class AbstractTree implements IDisposable this.focus.onDidModelSplice(e); this.selection.onDidModelSplice(e); }); - }); + }, this.disposables); // Make sure the `forEach` always runs onDidModelSplice(() => null, null, this.disposables); @@ -1435,11 +1460,14 @@ export abstract class AbstractTree implements IDisposable onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } - if (_options.keyboardNavigationLabelProvider) { - const delegate = _options.keyboardNavigationDelegate || DefaultKeyboardNavigationDelegate; - this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, delegate); - this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node); - this.disposables.add(this.typeFilterController!); + if ((_options.findWidgetEnabled ?? true) && _options.keyboardNavigationLabelProvider && _options.contextViewProvider) { + this.findController = new FindController(this, this.model, this.view, filter!, _options.contextViewProvider); + this.focusNavigationFilter = node => this.findController!.shouldAllowFocus(node); + this.onDidChangeFindOpenState = this.findController.onDidChangeOpenState; + this.disposables.add(this.findController!); + this.onDidChangeFindMode = this.findController.onDidChangeMode; + } else { + this.onDidChangeFindMode = Event.None; } this.styleElement = createStyleSheet(this.view.getHTMLElement()); @@ -1453,15 +1481,7 @@ export abstract class AbstractTree implements IDisposable renderer.updateOptions(optionsUpdate); } - this.view.updateOptions({ - ...this._options, - enableKeyboardNavigation: this._options.simpleKeyboardNavigation, - }); - - if (this.typeFilterController) { - this.typeFilterController.updateOptions(this._options); - } - + this.view.updateOptions(this._options); this._onDidUpdateOptions.fire(this._options); this.getHTMLElement().classList.toggle('always', this._options.renderIndentGuides === RenderIndentGuides.Always); @@ -1488,21 +1508,11 @@ export abstract class AbstractTree implements IDisposable } get contentHeight(): number { - if (this.typeFilterController && this.typeFilterController.filterOnType && this.typeFilterController.empty) { - return 100; - } - return this.view.contentHeight; } get onDidChangeContentHeight(): Event { - let result = this.view.onDidChangeContentHeight; - - if (this.typeFilterController) { - result = Event.any(result, Event.map(this.typeFilterController.onDidChangeEmptyState, () => this.contentHeight)); - } - - return result; + return this.view.onDidChangeContentHeight; } get scrollTop(): number { @@ -1564,6 +1574,10 @@ export abstract class AbstractTree implements IDisposable layout(height?: number, width?: number): void { this.view.layout(height, width); + + if (isNumber(width)) { + this.findController?.layout(width); + } } style(styles: IListStyles): void { @@ -1576,6 +1590,8 @@ export abstract class AbstractTree implements IDisposable } this.styleElement.textContent = content.join('\n'); + + this.findController?.style(styles); this.view.style(styles); } @@ -1629,12 +1645,16 @@ export abstract class AbstractTree implements IDisposable return this.model.isCollapsed(location); } - toggleKeyboardNavigation(): void { - this.view.toggleKeyboardNavigation(); + triggerTypeNavigation(): void { + this.view.triggerTypeNavigation(); + } - if (this.typeFilterController) { - this.typeFilterController.toggle(); - } + openFind(): void { + this.findController?.open(); + } + + closeFind(): void { + this.findController?.close(); } refilter(): void { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index b0d35a1482..9ff33775f8 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -7,7 +7,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd'; import { IIdentityProvider, IListDragAndDrop, IListDragOverReaction, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; +import { ComposedTreeDelegate, TreeFindMode as TreeFindMode, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; import { CompressibleObjectTree, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions, ICompressibleTreeRenderer, IObjectTreeOptions, IObjectTreeSetChildrenOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; @@ -119,9 +119,7 @@ class AsyncDataTreeRenderer implements IT } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - if (this.renderer.disposeElement) { - this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); - } + this.renderer.disposeElement?.(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); } disposeTemplate(templateData: IDataTreeListTemplateData): void { @@ -196,9 +194,7 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { @@ -210,9 +206,7 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop implements IDisposable get onDidUpdateOptions(): Event { return this.tree.onDidUpdateOptions; } - get filterOnType(): boolean { return this.tree.filterOnType; } + get onDidChangeFindOpenState(): Event { return this.tree.onDidChangeFindOpenState; } + + get findMode(): TreeFindMode { return this.tree.findMode; } + set findMode(mode: TreeFindMode) { this.tree.findMode = mode; } + readonly onDidChangeFindMode: Event; + get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { if (typeof this.tree.expandOnlyOnTwistieClick === 'boolean') { return this.tree.expandOnlyOnTwistieClick; @@ -373,6 +372,7 @@ export class AsyncDataTree implements IDisposable this.collapseByDefault = options.collapseByDefault; this.tree = this.createTree(user, container, delegate, renderers, options); + this.onDidChangeFindMode = this.tree.onDidChangeFindMode; this.root = createAsyncDataTreeNode({ element: undefined!, @@ -622,8 +622,16 @@ export class AsyncDataTree implements IDisposable return this.tree.isCollapsed(this.getDataNode(element)); } - toggleKeyboardNavigation(): void { - this.tree.toggleKeyboardNavigation(); + triggerTypeNavigation(): void { + this.tree.triggerTypeNavigation(); + } + + openFind(): void { + this.tree.openFind(); + } + + closeFind(): void { + this.tree.closeFind(); } refilter(): void { @@ -734,6 +742,16 @@ export class AsyncDataTree implements IDisposable return result; } + if (node !== this.root) { + const treeNode = this.tree.getNode(node); + + if (treeNode.collapsed) { + node.hasChildren = !!this.dataSource.hasChildren(node.element!); + node.stale = true; + return; + } + } + return this.doRefreshSubTree(node, recursive, viewStateContext); } @@ -1086,15 +1104,11 @@ class CompressibleAsyncDataTreeRenderer i } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - if (this.renderer.disposeElement) { - this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); - } + this.renderer.disposeElement?.(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); } disposeCompressedElements(node: ITreeNode>, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - if (this.renderer.disposeCompressedElements) { - this.renderer.disposeCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode, TFilterData>, index, templateData.templateData, height); - } + this.renderer.disposeCompressedElements?.(this.compressibleNodeMapperProvider().map(node) as ITreeNode, TFilterData>, index, templateData.templateData, height); } disposeTemplate(templateData: IDataTreeListTemplateData): void { @@ -1189,10 +1203,10 @@ export class CompressibleAsyncDataTree extends As const expanded: string[] = []; const root = this.tree.getCompressedTreeNode(); - const queue = [root]; + const stack = [root]; - while (queue.length > 0) { - const node = queue.shift()!; + while (stack.length > 0) { + const node = stack.pop()!; if (node !== root && node.collapsible && !node.collapsed) { for (const asyncNode of node.element!.elements) { @@ -1200,7 +1214,7 @@ export class CompressibleAsyncDataTree extends As } } - queue.push(...node.children); + stack.push(...node.children); } return { focus, selection, expanded, scrollTop: this.scrollTop }; diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 7627a9fd0f..30be08f52d 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -140,9 +140,7 @@ export class DataTree extends AbstractTree) => { diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index aa911cc6f5..86b6ac8c4f 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -554,9 +554,7 @@ export class IndexTreeModel, TFilterData = voi node.renderNodeCount = renderNodeCount; } - if (onDidCreateNode) { - onDidCreateNode(node); - } + onDidCreateNode?.(node); return node; } diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index edfce3bcc1..6d3faee477 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -67,3 +67,55 @@ /* Use steps to throttle FPS to reduce CPU usage */ animation: codicon-spin 1.25s steps(30) infinite; } + +.monaco-tree-type-filter { + position: absolute; + top: 0; + display: flex; + padding: 3px; + transition: top 0.3s; + max-width: 200px; + z-index: 100; + margin: 0 6px; +} + +.monaco-tree-type-filter.disabled { + top: -40px; +} + +.monaco-tree-type-filter-grab { + display: flex !important; + align-items: center; + justify-content: center; + cursor: grab; + margin-right: 2px; +} + +.monaco-tree-type-filter-grab.grabbing { + cursor: grabbing; +} + +.monaco-tree-type-filter-input { + flex: 1; +} + +.monaco-tree-type-filter-input .monaco-inputbox { + height: 23px; +} + +.monaco-tree-type-filter-input .monaco-inputbox > .ibwrapper > .input, +.monaco-tree-type-filter-input .monaco-inputbox > .ibwrapper > .mirror { + padding: 2px 4px; +} + +.monaco-tree-type-filter-input .monaco-findInput > .controls { + top: 2px; +} + +.monaco-tree-type-filter-actionbar { + margin-left: 4px; +} + +.monaco-tree-type-filter-actionbar .monaco-action-bar .action-label { + padding: 2px; +} diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 3b523a655d..c967ad91b1 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -140,13 +140,9 @@ class CompressibleRenderer, TFilterData, TTemplateDat disposeElement(node: ITreeNode, index: number, templateData: CompressibleTemplateData, height: number | undefined): void { if (templateData.compressedTreeNode) { - if (this.renderer.disposeCompressedElements) { - this.renderer.disposeCompressedElements(templateData.compressedTreeNode, index, templateData.data, height); - } + this.renderer.disposeCompressedElements?.(templateData.compressedTreeNode, index, templateData.data, height); } else { - if (this.renderer.disposeElement) { - this.renderer.disposeElement(node, index, templateData.data, height); - } + this.renderer.disposeElement?.(node, index, templateData.data, height); } } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index a6ff56de80..126012ace8 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -141,7 +141,8 @@ export interface ITreeEvent { export enum TreeMouseEventTarget { Unknown, Twistie, - Element + Element, + Filter } export interface ITreeMouseEvent { diff --git a/src/vs/base/browser/ui/widget.ts b/src/vs/base/browser/ui/widget.ts index ea293f638d..669da7baa0 100644 --- a/src/vs/base/browser/ui/widget.ts +++ b/src/vs/base/browser/ui/widget.ts @@ -23,8 +23,8 @@ export abstract class Widget extends Disposable { this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); } - protected onnonbubblingmouseout(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { - this._register(dom.addDisposableNonBubblingMouseOutListener(domNode, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); + protected onmouseleave(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void { + this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_LEAVE, (e: MouseEvent) => listener(new StandardMouseEvent(e)))); } protected onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void { diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 2ae52a3e3f..3beed2aaec 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -14,8 +14,9 @@ export interface ITelemetryData { } export type WorkbenchActionExecutedClassification = { - id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; - from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; + owner: 'bpasero'; + id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the action that was run.' }; + from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the component the action was run from.' }; }; export type WorkbenchActionExecutedEvent = { diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index 75dff825b0..046561da04 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -26,7 +26,7 @@ export abstract class LoaderStats { } function diff(map: Map, stat: LoaderEvent) { - let duration = map.get(stat.detail); + const duration = map.get(stat.detail); if (!duration) { // console.warn('BAD events, end WITHOUT start', stat); // map.delete(stat.detail); @@ -79,7 +79,7 @@ export abstract class LoaderStats { nodeRequire.forEach(value => nodeRequireTotal += value); function to2dArray(map: Map): [string, number][] { - let res: [string, number][] = []; + const res: [string, number][] = []; map.forEach((value, index) => res.push([index, value])); return res; } @@ -96,7 +96,7 @@ export abstract class LoaderStats { static toMarkdownTable(header: string[], rows: Array>): string { let result = ''; - let lengths: number[] = []; + const lengths: number[] = []; header.forEach((cell, ci) => { lengths[ci] = cell.length; }); diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 7637d21a89..be9df2bae8 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -46,13 +46,55 @@ export function equals(one: ReadonlyArray | undefined, other: ReadonlyArra return true; } +/** + * Remove the element at `index` by replacing it with the last element. This is faster than `splice` + * but changes the order of the array + */ +export function removeFastWithoutKeepingOrder(array: T[], index: number) { + const last = array.length - 1; + if (index < last) { + array[index] = array[last]; + } + array.pop(); +} + +/** + * Performs a binary search algorithm over a sorted array. + * + * @param array The array being searched. + * @param key The value we search for. + * @param comparator A function that takes two array elements and returns zero + * if they are equal, a negative number if the first element precedes the + * second one in the sorting order, or a positive number if the second element + * precedes the first one. + * @return See {@link binarySearch2} + */ export function binarySearch(array: ReadonlyArray, key: T, comparator: (op1: T, op2: T) => number): number { + return binarySearch2(array.length, i => comparator(array[i], key)); +} + +/** + * Performs a binary search algorithm over a sorted collection. Useful for cases + * when we need to perform a binary search over something that isn't actually an + * array, and converting data to an array would defeat the use of binary search + * in the first place. + * + * @param length The collection length. + * @param compareToKey A function that takes an index of an element in the + * collection and returns zero if the value at this index is equal to the + * search key, a negative number if the value precedes the search key in the + * sorting order, or a positive number if the search key precedes the value. + * @return A non-negative index of an element, if found. If not found, the + * result is -(n+1) (or ~n, using bitwise notation), where n is the index + * where the key should be inserted to maintain the sorting order. + */ +export function binarySearch2(length: number, compareToKey: (index: number) => number): number { let low = 0, - high = array.length - 1; + high = length - 1; while (low <= high) { const mid = ((low + high) / 2) | 0; - const comp = comparator(array[mid], key); + const comp = compareToKey(mid); if (comp < 0) { low = mid + 1; } else if (comp > 0) { @@ -96,12 +138,12 @@ export function quickSelect(nth: number, data: T[], compare: Compare): T { throw new TypeError('invalid index'); } - let pivotValue = data[Math.floor(data.length * Math.random())]; - let lower: T[] = []; - let higher: T[] = []; - let pivots: T[] = []; + const pivotValue = data[Math.floor(data.length * Math.random())]; + const lower: T[] = []; + const higher: T[] = []; + const pivots: T[] = []; - for (let value of data) { + for (const value of data) { const val = compare(value, pivotValue); if (val < 0) { lower.push(value); @@ -610,17 +652,55 @@ function getActualStartIndex(array: T[], start: number): number { return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length); } +/** + * When comparing two values, + * a negative number indicates that the first value is less than the second, + * a positive number indicates that the first value is greater than the second, + * and zero indicates that neither is the case. +*/ +export type CompareResult = number; + +export namespace CompareResult { + export function isLessThan(result: CompareResult): boolean { + return result < 0; + } + + export function isGreaterThan(result: CompareResult): boolean { + return result > 0; + } + + export function isNeitherLessOrGreaterThan(result: CompareResult): boolean { + return result === 0; + } + + export const greaterThan = 1; + export const lessThan = -1; + export const neitherLessOrGreaterThan = 0; +} + /** * A comparator `c` defines a total order `<=` on `T` as following: * `c(a, b) <= 0` iff `a` <= `b`. * We also have `c(a, b) == 0` iff `c(b, a) == 0`. */ -export type Comparator = (a: T, b: T) => number; +export type Comparator = (a: T, b: T) => CompareResult; export function compareBy(selector: (item: TItem) => TCompareBy, comparator: Comparator): Comparator { return (a, b) => comparator(selector(a), selector(b)); } +export function tieBreakComparators(...comparators: Comparator[]): Comparator { + return (item1, item2) => { + for (const comparator of comparators) { + const result = comparator(item1, item2); + if (!CompareResult.isNeitherLessOrGreaterThan(result)) { + return result; + } + } + return CompareResult.neitherLessOrGreaterThan; + }; +} + /** * The natural order on numbers. */ @@ -676,7 +756,7 @@ export class ArrayQueue { /** * Constructs a queue that is backed by the given array. Runtime is O(1). */ - constructor(private readonly items: T[]) { } + constructor(private readonly items: readonly T[]) { } get length(): number { return this.lastIdx - this.firstIdx + 1; @@ -718,15 +798,31 @@ export class ArrayQueue { } peek(): T | undefined { + if (this.length === 0) { + return undefined; + } return this.items[this.firstIdx]; } + peekLast(): T | undefined { + if (this.length === 0) { + return undefined; + } + return this.items[this.lastIdx]; + } + dequeue(): T | undefined { const result = this.items[this.firstIdx]; this.firstIdx++; return result; } + removeLast(): T | undefined { + const result = this.items[this.lastIdx]; + this.lastIdx--; + return result; + } + takeCount(count: number): T[] { const result = this.items.slice(this.firstIdx, this.firstIdx + count); this.firstIdx += count; diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index d493ec7874..0979ef200e 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -354,9 +354,7 @@ export class Delayer implements IDisposable { this.cancelTimeout(); if (this.completionPromise) { - if (this.doReject) { - this.doReject(new CancellationError()); - } + this.doReject?.(new CancellationError()); this.completionPromise = null; } } @@ -885,9 +883,7 @@ export class RunOnceScheduler { } protected doRun(): void { - if (this.runner) { - this.runner(); - } + this.runner?.(); } } @@ -960,9 +956,7 @@ export class ProcessTimeRunOnceScheduler { // time elapsed clearInterval(this.intervalToken); this.intervalToken = -1; - if (this.runner) { - this.runner(); - } + this.runner?.(); } } @@ -985,9 +979,7 @@ export class RunOnceWorker extends RunOnceScheduler { const units = this.units; this.units = []; - if (this.runner) { - this.runner(units); - } + this.runner?.(units); } override dispose(): void { diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index d87f016735..9f94df4e4f 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -429,7 +429,8 @@ export class Codicon implements CSSIcon { public static readonly debugBreakpointFunction = new Codicon('debug-breakpoint-function', { fontCharacter: '\\eb88' }); public static readonly debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { fontCharacter: '\\eb88' }); public static readonly debugStackframeActive = new Codicon('debug-stackframe-active', { fontCharacter: '\\eb89' }); - public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', { fontCharacter: '\\eb8a' }); + public static readonly circleSmallFilled = new Codicon('circle-small-filled', { fontCharacter: '\\eb8a' }); + public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', Codicon.circleSmallFilled.definition); public static readonly debugStackframe = new Codicon('debug-stackframe', { fontCharacter: '\\eb8b' }); public static readonly debugStackframeFocused = new Codicon('debug-stackframe-focused', { fontCharacter: '\\eb8b' }); public static readonly debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { fontCharacter: '\\eb8c' }); @@ -542,6 +543,9 @@ export class Codicon implements CSSIcon { public static readonly layoutStatusbar = new Codicon('layout-statusbar', { fontCharacter: '\\ebf5' }); public static readonly layoutMenubar = new Codicon('layout-menubar', { fontCharacter: '\\ebf6' }); public static readonly layoutCentered = new Codicon('layout-centered', { fontCharacter: '\\ebf7' }); + public static readonly layoutSidebarRightOff = new Codicon('layout-sidebar-right-off', { fontCharacter: '\\ec00' }); + public static readonly layoutPanelOff = new Codicon('layout-panel-off', { fontCharacter: '\\ec01' }); + public static readonly layoutSidebarLeftOff = new Codicon('layout-sidebar-left-off', { fontCharacter: '\\ec02' }); public static readonly target = new Codicon('target', { fontCharacter: '\\ebf8' }); public static readonly indent = new Codicon('indent', { fontCharacter: '\\ebf9' }); public static readonly recordSmall = new Codicon('record-small', { fontCharacter: '\\ebfa' }); @@ -550,6 +554,15 @@ export class Codicon implements CSSIcon { public static readonly arrowCircleLeft = new Codicon('arrow-circle-left', { fontCharacter: '\\ebfd' }); public static readonly arrowCircleRight = new Codicon('arrow-circle-right', { fontCharacter: '\\ebfe' }); public static readonly arrowCircleUp = new Codicon('arrow-circle-up', { fontCharacter: '\\ebff' }); + public static readonly heartFilled = new Codicon('heart-filled', { fontCharacter: '\\ec04' }); + public static readonly map = new Codicon('map', { fontCharacter: '\\ec05' }); + public static readonly mapFilled = new Codicon('map-filled', { fontCharacter: '\\ec06' }); + public static readonly circleSmall = new Codicon('circle-small', { fontCharacter: '\\ec07' }); + public static readonly bellSlash = new Codicon('bell-slash', { fontCharacter: '\\ec08' }); + public static readonly bellSlashDot = new Codicon('bell-slash-dot', { fontCharacter: '\\ec09' }); + public static readonly commentUnresolved = new Codicon('comment-unresolved', { fontCharacter: '\\ec0a' }); + public static readonly gitPullRequestGoToChanges = new Codicon('git-pull-request-go-to-changes', { fontCharacter: '\\ec0b' }); + public static readonly gitPullRequestNewChanges = new Codicon('git-pull-request-new-changes', { fontCharacter: '\\ec0c' }); // derived icons, that could become separate icons @@ -612,7 +625,7 @@ export namespace CSSIcon { if (!match) { return asClassNameArray(Codicon.error); } - let [, id, modifier] = match; + const [, id, modifier] = match; // {{SQL CARBON EDIT}} Modifying method to not add 'codicon' in front of sql carbon icons. let sqlCarbonIcons: string[] = [SqlIconId.activeConnectionsAction, SqlIconId.addServerAction, SqlIconId.addServerGroupAction, SqlIconId.serverPage]; diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 7f79571e3a..1c6bd89e5d 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -9,13 +9,13 @@ */ export type IStringDictionary = Record; - /** * An interface for a JavaScript object that * acts a dictionary. The keys are numbers. */ export type INumberDictionary = Record; +// {{ SQL CARBON EDIT }} - BEGIN - Needed to retrive values from IStringDictionary's and INumberDictionary's const hasOwnProperty = Object.prototype.hasOwnProperty; /** @@ -31,6 +31,9 @@ export function values(from: IStringDictionary | INumberDictionary): T[ } return result; } +// {{SQL CARBON EDIT}} - END - Needed to retrive values from IStringDictionary's and INumberDictionary's + +// {{ SQL CARBON EDIT }} - BEGIN - Adding forEach definition /** * Iterates over each entry in the provided dictionary. The iterator allows @@ -51,6 +54,8 @@ export function forEach(from: IStringDictionary | INumberDictionary, ca } } +// {{ SQL CARBON EDIT }} - END - Adding forEach definition + /** * Groups the collection into a dictionary based on the provided * group function. @@ -68,25 +73,15 @@ export function groupBy(data: V[], groupF return result; } -export function fromMap(original: Map): IStringDictionary { - const result: IStringDictionary = Object.create(null); - if (original) { - original.forEach((value, key) => { - result[key] = value; - }); - } - return result; -} - export function diffSets(before: Set, after: Set): { removed: T[]; added: T[] } { const removed: T[] = []; const added: T[] = []; - for (let element of before) { + for (const element of before) { if (!after.has(element)) { removed.push(element); } } - for (let element of after) { + for (const element of after) { if (!before.has(element)) { added.push(element); } @@ -97,12 +92,12 @@ export function diffSets(before: Set, after: Set): { removed: T[]; adde export function diffMaps(before: Map, after: Map): { removed: V[]; added: V[] } { const removed: V[] = []; const added: V[] = []; - for (let [index, value] of before) { + for (const [index, value] of before) { if (!after.has(index)) { removed.push(value); } } - for (let [index, value] of after) { + for (const [index, value] of after) { if (!before.has(index)) { added.push(value); } diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index 61b28b5d26..8df41f1917 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -219,7 +219,7 @@ function extractExtension(str?: string | null): string { function compareAndDisambiguateByLength(collator: Intl.Collator, one: string, other: string) { // Check for differences - let result = collator.compare(one, other); + const result = collator.compare(one, other); if (result !== 0) { return result; } diff --git a/src/vs/base/common/console.ts b/src/vs/base/common/console.ts index 94162c84f0..6e5a975df3 100644 --- a/src/vs/base/common/console.ts +++ b/src/vs/base/common/console.ts @@ -11,7 +11,7 @@ export interface IRemoteConsoleLog { arguments: string; } -interface IStackArgument { +export interface IStackArgument { __$stack: string; } diff --git a/src/vs/base/common/dataTransfer.ts b/src/vs/base/common/dataTransfer.ts new file mode 100644 index 0000000000..02dbe6cdb7 --- /dev/null +++ b/src/vs/base/common/dataTransfer.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; + +export interface IDataTransferFile { + readonly name: string; + readonly uri?: URI; + data(): Promise; +} + +export interface IDataTransferItem { + asString(): Thenable; + asFile(): IDataTransferFile | undefined; + value: any; +} + +export function createStringDataTransferItem(stringOrPromise: string | Promise): IDataTransferItem { + return { + asString: async () => stringOrPromise, + asFile: () => undefined, + value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined, + }; +} + +export function createFileDataTransferItem(fileName: string, uri: URI | undefined, data: () => Promise): IDataTransferItem { + return { + asString: async () => '', + asFile: () => ({ name: fileName, uri, data }), + value: undefined, + }; +} + +export class VSDataTransfer { + + private readonly _entries = new Map(); + + public get size(): number { + return this._entries.size; + } + + public has(mimeType: string): boolean { + return this._entries.has(this.toKey(mimeType)); + } + + public get(mimeType: string): IDataTransferItem | undefined { + return this._entries.get(this.toKey(mimeType))?.[0]; + } + + public append(mimeType: string, value: IDataTransferItem): void { + const existing = this._entries.get(mimeType); + if (existing) { + existing.push(value); + } else { + this._entries.set(this.toKey(mimeType), [value]); + } + } + + public replace(mimeType: string, value: IDataTransferItem): void { + this._entries.set(this.toKey(mimeType), [value]); + } + + public delete(mimeType: string) { + this._entries.delete(this.toKey(mimeType)); + } + + public *entries(): Iterable<[string, IDataTransferItem]> { + for (const [mine, items] of this._entries.entries()) { + for (const item of items) { + yield [mine, item]; + } + } + } + + public values(): Iterable { + return Array.from(this._entries.values()).flat(); + } + + public forEach(f: (value: IDataTransferItem, key: string) => void) { + for (const [mime, item] of this.entries()) { + f(item, mime); + } + } + + private toKey(mimeType: string): string { + return mimeType.toLowerCase(); + } +} diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index 485b2a8ce5..382b7d6b19 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -851,7 +851,7 @@ export class LcsDiff { change.modifiedStart++; } - let mergedChangeArr: Array = [null]; + const mergedChangeArr: Array = [null]; if (i < changes.length - 1 && this.ChangesOverlap(changes[i], changes[i + 1], mergedChangeArr)) { changes[i] = mergedChangeArr[0]!; changes.splice(i + 1, 1); @@ -1047,7 +1047,7 @@ export class LcsDiff { * @returns The concatenated list */ private ConcatenateChanges(left: DiffChange[], right: DiffChange[]): DiffChange[] { - let mergedChangeArr: DiffChange[] = []; + const mergedChangeArr: DiffChange[] = []; if (left.length === 0 || right.length === 0) { return (right.length > 0) ? right : left; diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 2ef9f0b28c..d00f276490 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -23,6 +23,10 @@ export class ErrorHandler { this.unexpectedErrorHandler = function (e: any) { setTimeout(() => { if (e.stack) { + if (ErrorNoTelemetry.isErrorNoTelemetry(e)) { + throw new ErrorNoTelemetry(e.message + '\n\n' + e.stack); + } + throw new Error(e.message + '\n\n' + e.stack); } @@ -95,13 +99,14 @@ export interface SerializedError { readonly name: string; readonly message: string; readonly stack: string; + readonly noTelemetry: boolean; } export function transformErrorForSerialization(error: Error): SerializedError; export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { - let { name, message } = error; + const { name, message } = error; let errorCode = (error).errorCode; // {{SQL CARBON EDIT}} Add error code to retain more information const stack: string = (error).stacktrace || (error).stack; return { @@ -236,24 +241,27 @@ export class ExpectedError extends Error { * Error that when thrown won't be logged in telemetry as an unhandled error. */ export class ErrorNoTelemetry extends Error { + override readonly name: string; - public static fromError(err: any): ErrorNoTelemetry { - if (err && err instanceof ErrorNoTelemetry) { + constructor(msg?: string) { + super(msg); + this.name = 'ErrorNoTelemetry'; + } + + public static fromError(err: Error): ErrorNoTelemetry { + if (err instanceof ErrorNoTelemetry) { return err; } - if (err && err instanceof Error) { - const result = new ErrorNoTelemetry(); - result.name = err.name; - result.message = err.message; - result.stack = err.stack; - return result; - } - - return new ErrorNoTelemetry(err); + const result = new ErrorNoTelemetry(); + result.message = err.message; + result.stack = err.stack; + return result; } - readonly logTelemetry = false; + public static isErrorNoTelemetry(err: Error): err is ErrorNoTelemetry { + return err.name === 'ErrorNoTelemetry'; + } } /** @@ -262,8 +270,8 @@ export class ErrorNoTelemetry extends Error { * Only catch this error to recover gracefully from bugs. */ export class BugIndicatingError extends Error { - constructor(message: string) { - super(message); + constructor(message?: string) { + super(message || 'An unexpected bug occurred.'); Object.setPrototypeOf(this, BugIndicatingError.prototype); // Because we know for sure only buggy code throws this, diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 0b713da184..91e039f410 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -8,13 +8,14 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; import { combinedDisposable, Disposable, DisposableStore, IDisposable, SafeDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; +import { IObservable, IObserver } from 'vs/base/common/observable'; import { StopWatch } from 'vs/base/common/stopwatch'; // ----------------------------------------------------------------------------------------------------------------------- // Uncomment the next line to print warnings whenever an emitter with listeners is disposed. That is a sign of code smell. // ----------------------------------------------------------------------------------------------------------------------- -let _enableDisposeWithListenerWarning = false; +const _enableDisposeWithListenerWarning = false; // _enableDisposeWithListenerWarning = Boolean("TRUE"); // causes a linter warning so that it cannot be pushed @@ -22,7 +23,7 @@ let _enableDisposeWithListenerWarning = false; // Uncomment the next line to print warnings whenever a snapshotted event is used repeatedly without cleanup. // See https://github.com/microsoft/vscode/issues/142851 // ----------------------------------------------------------------------------------------------------------------------- -let _enableSnapshotPotentialLeakWarning = false; +const _enableSnapshotPotentialLeakWarning = false; // _enableSnapshotPotentialLeakWarning = Boolean("TRUE"); // causes a linter warning so that it cannot be pushed /** @@ -60,7 +61,7 @@ export namespace Event { return (listener, thisArgs = null, disposables?) => { // we need this, in case the event fires during the listener call let didFire = false; - let result: IDisposable; + let result: IDisposable | undefined = undefined; result = event(e => { if (didFire) { return; @@ -143,14 +144,14 @@ export namespace Event { } function snapshot(event: Event, disposable: DisposableStore | undefined): Event { - let listener: IDisposable; + let listener: IDisposable | undefined; const options: EmitterOptions | undefined = { onFirstListenerAdd() { listener = event(emitter.fire, emitter); }, onLastListenerRemove() { - listener.dispose(); + listener?.dispose(); } }; @@ -160,9 +161,7 @@ export namespace Event { const emitter = new Emitter(options); - if (disposable) { - disposable.add(emitter); - } + disposable?.add(emitter); return emitter.event; } @@ -223,9 +222,7 @@ export namespace Event { const emitter = new Emitter(options); - if (disposable) { - disposable.add(emitter); - } + disposable?.add(emitter); return emitter.event; } @@ -276,9 +273,7 @@ export namespace Event { }); const flush = () => { - if (buffer) { - buffer.forEach(e => emitter.fire(e)); - } + buffer?.forEach(e => emitter.fire(e)); buffer = null; }; @@ -310,7 +305,7 @@ export namespace Event { return emitter.event; } - export interface IChainableEvent { + export interface IChainableEvent extends IDisposable { event: Event; map(fn: (i: T) => O): IChainableEvent; @@ -327,34 +322,36 @@ export namespace Event { class ChainableEvent implements IChainableEvent { + private readonly disposables = new DisposableStore(); + constructor(readonly event: Event) { } map(fn: (i: T) => O): IChainableEvent { - return new ChainableEvent(map(this.event, fn)); + return new ChainableEvent(map(this.event, fn, this.disposables)); } forEach(fn: (i: T) => void): IChainableEvent { - return new ChainableEvent(forEach(this.event, fn)); + return new ChainableEvent(forEach(this.event, fn, this.disposables)); } filter(fn: (e: T) => boolean): IChainableEvent; filter(fn: (e: T | R) => e is R): IChainableEvent; filter(fn: (e: T) => boolean): IChainableEvent { - return new ChainableEvent(filter(this.event, fn)); + return new ChainableEvent(filter(this.event, fn, this.disposables)); } reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent { - return new ChainableEvent(reduce(this.event, merge, initial)); + return new ChainableEvent(reduce(this.event, merge, initial, this.disposables)); } latch(): IChainableEvent { - return new ChainableEvent(latch(this.event)); + return new ChainableEvent(latch(this.event, undefined, this.disposables)); } debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent; debounce(merge: (last: R | undefined, event: T) => R, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent; debounce(merge: (last: R | undefined, event: T) => R, delay: number = 100, leading = false, leakWarningThreshold?: number): IChainableEvent { - return new ChainableEvent(debounce(this.event, merge, delay, leading, leakWarningThreshold)); + return new ChainableEvent(debounce(this.event, merge, delay, leading, leakWarningThreshold, this.disposables)); } on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[] | DisposableStore) { @@ -364,11 +361,12 @@ export namespace Event { once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { return once(this.event)(listener, thisArgs, disposables); } + + dispose() { + this.disposables.dispose(); + } } - /** - * @deprecated DO NOT use, this leaks memory - */ export function chain(event: Event): IChainableEvent { return new ChainableEvent(event); } @@ -426,6 +424,55 @@ export namespace Event { store?.dispose(); }); } + + class EmitterObserver implements IObserver { + + readonly emitter: Emitter; + + private _counter = 0; + private _hasChanged = false; + + constructor(readonly obs: IObservable, store: DisposableStore | undefined) { + const options = { + onFirstListenerAdd: () => { + obs.addObserver(this); + }, + onLastListenerRemove: () => { + obs.removeObserver(this); + } + }; + if (!store) { + _addLeakageTraceLogic(options); + } + this.emitter = new Emitter(options); + if (store) { + store.add(this.emitter); + } + } + + beginUpdate(_observable: IObservable): void { + // console.assert(_observable === this.obs); + this._counter++; + } + + handleChange(_observable: IObservable, _change: TChange): void { + this._hasChanged = true; + } + + endUpdate(_observable: IObservable): void { + if (--this._counter === 0) { + if (this._hasChanged) { + this._hasChanged = false; + this.emitter.fire(this.obs.get()); + } + } + } + } + + export function fromObservable(obs: IObservable, store?: DisposableStore): Event { + const observer = new EmitterObserver(obs, store); + return observer.emitter.event; + } } export interface EmitterOptions { @@ -690,9 +737,7 @@ export class Emitter { } const result = listener.subscription.set(() => { - if (removeMonitor) { - removeMonitor(); - } + removeMonitor?.(); if (!this._disposed) { removeListener(); if (this._options && this._options.onLastListenerRemove) { @@ -730,7 +775,7 @@ export class Emitter { this._deliveryQueue = new PrivateEventDeliveryQueue(); } - for (let listener of this._listeners) { + for (const listener of this._listeners) { this._deliveryQueue.push(this, listener, event); } diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 9d30cdf518..8bf09d8522 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -315,7 +315,7 @@ function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], pat } function doScoreFuzzy2Single(target: string, query: IPreparedQueryPiece, patternStart: number, wordStart: number): FuzzyScore2 { - const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), wordStart); + const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), wordStart, { firstMatchCanBeWeak: true, boostFullMatch: true }); if (!score) { return NO_SCORE2; } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 219e564a23..2c4f32cf17 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -139,6 +139,10 @@ export function escapeMarkdownSyntaxTokens(text: string): string { return text.replace(/[\\`*_{}[\]()#+\-!]/g, '\\$&'); } +export function escapeDoubleQuotes(input: string) { + return input.replace(/"/g, '"'); +} + export function removeMarkdownEscapes(text: string): string { if (!text) { return text; diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index e0f740a7a6..3dcdea66df 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -200,12 +200,12 @@ export interface JSONVisitor { */ export function createScanner(text: string, ignoreTrivia: boolean = false): JSONScanner { - let pos = 0, - len = text.length, - value: string = '', - tokenOffset = 0, - token: SyntaxKind = SyntaxKind.Unknown, - scanError: ScanError = ScanError.None; + let pos = 0; + const len = text.length; + let value: string = ''; + let tokenOffset = 0; + let token: SyntaxKind = SyntaxKind.Unknown; + let scanError: ScanError = ScanError.None; function scanHexDigits(count: number): number { let digits = 0; @@ -963,7 +963,7 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined return undefined; } let node = root; - for (let segment of path) { + for (const segment of path) { if (typeof segment === 'string') { if (node.type !== 'object' || !Array.isArray(node.children)) { return undefined; @@ -1019,7 +1019,7 @@ export function getNodeValue(node: Node): any { return node.children!.map(getNodeValue); case 'object': { const obj = Object.create(null); - for (let prop of node.children!) { + for (const prop of node.children!) { const valueNode = prop.children![1]; if (valueNode) { obj[prop.children![0].value] = getNodeValue(valueNode); @@ -1315,11 +1315,11 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions */ export function stripComments(text: string, replaceCh?: string): string { - let _scanner = createScanner(text), - parts: string[] = [], - kind: SyntaxKind, - offset = 0, - pos: number; + const _scanner = createScanner(text); + const parts: string[] = []; + let kind: SyntaxKind; + let offset = 0; + let pos: number; do { pos = _scanner.getPosition(); diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 5219796362..886df59218 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -155,7 +155,7 @@ export function applyEdit(text: string, edit: Edit): string { } export function applyEdits(text: string, edits: Edit[]): string { - let sortedEdits = edits.slice(0).sort((a, b) => { + const sortedEdits = edits.slice(0).sort((a, b) => { const diff = a.offset - b.offset; if (diff === 0) { return a.length - b.length; @@ -164,7 +164,7 @@ export function applyEdits(text: string, edits: Edit[]): string { }); let lastModifiedOffset = text.length; for (let i = sortedEdits.length - 1; i >= 0; i--) { - let e = sortedEdits[i]; + const e = sortedEdits[i]; if (e.offset + e.length <= lastModifiedOffset) { text = applyEdit(text, e); } else { diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index 8834dc3807..c75f6dae7c 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -46,6 +46,7 @@ export interface IJSONSchema { const?: any; contains?: IJSONSchema; propertyNames?: IJSONSchema; + examples?: any[]; // schema draft 07 $comment?: string; @@ -53,7 +54,27 @@ export interface IJSONSchema { then?: IJSONSchema; else?: IJSONSchema; - // VS Code extensions + // schema 2019-09 + unevaluatedProperties?: boolean | IJSONSchema; + unevaluatedItems?: boolean | IJSONSchema; + minContains?: number; + maxContains?: number; + deprecated?: boolean; + dependentRequired?: { [prop: string]: string[] }; + dependentSchemas?: IJSONSchemaMap; + $defs?: { [name: string]: IJSONSchema }; + $anchor?: string; + $recursiveRef?: string; + $recursiveAnchor?: string; + $vocabulary?: any; + + // schema 2020-12 + prefixItems?: IJSONSchema[]; + $dynamicRef?: string; + $dynamicAnchor?: string; + + // VSCode extensions + defaultSnippets?: IJSONSchemaSnippet[]; errorMessage?: string; patternErrorMessage?: string; diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index 332cd91b71..282e7f3aa8 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -722,8 +722,8 @@ for (let i = 0; i <= KeyCode.MAX_VALUE; i++) { [0, 1, ScanCode.None, empty, KeyCode.Unknown, empty, 0, 'VK_OEM_CLEAR', empty, empty], ]; - let seenKeyCode: boolean[] = []; - let seenScanCode: boolean[] = []; + const seenKeyCode: boolean[] = []; + const seenScanCode: boolean[] = []; for (const mapping of mappings) { const [_keyCodeOrd, immutable, scanCode, scanCodeStr, keyCode, keyCodeStr, eventKeyCode, vkey, usUserSettingsLabel, generalUserSettingsLabel] = mapping; if (!seenScanCode[scanCode]) { diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 7d161badcd..dd35571a25 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -4,11 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { firstOrDefault } from 'vs/base/common/arrays'; -import { hasDriveLetter, isRootOrDriveLetter, toSlashes } from 'vs/base/common/extpath'; -import { Schemas } from 'vs/base/common/network'; +import { hasDriveLetter, toSlashes } from 'vs/base/common/extpath'; import { posix, sep, win32 } from 'vs/base/common/path'; import { isMacintosh, isWindows, OperatingSystem, OS } from 'vs/base/common/platform'; -import { basename, extUri, extUriIgnorePathCase } from 'vs/base/common/resources'; +import { extUri, extUriIgnorePathCase } from 'vs/base/common/resources'; import { rtrim, startsWithIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -74,7 +73,7 @@ export function getPathLabel(resource: URI, formatting: IPathLabelFormatting): s // macOS/Linux: tildify with provided user home directory if (os !== OperatingSystem.Windows && tildifier?.userHome) { - let userHome = tildifier.userHome.fsPath; + const userHome = tildifier.userHome.fsPath; // This is a bit of a hack, but in order to figure out if the // resource is in the user home, we need to make sure to convert it @@ -139,27 +138,6 @@ function getRelativePathLabel(resource: URI, relativePathProvider: IRelativePath return relativePathLabel; } -export function getBaseLabel(resource: URI | string): string; -export function getBaseLabel(resource: URI | string | undefined): string | undefined; -export function getBaseLabel(resource: URI | string | undefined): string | undefined { - if (!resource) { - return undefined; - } - - if (typeof resource === 'string') { - resource = URI.file(resource); - } - - const base = basename(resource) || (resource.scheme === Schemas.file ? resource.fsPath : resource.path) /* can be empty string if '/' is passed in */; - - // convert c: => C: - if (isWindows && isRootOrDriveLetter(base)) { - return normalizeDriveLetter(base); - } - - return base; -} - export function normalizeDriveLetter(path: string, isWindowsOS: boolean = isWindows): string { if (hasDriveLetter(path, isWindowsOS)) { return path.charAt(0).toUpperCase() + path.slice(1); @@ -239,7 +217,7 @@ export function shorten(paths: string[], pathSeparator: string = sep): string[] // for every path let match = false; for (let pathIndex = 0; pathIndex < paths.length; pathIndex++) { - let originalPath = paths[pathIndex]; + const originalPath = paths[pathIndex]; if (originalPath === '') { shortenedPaths[pathIndex] = `.${pathSeparator}`; diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index e377bcf55d..6bb098754a 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -131,7 +131,7 @@ export function dispose(disposables: Array): Array; export function dispose(disposables: ReadonlyArray): ReadonlyArray; export function dispose(arg: T | IterableIterator | undefined): any { if (Iterable.is(arg)) { - let errors: any[] = []; + const errors: any[] = []; for (const d of arg) { if (d) { diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 24cf310150..d50f2b5747 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -371,13 +371,13 @@ export class TernarySearchTree { if (keys) { const arr = keys.slice(0); shuffle(arr); - for (let k of arr) { + for (const k of arr) { this.set(k, (values)); } } else { const arr = (<[K, V][]>values).slice(0); shuffle(arr); - for (let entry of arr) { + for (const entry of arr) { this.set(entry[0], entry[1]); } } @@ -715,22 +715,28 @@ export class TernarySearchTree { yield* this._entries(this._root); } - private *_entries(node: TernarySearchTreeNode | undefined): IterableIterator<[K, V]> { + private _entries(node: TernarySearchTreeNode | undefined): IterableIterator<[K, V]> { + const result: [K, V][] = []; + this._dfsEntries(node, result); + return result[Symbol.iterator](); + } + + private _dfsEntries(node: TernarySearchTreeNode | undefined, bucket: [K, V][]) { // DFS if (!node) { return; } if (node.left) { - yield* this._entries(node.left); + this._dfsEntries(node.left, bucket); } if (node.value) { - yield [node.key!, node.value]; + bucket.push([node.key!, node.value]); } if (node.mid) { - yield* this._entries(node.mid); + this._dfsEntries(node.mid, bucket); } if (node.right) { - yield* this._entries(node.right); + this._dfsEntries(node.right, bucket); } } @@ -819,31 +825,31 @@ export class ResourceMap implements Map { if (typeof thisArg !== 'undefined') { clb = clb.bind(thisArg); } - for (let [_, entry] of this.map) { + for (const [_, entry] of this.map) { clb(entry.value, entry.uri, this); } } *values(): IterableIterator { - for (let entry of this.map.values()) { + for (const entry of this.map.values()) { yield entry.value; } } *keys(): IterableIterator { - for (let entry of this.map.values()) { + for (const entry of this.map.values()) { yield entry.uri; } } *entries(): IterableIterator<[URI, T]> { - for (let entry of this.map.values()) { + for (const entry of this.map.values()) { yield [entry.uri, entry.value]; } } *[Symbol.iterator](): IterableIterator<[URI, T]> { - for (let [, entry] of this.map) { + for (const [, entry] of this.map) { yield [entry.uri, entry.value]; } } diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json index 47100d82d7..60e11b4144 100644 --- a/src/vs/base/common/marked/cgmanifest.json +++ b/src/vs/base/common/marked/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "marked", "repositoryUrl": "https://github.com/markedjs/marked", - "commitHash": "d1b7d521c41bcf915f81f0218b0e5acd607c1b72" + "commitHash": "2002557d004139ca2208c910d9ca999829b65406" } }, "license": "MIT", - "version": "3.0.2" + "version": "4.0.16" } ], "version": 1 diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index 09c308378d..b7d8fe40e5 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -23,6 +23,7 @@ typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.marked = {})); })(this, (function (exports) { 'use strict'; + function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; @@ -141,6 +142,10 @@ return html; } var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; + /** + * @param {string} html + */ + function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, function (_, n) { @@ -155,8 +160,13 @@ }); } var caret = /(^|[^\[])\^/g; + /** + * @param {string | RegExp} regex + * @param {string} opt + */ + function edit(regex, opt) { - regex = regex.source || regex; + regex = typeof regex === 'string' ? regex : regex.source; opt = opt || ''; var obj = { replace: function replace(name, val) { @@ -173,6 +183,12 @@ } var nonWordAndColonTest = /[^\w:]/g; var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; + /** + * @param {boolean} sanitize + * @param {string} base + * @param {string} href + */ + function cleanUrl(sanitize, base, href) { if (sanitize) { var prot; @@ -204,6 +220,11 @@ var justDomain = /^[^:]+:\/*[^/]*$/; var protocol = /^([^:]+:)[\s\S]*$/; var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/; + /** + * @param {string} base + * @param {string} href + */ + function resolveUrl(base, href) { if (!baseUrls[' ' + base]) { // we can ignore everything in base after the last slash of its path component, @@ -282,7 +303,7 @@ cells.shift(); } - if (!cells[cells.length - 1].trim()) { + if (cells.length > 0 && !cells[cells.length - 1].trim()) { cells.pop(); } @@ -300,9 +321,15 @@ } return cells; - } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). - // /c*$/ is vulnerable to REDOS. - // invert: Remove suffix of non-c chars instead. Default falsey. + } + /** + * Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). + * /c*$/ is vulnerable to REDOS. + * + * @param {string} str + * @param {string} c + * @param {boolean} invert Remove suffix of non-c chars instead. Default falsey. + */ function rtrim(str, c, invert) { var l = str.length; @@ -326,7 +353,7 @@ } } - return str.substr(0, l - suffLen); + return str.slice(0, l - suffLen); } function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { @@ -359,6 +386,11 @@ } } // copied from https://stackoverflow.com/a/5450113/806777 + /** + * @param {string} pattern + * @param {number} count + */ + function repeatString(pattern, count) { if (count < 1) { return ''; @@ -395,15 +427,15 @@ }; lexer.state.inLink = false; return token; - } else { - return { - type: 'image', - raw: raw, - href: href, - title: title, - text: escape(text) - }; } + + return { + type: 'image', + raw: raw, + href: href, + title: title, + text: escape(text) + }; } function indentCodeCompensation(raw, text) { @@ -446,11 +478,11 @@ var cap = this.rules.block.newline.exec(src); if (cap && cap[0].length > 0) { - return { - type: 'space', - raw: cap[0] - }; - } + return { + type: 'space', + raw: cap[0] + }; + } }; _proto.code = function code(src) { @@ -526,7 +558,7 @@ var cap = this.rules.block.blockquote.exec(src); if (cap) { - var text = cap[0].replace(/^ *> ?/gm, ''); + var text = cap[0].replace(/^ *>[ \t]?/gm, ''); return { type: 'blockquote', raw: cap[0], @@ -558,7 +590,7 @@ } // Get next list item - var itemRegex = new RegExp("^( {0,3}" + bull + ")((?: [^\\n]*)?(?:\\n|$))"); // Check if current bullet point can start a new List Item + var itemRegex = new RegExp("^( {0,3}" + bull + ")((?:[\t ][^\\n]*)?(?:\\n|$))"); // Check if current bullet point can start a new List Item while (src) { endEarly = false; @@ -599,31 +631,37 @@ } if (!endEarly) { - var nextBulletRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}(?:[*+-]|\\d{1,9}[.)])"); // Check if following lines should be included in List Item + var nextBulletRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}(?:[*+-]|\\d{1,9}[.)])((?: [^\\n]*)?(?:\\n|$))"); + var hrRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)"); // Check if following lines should be included in List Item while (src) { rawLine = src.split('\n', 1)[0]; line = rawLine; // Re-align to follow commonmark nesting rules - if (this.options.pedantic) { - line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); - } // End list item if found start of new bullet + if (this.options.pedantic) { + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } // End list item if found start of new bullet - if (nextBulletRegex.test(line)) { - break; + if (nextBulletRegex.test(line)) { + break; + } // Horizontal rule found + + + if (hrRegex.test(src)) { + break; } - if (line.search(/[^ ]/) >= indent || !line.trim()) { + if (line.search(/[^ ]/) >= indent || !line.trim()) { // Dedent if possible - itemContents += '\n' + line.slice(indent); + itemContents += '\n' + line.slice(indent); } else if (!blankLine) { // Until blank line, item doesn't need indentation itemContents += '\n' + line; - } else { + } else { // Otherwise, improper indentation ends this item - break; - } + break; + } if (!blankLine && !line.trim()) { // Check if current line is blank @@ -757,7 +795,7 @@ }; }), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - rows: cap[3] ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : [] + rows: cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : [] }; if (item.header.length === item.align.length) { @@ -793,7 +831,7 @@ for (j = 0; j < l; j++) { item.header[j].tokens = []; - this.lexer.inlineTokens(item.header[j].text, item.header[j].tokens); + this.lexer.inline(item.header[j].text, item.header[j].tokens); } // cell child tokens @@ -804,7 +842,7 @@ for (k = 0; k < row.length; k++) { row[k].tokens = []; - this.lexer.inlineTokens(row[k].text, row[k].tokens); + this.lexer.inline(row[k].text, row[k].tokens); } } @@ -1195,10 +1233,10 @@ newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, - hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, + hr: /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, + list: /^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1288,9 +1326,9 @@ emStrong: { lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/, // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right. - // () Skip orphan delim inside strong (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a - rDelimAst: /^[^_*]*?\_\_[^_*]*?\*[^_*]*?(?=\_\_)|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/, - rDelimUnd: /^[^_*]*?\*\*[^_*]*?\_[^_*]*?(?=\*\*)|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _ + // () Skip orphan inside strong () Consume to delim (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a + rDelimAst: /^[^_*]*?\_\_[^_*]*?\*[^_*]*?(?=\_\_)|[^*]+(?=[^*])|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/, + rDelimUnd: /^[^_*]*?\*\*[^_*]*?\_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _ }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, @@ -1372,6 +1410,7 @@ /** * smartypants text replacement + * @param {string} text */ function smartypants(text) { @@ -1386,6 +1425,7 @@ } /** * mangle email addresses + * @param {string} text */ @@ -1476,7 +1516,7 @@ var _proto = Lexer.prototype; _proto.lex = function lex(src) { - src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' '); + src = src.replace(/\r\n|\r/g, '\n'); this.blockTokens(src, this.tokens); var next; @@ -1499,7 +1539,11 @@ } if (this.options.pedantic) { - src = src.replace(/^ +$/gm, ''); + src = src.replace(/\t/g, ' ').replace(/^ +$/gm, ''); + } else { + src = src.replace(/^( *)(\t+)/gm, function (_, leading, tabs) { + return leading + ' '.repeat(tabs.length); + }); } var token, lastToken, cutSrc, lastParagraphClipped; @@ -1959,23 +2003,35 @@ } return '
' + (escaped ? _code : escape(_code, true)) + '
\n'; - }; + } + /** + * @param {string} quote + */ + ; _proto.blockquote = function blockquote(quote) { - return '
\n' + quote + '
\n'; + return "
\n" + quote + "
\n"; }; _proto.html = function html(_html) { return _html; - }; + } + /** + * @param {string} text + * @param {string} level + * @param {string} raw + * @param {any} slugger + */ + ; _proto.heading = function heading(text, level, raw, slugger) { if (this.options.headerIds) { - return '' + text + '\n'; + var id = this.options.headerPrefix + slugger.slug(raw); + return "" + text + "\n"; } // ignore IDs - return '' + text + '\n'; + return "" + text + "\n"; }; _proto.hr = function hr() { @@ -1986,55 +2042,94 @@ var type = ordered ? 'ol' : 'ul', startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; return '<' + type + startatt + '>\n' + body + '\n'; - }; + } + /** + * @param {string} text + */ + ; _proto.listitem = function listitem(text) { - return '
  • ' + text + '
  • \n'; + return "
  • " + text + "
  • \n"; }; _proto.checkbox = function checkbox(checked) { return ' '; - }; + } + /** + * @param {string} text + */ + ; _proto.paragraph = function paragraph(text) { - return '

    ' + text + '

    \n'; - }; + return "

    " + text + "

    \n"; + } + /** + * @param {string} header + * @param {string} body + */ + ; _proto.table = function table(header, body) { - if (body) body = '' + body + ''; + if (body) body = "" + body + ""; return '\n' + '\n' + header + '\n' + body + '
    \n'; - }; + } + /** + * @param {string} content + */ + ; _proto.tablerow = function tablerow(content) { - return '\n' + content + '\n'; + return "\n" + content + "\n"; }; _proto.tablecell = function tablecell(content, flags) { var type = flags.header ? 'th' : 'td'; - var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>'; - return tag + content + '\n'; - } // span level renderer + var tag = flags.align ? "<" + type + " align=\"" + flags.align + "\">" : "<" + type + ">"; + return tag + content + ("\n"); + } + /** + * span level renderer + * @param {string} text + */ ; _proto.strong = function strong(text) { - return '' + text + ''; - }; + return "" + text + ""; + } + /** + * @param {string} text + */ + ; _proto.em = function em(text) { - return '' + text + ''; - }; + return "" + text + ""; + } + /** + * @param {string} text + */ + ; _proto.codespan = function codespan(text) { - return '' + text + ''; + return "" + text + ""; }; _proto.br = function br() { return this.options.xhtml ? '
    ' : '
    '; - }; + } + /** + * @param {string} text + */ + ; _proto.del = function del(text) { - return '' + text + ''; - }; + return "" + text + ""; + } + /** + * @param {string} href + * @param {string} title + * @param {string} text + */ + ; _proto.link = function link(href, title, text) { href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); @@ -2051,7 +2146,13 @@ out += '>' + text + ''; return out; - }; + } + /** + * @param {string} href + * @param {string} title + * @param {string} text + */ + ; _proto.image = function image(href, title, text) { href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); @@ -2060,10 +2161,10 @@ return text; } - var out = '' + text + '' : '>'; @@ -2133,6 +2234,10 @@ function Slugger() { this.seen = {}; } + /** + * @param {string} value + */ + var _proto = Slugger.prototype; @@ -2143,6 +2248,8 @@ } /** * Finds the next safe (unique) slug to use + * @param {string} originalSlug + * @param {boolean} isDryRun */ ; @@ -2168,8 +2275,9 @@ } /** * Convert string to unique id - * @param {object} options - * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator. + * @param {object} [options] + * @param {boolean} [options.dryrun] Generates the next unique slug without + * updating the internal accumulator. */ ; @@ -2866,6 +2974,7 @@ }; /** * Parse Inline + * @param {string} src */ @@ -2941,6 +3050,7 @@ exports.walkTokens = walkTokens; Object.defineProperty(exports, '__esModule', { value: true }); + })); // ESM-uncomment-begin diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index b43d99845c..6378548d9e 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -5,14 +5,14 @@ import { extname } from 'vs/base/common/path'; -export namespace Mimes { - export const text = 'text/plain'; - export const binary = 'application/octet-stream'; - export const unknown = 'application/unknown'; - export const markdown = 'text/markdown'; - export const latex = 'text/latex'; - export const uriList = 'text/uri-list'; -} +export const Mimes = Object.freeze({ + text: 'text/plain', + binary: 'application/octet-stream', + unknown: 'application/unknown', + markdown: 'text/markdown', + latex: 'text/latex', + uriList: 'text/uri-list', +}); interface MapExtToMediaMimes { [index: string]: string; diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 665b2cd3a5..a6c0747626 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -103,6 +103,11 @@ export namespace Schemas { * Scheme used vs live share */ export const vsls = 'vsls'; + + /** + * Scheme used for the Source Control commit input's text document + */ + export const vscodeSourceControl = 'vscode-scm'; } export const connectionTokenCookieName = 'vscode-tkn'; @@ -116,6 +121,7 @@ class RemoteAuthoritiesImpl { private readonly _connectionTokens: { [authority: string]: string | undefined } = Object.create(null); private _preferredWebSchema: 'http' | 'https' = 'http'; private _delegate: ((uri: URI) => URI) | null = null; + private _remoteResourcesPath: string = `/${Schemas.vscodeRemoteResource}`; setPreferredWebSchema(schema: 'http' | 'https') { this._preferredWebSchema = schema; @@ -125,6 +131,10 @@ class RemoteAuthoritiesImpl { this._delegate = delegate; } + setServerRootPath(serverRootPath: string): void { + this._remoteResourcesPath = `${serverRootPath}/${Schemas.vscodeRemoteResource}`; + } + set(authority: string, host: string, port: number): void { this._hosts[authority] = host; this._ports[authority] = port; @@ -156,7 +166,7 @@ class RemoteAuthoritiesImpl { return URI.from({ scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, authority: platform.isWeb && port === this._defaultWebPort ? `${host}` : `${host}:${port}`, // {{SQL CARBON EDIT}} addresses same-origin-policy violation in web mode when port number is in authority, but not in URI. - path: `/vscode-remote-resource`, + path: this._remoteResourcesPath, query }); } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 24a5b41adf..b87b079948 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -75,7 +75,7 @@ function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set): } seen.add(obj); const r2 = {}; - for (let i2 in obj) { + for (const i2 in obj) { if (_hasOwnProperty.call(obj, i2)) { (r2 as any)[i2] = _cloneAndChange(obj[i2], changer, seen); } @@ -186,6 +186,7 @@ export function safeStringify(obj: any): string { }); } +// {{SQL CARBON EDIT}} - define getOrDefault export function getOrDefault(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { const result = fn(obj); return typeof result === 'undefined' ? defaultValue : result; diff --git a/src/vs/base/common/observable.ts b/src/vs/base/common/observable.ts new file mode 100644 index 0000000000..850a642d73 --- /dev/null +++ b/src/vs/base/common/observable.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export { + IObservable, + IObserver, + IReader, + ISettable, + ISettableObservable, + ITransaction, + observableValue, + transaction, +} from 'vs/base/common/observableImpl/base'; +export { derived } from 'vs/base/common/observableImpl/derived'; +export { + autorun, + autorunDelta, + autorunHandleChanges, + autorunWithStore, +} from 'vs/base/common/observableImpl/autorun'; +export * from 'vs/base/common/observableImpl/utils'; + +import { ConsoleObservableLogger, setLogger } from 'vs/base/common/observableImpl/logging'; + +const enableLogging = false; +if (enableLogging) { + setLogger(new ConsoleObservableLogger()); +} diff --git a/src/vs/base/common/observableImpl/autorun.ts b/src/vs/base/common/observableImpl/autorun.ts new file mode 100644 index 0000000000..ca250de13e --- /dev/null +++ b/src/vs/base/common/observableImpl/autorun.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IReader, IObservable, IObserver } from 'vs/base/common/observableImpl/base'; +import { getLogger } from 'vs/base/common/observableImpl/logging'; + +export function autorun(debugName: string, fn: (reader: IReader) => void): IDisposable { + return new AutorunObserver(debugName, fn, undefined); +} + +interface IChangeContext { + readonly changedObservable: IObservable; + readonly change: unknown; + + didChange(observable: IObservable): this is { change: TChange }; +} + +export function autorunHandleChanges( + debugName: string, + options: { + /** + * Returns if this change should cause a re-run of the autorun. + */ + handleChange: (context: IChangeContext) => boolean; + }, + fn: (reader: IReader) => void +): IDisposable { + return new AutorunObserver(debugName, fn, options.handleChange); +} + +export function autorunWithStore( + fn: (reader: IReader, store: DisposableStore) => void, + debugName: string +): IDisposable { + const store = new DisposableStore(); + const disposable = autorun( + debugName, + reader => { + store.clear(); + fn(reader, store); + } + ); + return toDisposable(() => { + disposable.dispose(); + store.dispose(); + }); +} + +export class AutorunObserver implements IObserver, IReader, IDisposable { + public needsToRun = true; + private updateCount = 0; + private disposed = false; + + /** + * The actual dependencies. + */ + private _dependencies = new Set>(); + public get dependencies() { + return this._dependencies; + } + + /** + * Dependencies that have to be removed when {@link runFn} ran through. + */ + private staleDependencies = new Set>(); + + constructor( + public readonly debugName: string, + private readonly runFn: (reader: IReader) => void, + private readonly _handleChange: ((context: IChangeContext) => boolean) | undefined + ) { + getLogger()?.handleAutorunCreated(this); + this.runIfNeeded(); + } + + public subscribeTo(observable: IObservable) { + // In case the run action disposes the autorun + if (this.disposed) { + return; + } + this._dependencies.add(observable); + if (!this.staleDependencies.delete(observable)) { + observable.addObserver(this); + } + } + + public handleChange(observable: IObservable, change: TChange): void { + const shouldReact = this._handleChange ? this._handleChange({ + changedObservable: observable, + change, + didChange: o => o === observable as any, + }) : true; + this.needsToRun = this.needsToRun || shouldReact; + + if (this.updateCount === 0) { + this.runIfNeeded(); + } + } + + public beginUpdate(): void { + this.updateCount++; + } + + public endUpdate(): void { + this.updateCount--; + if (this.updateCount === 0) { + this.runIfNeeded(); + } + } + + private runIfNeeded(): void { + if (!this.needsToRun) { + return; + } + // Assert: this.staleDependencies is an empty set. + const emptySet = this.staleDependencies; + this.staleDependencies = this._dependencies; + this._dependencies = emptySet; + + this.needsToRun = false; + + getLogger()?.handleAutorunTriggered(this); + + try { + this.runFn(this); + } finally { + // We don't want our observed observables to think that they are (not even temporarily) not being observed. + // Thus, we only unsubscribe from observables that are definitely not read anymore. + for (const o of this.staleDependencies) { + o.removeObserver(this); + } + this.staleDependencies.clear(); + } + } + + public dispose(): void { + this.disposed = true; + for (const o of this._dependencies) { + o.removeObserver(this); + } + this._dependencies.clear(); + } + + public toString(): string { + return `Autorun<${this.debugName}>`; + } +} + +export namespace autorun { + export const Observer = AutorunObserver; +} +export function autorunDelta( + name: string, + observable: IObservable, + handler: (args: { lastValue: T | undefined; newValue: T }) => void +): IDisposable { + let _lastValue: T | undefined; + return autorun(name, (reader) => { + const newValue = observable.read(reader); + const lastValue = _lastValue; + _lastValue = newValue; + handler({ lastValue, newValue }); + }); +} diff --git a/src/vs/base/common/observableImpl/base.ts b/src/vs/base/common/observableImpl/base.ts new file mode 100644 index 0000000000..02448c47ff --- /dev/null +++ b/src/vs/base/common/observableImpl/base.ts @@ -0,0 +1,244 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { derived } from 'vs/base/common/observableImpl/derived'; +import { getLogger } from 'vs/base/common/observableImpl/logging'; + +export interface IObservable { + readonly TChange: TChange; + + /** + * Reads the current value. + * + * Must not be called from {@link IObserver.handleChange}. + */ + get(): T; + + /** + * Adds an observer. + */ + addObserver(observer: IObserver): void; + removeObserver(observer: IObserver): void; + + /** + * Subscribes the reader to this observable and returns the current value of this observable. + */ + read(reader: IReader): T; + + map(fn: (value: T) => TNew): IObservable; + + readonly debugName: string; +} + +export interface IReader { + /** + * Reports an observable that was read. + * + * Is called by {@link IObservable.read}. + */ + subscribeTo(observable: IObservable): void; +} + +export interface IObserver { + /** + * Indicates that an update operation is about to begin. + * + * During an update, invariants might not hold for subscribed observables and + * change events might be delayed. + * However, all changes must be reported before all update operations are over. + */ + beginUpdate(observable: IObservable): void; + + /** + * Is called by a subscribed observable immediately after it notices a change. + * + * When {@link IObservable.get} returns and no change has been reported, + * there has been no change for that observable. + * + * Implementations must not call into other observables! + * The change should be processed when {@link IObserver.endUpdate} is called. + */ + handleChange(observable: IObservable, change: TChange): void; + + /** + * Indicates that an update operation has completed. + */ + endUpdate(observable: IObservable): void; +} + +export interface ISettable { + set(value: T, transaction: ITransaction | undefined, change: TChange): void; +} + +export interface ITransaction { + /** + * Calls `Observer.beginUpdate` immediately + * and `Observer.endUpdate` when the transaction is complete. + */ + updateObserver( + observer: IObserver, + observable: IObservable + ): void; +} + +let _derived: typeof derived; +/** + * @internal + * This is to allow splitting files. +*/ +export function _setDerived(derived: typeof _derived) { + _derived = derived; +} + +export abstract class ConvenientObservable implements IObservable { + get TChange(): TChange { return null!; } + + public abstract get(): T; + public abstract addObserver(observer: IObserver): void; + public abstract removeObserver(observer: IObserver): void; + + /** @sealed */ + public read(reader: IReader): T { + reader.subscribeTo(this); + return this.get(); + } + + /** @sealed */ + public map(fn: (value: T) => TNew): IObservable { + return _derived( + () => { + const name = getFunctionName(fn); + return name !== undefined ? name : `${this.debugName} (mapped)`; + }, + (reader) => fn(this.read(reader)) + ); + } + + public abstract get debugName(): string; +} + +export abstract class BaseObservable extends ConvenientObservable { + protected readonly observers = new Set(); + + /** @sealed */ + public addObserver(observer: IObserver): void { + const len = this.observers.size; + this.observers.add(observer); + if (len === 0) { + this.onFirstObserverAdded(); + } + } + + /** @sealed */ + public removeObserver(observer: IObserver): void { + const deleted = this.observers.delete(observer); + if (deleted && this.observers.size === 0) { + this.onLastObserverRemoved(); + } + } + + protected onFirstObserverAdded(): void { } + protected onLastObserverRemoved(): void { } +} + +export function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void { + const tx = new TransactionImpl(fn, getDebugName); + try { + getLogger()?.handleBeginTransaction(tx); + fn(tx); + } finally { + tx.finish(); + getLogger()?.handleEndTransaction(); + } +} + +export function getFunctionName(fn: Function): string | undefined { + const fnSrc = fn.toString(); + // Pattern: /** @description ... */ + const regexp = /\/\*\*\s*@description\s*([^*]*)\*\//; + const match = regexp.exec(fnSrc); + const result = match ? match[1] : undefined; + return result?.trim(); +} + +export class TransactionImpl implements ITransaction { + private updatingObservers: { observer: IObserver; observable: IObservable }[] | null = []; + + constructor(private readonly fn: Function, private readonly _getDebugName?: () => string) { } + + public getDebugName(): string | undefined { + if (this._getDebugName) { + return this._getDebugName(); + } + return getFunctionName(this.fn); + } + + public updateObserver( + observer: IObserver, + observable: IObservable + ): void { + this.updatingObservers!.push({ observer, observable }); + observer.beginUpdate(observable); + } + + public finish(): void { + const updatingObservers = this.updatingObservers!; + // Prevent anyone from updating observers from now on. + this.updatingObservers = null; + for (const { observer, observable } of updatingObservers) { + observer.endUpdate(observable); + } + } +} + +export interface ISettableObservable extends IObservable, ISettable { +} + +export function observableValue(name: string, initialValue: T): ISettableObservable { + return new ObservableValue(name, initialValue); +} + +export class ObservableValue + extends BaseObservable + implements ISettableObservable +{ + private value: T; + + constructor(public readonly debugName: string, initialValue: T) { + super(); + this.value = initialValue; + } + + public get(): T { + return this.value; + } + + public set(value: T, tx: ITransaction | undefined, change: TChange): void { + if (this.value === value) { + return; + } + + if (!tx) { + transaction((tx) => { + this.set(value, tx, change); + }, () => `Setting ${this.debugName}`); + return; + } + + const oldValue = this.value; + this.value = value; + getLogger()?.handleObservableChanged(this, { oldValue, newValue: value, change, didChange: true }); + + for (const observer of this.observers) { + tx.updateObserver(observer, this); + observer.handleChange(this, change); + } + } + + override toString(): string { + return `${this.debugName}: ${this.value}`; + } +} + diff --git a/src/vs/base/common/observableImpl/derived.ts b/src/vs/base/common/observableImpl/derived.ts new file mode 100644 index 0000000000..cf8237d59b --- /dev/null +++ b/src/vs/base/common/observableImpl/derived.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IReader, IObservable, BaseObservable, IObserver, _setDerived } from 'vs/base/common/observableImpl/base'; +import { getLogger } from 'vs/base/common/observableImpl/logging'; + +export function derived(debugName: string | (() => string), computeFn: (reader: IReader) => T): IObservable { + return new Derived(debugName, computeFn); +} + +_setDerived(derived); + +export class Derived extends BaseObservable implements IReader, IObserver { + private hadValue = false; + private hasValue = false; + private value: T | undefined = undefined; + private updateCount = 0; + + private _dependencies = new Set>(); + public get dependencies(): ReadonlySet> { + return this._dependencies; + } + + /** + * Dependencies that have to be removed when {@link runFn} ran through. + */ + private staleDependencies = new Set>(); + + public override get debugName(): string { + return typeof this._debugName === 'function' ? this._debugName() : this._debugName; + } + + constructor( + private readonly _debugName: string | (() => string), + private readonly computeFn: (reader: IReader) => T + ) { + super(); + + getLogger()?.handleDerivedCreated(this); + } + + protected override onLastObserverRemoved(): void { + /** + * We are not tracking changes anymore, thus we have to assume + * that our cache is invalid. + */ + this.hasValue = false; + this.hadValue = false; + this.value = undefined; + for (const d of this._dependencies) { + d.removeObserver(this); + } + this._dependencies.clear(); + } + + public get(): T { + if (this.observers.size === 0) { + // Cache is not valid and don't refresh the cache. + // Observables should not be read in non-reactive contexts. + const result = this.computeFn(this); + // Clear new dependencies + this.onLastObserverRemoved(); + return result; + } + + if (this.updateCount > 0 && this.hasValue) { + // Refresh dependencies + for (const d of this._dependencies) { + // Maybe `.get()` triggers `handleChange`? + d.get(); + if (!this.hasValue) { + // The other dependencies will refresh on demand + break; + } + } + } + + if (!this.hasValue) { + const emptySet = this.staleDependencies; + this.staleDependencies = this._dependencies; + this._dependencies = emptySet; + + const oldValue = this.value; + try { + this.value = this.computeFn(this); + } finally { + // We don't want our observed observables to think that they are (not even temporarily) not being observed. + // Thus, we only unsubscribe from observables that are definitely not read anymore. + for (const o of this.staleDependencies) { + o.removeObserver(this); + } + this.staleDependencies.clear(); + } + + this.hasValue = true; + const didChange = this.hadValue && oldValue !== this.value; + getLogger()?.handleDerivedRecomputed(this, { + oldValue, + newValue: this.value, + change: undefined, + didChange + }); + if (didChange) { + for (const r of this.observers) { + r.handleChange(this, undefined); + } + } + } + return this.value!; + } + + // IObserver Implementation + public beginUpdate(): void { + if (this.updateCount === 0) { + for (const r of this.observers) { + r.beginUpdate(this); + } + } + this.updateCount++; + } + + public handleChange( + _observable: IObservable, + _change: TChange + ): void { + if (this.hasValue) { + this.hadValue = true; + this.hasValue = false; + } + + // Not in transaction: Recompute & inform observers immediately + if (this.updateCount === 0 && this.observers.size > 0) { + this.get(); + } + + // Otherwise, recompute in `endUpdate` or on demand. + } + + public endUpdate(): void { + this.updateCount--; + if (this.updateCount === 0) { + if (this.observers.size > 0) { + // Propagate invalidation + this.get(); + } + + for (const r of this.observers) { + r.endUpdate(this); + } + } + } + + // IReader Implementation + public subscribeTo(observable: IObservable) { + this._dependencies.add(observable); + // We are already added as observer for stale dependencies. + if (!this.staleDependencies.delete(observable)) { + observable.addObserver(this); + } + } + + override toString(): string { + return `LazyDerived<${this.debugName}>`; + } +} diff --git a/src/vs/base/common/observableImpl/logging.ts b/src/vs/base/common/observableImpl/logging.ts new file mode 100644 index 0000000000..ba0f10cf14 --- /dev/null +++ b/src/vs/base/common/observableImpl/logging.ts @@ -0,0 +1,312 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AutorunObserver } from 'vs/base/common/observableImpl/autorun'; +import { IObservable, ObservableValue, TransactionImpl } from 'vs/base/common/observableImpl/base'; +import { Derived } from 'vs/base/common/observableImpl/derived'; +import { FromEventObservable } from 'vs/base/common/observableImpl/utils'; + +let globalObservableLogger: IObservableLogger | undefined; + +export function setLogger(logger: IObservableLogger): void { + globalObservableLogger = logger; +} + +export function getLogger(): IObservableLogger | undefined { + return globalObservableLogger; +} + +interface IChangeInformation { + oldValue: unknown; + newValue: unknown; + change: unknown; + didChange: boolean; +} + +export interface IObservableLogger { + handleObservableChanged(observable: ObservableValue, info: IChangeInformation): void; + handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void; + + handleAutorunCreated(autorun: AutorunObserver): void; + handleAutorunTriggered(autorun: AutorunObserver): void; + + handleDerivedCreated(observable: Derived): void; + handleDerivedRecomputed(observable: Derived, info: IChangeInformation): void; + + handleBeginTransaction(transaction: TransactionImpl): void; + handleEndTransaction(): void; +} + +export class ConsoleObservableLogger implements IObservableLogger { + private indentation = 0; + + private textToConsoleArgs(text: ConsoleText): unknown[] { + return consoleTextToArgs([ + normalText(repeat('| ', this.indentation)), + text, + ]); + } + + private formatInfo(info: IChangeInformation): ConsoleText[] { + return info.didChange + ? [ + normalText(` `), + styled(formatValue(info.oldValue, 70), { + color: 'red', + strikeThrough: true, + }), + normalText(` `), + styled(formatValue(info.newValue, 60), { + color: 'green', + }), + ] + : [normalText(` (unchanged)`)]; + } + + handleObservableChanged(observable: IObservable, info: IChangeInformation): void { + console.log(...this.textToConsoleArgs([ + formatKind('observable value changed'), + styled(observable.debugName, { color: 'BlueViolet' }), + ...this.formatInfo(info), + ])); + } + + private readonly changedObservablesSets = new WeakMap>>(); + + formatChanges(changes: Set>): ConsoleText | undefined { + if (changes.size === 0) { + return undefined; + } + return styled( + ' (changed deps: ' + + [...changes].map((o) => o.debugName).join(', ') + + ')', + { color: 'gray' } + ); + } + + handleDerivedCreated(derived: Derived): void { + const existingHandleChange = derived.handleChange; + this.changedObservablesSets.set(derived, new Set()); + derived.handleChange = (observable, change) => { + this.changedObservablesSets.get(derived)!.add(observable); + return existingHandleChange.apply(derived, [observable, change]); + }; + } + + handleDerivedRecomputed(derived: Derived, info: IChangeInformation): void { + const changedObservables = this.changedObservablesSets.get(derived)!; + console.log(...this.textToConsoleArgs([ + formatKind('derived recomputed'), + styled(derived.debugName, { color: 'BlueViolet' }), + ...this.formatInfo(info), + this.formatChanges(changedObservables) + ])); + changedObservables.clear(); + } + + handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void { + console.log(...this.textToConsoleArgs([ + formatKind('observable from event triggered'), + styled(observable.debugName, { color: 'BlueViolet' }), + ...this.formatInfo(info), + ])); + } + + handleAutorunCreated(autorun: AutorunObserver): void { + const existingHandleChange = autorun.handleChange; + this.changedObservablesSets.set(autorun, new Set()); + autorun.handleChange = (observable, change) => { + this.changedObservablesSets.get(autorun)!.add(observable); + return existingHandleChange.apply(autorun, [observable, change]); + }; + } + + handleAutorunTriggered(autorun: AutorunObserver): void { + const changedObservables = this.changedObservablesSets.get(autorun)!; + console.log(...this.textToConsoleArgs([ + formatKind('autorun'), + styled(autorun.debugName, { color: 'BlueViolet' }), + this.formatChanges(changedObservables) + ])); + changedObservables.clear(); + } + + handleBeginTransaction(transaction: TransactionImpl): void { + let transactionName = transaction.getDebugName(); + if (transactionName === undefined) { + transactionName = ''; + } + console.log(...this.textToConsoleArgs([ + formatKind('transaction'), + styled(transactionName, { color: 'BlueViolet' }), + ])); + this.indentation++; + } + + handleEndTransaction(): void { + this.indentation--; + } +} + +type ConsoleText = + | (ConsoleText | undefined)[] + | { text: string; style: string; data?: Record } + | { data: Record }; + +function consoleTextToArgs(text: ConsoleText): unknown[] { + const styles = new Array(); + const initial = {}; + const data = initial; + let firstArg = ''; + + function process(t: ConsoleText): void { + if ('length' in t) { + for (const item of t) { + if (item) { + process(item); + } + } + } else if ('text' in t) { + firstArg += `%c${t.text}`; + styles.push(t.style); + if (t.data) { + Object.assign(data, t.data); + } + } else if ('data' in t) { + Object.assign(data, t.data); + } + } + + process(text); + + const result = [firstArg, ...styles]; + if (Object.keys(data).length > 0) { + result.push(data); + } + + return result; +} + +function normalText(text: string): ConsoleText { + return styled(text, { color: 'black' }); +} + +function formatKind(kind: string): ConsoleText { + return styled(padStr(`${kind}: `, 10), { color: 'black', bold: true }); +} + +function styled( + text: string, + options: { color: string; strikeThrough?: boolean; bold?: boolean } = { + color: 'black', + } +): ConsoleText { + function objToCss(styleObj: Record): string { + return Object.entries(styleObj).reduce( + (styleString, [propName, propValue]) => { + return `${styleString}${propName}:${propValue};`; + }, + '' + ); + } + + const style: Record = { + color: options.color, + }; + if (options.strikeThrough) { + style['text-decoration'] = 'line-through'; + } + if (options.bold) { + style['font-weight'] = 'bold'; + } + + return { + text, + style: objToCss(style), + }; +} + +function formatValue(value: unknown, availableLen: number): string { + switch (typeof value) { + case 'number': + return '' + value; + case 'string': + if (value.length + 2 <= availableLen) { + return `"${value}"`; + } + return `"${value.substr(0, availableLen - 7)}"+...`; + + case 'boolean': + return value ? 'true' : 'false'; + case 'undefined': + return 'undefined'; + case 'object': + if (value === null) { + return 'null'; + } + if (Array.isArray(value)) { + return formatArray(value, availableLen); + } + return formatObject(value, availableLen); + case 'symbol': + return value.toString(); + case 'function': + return `[[Function${value.name ? ' ' + value.name : ''}]]`; + default: + return '' + value; + } +} + +function formatArray(value: unknown[], availableLen: number): string { + let result = '[ '; + let first = true; + for (const val of value) { + if (!first) { + result += ', '; + } + if (result.length - 5 > availableLen) { + result += '...'; + break; + } + first = false; + result += `${formatValue(val, availableLen - result.length)}`; + } + result += ' ]'; + return result; +} + +function formatObject(value: object, availableLen: number): string { + let result = '{ '; + let first = true; + for (const [key, val] of Object.entries(value)) { + if (!first) { + result += ', '; + } + if (result.length - 5 > availableLen) { + result += '...'; + break; + } + first = false; + result += `${key}: ${formatValue(val, availableLen - result.length)}`; + } + result += ' }'; + return result; +} + +function repeat(str: string, count: number): string { + let result = ''; + for (let i = 1; i <= count; i++) { + result += str; + } + return result; +} + +function padStr(str: string, length: number): string { + while (str.length < length) { + str += ' '; + } + return str; +} diff --git a/src/vs/base/common/observableImpl/utils.ts b/src/vs/base/common/observableImpl/utils.ts new file mode 100644 index 0000000000..242594e7eb --- /dev/null +++ b/src/vs/base/common/observableImpl/utils.ts @@ -0,0 +1,281 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { autorun } from 'vs/base/common/observableImpl/autorun'; +import { IObservable, BaseObservable, transaction, IReader, ITransaction, ConvenientObservable, IObserver, observableValue, getFunctionName } from 'vs/base/common/observableImpl/base'; +import { derived } from 'vs/base/common/observableImpl/derived'; +import { Event } from 'vs/base/common/event'; +import { getLogger } from 'vs/base/common/observableImpl/logging'; + +export function constObservable(value: T): IObservable { + return new ConstObservable(value); +} + +class ConstObservable extends ConvenientObservable { + constructor(private readonly value: T) { + super(); + } + + public override get debugName(): string { + return this.toString(); + } + + public get(): T { + return this.value; + } + public addObserver(observer: IObserver): void { + // NO OP + } + public removeObserver(observer: IObserver): void { + // NO OP + } + + override toString(): string { + return `Const: ${this.value}`; + } +} + + +export function observableFromPromise(promise: Promise): IObservable<{ value?: T }> { + const observable = observableValue<{ value?: T }>('promiseValue', {}); + promise.then((value) => { + observable.set({ value }, undefined); + }); + return observable; +} + +export function waitForState(observable: IObservable, predicate: (state: T) => state is TState): Promise; +export function waitForState(observable: IObservable, predicate: (state: T) => boolean): Promise; +export function waitForState(observable: IObservable, predicate: (state: T) => boolean): Promise { + return new Promise(resolve => { + const d = autorun('waitForState', reader => { + const currentState = observable.read(reader); + if (predicate(currentState)) { + d.dispose(); + resolve(currentState); + } + }); + }); +} + +export function observableFromEvent( + event: Event, + getValue: (args: TArgs | undefined) => T +): IObservable { + return new FromEventObservable(event, getValue); +} + +export class FromEventObservable extends BaseObservable { + private value: T | undefined; + private hasValue = false; + private subscription: IDisposable | undefined; + + constructor( + private readonly event: Event, + private readonly getValue: (args: TArgs | undefined) => T + ) { + super(); + } + + private getDebugName(): string | undefined { + return getFunctionName(this.getValue); + } + + public get debugName(): string { + const name = this.getDebugName(); + return 'From Event' + (name ? `: ${name}` : ''); + } + + protected override onFirstObserverAdded(): void { + this.subscription = this.event(this.handleEvent); + } + + private readonly handleEvent = (args: TArgs | undefined) => { + const newValue = this.getValue(args); + + const didChange = this.value !== newValue; + + getLogger()?.handleFromEventObservableTriggered(this, { oldValue: this.value, newValue, change: undefined, didChange }); + + if (didChange) { + this.value = newValue; + + if (this.hasValue) { + transaction( + (tx) => { + for (const o of this.observers) { + tx.updateObserver(o, this); + o.handleChange(this, undefined); + } + }, + () => { + const name = this.getDebugName(); + return 'Event fired' + (name ? `: ${name}` : ''); + } + ); + } + this.hasValue = true; + } + }; + + protected override onLastObserverRemoved(): void { + this.subscription!.dispose(); + this.subscription = undefined; + this.hasValue = false; + this.value = undefined; + } + + public get(): T { + if (this.subscription) { + if (!this.hasValue) { + this.handleEvent(undefined); + } + return this.value!; + } else { + // no cache, as there are no subscribers to keep it updated + return this.getValue(undefined); + } + } +} + +export namespace observableFromEvent { + export const Observer = FromEventObservable; +} + +export function observableSignalFromEvent( + debugName: string, + event: Event +): IObservable { + return new FromEventObservableSignal(debugName, event); +} + +class FromEventObservableSignal extends BaseObservable { + private subscription: IDisposable | undefined; + + constructor( + public readonly debugName: string, + private readonly event: Event, + ) { + super(); + } + + protected override onFirstObserverAdded(): void { + this.subscription = this.event(this.handleEvent); + } + + private readonly handleEvent = () => { + transaction( + (tx) => { + for (const o of this.observers) { + tx.updateObserver(o, this); + o.handleChange(this, undefined); + } + }, + () => this.debugName + ); + }; + + protected override onLastObserverRemoved(): void { + this.subscription!.dispose(); + this.subscription = undefined; + } + + public override get(): void { + // NO OP + } +} + +export function debouncedObservable(observable: IObservable, debounceMs: number, disposableStore: DisposableStore): IObservable { + const debouncedObservable = observableValue('debounced', undefined); + + let timeout: any = undefined; + + disposableStore.add(autorun('debounce', reader => { + const value = observable.read(reader); + + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + transaction(tx => { + debouncedObservable.set(value, tx); + }); + }, debounceMs); + + })); + + return debouncedObservable; +} + +export function wasEventTriggeredRecently(event: Event, timeoutMs: number, disposableStore: DisposableStore): IObservable { + const observable = observableValue('triggeredRecently', false); + + let timeout: any = undefined; + + disposableStore.add(event(() => { + observable.set(true, undefined); + + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + observable.set(false, undefined); + }, timeoutMs); + })); + + return observable; +} + +/** + * This ensures the observable is kept up-to-date. + * This is useful when the observables `get` method is used. +*/ +export function keepAlive(observable: IObservable): IDisposable { + const o = new KeepAliveObserver(); + observable.addObserver(o); + return toDisposable(() => { + observable.removeObserver(o); + }); +} + +class KeepAliveObserver implements IObserver { + beginUpdate(observable: IObservable): void { + // NO OP + } + + handleChange(observable: IObservable, change: TChange): void { + // NO OP + } + + endUpdate(observable: IObservable): void { + // NO OP + } +} + +export function derivedObservableWithCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable { + let lastValue: T | undefined = undefined; + const observable = derived(name, reader => { + lastValue = computeFn(reader, lastValue); + return lastValue; + }); + return observable; +} + +export function derivedObservableWithWritableCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable & { clearCache(transaction: ITransaction): void } { + let lastValue: T | undefined = undefined; + const counter = observableValue('derivedObservableWithWritableCache.counter', 0); + const observable = derived(name, reader => { + counter.read(reader); + lastValue = computeFn(reader, lastValue); + return lastValue; + }); + return Object.assign(observable, { + clearCache: (transaction: ITransaction) => { + lastValue = undefined; + counter.set(counter.get() + 1, transaction); + }, + }); +} diff --git a/src/vs/base/common/observableValue.ts b/src/vs/base/common/observableValue.ts index 9c2722f472..5d7130bb43 100644 --- a/src/vs/base/common/observableValue.ts +++ b/src/vs/base/common/observableValue.ts @@ -5,17 +5,28 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +//@ts-ignore +import type { IObservable } from 'vs/base/common/observable'; +/** + * @deprecated Use {@link IObservable} instead. + */ export interface IObservableValue { onDidChange: Event; readonly value: T; } +/** + * @deprecated Use {@link IObservable} instead. + */ export const staticObservableValue = (value: T): IObservableValue => ({ onDidChange: Event.None, value, }); +/** + * @deprecated Use {@link IObservable} instead. + */ export class MutableObservableValue extends Disposable implements IObservableValue { private readonly changeEmitter = this._register(new Emitter()); diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 7d59bd1fa2..e3b2cc6b66 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; const LANGUAGE_DEFAULT = 'en'; @@ -67,7 +68,6 @@ const isElectronRenderer = isElectronProcess && nodeProcess?.type === 'renderer' interface INavigator { userAgent: string; - language: string; maxTouchPoints?: number; } declare const navigator: INavigator; @@ -80,7 +80,17 @@ if (typeof navigator === 'object' && !isElectronRenderer) { _isIOS = (_userAgent.indexOf('Macintosh') >= 0 || _userAgent.indexOf('iPad') >= 0 || _userAgent.indexOf('iPhone') >= 0) && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0; _isLinux = _userAgent.indexOf('Linux') >= 0; _isWeb = true; - _locale = navigator.language; + + const configuredLocale = nls.getConfiguredDefaultLocale( + // This call _must_ be done in the file that calls `nls.getConfiguredDefaultLocale` + // to ensure that the NLS AMD Loader plugin has been loaded and configured. + // This is because the loader plugin decides what the default locale is based on + // how it's able to resolve the strings. + nls.localize({ key: 'ensureLoaderPluginIsLoaded', comment: ['{Locked}'] }, '_') + ); + + _locale = configuredLocale || LANGUAGE_DEFAULT; + _language = _locale; } @@ -195,6 +205,8 @@ export const locale = _locale; */ export const translationsConfigFile = _translationsConfigFile; +export const setTimeout0IsFaster = (typeof globals.postMessage === 'function' && !globals.importScripts); + /** * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-. * @@ -202,12 +214,12 @@ export const translationsConfigFile = _translationsConfigFile; * that browsers set when the nesting level is > 5. */ export const setTimeout0 = (() => { - if (typeof globals.postMessage === 'function' && !globals.importScripts) { + if (setTimeout0IsFaster) { interface IQueueElement { id: number; callback: () => void; } - let pending: IQueueElement[] = []; + const pending: IQueueElement[] = []; globals.addEventListener('message', (e: MessageEvent) => { if (e.data && e.data.vscodeScheduleAsyncWork) { for (let i = 0, len = pending.length; i < len; i++) { diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index c01921a772..2ce50e78fa 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -71,9 +71,11 @@ export interface IProductConfiguration { readonly extensionsGallery?: { readonly serviceUrl: string; readonly itemUrl: string; + readonly publisherUrl: string; readonly resourceUrlTemplate: string; readonly controlUrl: string; readonly recommendationsUrl: string; + readonly nlsBaseUrl: string; }; readonly extensionTips?: { [id: string]: string }; @@ -158,6 +160,8 @@ export interface IProductConfiguration { readonly 'configurationSync.store'?: ConfigurationSyncStore; + readonly 'editSessions.store'?: Omit; + readonly darwinUniversalAssetId?: string; } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index b9297ac9ca..b968ad9aca 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -240,7 +240,8 @@ export class ExtUri implements IExtUri { const relativePath = paths.relative(originalFSPath(from), originalFSPath(to)); return isWindows ? extpath.toSlashes(relativePath) : relativePath; } - let fromPath = from.path || '/', toPath = to.path || '/'; + let fromPath = from.path || '/'; + const toPath = to.path || '/'; if (this._ignorePathCasing(from)) { // make casing of fromPath match toPath let i = 0; diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index 0334b613bd..576d463636 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -268,9 +268,7 @@ export class Scrollable extends Disposable { this._setState(newState, Boolean(this._smoothScrolling)); // Validate outstanding animated scroll position target - if (this._smoothScrolling) { - this._smoothScrolling.acceptScrollDimensions(this._state); - } + this._smoothScrolling?.acceptScrollDimensions(this._state); } /** diff --git a/src/vs/base/common/skipList.ts b/src/vs/base/common/skipList.ts index 11cc8e3790..7de346c676 100644 --- a/src/vs/base/common/skipList.ts +++ b/src/vs/base/common/skipList.ts @@ -135,7 +135,7 @@ export class SkipList implements Map { } private static _insert(list: SkipList, searchKey: K, value: V, comparator: Comparator) { - let update: Node[] = []; + const update: Node[] = []; let x = list._header; for (let i = list._level - 1; i >= 0; i--) { while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { @@ -150,7 +150,7 @@ export class SkipList implements Map { return false; } else { // insert - let lvl = SkipList._randomLevel(list); + const lvl = SkipList._randomLevel(list); if (lvl > list._level) { for (let i = list._level; i < lvl; i++) { update[i] = list._header; @@ -175,7 +175,7 @@ export class SkipList implements Map { } private static _delete(list: SkipList, searchKey: K, comparator: Comparator) { - let update: Node[] = []; + const update: Node[] = []; let x = list._header; for (let i = list._level - 1; i >= 0; i--) { while (x.forward[i] && comparator(x.forward[i].key, searchKey) < 0) { diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index ac9bd17c11..6d8d9e06fc 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -279,7 +279,7 @@ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.len * replace function is allowed to be async and return a Promise. */ export function replaceAsync(str: string, search: RegExp, replacer: (match: string, ...args: any[]) => Promise): Promise { - let parts: (string | Promise)[] = []; + const parts: (string | Promise)[] = []; let last = 0; for (const match of str.matchAll(search)) { @@ -309,8 +309,8 @@ export function compare(a: string, b: string): number { export function compareSubstring(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) { - let codeA = a.charCodeAt(aStart); - let codeB = b.charCodeAt(bStart); + const codeA = a.charCodeAt(aStart); + const codeB = b.charCodeAt(bStart); if (codeA < codeB) { return -1; } else if (codeA > codeB) { @@ -378,6 +378,10 @@ export function compareSubstringIgnoreCase(a: string, b: string, aStart: number return 0; } +export function isAsciiDigit(code: number): boolean { + return code >= CharCode.Digit0 && code <= CharCode.Digit9; +} + export function isLowerAsciiLetter(code: number): boolean { return code >= CharCode.a && code <= CharCode.z; } @@ -404,8 +408,8 @@ export function startsWithIgnoreCase(str: string, candidate: string): boolean { */ export function commonPrefixLength(a: string, b: string): number { - let i: number, - len = Math.min(a.length, b.length); + const len = Math.min(a.length, b.length); + let i: number; for (i = 0; i < len; i++) { if (a.charCodeAt(i) !== b.charCodeAt(i)) { @@ -421,8 +425,8 @@ export function commonPrefixLength(a: string, b: string): number { */ export function commonSuffixLength(a: string, b: string): number { - let i: number, - len = Math.min(a.length, b.length); + const len = Math.min(a.length, b.length); + let i: number; const aLastIndex = a.length - 1; const bLastIndex = b.length - 1; diff --git a/src/vs/base/common/stripComments.js b/src/vs/base/common/stripComments.js index 3c9dd0c9d2..d580a397d9 100644 --- a/src/vs/base/common/stripComments.js +++ b/src/vs/base/common/stripComments.js @@ -13,7 +13,8 @@ // Second group matches a single quoted string // Third group matches a multi line comment // Forth group matches a single line comment - const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + // Fifth group matches a trailing comma + const regexp = /("[^"\\]*(?:\\.[^"\\]*)*")|('[^'\\]*(?:\\.[^'\\]*)*')|(\/\*[^\/\*]*(?:(?:\*|\/)[^\/\*]*)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))|(,\s*[}\]])/g; /** * @@ -21,8 +22,8 @@ * @returns {string} */ function stripComments(content) { - return content.replace(regexp, function (match, _m1, _m2, m3, m4) { - // Only one of m1, m2, m3, m4 matches + return content.replace(regexp, function (match, _m1, _m2, m3, m4, m5) { + // Only one of m1, m2, m3, m4, m5 matches if (m3) { // A block comment. Replace with nothing return ''; @@ -36,6 +37,9 @@ else { return ''; } + } else if (m5) { + // Remove the trailing comma + return match.substring(1); } else { // We match a string return match; diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index a6dea79439..e073ea9c8b 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -47,18 +47,9 @@ export function isObject(obj: unknown): obj is Object { * @returns whether the provided parameter is of type `Buffer` or Uint8Array dervived type */ export function isTypedArray(obj: unknown): obj is Object { + const TypedArray = Object.getPrototypeOf(Uint8Array); return typeof obj === 'object' - && (obj instanceof Uint8Array || - obj instanceof Uint16Array || - obj instanceof Uint32Array || - obj instanceof Float32Array || - obj instanceof Float64Array || - obj instanceof Int8Array || - obj instanceof Int16Array || - obj instanceof Int32Array || - obj instanceof BigInt64Array || - obj instanceof BigUint64Array || - obj instanceof Uint8ClampedArray); + && obj instanceof TypedArray; } /** @@ -154,7 +145,7 @@ export function isEmptyObject(obj: unknown): obj is object { return false; } - for (let key in obj) { + for (const key in obj) { if (hasOwnProperty.call(obj, key)) { return false; } @@ -238,7 +229,7 @@ export function createProxyObject(methodNames: string[], invok }; }; - let result = {} as T; + const result = {} as T; for (const methodName of methodNames) { (result)[methodName] = createProxyMethod(methodName); } diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 7acf449519..04ca2ffb49 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -90,7 +90,7 @@ function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: n } // walk object (or array) - for (let key in obj) { + for (const key in obj) { if (Object.hasOwnProperty.call(obj, key)) { const r = _transformOutgoingURIs(obj[key], transformer, depth + 1); if (r !== null) { @@ -126,7 +126,7 @@ function _transformIncomingURIs(obj: any, transformer: IURITransformer, revive: } // walk object (or array) - for (let key in obj) { + for (const key in obj) { if (Object.hasOwnProperty.call(obj, key)) { const r = _transformIncomingURIs(obj[key], transformer, revive, depth + 1); if (r !== null) { diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 95acf8263d..a9a1b1ff1d 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -181,7 +181,7 @@ class SimpleWorkerProtocol { return; } - let reply = this._pendingReplies[replyMessage.seq]; + const reply = this._pendingReplies[replyMessage.seq]; delete this._pendingReplies[replyMessage.seq]; if (replyMessage.err) { @@ -200,8 +200,8 @@ class SimpleWorkerProtocol { } private _handleRequestMessage(requestMessage: RequestMessage): void { - let req = requestMessage.req; - let result = this._handler.handleMessage(requestMessage.method, requestMessage.args); + const req = requestMessage.req; + const result = this._handler.handleMessage(requestMessage.method, requestMessage.args); result.then((r) => { this._send(new ReplyMessage(this._workerId, req, r, undefined)); }, (e) => { @@ -239,7 +239,7 @@ class SimpleWorkerProtocol { } private _send(msg: Message): void { - let transfer: ArrayBuffer[] = []; + const transfer: ArrayBuffer[] = []; if (msg.type === MessageType.Request) { for (let i = 0; i < msg.args.length; i++) { if (msg.args[i] instanceof ArrayBuffer) { @@ -283,9 +283,7 @@ export class SimpleWorkerClient extends Disp (err: any) => { // in Firefox, web workers fail lazily :( // we will reject the proxy - if (lazyProxyReject) { - lazyProxyReject(err); - } + lazyProxyReject?.(err); } )); @@ -408,7 +406,7 @@ function createProxyObject( }; }; - let result = {} as T; + const result = {} as T; for (const methodName of methodNames) { if (propertyIsDynamicEvent(methodName)) { (result)[methodName] = createProxyDynamicEvent(methodName); diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index e7ccc471c7..e16801508c 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -55,7 +55,7 @@ export const virtualMachineHint: { value(): number } = new class { let interfaceCount = 0; const interfaces = networkInterfaces(); - for (let name in interfaces) { + for (const name in interfaces) { const networkInterface = interfaces[name]; if (networkInterface) { for (const { mac, internal } of networkInterface) { diff --git a/src/vs/base/node/macAddress.ts b/src/vs/base/node/macAddress.ts index bca52855b8..7e6cf526ad 100644 --- a/src/vs/base/node/macAddress.ts +++ b/src/vs/base/node/macAddress.ts @@ -18,7 +18,7 @@ function validateMacAddress(candidate: string): boolean { export function getMac(): string { const ifaces = networkInterfaces(); - for (let name in ifaces) { + for (const name in ifaces) { const networkInterface = ifaces[name]; if (networkInterface) { for (const { mac } of networkInterface) { diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 164a3fbe80..b4edc7d8af 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -390,6 +390,9 @@ interface IEnsuredWriteFileOptions extends IWriteFileOptions { } let canFlush = true; +export function configureFlushOnWrite(enabled: boolean): void { + canFlush = enabled; +} // Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk // We do this in cases where we want to make sure the data is really on disk and @@ -421,7 +424,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o // In that case we disable flushing and warn to the console if (syncError) { console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError); - canFlush = false; + configureFlushOnWrite(false); } return fs.close(fd, closeError => callback(closeError)); @@ -455,7 +458,7 @@ export function writeFileSync(path: string, data: string | Buffer, options?: IWr fs.fdatasyncSync(fd); // https://github.com/microsoft/vscode/issues/9589 } catch (syncError) { console.warn('[node.js fs] fdatasyncSync is now disabled for this session because it failed: ', syncError); - canFlush = false; + configureFlushOnWrite(false); } } finally { fs.closeSync(fd); diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 9831d195fc..0e2f57739a 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -474,7 +474,7 @@ export namespace win32 { // We have a simple file name. We get the path variable from the env // and try to find the executable on the path. - for (let pathEntry of paths) { + for (const pathEntry of paths) { // The path entry is absolute. let fullPath: string; if (path.isAbsolute(pathEntry)) { diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index f1dd66a2e2..9d29195666 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -52,6 +52,7 @@ export function listProcesses(rootPid: number): Promise { const ISSUE_REPORTER_HINT = /--vscode-window-kind=issue-reporter/; const PROCESS_EXPLORER_HINT = /--vscode-window-kind=process-explorer/; const UTILITY_NETWORK_HINT = /--utility-sub-type=network/; + const UTILITY_EXTENSION_HOST_HINT = /--utility-sub-type=node.mojom.NodeService/; const WINDOWS_CRASH_REPORTER = /--crashes-directory/; const WINDOWS_PTY = /\\pipe\\winpty-control/; const WINDOWS_CONSOLE_HOST = /conhost\.exe/; @@ -93,6 +94,10 @@ export function listProcesses(rootPid: number): Promise { if (UTILITY_NETWORK_HINT.exec(cmd)) { return 'utility-network-service'; } + + if (UTILITY_EXTENSION_HOST_HINT.exec(cmd)) { + return 'extension-host'; + } } return matches[1]; } diff --git a/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts index 377b8899d9..2cf545ae34 100644 --- a/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts @@ -15,9 +15,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide const onClickChannel = `vscode:onContextMenu${contextMenuId}`; const onClickChannelHandler = (event: unknown, itemId: number, context: IContextMenuEvent) => { const item = processedItems[itemId]; - if (item.click) { - item.click(context); - } + item.click?.(context); }; ipcRenderer.once(onClickChannel, onClickChannelHandler); @@ -28,9 +26,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); - if (onHide) { - onHide(); - } + onHide?.(); }); ipcRenderer.send(CONTEXT_MENU_CHANNEL, contextMenuId, items.map(item => createItem(item, processedItems)), onClickChannel, options); diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 89c6409ac8..d6833ae6ed 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -212,7 +212,7 @@ export class ChunkStream { return result; } - let result = VSBuffer.alloc(byteCount); + const result = VSBuffer.alloc(byteCount); let resultOffset = 0; let chunkIndex = 0; while (byteCount > 0) { @@ -675,7 +675,8 @@ class Queue { } public toArray(): T[] { - let result: T[] = [], resultLen = 0; + const result: T[] = []; + let resultLen = 0; let it = this._first; while (it) { result[resultLen++] = it.data; diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 3f8db83855..69e3ecaefb 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -640,18 +640,14 @@ export class ChannelClient implements IChannelClient, IDisposable { case RequestType.Promise: case RequestType.EventListen: { const msgLength = this.send([request.type, request.id, request.channelName, request.name], request.arg); - if (this.logger) { - this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); - } + this.logger?.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); return; } case RequestType.PromiseCancel: case RequestType.EventDispose: { const msgLength = this.send([request.type, request.id]); - if (this.logger) { - this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); - } + this.logger?.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); return; } } @@ -682,18 +678,14 @@ export class ChannelClient implements IChannelClient, IDisposable { switch (type) { case ResponseType.Initialize: - if (this.logger) { - this.logger.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); - } + this.logger?.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); return this.onResponse({ type: header[0] }); case ResponseType.PromiseSuccess: case ResponseType.PromiseError: case ResponseType.EventFire: case ResponseType.PromiseErrorObj: - if (this.logger) { - this.logger.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); - } + this.logger?.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); return this.onResponse({ type: header[0], id: header[1], data: body }); } } @@ -707,9 +699,7 @@ export class ChannelClient implements IChannelClient, IDisposable { const handler = this.handlers.get(response.id); - if (handler) { - handler(response); - } + handler?.(response); } @memoize @@ -816,7 +806,7 @@ export class IPCServer implements IChannelServer, I if (isFunction(routerOrClientFilter)) { // when no router is provided, we go random client picking - let connection = getRandomElement(that.connections.filter(routerOrClientFilter)); + const connection = getRandomElement(that.connections.filter(routerOrClientFilter)); connectionPromise = connection // if we found a client, let's call on it @@ -1181,7 +1171,7 @@ function prettyWithoutArrays(data: any): any { return data; } if (data && typeof data === 'object' && typeof data.toString === 'function') { - let result = data.toString(); + const result = data.toString(); if (result !== '[object Object]') { return result; } diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 47e0b55426..11751a790c 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -26,9 +26,7 @@ export class Server extends IPCServer { super({ send: r => { try { - if (process.send) { - process.send((r.buffer).toString('base64')); - } + process.send?.((r.buffer).toString('base64')); } catch (e) { /* not much to do */ } }, onMessage: Event.fromNodeEventEmitter(process, 'message', msg => VSBuffer.wrap(Buffer.from(msg, 'base64'))) diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index dc62e445e1..33331ff7e7 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -3,9 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createHash } from 'crypto'; -import { createConnection, createServer, Server as NetServer, Socket } from 'net'; -import { tmpdir } from 'os'; +// import { createHash } from 'crypto'; +import type { Server as NetServer, Socket } from 'net'; +// import { tmpdir } from 'os'; +import type * as zlib from 'zlib'; import { VSBuffer } from 'vs/base/common/buffer'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -15,7 +16,16 @@ import { Platform, platform } from 'vs/base/common/platform'; import { generateUuid } from 'vs/base/common/uuid'; import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc'; import { ChunkStream, Client, ISocket, Protocol, SocketCloseEvent, SocketCloseEventType, SocketDiagnostics, SocketDiagnosticsEventType } from 'vs/base/parts/ipc/common/ipc.net'; -import * as zlib from 'zlib'; + +// TODO@bpasero remove me once electron utility process has landed +function getNodeDependencies() { + return { + crypto: (require.__$__nodeRequire('crypto') as any) as typeof import('crypto'), + zlib: (require.__$__nodeRequire('zlib') as any) as typeof import('zlib'), + net: (require.__$__nodeRequire('net') as any) as typeof import('net'), + os: (require.__$__nodeRequire('os') as any) as typeof import('os') + }; +} export class NodeSocket implements ISocket { @@ -580,7 +590,7 @@ class ZlibInflateStream extends Disposable { options: zlib.ZlibOptions ) { super(); - this._zlibInflate = zlib.createInflateRaw(options); + this._zlibInflate = getNodeDependencies().zlib.createInflateRaw(options); this._zlibInflate.on('error', (err) => { this._tracer.traceSocketEvent(SocketDiagnosticsEventType.zlibInflateError, { message: err?.message, code: (err)?.code }); this._onError.fire(err); @@ -631,7 +641,7 @@ class ZlibDeflateStream extends Disposable { ) { super(); - this._zlibDeflate = zlib.createDeflateRaw({ + this._zlibDeflate = getNodeDependencies().zlib.createDeflateRaw({ windowBits: 15 }); this._zlibDeflate.on('error', (err) => { @@ -669,13 +679,13 @@ function unmask(buffer: VSBuffer, mask: number): void { if (mask === 0) { return; } - let cnt = buffer.byteLength >>> 2; + const cnt = buffer.byteLength >>> 2; for (let i = 0; i < cnt; i++) { const v = buffer.readUInt32BE(i * 4); buffer.writeUInt32BE(v ^ mask, i * 4); } - let offset = cnt * 4; - let bytesLeft = buffer.byteLength - offset; + const offset = cnt * 4; + const bytesLeft = buffer.byteLength - offset; const m3 = (mask >>> 24) & 0b11111111; const m2 = (mask >>> 16) & 0b11111111; const m1 = (mask >>> 8) & 0b11111111; @@ -692,7 +702,8 @@ function unmask(buffer: VSBuffer, mask: number): void { // Read this before there's any chance it is overwritten // Related to https://github.com/microsoft/vscode/issues/30624 -export const XDG_RUNTIME_DIR = process.env['XDG_RUNTIME_DIR']; +// TODO@bpasero revert me once electron utility process has landed +export const XDG_RUNTIME_DIR = typeof process !== 'undefined' ? process.env['XDG_RUNTIME_DIR'] : undefined; const safeIpcPathLengths: { [platform: number]: number } = { [Platform.Linux]: 107, @@ -713,7 +724,7 @@ export function createRandomIPCHandle(): string { if (XDG_RUNTIME_DIR) { result = join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`); } else { - result = join(tmpdir(), `vscode-ipc-${randomSuffix}.sock`); + result = join(getNodeDependencies().os.tmpdir(), `vscode-ipc-${randomSuffix}.sock`); } // Validate length @@ -723,7 +734,7 @@ export function createRandomIPCHandle(): string { } export function createStaticIPCHandle(directoryPath: string, type: string, version: string): string { - const scope = createHash('md5').update(directoryPath).digest('hex'); + const scope = getNodeDependencies().crypto.createHash('md5').update(directoryPath).digest('hex'); // Windows: use named pipe if (process.platform === 'win32') { @@ -785,7 +796,7 @@ export function serve(port: number): Promise; export function serve(namedPipe: string): Promise; export function serve(hook: any): Promise { return new Promise((c, e) => { - const server = createServer(); + const server = getNodeDependencies().net.createServer(); server.on('error', e); server.listen(hook, () => { @@ -800,7 +811,7 @@ export function connect(port: number, clientId: string): Promise; export function connect(namedPipe: string, clientId: string): Promise; export function connect(hook: any, clientId: string): Promise { return new Promise((c, e) => { - const socket = createConnection(hook, () => { + const socket = getNodeDependencies().net.createConnection(hook, () => { socket.removeListener('error', e); c(Client.fromSocket(new NodeSocket(socket, `ipc-client${clientId}`), clientId)); }); diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index 1cfc8bf506..6bcfb8e1e5 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -6,10 +6,10 @@ .quick-input-widget { position: absolute; width: 600px; - z-index: 2000; - padding: 0 1px 1px 1px; + z-index: 2550; left: 50%; margin-left: -300px; + -webkit-app-region: no-drag; } .quick-input-titlebar { @@ -150,6 +150,7 @@ .quick-input-list { line-height: 22px; margin-top: 6px; + padding: 0px 1px 1px 1px; } .quick-input-widget.hidden-input .quick-input-list { diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 3be9cd4417..8ca3721f8f 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -450,6 +450,7 @@ class QuickPick extends QuickInput implements IQuickPi private _matchOnDescription = false; private _matchOnDetail = false; private _matchOnLabel = true; + private _matchOnLabelMode: 'fuzzy' | 'contiguous' = 'fuzzy'; private _sortByLabel = true; private _autoFocusOnList = true; private _keepScrollPosition = false; @@ -595,6 +596,15 @@ class QuickPick extends QuickInput implements IQuickPi this.update(); } + get matchOnLabelMode() { + return this._matchOnLabelMode; + } + + set matchOnLabelMode(matchOnLabelMode: 'fuzzy' | 'contiguous') { + this._matchOnLabelMode = matchOnLabelMode; + this.update(); + } + get sortByLabel() { return this._sortByLabel; } @@ -979,13 +989,22 @@ class QuickPick extends QuickInput implements IQuickPi if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { this.ui.inputBox.placeholder = (this.placeholder || ''); } - const ariaLabel = this.ariaLabel || this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; + + let ariaLabel = this.ariaLabel; + if (!ariaLabel) { + ariaLabel = this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; + // If we have a title, include it in the aria label. + if (this.title) { + ariaLabel += ` - ${this.title}`; + } + } if (this.ui.inputBox.ariaLabel !== ariaLabel) { this.ui.inputBox.ariaLabel = ariaLabel; } this.ui.list.matchOnDescription = this.matchOnDescription; this.ui.list.matchOnDetail = this.matchOnDetail; this.ui.list.matchOnLabel = this.matchOnLabel; + this.ui.list.matchOnLabelMode = this.matchOnLabelMode; this.ui.list.sortByLabel = this.sortByLabel; if (this.itemsUpdated) { this.itemsUpdated = false; @@ -1233,6 +1252,7 @@ export class QuickInputController extends Disposable { const checkAll = dom.append(headerContainer, $('input.quick-input-check-all')); checkAll.type = 'checkbox'; + checkAll.setAttribute('aria-label', localize('quickInput.checkAll', "Toggle all checkboxes")); this._register(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => { const checked = checkAll.checked; list.setAllVisibleChecked(checked); @@ -1397,9 +1417,7 @@ export class QuickInputController extends Disposable { return new Promise((doResolve, reject) => { let resolve = (result: R) => { resolve = doResolve; - if (options.onKeyMods) { - options.onKeyMods(input.keyMods); - } + options.onKeyMods?.(input.keyMods); doResolve(result); }; if (token.isCancellationRequested) { diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts index df4b425cc6..687eb13ad8 100644 --- a/src/vs/base/parts/quickinput/browser/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -17,10 +17,11 @@ import { compareAnything } from 'vs/base/common/comparers'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { IMatch } from 'vs/base/common/filters'; -import { matchesFuzzyIconAware, parseLabelWithIcons } from 'vs/base/common/iconLabels'; +import { IParsedLabelWithIcons, matchesFuzzyIconAware, parseLabelWithIcons } from 'vs/base/common/iconLabels'; import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; +import { ltrim } from 'vs/base/common/strings'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'; import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; @@ -35,6 +36,7 @@ interface IListElement { readonly index: number; readonly item: IQuickPickItem; readonly saneLabel: string; + readonly saneSortLabel: string; readonly saneMeta?: string; readonly saneAriaLabel: string; readonly saneDescription?: string; @@ -52,6 +54,7 @@ class ListElement implements IListElement, IDisposable { index!: number; item!: IQuickPickItem; saneLabel!: string; + saneSortLabel!: string; saneMeta!: string; saneAriaLabel!: string; saneDescription?: string; @@ -256,6 +259,7 @@ export class QuickInputList { matchOnDescription = false; matchOnDetail = false; matchOnLabel = true; + matchOnLabelMode: 'fuzzy' | 'contiguous' = 'fuzzy'; matchOnMeta = true; sortByLabel = true; private readonly _onChangedAllVisibleChecked = new Emitter(); @@ -440,6 +444,7 @@ export class QuickInputList { if (item.type !== 'separator') { const previous = index && inputElements[index - 1]; const saneLabel = item.label && item.label.replace(/\r?\n/g, ' '); + const saneSortLabel = parseLabelWithIcons(saneLabel).text.trim(); const saneMeta = item.meta && item.meta.replace(/\r?\n/g, ' '); const saneDescription = item.description && item.description.replace(/\r?\n/g, ' '); const saneDetail = item.detail && item.detail.replace(/\r?\n/g, ' '); @@ -454,6 +459,7 @@ export class QuickInputList { index, item, saneLabel, + saneSortLabel, saneMeta, saneAriaLabel, saneDescription, @@ -606,6 +612,8 @@ export class QuickInputList { this.list.layout(); return false; } + + const queryWithWhitespace = query; query = query.trim(); // Reset filtering @@ -624,7 +632,12 @@ export class QuickInputList { else { let currentSeparator: IQuickPickSeparator | undefined; this.elements.forEach(element => { - const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneLabel))) : undefined; + let labelHighlights: IMatch[] | undefined; + if (this.matchOnLabelMode === 'fuzzy') { + labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneLabel))) : undefined; + } else { + labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesContiguousIconAware(queryWithWhitespace, parseLabelWithIcons(element.saneLabel))) : undefined; + } const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneDescription || ''))) : undefined; const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneDetail || ''))) : undefined; const metaHighlights = this.matchOnMeta ? withNullAsUndefined(matchesFuzzyIconAware(query, parseLabelWithIcons(element.saneMeta || ''))) : undefined; @@ -722,6 +735,43 @@ export class QuickInputList { } } +export function matchesContiguousIconAware(query: string, target: IParsedLabelWithIcons): IMatch[] | null { + + const { text, iconOffsets } = target; + + // Return early if there are no icon markers in the word to match against + if (!iconOffsets || iconOffsets.length === 0) { + return matchesContiguous(query, text); + } + + // Trim the word to match against because it could have leading + // whitespace now if the word started with an icon + const wordToMatchAgainstWithoutIconsTrimmed = ltrim(text, ' '); + const leadingWhitespaceOffset = text.length - wordToMatchAgainstWithoutIconsTrimmed.length; + + // match on value without icon + const matches = matchesContiguous(query, wordToMatchAgainstWithoutIconsTrimmed); + + // Map matches back to offsets with icon and trimming + if (matches) { + for (const match of matches) { + const iconOffset = iconOffsets[match.start + leadingWhitespaceOffset] /* icon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */; + match.start += iconOffset; + match.end += iconOffset; + } + } + + return matches; +} + +function matchesContiguous(word: string, wordToMatchAgainst: string): IMatch[] | null { + const matchIndex = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase()); + if (matchIndex !== -1) { + return [{ start: matchIndex, end: matchIndex + word.length }]; + } + return null; +} + function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number { const labelHighlightsA = elementA.labelHighlights || []; @@ -738,7 +788,7 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s return 0; } - return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor); + return compareAnything(elementA.saneSortLabel, elementB.saneSortLabel, lookFor); } class QuickInputAccessibilityProvider implements IListAccessibilityProvider { @@ -748,7 +798,9 @@ class QuickInputAccessibilityProvider implements IListAccessibilityProvider extends IQuickInput { matchOnLabel: boolean; + /** + * The mode to filter label with. Fuzzy will use fuzzy searching and + * contiguous will make filter entries that do not contain the exact string + * (including whitespace). This defaults to `'fuzzy'`. + */ + matchOnLabelMode: 'fuzzy' | 'contiguous'; + sortByLabel: boolean; autoFocusOnList: boolean; diff --git a/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts b/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts index a1a6962b4d..d968ba648e 100644 --- a/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts +++ b/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts @@ -6,18 +6,26 @@ import * as assert from 'assert'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListOptions, List } from 'vs/base/browser/ui/list/listWidget'; +import { raceTimeout } from 'vs/base/common/async'; import { QuickInputController } from 'vs/base/parts/quickinput/browser/quickInput'; import { IQuickPick, IQuickPickItem } from 'vs/base/parts/quickinput/common/quickInput'; -import { flakySuite } from 'vs/base/test/common/testUtils'; -// Simple promisify of setTimeout -function wait(delayMS: number) { - return new Promise(function (resolve) { - setTimeout(resolve, delayMS); - }); +// Sets up an `onShow` listener to allow us to wait until the quick pick is shown (useful when triggering an `accept()` right after launching a quick pick) +// kick this off before you launch the picker and then await the promise returned after you launch the picker. +async function setupWaitTilShownListener(controller: QuickInputController): Promise { + const result = await raceTimeout(new Promise(resolve => { + const event = controller.onShow(_ => { + event.dispose(); + resolve(true); + }); + }), 2000); + + if (!result) { + throw new Error('Cancelled'); + } } -flakySuite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 +suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 let fixture: HTMLElement, controller: QuickInputController, quickpick: IQuickPick; function getScrollTop(): number { @@ -66,30 +74,38 @@ flakySuite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/1 test('pick - basecase', async () => { const item = { label: 'foo' }; + + const wait = setupWaitTilShownListener(controller); const pickPromise = controller.pick([item, { label: 'bar' }]); - // wait a bit to let the pick get set up. - await wait(200); + await wait; + controller.accept(); const pick = await pickPromise; + assert.strictEqual(pick, item); }); test('pick - activeItem is honored', async () => { const item = { label: 'foo' }; + + const wait = setupWaitTilShownListener(controller); const pickPromise = controller.pick([{ label: 'bar' }, item], { activeItem: item }); - // wait a bit to let the pick get set up. - await wait(200); + await wait; + controller.accept(); const pick = await pickPromise; + assert.strictEqual(pick, item); }); test('input - basecase', async () => { + const wait = setupWaitTilShownListener(controller); const inputPromise = controller.input({ value: 'foo' }); - // wait a bit to let the pick get set up. - await wait(200); + await wait; + controller.accept(); const value = await inputPromise; + assert.strictEqual(value, 'foo'); }); @@ -103,8 +119,6 @@ flakySuite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/1 quickpick.value = 'changed'; try { - // wait a bit to let the event play out. - await wait(200); assert.strictEqual(value, quickpick.value); } finally { quickpick.dispose(); @@ -123,7 +137,7 @@ flakySuite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/1 quickpick.activeItems = [items[items.length - 1]]; quickpick.show(); - let cursorTop = getScrollTop(); + const cursorTop = getScrollTop(); assert.notStrictEqual(cursorTop, 0); @@ -148,7 +162,7 @@ flakySuite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/1 quickpick.activeItems = [items[items.length - 1]]; quickpick.show(); - let cursorTop = getScrollTop(); + const cursorTop = getScrollTop(); assert.notStrictEqual(cursorTop, 0); quickpick.keepScrollPosition = true; diff --git a/src/vs/base/parts/request/browser/request.ts b/src/vs/base/parts/request/browser/request.ts index b4710e25a0..7184c4f65f 100644 --- a/src/vs/base/parts/request/browser/request.ts +++ b/src/vs/base/parts/request/browser/request.ts @@ -55,7 +55,7 @@ export function request(options: IRequestOptions, token: CancellationToken): Pro function setRequestHeaders(xhr: XMLHttpRequest, options: IRequestOptions): void { if (options.headers) { - outer: for (let k in options.headers) { + outer: for (const k in options.headers) { switch (k) { case 'User-Agent': case 'Accept-Encoding': diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index b229856517..4596427566 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -145,7 +145,7 @@ /** * @param {string} channel * @param {any[]} args - * @returns {Promise | undefined} + * @returns {Promise | never} */ invoke(channel, ...args) { if (validateIPC(channel)) { @@ -156,7 +156,7 @@ /** * @param {string} channel * @param {(event: IpcRendererEvent, ...args: any[]) => void} listener - * @returns {IpcRenderer} + * @returns {IpcRenderer | never} */ on(channel, listener) { if (validateIPC(channel)) { @@ -169,7 +169,7 @@ /** * @param {string} channel * @param {(event: IpcRendererEvent, ...args: any[]) => void} listener - * @returns {IpcRenderer} + * @returns {IpcRenderer | never} */ once(channel, listener) { if (validateIPC(channel)) { @@ -182,7 +182,7 @@ /** * @param {string} channel * @param {(event: IpcRendererEvent, ...args: any[]) => void} listener - * @returns {IpcRenderer} + * @returns {IpcRenderer | never} */ removeListener(channel, listener) { if (validateIPC(channel)) { diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index c8c975eeb5..89567fc947 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -36,6 +36,10 @@ export interface ISandboxNodeProcess extends INodeProcess { /** * The `process.pid` property returns the PID of the process. + * + * @deprecated this property will be removed once sandbox is enabled. + * + * TODO@bpasero remove this property when sandbox is on */ readonly pid: number; diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index 4024f471e9..ab30b16ddf 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -357,13 +357,9 @@ export class InMemoryStorageDatabase implements IStorageDatabase { } async updateItems(request: IUpdateRequest): Promise { - if (request.insert) { - request.insert.forEach((value, key) => this.items.set(key, value)); - } + request.insert?.forEach((value, key) => this.items.set(key, value)); - if (request.delete) { - request.delete.forEach(key => this.items.delete(key)); - } + request.delete?.forEach(key => this.items.delete(key)); } async close(): Promise { } diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 89dbbcf0bb..8258ddafd0 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -301,9 +301,9 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { import('@vscode/sqlite3').then(sqlite3 => { const connection: IDatabaseConnection = { - db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, (error: (Error & { code?: string }) | null) => { if (error) { - return connection.db ? connection.db.close(() => reject(error)) : reject(error); + return (connection.db && error.code !== 'SQLITE_CANTOPEN' /* https://github.com/TryGhost/node-sqlite3/issues/1617 */) ? connection.db.close(() => reject(error)) : reject(error); } // The following exec() statement serves two purposes: @@ -440,14 +440,10 @@ class SQLiteStorageDatabaseLogger { } trace(msg: string): void { - if (this.logTrace) { - this.logTrace(msg); - } + this.logTrace?.(msg); } error(error: string | Error): void { - if (this.logError) { - this.logError(error); - } + this.logError?.(error); } } diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 788cfb47c7..68a304ed5f 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -123,7 +123,7 @@ flakySuite('Storage Library', function () { const database = new TestSQLiteStorageDatabase(join(testDir, 'storage.db')); const storage = new Storage(database); - let changes = new Set(); + const changes = new Set(); storage.onDidChangeStorage(key => { changes.add(key); }); @@ -223,7 +223,7 @@ flakySuite('Storage Library', function () { }); test('explicit flush', async () => { - let storage = new Storage(new SQLiteStorageDatabase(join(testDir, 'storage.db'))); + const storage = new Storage(new SQLiteStorageDatabase(join(testDir, 'storage.db'))); await storage.init(); storage.set('foo', 'bar'); @@ -243,7 +243,7 @@ flakySuite('Storage Library', function () { test('conflicting updates', () => { return runWithFakedTimers({}, async function () { - let storage = new Storage(new SQLiteStorageDatabase(join(testDir, 'storage.db'))); + const storage = new Storage(new SQLiteStorageDatabase(join(testDir, 'storage.db'))); await storage.init(); let changes = new Set(); @@ -623,7 +623,7 @@ flakySuite('SQLite Storage Library', function () { }); test('very large item value', async function () { - let storage = new SQLiteStorageDatabase(join(testdir, 'storage.db')); + const storage = new SQLiteStorageDatabase(join(testdir, 'storage.db')); const items = new Map(); items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); @@ -670,56 +670,57 @@ flakySuite('SQLite Storage Library', function () { }); test('multiple concurrent writes execute in sequence', async () => { - - class TestStorage extends Storage { - getStorage(): IStorageDatabase { - return this.database; + return runWithFakedTimers({}, async () => { + class TestStorage extends Storage { + getStorage(): IStorageDatabase { + return this.database; + } } - } - const storage = new TestStorage(new SQLiteStorageDatabase(join(testdir, 'storage.db'))); + const storage = new TestStorage(new SQLiteStorageDatabase(join(testdir, 'storage.db'))); - await storage.init(); + await storage.init(); - storage.set('foo', 'bar'); - storage.set('some/foo/path', 'some/bar/path'); + storage.set('foo', 'bar'); + storage.set('some/foo/path', 'some/bar/path'); - await timeout(2); + await timeout(2); - storage.set('foo1', 'bar'); - storage.set('some/foo1/path', 'some/bar/path'); + storage.set('foo1', 'bar'); + storage.set('some/foo1/path', 'some/bar/path'); - await timeout(2); + await timeout(2); - storage.set('foo2', 'bar'); - storage.set('some/foo2/path', 'some/bar/path'); + storage.set('foo2', 'bar'); + storage.set('some/foo2/path', 'some/bar/path'); - await timeout(2); + await timeout(2); - storage.delete('foo1'); - storage.delete('some/foo1/path'); + storage.delete('foo1'); + storage.delete('some/foo1/path'); - await timeout(2); + await timeout(2); - storage.delete('foo4'); - storage.delete('some/foo4/path'); + storage.delete('foo4'); + storage.delete('some/foo4/path'); - await timeout(5); + await timeout(5); - storage.set('foo3', 'bar'); - await storage.set('some/foo3/path', 'some/bar/path'); + storage.set('foo3', 'bar'); + await storage.set('some/foo3/path', 'some/bar/path'); - const items = await storage.getStorage().getItems(); - strictEqual(items.get('foo'), 'bar'); - strictEqual(items.get('some/foo/path'), 'some/bar/path'); - strictEqual(items.has('foo1'), false); - strictEqual(items.has('some/foo1/path'), false); - strictEqual(items.get('foo2'), 'bar'); - strictEqual(items.get('some/foo2/path'), 'some/bar/path'); - strictEqual(items.get('foo3'), 'bar'); - strictEqual(items.get('some/foo3/path'), 'some/bar/path'); + const items = await storage.getStorage().getItems(); + strictEqual(items.get('foo'), 'bar'); + strictEqual(items.get('some/foo/path'), 'some/bar/path'); + strictEqual(items.has('foo1'), false); + strictEqual(items.has('some/foo1/path'), false); + strictEqual(items.get('foo2'), 'bar'); + strictEqual(items.get('some/foo2/path'), 'some/bar/path'); + strictEqual(items.get('foo3'), 'bar'); + strictEqual(items.get('some/foo3/path'), 'some/bar/path'); - await storage.close(); + await storage.close(); + }); }); test('lots of INSERT & DELETE (below inline max)', async () => { @@ -773,4 +774,18 @@ flakySuite('SQLite Storage Library', function () { await storage.close(); }); + + test('invalid path does not hang', async () => { + const storage = new SQLiteStorageDatabase(join(testdir, 'nonexist', 'storage.db')); + + let error; + try { + await storage.getItems(); + await storage.close(); + } catch (e) { + error = e; + } + + ok(error); + }); }); diff --git a/src/vs/base/test/browser/actionbar.test.ts b/src/vs/base/test/browser/actionbar.test.ts index f9c22ec75e..ddb9b65e6d 100644 --- a/src/vs/base/test/browser/actionbar.test.ts +++ b/src/vs/base/test/browser/actionbar.test.ts @@ -10,15 +10,15 @@ import { Action, Separator } from 'vs/base/common/actions'; suite('Actionbar', () => { test('prepareActions()', function () { - let a1 = new Separator(); - let a2 = new Separator(); - let a3 = new Action('a3'); - let a4 = new Separator(); - let a5 = new Separator(); - let a6 = new Action('a6'); - let a7 = new Separator(); + const a1 = new Separator(); + const a2 = new Separator(); + const a3 = new Action('a3'); + const a4 = new Separator(); + const a5 = new Separator(); + const a6 = new Action('a6'); + const a7 = new Separator(); - let actions = prepareActions([a1, a2, a3, a4, a5, a6, a7]); + const actions = prepareActions([a1, a2, a3, a4, a5, a6, a7]); assert.strictEqual(actions.length, 3); // duplicate separators get removed assert(actions[0] === a3); assert(actions[1] === a5); @@ -29,8 +29,8 @@ suite('Actionbar', () => { const container = document.createElement('div'); const actionbar = new ActionBar(container); - let a1 = new Action('a1'); - let a2 = new Action('a2'); + const a1 = new Action('a1'); + const a2 = new Action('a2'); actionbar.push(a1); assert.strictEqual(actionbar.hasAction(a1), true); diff --git a/src/vs/base/test/browser/dom.test.ts b/src/vs/base/test/browser/dom.test.ts index e6f0831f85..5296b529a8 100644 --- a/src/vs/base/test/browser/dom.test.ts +++ b/src/vs/base/test/browser/dom.test.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as dom from 'vs/base/browser/dom'; -const $ = dom.$; +import { $, h, multibyteAwareBtoa } from 'vs/base/browser/dom'; suite('dom', () => { test.skip('hasClass', () => { // {{SQL CARBON EDIT}} skip test - let element = document.createElement('div'); + const element = document.createElement('div'); element.className = 'foobar boo far'; assert(element.classList.contains('foobar')); @@ -55,7 +54,7 @@ suite('dom', () => { }); test.skip('removeClass should consider hyphens', function () { // {{SQL CARBON EDIT}} skip test - let element = document.createElement('div'); + const element = document.createElement('div'); element.classList.add('foo-bar'); element.classList.add('bar'); @@ -73,9 +72,9 @@ suite('dom', () => { }); test('multibyteAwareBtoa', () => { - assert.ok(dom.multibyteAwareBtoa('hello world').length > 0); - assert.ok(dom.multibyteAwareBtoa('平仮名').length > 0); - assert.ok(dom.multibyteAwareBtoa(new Array(100000).fill('vs').join('')).length > 0); // https://github.com/microsoft/vscode/issues/112013 + assert.ok(multibyteAwareBtoa('hello world').length > 0); + assert.ok(multibyteAwareBtoa('平仮名').length > 0); + assert.ok(multibyteAwareBtoa(new Array(100000).fill('vs').join('')).length > 0); // https://github.com/microsoft/vscode/issues/112013 }); suite('$', () => { @@ -113,7 +112,7 @@ suite('dom', () => { test('should build nodes with children', () => { let div = $('div', undefined, $('span', { id: 'demospan' })); - let firstChild = div.firstChild as HTMLElement; + const firstChild = div.firstChild as HTMLElement; assert.strictEqual(firstChild.tagName, 'SPAN'); assert.strictEqual(firstChild.id, 'demospan'); @@ -123,10 +122,158 @@ suite('dom', () => { }); test('should build nodes with text children', () => { - let div = $('div', undefined, 'foobar'); - let firstChild = div.firstChild as HTMLElement; + const div = $('div', undefined, 'foobar'); + const firstChild = div.firstChild as HTMLElement; assert.strictEqual(firstChild.tagName, undefined); assert.strictEqual(firstChild.textContent, 'foobar'); }); }); + + suite('h', () => { + test('should build simple nodes', () => { + const div = h('div'); + assert(div.root instanceof HTMLElement); + assert.strictEqual(div.root.tagName, 'DIV'); + + const span = h('span'); + assert(span.root instanceof HTMLElement); + assert.strictEqual(span.root.tagName, 'SPAN'); + + const img = h('img'); + assert(img.root instanceof HTMLElement); + assert.strictEqual(img.root.tagName, 'IMG'); + }); + + test('should handle ids and classes', () => { + const divId = h('div#myid'); + assert.strictEqual(divId.root.tagName, 'DIV'); + assert.strictEqual(divId.root.id, 'myid'); + + const divClass = h('div.a'); + assert.strictEqual(divClass.root.tagName, 'DIV'); + assert.strictEqual(divClass.root.classList.length, 1); + assert(divClass.root.classList.contains('a')); + + const divClasses = h('div.a.b.c'); + assert.strictEqual(divClasses.root.tagName, 'DIV'); + assert.strictEqual(divClasses.root.classList.length, 3); + assert(divClasses.root.classList.contains('a')); + assert(divClasses.root.classList.contains('b')); + assert(divClasses.root.classList.contains('c')); + + const divAll = h('div#myid.a.b.c'); + assert.strictEqual(divAll.root.tagName, 'DIV'); + assert.strictEqual(divAll.root.id, 'myid'); + assert.strictEqual(divAll.root.classList.length, 3); + assert(divAll.root.classList.contains('a')); + assert(divAll.root.classList.contains('b')); + assert(divAll.root.classList.contains('c')); + + const spanId = h('span#myid'); + assert.strictEqual(spanId.root.tagName, 'SPAN'); + assert.strictEqual(spanId.root.id, 'myid'); + + const spanClass = h('span.a'); + assert.strictEqual(spanClass.root.tagName, 'SPAN'); + assert.strictEqual(spanClass.root.classList.length, 1); + assert(spanClass.root.classList.contains('a')); + + const spanClasses = h('span.a.b.c'); + assert.strictEqual(spanClasses.root.tagName, 'SPAN'); + assert.strictEqual(spanClasses.root.classList.length, 3); + assert(spanClasses.root.classList.contains('a')); + assert(spanClasses.root.classList.contains('b')); + assert(spanClasses.root.classList.contains('c')); + + const spanAll = h('span#myid.a.b.c'); + assert.strictEqual(spanAll.root.tagName, 'SPAN'); + assert.strictEqual(spanAll.root.id, 'myid'); + assert.strictEqual(spanAll.root.classList.length, 3); + assert(spanAll.root.classList.contains('a')); + assert(spanAll.root.classList.contains('b')); + assert(spanAll.root.classList.contains('c')); + }); + + test('should implicitly handle ids and classes', () => { + const divId = h('#myid'); + assert.strictEqual(divId.root.tagName, 'DIV'); + assert.strictEqual(divId.root.id, 'myid'); + + const divClass = h('.a'); + assert.strictEqual(divClass.root.tagName, 'DIV'); + assert.strictEqual(divClass.root.classList.length, 1); + assert(divClass.root.classList.contains('a')); + + const divClasses = h('.a.b.c'); + assert.strictEqual(divClasses.root.tagName, 'DIV'); + assert.strictEqual(divClasses.root.classList.length, 3); + assert(divClasses.root.classList.contains('a')); + assert(divClasses.root.classList.contains('b')); + assert(divClasses.root.classList.contains('c')); + + const divAll = h('#myid.a.b.c'); + assert.strictEqual(divAll.root.tagName, 'DIV'); + assert.strictEqual(divAll.root.id, 'myid'); + assert.strictEqual(divAll.root.classList.length, 3); + assert(divAll.root.classList.contains('a')); + assert(divAll.root.classList.contains('b')); + assert(divAll.root.classList.contains('c')); + }); + + test('should handle @ identifiers', () => { + const implicit = h('@el'); + assert.strictEqual(implicit.root, implicit.el); + assert.strictEqual(implicit.el.tagName, 'DIV'); + + const explicit = h('div@el'); + assert.strictEqual(explicit.root, explicit.el); + assert.strictEqual(explicit.el.tagName, 'DIV'); + + const implicitId = h('#myid@el'); + assert.strictEqual(implicitId.root, implicitId.el); + assert.strictEqual(implicitId.el.tagName, 'DIV'); + assert.strictEqual(implicitId.root.id, 'myid'); + + const explicitId = h('div#myid@el'); + assert.strictEqual(explicitId.root, explicitId.el); + assert.strictEqual(explicitId.el.tagName, 'DIV'); + assert.strictEqual(explicitId.root.id, 'myid'); + + const implicitClass = h('.a@el'); + assert.strictEqual(implicitClass.root, implicitClass.el); + assert.strictEqual(implicitClass.el.tagName, 'DIV'); + assert.strictEqual(implicitClass.root.classList.length, 1); + assert(implicitClass.root.classList.contains('a')); + + const explicitClass = h('div.a@el'); + assert.strictEqual(explicitClass.root, explicitClass.el); + assert.strictEqual(explicitClass.el.tagName, 'DIV'); + assert.strictEqual(explicitClass.root.classList.length, 1); + assert(explicitClass.root.classList.contains('a')); + }); + }); + + test('should recurse', () => { + const result = h('div.code-view', [ + h('div.title@title'), + h('div.container', [ + h('div.gutter@gutterDiv'), + h('span@editor'), + ]), + ]); + + assert.strictEqual(result.root.tagName, 'DIV'); + assert.strictEqual(result.root.className, 'code-view'); + assert.strictEqual(result.root.childElementCount, 2); + assert.strictEqual(result.root.firstElementChild, result.title); + assert.strictEqual(result.title.tagName, 'DIV'); + assert.strictEqual(result.title.className, 'title'); + assert.strictEqual(result.title.childElementCount, 0); + assert.strictEqual(result.gutterDiv.tagName, 'DIV'); + assert.strictEqual(result.gutterDiv.className, 'gutter'); + assert.strictEqual(result.gutterDiv.childElementCount, 0); + assert.strictEqual(result.editor.tagName, 'SPAN'); + assert.strictEqual(result.editor.className, ''); + assert.strictEqual(result.editor.childElementCount, 0); + }); }); diff --git a/src/vs/base/test/browser/formattedTextRenderer.test.ts b/src/vs/base/test/browser/formattedTextRenderer.test.ts index a420133ed2..3960a6aa25 100644 --- a/src/vs/base/test/browser/formattedTextRenderer.test.ts +++ b/src/vs/base/test/browser/formattedTextRenderer.test.ts @@ -19,7 +19,7 @@ suite('FormattedTextRenderer', () => { }); test('render simple element', () => { - let result: HTMLElement = renderText('testing'); + const result: HTMLElement = renderText('testing'); assert.strictEqual(result.nodeType, document.ELEMENT_NODE); assert.strictEqual(result.textContent, 'testing'); @@ -27,7 +27,7 @@ suite('FormattedTextRenderer', () => { }); test('render element with class', () => { - let result: HTMLElement = renderText('testing', { + const result: HTMLElement = renderText('testing', { className: 'testClass' }); assert.strictEqual(result.nodeType, document.ELEMENT_NODE); @@ -55,18 +55,18 @@ suite('FormattedTextRenderer', () => { }); test('no formatting', () => { - let result: HTMLElement = renderFormattedText('this is just a string'); + const result: HTMLElement = renderFormattedText('this is just a string'); assert.strictEqual(result.innerHTML, 'this is just a string'); }); test('preserve newlines', () => { - let result: HTMLElement = renderFormattedText('line one\nline two'); + const result: HTMLElement = renderFormattedText('line one\nline two'); assert.strictEqual(result.innerHTML, 'line one
    line two'); }); test('action', () => { let callbackCalled = false; - let result: HTMLElement = renderFormattedText('[[action]]', { + const result: HTMLElement = renderFormattedText('[[action]]', { actionHandler: { callback(content) { assert.strictEqual(content, '0'); @@ -77,7 +77,7 @@ suite('FormattedTextRenderer', () => { }); assert.strictEqual(result.innerHTML, 'action'); - let event: MouseEvent = document.createEvent('MouseEvent'); + const event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); result.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); @@ -85,7 +85,7 @@ suite('FormattedTextRenderer', () => { test('fancy action', () => { let callbackCalled = false; - let result: HTMLElement = renderFormattedText('__**[[action]]**__', { + const result: HTMLElement = renderFormattedText('__**[[action]]**__', { actionHandler: { callback(content) { assert.strictEqual(content, '0'); @@ -96,7 +96,7 @@ suite('FormattedTextRenderer', () => { }); assert.strictEqual(result.innerHTML, 'action'); - let event: MouseEvent = document.createEvent('MouseEvent'); + const event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); result.firstChild!.firstChild!.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); @@ -104,7 +104,7 @@ suite('FormattedTextRenderer', () => { test('fancier action', () => { let callbackCalled = false; - let result: HTMLElement = renderFormattedText('``__**[[action]]**__``', { + const result: HTMLElement = renderFormattedText('``__**[[action]]**__``', { renderCodeSegments: true, actionHandler: { callback(content) { @@ -116,14 +116,14 @@ suite('FormattedTextRenderer', () => { }); assert.strictEqual(result.innerHTML, 'action'); - let event: MouseEvent = document.createEvent('MouseEvent'); + const event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); result.firstChild!.firstChild!.firstChild!.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); }); test('escaped formatting', () => { - let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); + const result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); assert.strictEqual(result.children.length, 0); assert.strictEqual(result.innerHTML, '**bold**'); }); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 11fd8c2e99..2cb9d63d33 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -92,7 +92,7 @@ suite('MarkdownRenderer', () => { codeBlockRenderer: simpleCodeBlockRenderer }); result.dispose(); - setTimeout(resolve, 250); + setTimeout(resolve, 50); }); }); @@ -111,8 +111,8 @@ suite('MarkdownRenderer', () => { setTimeout(() => { result.dispose(); resolveCodeBlockRendering(document.createElement('code')); - setTimeout(resolve, 250); - }, 250); + setTimeout(resolve, 50); + }, 50); }); }); }); @@ -123,7 +123,7 @@ suite('MarkdownRenderer', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: true }); mds.appendText('$(zap) $(not a theme icon) $(add)'); - let result: HTMLElement = renderMarkdown(mds).element; + const result: HTMLElement = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon) $(add)

    `); }); @@ -131,7 +131,7 @@ suite('MarkdownRenderer', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: true }); mds.appendMarkdown('$(zap) $(not a theme icon) $(add)'); - let result: HTMLElement = renderMarkdown(mds).element; + const result: HTMLElement = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, `

    $(not a theme icon)

    `); }); @@ -139,7 +139,7 @@ suite('MarkdownRenderer', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: true }); mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)'); - let result: HTMLElement = renderMarkdown(mds).element; + const result: HTMLElement = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon)

    `); }); @@ -147,8 +147,8 @@ suite('MarkdownRenderer', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: true }); mds.appendMarkdown(`[$(zap)-link](#link)`); - let result: HTMLElement = renderMarkdown(mds).element; - assert.strictEqual(result.innerHTML, `

    -link

    `); + const result: HTMLElement = renderMarkdown(mds).element; + assert.strictEqual(result.innerHTML, `

    -link

    `); }); test('render icon in table', () => { @@ -158,7 +158,7 @@ suite('MarkdownRenderer', () => { |--------|----------------------| | $(zap) | [$(zap)-link](#link) |`); - let result: HTMLElement = renderMarkdown(mds).element; + const result: HTMLElement = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, ` @@ -168,11 +168,19 @@ suite('MarkdownRenderer', () => { - +
    -link-link
    `); }); + + test('render icon in without href (#152170)', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true, supportHtml: true }); + mds.appendMarkdown(`$(sync)`); + + const result: HTMLElement = renderMarkdown(mds).element; + assert.strictEqual(result.innerHTML, `

    `); + }); }); suite('ThemeIcons Support Off', () => { @@ -181,7 +189,7 @@ suite('MarkdownRenderer', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: false }); mds.appendText('$(zap) $(not a theme icon) $(add)'); - let result: HTMLElement = renderMarkdown(mds).element; + const result: HTMLElement = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon) $(add)

    `); }); @@ -189,7 +197,7 @@ suite('MarkdownRenderer', () => { const mds = new MarkdownString(undefined, { supportThemeIcons: false }); mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)'); - let result: HTMLElement = renderMarkdown(mds).element; + const result: HTMLElement = renderMarkdown(mds).element; assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon) $(add)

    `); }); }); @@ -211,6 +219,25 @@ suite('MarkdownRenderer', () => { assert.ok(data.documentUri.toString().startsWith('file:///c%3A/')); }); + test('Should not render command links by default', () => { + const md = new MarkdownString(`[command1](command:doFoo) command2`, { + supportHtml: true + }); + + const result: HTMLElement = renderMarkdown(md).element; + assert.strictEqual(result.innerHTML, `

    command1 command2

    `); + }); + + test('Should render command links in trusted strings', () => { + const md = new MarkdownString(`[command1](command:doFoo) command2`, { + isTrusted: true, + supportHtml: true, + }); + + const result: HTMLElement = renderMarkdown(md).element; + assert.strictEqual(result.innerHTML, `

    command1 command2

    `); + }); + suite('PlaintextMarkdownRender', () => { test('test code, blockquote, heading, list, listitem, paragraph, table, tablerow, tablecell, strong, em, br, del, text are rendered plaintext', () => { diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index 9659118eef..3df348529e 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -785,26 +785,26 @@ suite('SerializableGrid', function () { }); test('sanitizeGridNodeDescriptor', () => { - const nodeDescriptor = { groups: [{ size: 0.2 }, { size: 0.2 }, { size: 0.6, groups: [{}, {}] }] }; - const nodeDescriptorCopy = deepClone(nodeDescriptor); + const nodeDescriptor: GridNodeDescriptor = { groups: [{ size: 0.2 }, { size: 0.2 }, { size: 0.6, groups: [{}, {}] }] }; + const nodeDescriptorCopy = deepClone(nodeDescriptor); sanitizeGridNodeDescriptor(nodeDescriptorCopy, true); assert.deepStrictEqual(nodeDescriptorCopy, { groups: [{ size: 0.2 }, { size: 0.2 }, { size: 0.6, groups: [{ size: 0.5 }, { size: 0.5 }] }] }); }); test('createSerializedGrid', () => { - const gridDescriptor = { orientation: Orientation.VERTICAL, groups: [{ size: 0.2 }, { size: 0.2 }, { size: 0.6, groups: [{}, {}] }] }; + const gridDescriptor = { orientation: Orientation.VERTICAL, groups: [{ size: 0.2, data: 'a' }, { size: 0.2, data: 'b' }, { size: 0.6, groups: [{ data: 'c' }, { data: 'd' }] }] }; const serializedGrid = createSerializedGrid(gridDescriptor); assert.deepStrictEqual(serializedGrid, { root: { type: 'branch', size: undefined, data: [ - { type: 'leaf', size: 0.2, data: null }, - { type: 'leaf', size: 0.2, data: null }, + { type: 'leaf', size: 0.2, data: 'a' }, + { type: 'leaf', size: 0.2, data: 'b' }, { type: 'branch', size: 0.6, data: [ - { type: 'leaf', size: 0.5, data: null }, - { type: 'leaf', size: 0.5, data: null } + { type: 'leaf', size: 0.5, data: 'c' }, + { type: 'leaf', size: 0.5, data: 'd' } ] } ] @@ -842,6 +842,30 @@ suite('SerializableGrid', function () { grid.removeView(views[2]); }); + test('from', () => { + const createView = (): ISerializableView => ({ + element: document.createElement('div'), + layout: () => null, + minimumWidth: 0, + maximumWidth: Number.POSITIVE_INFINITY, + minimumHeight: 0, + maximumHeight: Number.POSITIVE_INFINITY, + onDidChange: Event.None, + toJSON: () => ({}) + }); + + const a = createView(); + const b = createView(); + const c = createView(); + const d = createView(); + + const gridDescriptor = { orientation: Orientation.VERTICAL, groups: [{ size: 0.2, data: a }, { size: 0.2, data: b }, { size: 0.6, groups: [{ data: c }, { data: d }] }] }; + const grid = SerializableGrid.from(gridDescriptor); + + assert.deepStrictEqual(nodesToArrays(grid.getViews()), [a, b, [c, d]]); + grid.dispose(); + }); + test('serialize should store visibility and previous size', function () { const view1 = new TestSerializableView('view1', 50, Number.MAX_VALUE, 50, Number.MAX_VALUE); const grid = new SerializableGrid(view1); diff --git a/src/vs/base/test/browser/ui/list/listWidget.test.ts b/src/vs/base/test/browser/ui/list/listWidget.test.ts new file mode 100644 index 0000000000..42d587052d --- /dev/null +++ b/src/vs/base/test/browser/ui/list/listWidget.test.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { range } from 'vs/base/common/arrays'; +import { timeout } from 'vs/base/common/async'; + +suite('ListWidget', function () { + test('Page up and down', async function () { + const element = document.createElement('div'); + element.style.height = '200px'; + element.style.width = '200px'; + + const delegate: IListVirtualDelegate = { + getHeight() { return 20; }, + getTemplateId() { return 'template'; } + }; + + let templatesCount = 0; + + const renderer: IListRenderer = { + templateId: 'template', + renderTemplate() { templatesCount++; }, + renderElement() { }, + disposeTemplate() { templatesCount--; } + }; + + const listWidget = new List('test', element, delegate, [renderer]); + + listWidget.layout(200); + assert.strictEqual(templatesCount, 0, 'no templates have been allocated'); + listWidget.splice(0, 0, range(100)); + listWidget.focusFirst(); + + listWidget.focusNextPage(); + assert.strictEqual(listWidget.getFocus()[0], 9, 'first page down moves focus to element at bottom'); + + // scroll to next page is async + listWidget.focusNextPage(); + await timeout(0); + assert.strictEqual(listWidget.getFocus()[0], 19, 'page down to next page'); + + listWidget.focusPreviousPage(); + assert.strictEqual(listWidget.getFocus()[0], 10, 'first page up moves focus to element at top'); + + // scroll to previous page is async + listWidget.focusPreviousPage(); + await timeout(0); + assert.strictEqual(listWidget.getFocus()[0], 0, 'page down to previous page'); + + listWidget.dispose(); + }); + + test('Page up and down with item taller than viewport #149502', async function () { + const element = document.createElement('div'); + element.style.height = '200px'; + element.style.width = '200px'; + + const delegate: IListVirtualDelegate = { + getHeight() { return 200; }, + getTemplateId() { return 'template'; } + }; + + let templatesCount = 0; + + const renderer: IListRenderer = { + templateId: 'template', + renderTemplate() { templatesCount++; }, + renderElement() { }, + disposeTemplate() { templatesCount--; } + }; + + const listWidget = new List('test', element, delegate, [renderer]); + + listWidget.layout(200); + assert.strictEqual(templatesCount, 0, 'no templates have been allocated'); + listWidget.splice(0, 0, range(100)); + listWidget.focusFirst(); + assert.strictEqual(listWidget.getFocus()[0], 0, 'initial focus is first element'); + + // scroll to next page is async + listWidget.focusNextPage(); + await timeout(0); + assert.strictEqual(listWidget.getFocus()[0], 1, 'page down to next page'); + + // scroll to previous page is async + listWidget.focusPreviousPage(); + await timeout(0); + assert.strictEqual(listWidget.getFocus()[0], 0, 'page up to next page'); + + listWidget.dispose(); + }); +}); diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index d2f4dd6b1e..d8d8b9ff60 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -8,7 +8,7 @@ import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; suite('ScrollbarState', () => { test('inflates slider size', () => { - let actual = new ScrollbarState(0, 14, 0, 339, 42423, 32787); + const actual = new ScrollbarState(0, 14, 0, 339, 42423, 32787); assert.strictEqual(actual.getArrowSize(), 0); assert.strictEqual(actual.getScrollPosition(), 32787); @@ -34,7 +34,7 @@ suite('ScrollbarState', () => { }); test('inflates slider size with arrows', () => { - let actual = new ScrollbarState(12, 14, 0, 339, 42423, 32787); + const actual = new ScrollbarState(12, 14, 0, 339, 42423, 32787); assert.strictEqual(actual.getArrowSize(), 12); assert.strictEqual(actual.getScrollPosition(), 32787); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 508634b99b..cf269fbfb5 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -271,7 +271,7 @@ suite('Splitview', () => { splitview.addView(view2, Sizing.Distribute); splitview.addView(view3, Sizing.Distribute); - let sashes = getSashes(splitview); + const sashes = getSashes(splitview); assert.strictEqual(sashes.length, 2, 'there are two sashes'); assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled'); assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled'); diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 82b054d3d1..b7b943b05a 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -8,6 +8,7 @@ import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IAsyncDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { timeout } from 'vs/base/common/async'; +import { Iterable } from 'vs/base/common/iterator'; interface Element { id: string; @@ -101,7 +102,7 @@ suite('AsyncDataTree', function () { await tree.setInput(model.root); assert.strictEqual(container.querySelectorAll('.monaco-list-row').length, 1); - let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + const twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; assert(!twistie.classList.contains('collapsible')); assert(!twistie.classList.contains('collapsed')); @@ -435,4 +436,60 @@ suite('AsyncDataTree', function () { assert.deepStrictEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b2']); }); + + test('issue #121567', async () => { + const container = document.createElement('div'); + + const calls: Element[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + async getChildren(element: Element) { + calls.push(element); + return element.children ?? Iterable.empty(); + } + }; + + const model = new Model({ + id: 'root', + children: [{ + id: 'a', children: [{ + id: 'aa' + }] + }] + }); + const a = model.get('a'); + + const tree = new AsyncDataTree('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() }); + tree.layout(200); + + await tree.setInput(model.root); + assert.strictEqual(calls.length, 1, 'There should be a single getChildren call for the root'); + assert(tree.isCollapsible(a), 'a is collapsible'); + assert(tree.isCollapsed(a), 'a is collapsed'); + + await tree.updateChildren(a, false); + assert.strictEqual(calls.length, 1, 'There should be no changes to the calls list, since a was collapsed'); + assert(tree.isCollapsible(a), 'a is collapsible'); + assert(tree.isCollapsed(a), 'a is collapsed'); + + const children = a.children; + a.children = []; + await tree.updateChildren(a, false); + assert.strictEqual(calls.length, 1, 'There should still be no changes to the calls list, since a was collapsed'); + assert(!tree.isCollapsible(a), 'a is no longer collapsible'); + assert(tree.isCollapsed(a), 'a is collapsed'); + + a.children = children; + await tree.updateChildren(a, false); + assert.strictEqual(calls.length, 1, 'There should still be no changes to the calls list, since a was collapsed'); + assert(tree.isCollapsible(a), 'a is collapsible again'); + assert(tree.isCollapsed(a), 'a is collapsed'); + + await tree.expand(a); + assert.strictEqual(calls.length, 2, 'Finally, there should be a getChildren call for a'); + assert(tree.isCollapsible(a), 'a is still collapsible'); + assert(!tree.isCollapsed(a), 'a is expanded'); + }); }); diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index 656a924a9c..e2f6b6b9d0 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -400,7 +400,7 @@ suite('IndexTreeModel', () => { const deleteCount = Math.ceil(Math.random() * (list.length - spliceIndex)); const insertCount = Math.floor(Math.random() * maxInserts + 1); - let inserts: ITreeElement[] = []; + const inserts: ITreeElement[] = []; for (let i = 0; i < insertCount; i++) { const element = elementCounter++; inserts.push({ element, children: [] }); diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index 556f5f8764..951d781f1e 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -172,7 +172,7 @@ suite('ObjectTreeModel', function () { }); test('sorter', () => { - let compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1; + const compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1; const list: ITreeNode[] = []; const model = new ObjectTreeModel('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } }); diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index a7f395ccd5..8d442e813f 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -6,6 +6,19 @@ import * as assert from 'assert'; import * as arrays from 'vs/base/common/arrays'; suite('Arrays', () => { + + test('removeFastWithoutKeepingOrder', () => { + const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; + arrays.removeFastWithoutKeepingOrder(array, 1); + assert.deepStrictEqual(array, [1, 69, 5, 7, 55, 59, 60, 61, 64]); + + arrays.removeFastWithoutKeepingOrder(array, 0); + assert.deepStrictEqual(array, [64, 69, 5, 7, 55, 59, 60, 61]); + + arrays.removeFastWithoutKeepingOrder(array, 7); + assert.deepStrictEqual(array, [64, 69, 5, 7, 55, 59, 60]); + }); + test('findFirst', () => { const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; @@ -35,10 +48,10 @@ suite('Arrays', () => { function assertMedian(expexted: number, data: number[], nth: number = Math.floor(data.length / 2)) { const compare = (a: number, b: number) => a - b; - let actual1 = arrays.quickSelect(nth, data, compare); + const actual1 = arrays.quickSelect(nth, data, compare); assert.strictEqual(actual1, expexted); - let actual2 = data.slice().sort(compare)[nth]; + const actual2 = data.slice().sort(compare)[nth]; assert.strictEqual(actual2, expexted); } @@ -140,7 +153,24 @@ suite('Arrays', () => { assert.strictEqual(arrays.binarySearch(array, 0, compare), ~0); assert.strictEqual(arrays.binarySearch(array, 6, compare), ~3); assert.strictEqual(arrays.binarySearch(array, 70, compare), ~10); + }); + test('binarySearch2', () => { + function compareTo(key: number) { + return (index: number) => { + return array[index] - key; + }; + } + const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; + + assert.strictEqual(arrays.binarySearch2(10, compareTo(1)), 0); + assert.strictEqual(arrays.binarySearch2(10, compareTo(5)), 2); + + // insertion point + assert.strictEqual(arrays.binarySearch2(10, compareTo(0)), ~0); + assert.strictEqual(arrays.binarySearch2(10, compareTo(6)), ~3); + assert.strictEqual(arrays.binarySearch2(10, compareTo(70)), ~10); + assert.strictEqual(arrays.binarySearch2(2, compareTo(5)), ~2); }); test('distinct', () => { @@ -214,7 +244,7 @@ suite('Arrays', () => { } test('coalesce', () => { - let a: Array = arrays.coalesce([null, 1, null, 2, 3]); + const a: Array = arrays.coalesce([null, 1, null, 2, 3]); assert.strictEqual(a.length, 3); assert.strictEqual(a[0], 1); assert.strictEqual(a[1], 2); @@ -264,7 +294,7 @@ suite('Arrays', () => { assert.strictEqual(a[1], 2); assert.strictEqual(a[2], 3); - let b: number[] = []; + const b: number[] = []; b[10] = 1; b[20] = 2; b[30] = 3; @@ -274,7 +304,7 @@ suite('Arrays', () => { assert.strictEqual(b[1], 2); assert.strictEqual(b[2], 3); - let sparse: number[] = []; + const sparse: number[] = []; sparse[0] = 1; sparse[1] = 1; sparse[17] = 1; diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 45ce194da5..ab22911bba 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -15,11 +15,11 @@ suite('Async', () => { suite('cancelablePromise', function () { test('set token, don\'t wait for inner promise', function () { let canceled = 0; - let promise = async.createCancelablePromise(token => { + const promise = async.createCancelablePromise(token => { token.onCancellationRequested(_ => { canceled += 1; }); return new Promise(resolve => { /*never*/ }); }); - let result = promise.then(_ => assert.ok(false), err => { + const result = promise.then(_ => assert.ok(false), err => { assert.strictEqual(canceled, 1); assert.ok(isCancellationError(err)); }); @@ -30,11 +30,11 @@ suite('Async', () => { test('cancel despite inner promise being resolved', function () { let canceled = 0; - let promise = async.createCancelablePromise(token => { + const promise = async.createCancelablePromise(token => { token.onCancellationRequested(_ => { canceled += 1; }); return Promise.resolve(1234); }); - let result = promise.then(_ => assert.ok(false), err => { + const result = promise.then(_ => assert.ok(false), err => { assert.strictEqual(canceled, 1); assert.ok(isCancellationError(err)); }); @@ -88,11 +88,11 @@ suite('Async', () => { }); test('get inner result', async function () { - let promise = async.createCancelablePromise(token => { + const promise = async.createCancelablePromise(token => { return async.timeout(12).then(_ => 1234); }); - let result = await promise; + const result = await promise; assert.strictEqual(result, 1234); }); }); @@ -100,11 +100,11 @@ suite('Async', () => { suite('Throttler', function () { test('non async', function () { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let throttler = new async.Throttler(); + const throttler = new async.Throttler(); return Promise.all([ throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }), @@ -117,9 +117,9 @@ suite('Async', () => { test('async', () => { let count = 0; - let factory = () => async.timeout(0).then(() => ++count); + const factory = () => async.timeout(0).then(() => ++count); - let throttler = new async.Throttler(); + const throttler = new async.Throttler(); return Promise.all([ throttler.queue(factory).then((result) => { assert.strictEqual(result, 1); }), @@ -139,13 +139,13 @@ suite('Async', () => { }); test('last factory should be the one getting called', function () { - let factoryFactory = (n: number) => () => { + const factoryFactory = (n: number) => () => { return async.timeout(0).then(() => n); }; - let throttler = new async.Throttler(); + const throttler = new async.Throttler(); - let promises: Promise[] = []; + const promises: Promise[] = []; promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.strictEqual(n, 1); })); promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.strictEqual(n, 3); })); @@ -158,12 +158,12 @@ suite('Async', () => { suite('Delayer', function () { test('simple', () => { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let delayer = new async.Delayer(0); - let promises: Promise[] = []; + const delayer = new async.Delayer(0); + const promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -183,12 +183,12 @@ suite('Async', () => { test('microtask delay simple', () => { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let delayer = new async.Delayer(async.MicrotaskDelay); - let promises: Promise[] = []; + const delayer = new async.Delayer(async.MicrotaskDelay); + const promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -223,11 +223,11 @@ suite('Async', () => { test('simple cancel', function () { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let delayer = new async.Delayer(0); + const delayer = new async.Delayer(0); assert(!delayer.isTriggered()); @@ -246,11 +246,11 @@ suite('Async', () => { test('simple cancel microtask', function () { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let delayer = new async.Delayer(async.MicrotaskDelay); + const delayer = new async.Delayer(async.MicrotaskDelay); assert(!delayer.isTriggered()); @@ -269,12 +269,12 @@ suite('Async', () => { test('cancel should cancel all calls to trigger', function () { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let delayer = new async.Delayer(0); - let promises: Promise[] = []; + const delayer = new async.Delayer(0); + const promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -296,11 +296,11 @@ suite('Async', () => { test('trigger, cancel, then trigger again', function () { let count = 0; - let factory = () => { + const factory = () => { return Promise.resolve(++count); }; - let delayer = new async.Delayer(0); + const delayer = new async.Delayer(0); let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -346,12 +346,12 @@ suite('Async', () => { }); test('last task should be the one getting called', function () { - let factoryFactory = (n: number) => () => { + const factoryFactory = (n: number) => () => { return Promise.resolve(n); }; - let delayer = new async.Delayer(0); - let promises: Promise[] = []; + const delayer = new async.Delayer(0); + const promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -371,7 +371,7 @@ suite('Async', () => { suite('sequence', () => { test('simple', () => { - let factoryFactory = (n: number) => () => { + const factoryFactory = (n: number) => () => { return Promise.resolve(n); }; @@ -394,7 +394,7 @@ suite('Async', () => { suite('Limiter', () => { test('sync', function () { - let factoryFactory = (n: number) => () => { + const factoryFactory = (n: number) => () => { return Promise.resolve(n); }; @@ -418,7 +418,7 @@ suite('Async', () => { }); test('async', function () { - let factoryFactory = (n: number) => () => async.timeout(0).then(() => n); + const factoryFactory = (n: number) => () => async.timeout(0).then(() => n); let limiter = new async.Limiter(1); let promises: Promise[] = []; @@ -440,15 +440,15 @@ suite('Async', () => { test('assert degree of paralellism', function () { let activePromises = 0; - let factoryFactory = (n: number) => () => { + const factoryFactory = (n: number) => () => { activePromises++; assert(activePromises < 6); return async.timeout(0).then(() => { activePromises--; return n; }); }; - let limiter = new async.Limiter(5); + const limiter = new async.Limiter(5); - let promises: Promise[] = []; + const promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -460,13 +460,13 @@ suite('Async', () => { suite('Queue', () => { test('simple', function () { - let queue = new async.Queue(); + const queue = new async.Queue(); let syncPromise = false; - let f1 = () => Promise.resolve(true).then(() => syncPromise = true); + const f1 = () => Promise.resolve(true).then(() => syncPromise = true); let asyncPromise = false; - let f2 = () => async.timeout(10).then(() => asyncPromise = true); + const f2 = () => async.timeout(10).then(() => asyncPromise = true); assert.strictEqual(queue.size, 0); @@ -483,15 +483,15 @@ suite('Async', () => { }); test('order is kept', function () { - let queue = new async.Queue(); + const queue = new async.Queue(); - let res: number[] = []; + const res: number[] = []; - let f1 = () => Promise.resolve(true).then(() => res.push(1)); - let f2 = () => async.timeout(10).then(() => res.push(2)); - let f3 = () => Promise.resolve(true).then(() => res.push(3)); - let f4 = () => async.timeout(20).then(() => res.push(4)); - let f5 = () => async.timeout(0).then(() => res.push(5)); + const f1 = () => Promise.resolve(true).then(() => res.push(1)); + const f2 = () => async.timeout(10).then(() => res.push(2)); + const f3 = () => Promise.resolve(true).then(() => res.push(3)); + const f4 = () => async.timeout(20).then(() => res.push(4)); + const f5 = () => async.timeout(0).then(() => res.push(5)); queue.queue(f1); queue.queue(f2); @@ -507,16 +507,16 @@ suite('Async', () => { }); test('errors bubble individually but not cause stop', function () { - let queue = new async.Queue(); + const queue = new async.Queue(); - let res: number[] = []; + const res: number[] = []; let error = false; - let f1 = () => Promise.resolve(true).then(() => res.push(1)); - let f2 = () => async.timeout(10).then(() => res.push(2)); - let f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error'))); - let f4 = () => async.timeout(20).then(() => res.push(4)); - let f5 = () => async.timeout(0).then(() => res.push(5)); + const f1 = () => Promise.resolve(true).then(() => res.push(1)); + const f2 = () => async.timeout(10).then(() => res.push(2)); + const f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error'))); + const f4 = () => async.timeout(20).then(() => res.push(4)); + const f5 = () => async.timeout(0).then(() => res.push(5)); queue.queue(f1); queue.queue(f2); @@ -532,15 +532,15 @@ suite('Async', () => { }); test('order is kept (chained)', function () { - let queue = new async.Queue(); + const queue = new async.Queue(); - let res: number[] = []; + const res: number[] = []; - let f1 = () => Promise.resolve(true).then(() => res.push(1)); - let f2 = () => async.timeout(10).then(() => res.push(2)); - let f3 = () => Promise.resolve(true).then(() => res.push(3)); - let f4 = () => async.timeout(20).then(() => res.push(4)); - let f5 = () => async.timeout(0).then(() => res.push(5)); + const f1 = () => Promise.resolve(true).then(() => res.push(1)); + const f2 = () => async.timeout(10).then(() => res.push(2)); + const f3 = () => Promise.resolve(true).then(() => res.push(3)); + const f4 = () => async.timeout(20).then(() => res.push(4)); + const f5 = () => async.timeout(0).then(() => res.push(5)); return queue.queue(f1).then(() => { return queue.queue(f2).then(() => { @@ -560,16 +560,16 @@ suite('Async', () => { }); test('events', async function () { - let queue = new async.Queue(); + const queue = new async.Queue(); let drained = false; const onDrained = Event.toPromise(queue.onDrained).then(() => drained = true); - let res: number[] = []; + const res: number[] = []; - let f1 = () => async.timeout(10).then(() => res.push(2)); - let f2 = () => async.timeout(20).then(() => res.push(4)); - let f3 = () => async.timeout(0).then(() => res.push(5)); + const f1 = () => async.timeout(10).then(() => res.push(2)); + const f2 = () => async.timeout(20).then(() => res.push(4)); + const f3 = () => async.timeout(0).then(() => res.push(5)); const q1 = queue.queue(f1); const q2 = queue.queue(f2); @@ -589,7 +589,7 @@ suite('Async', () => { suite('ResourceQueue', () => { test('simple', async function () { - let queue = new async.ResourceQueue(); + const queue = new async.ResourceQueue(); await queue.whenDrained(); // returns immediately since empty @@ -651,7 +651,7 @@ suite('Async', () => { }); test('error case', async () => { - let expectedError = new Error('fail'); + const expectedError = new Error('fail'); try { await async.retry(() => { return Promise.reject(expectedError); @@ -727,13 +727,13 @@ suite('Async', () => { // next finishes after async.timeout let firstDone = false; - let firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); + const firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); let secondDone = false; - let secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); + const secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); let thirdDone = false; - let thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); + const thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); await Promise.all([firstRes, secondRes, thirdRes]); assert.ok(pendingDone); @@ -1177,7 +1177,7 @@ suite('Async', () => { }); test('do not accept too much work', async () => { - let handled: number[] = []; + const handled: number[] = []; const handler = (units: readonly number[]) => handled.push(...units); const worker = new async.ThrottledWorker({ @@ -1203,7 +1203,7 @@ suite('Async', () => { }); test('do not accept too much work (account for max chunk size', async () => { - let handled: number[] = []; + const handled: number[] = []; const handler = (units: readonly number[]) => handled.push(...units); const worker = new async.ThrottledWorker({ @@ -1222,7 +1222,7 @@ suite('Async', () => { }); test('disposed', async () => { - let handled: number[] = []; + const handled: number[] = []; const handler = (units: readonly number[]) => handled.push(...units); const worker = new async.ThrottledWorker({ diff --git a/src/vs/base/test/common/buffer.test.ts b/src/vs/base/test/common/buffer.test.ts index ef7c1254d3..08cb744384 100644 --- a/src/vs/base/test/common/buffer.test.ts +++ b/src/vs/base/test/common/buffer.test.ts @@ -40,7 +40,7 @@ suite('Buffer', () => { test('bufferWriteableStream - basics (no error)', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -50,7 +50,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -70,7 +70,7 @@ suite('Buffer', () => { test('bufferWriteableStream - basics (error)', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -80,7 +80,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -105,7 +105,7 @@ suite('Buffer', () => { await timeout(0); stream.end(VSBuffer.fromString('World')); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -115,7 +115,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -134,12 +134,12 @@ suite('Buffer', () => { await timeout(0); stream.error(new Error()); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -170,12 +170,12 @@ suite('Buffer', () => { ended = true; }); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -189,7 +189,7 @@ suite('Buffer', () => { test('bufferWriteableStream - nothing happens after end()', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -233,7 +233,7 @@ suite('Buffer', () => { test('bufferWriteableStream - pause/resume (simple)', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -243,7 +243,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -270,7 +270,7 @@ suite('Buffer', () => { test('bufferWriteableStream - pause/resume (pause after first write)', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -280,7 +280,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -310,7 +310,7 @@ suite('Buffer', () => { test('bufferWriteableStream - pause/resume (error)', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -320,7 +320,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); @@ -348,7 +348,7 @@ suite('Buffer', () => { test('bufferWriteableStream - destroy', async () => { const stream = newWriteableBufferStream(); - let chunks: VSBuffer[] = []; + const chunks: VSBuffer[] = []; stream.on('data', data => { chunks.push(data); }); @@ -358,7 +358,7 @@ suite('Buffer', () => { ended = true; }); - let errors: Error[] = []; + const errors: Error[] = []; stream.on('error', error => { errors.push(error); }); diff --git a/src/vs/base/test/common/cancellation.test.ts b/src/vs/base/test/common/cancellation.test.ts index ec9fac91fd..8d516aa2c9 100644 --- a/src/vs/base/test/common/cancellation.test.ts +++ b/src/vs/base/test/common/cancellation.test.ts @@ -27,7 +27,7 @@ suite('CancellationToken', function () { test('cancel happens only once', function () { - let source = new CancellationTokenSource(); + const source = new CancellationTokenSource(); assert.strictEqual(source.token.isCancellationRequested, false); let cancelCount = 0; @@ -47,7 +47,7 @@ suite('CancellationToken', function () { let count = 0; - let source = new CancellationTokenSource(); + const source = new CancellationTokenSource(); source.token.onCancellationRequested(function () { count += 1; }); @@ -84,7 +84,7 @@ suite('CancellationToken', function () { let count = 0; - let source = new CancellationTokenSource(); + const source = new CancellationTokenSource(); source.token.onCancellationRequested(function () { count += 1; }); @@ -98,7 +98,7 @@ suite('CancellationToken', function () { let count = 0; - let source = new CancellationTokenSource(); + const source = new CancellationTokenSource(); source.token.onCancellationRequested(function () { count += 1; }); @@ -110,8 +110,8 @@ suite('CancellationToken', function () { test('parent cancels child', function () { - let parent = new CancellationTokenSource(); - let child = new CancellationTokenSource(parent.token); + const parent = new CancellationTokenSource(); + const child = new CancellationTokenSource(parent.token); let count = 0; child.token.onCancellationRequested(() => count += 1); diff --git a/src/vs/base/test/common/collections.test.ts b/src/vs/base/test/common/collections.test.ts index b194909720..653eab91b4 100644 --- a/src/vs/base/test/common/collections.test.ts +++ b/src/vs/base/test/common/collections.test.ts @@ -8,41 +8,17 @@ import * as collections from 'vs/base/common/collections'; suite('Collections', () => { - test('forEach', () => { - collections.forEach({}, () => assert(false)); - collections.forEach(Object.create(null), () => assert(false)); - - let count = 0; - collections.forEach({ toString: 123 }, () => count++); - assert.strictEqual(count, 1); - - count = 0; - let dict = Object.create(null); - dict['toString'] = 123; - collections.forEach(dict, () => count++); - assert.strictEqual(count, 1); - - collections.forEach(dict, () => false); - - collections.forEach(dict, (x, remove) => remove()); - assert.strictEqual(dict['toString'], undefined); - - // don't iterate over properties that are not on the object itself - let test = Object.create({ 'derived': true }); - collections.forEach(test, () => assert(false)); - }); - test('groupBy', () => { const group1 = 'a', group2 = 'b'; const value1 = 1, value2 = 2, value3 = 3; - let source = [ + const source = [ { key: group1, value: value1 }, { key: group1, value: value2 }, { key: group2, value: value3 }, ]; - let grouped = collections.groupBy(source, x => x.key); + const grouped = collections.groupBy(source, x => x.key); // Group 1 assert.strictEqual(grouped[group1].length, 2); diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index 429ab7c804..9b885236ef 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -9,7 +9,7 @@ import { Color, HSLA, HSVA, RGBA } from 'vs/base/common/color'; suite('Color', () => { test('isLighterColor', () => { - let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); + const color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); assert.ok(color1.isLighterThan(color2)); @@ -18,7 +18,7 @@ suite('Color', () => { }); test('getLighterColor', () => { - let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); + const color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); assert.deepStrictEqual(color1.hsla, Color.getLighterColor(color1, color2).hsla); assert.deepStrictEqual(new HSLA(0, 0, 0.916, 1), Color.getLighterColor(color2, color1).hsla); @@ -29,14 +29,14 @@ suite('Color', () => { }); test('isDarkerColor', () => { - let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); + const color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); assert.ok(color2.isDarkerThan(color1)); }); test('getDarkerColor', () => { - let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); + const color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1)); assert.deepStrictEqual(color2.hsla, Color.getDarkerColor(color2, color1).hsla); assert.deepStrictEqual(new HSLA(60, 1, 0.392, 1), Color.getDarkerColor(color1, color2).hsla); diff --git a/src/vs/base/test/common/diff/diff.test.ts b/src/vs/base/test/common/diff/diff.test.ts index 661d96b984..a6eec89f52 100644 --- a/src/vs/base/test/common/diff/diff.test.ts +++ b/src/vs/base/test/common/diff/diff.test.ts @@ -25,8 +25,8 @@ function maskBasedSubstring(str: string, mask: boolean[]): string { } function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffChange[], answerStr: string, onlyLength: boolean = false): void { - let originalMask = createArray(originalStr.length, true); - let modifiedMask = createArray(modifiedStr.length, true); + const originalMask = createArray(originalStr.length, true); + const modifiedMask = createArray(modifiedStr.length, true); let i, j, change; for (i = 0; i < changes.length; i++) { @@ -45,8 +45,8 @@ function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffCh } } - let originalAnswer = maskBasedSubstring(originalStr, originalMask); - let modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask); + const originalAnswer = maskBasedSubstring(originalStr, originalMask); + const modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask); if (onlyLength) { assert.strictEqual(originalAnswer.length, answerStr.length); @@ -58,8 +58,8 @@ function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffCh } function lcsInnerTest(originalStr: string, modifiedStr: string, answerStr: string, onlyLength: boolean = false): void { - let diff = new LcsDiff(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr)); - let changes = diff.ComputeDiff(false).changes; + const diff = new LcsDiff(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr)); + const changes = diff.ComputeDiff(false).changes; assertAnswer(originalStr, modifiedStr, changes, answerStr, onlyLength); } @@ -98,8 +98,8 @@ suite('Diff', () => { suite('Diff - Ported from VS', () => { test('using continue processing predicate to quit early', function () { - let left = 'abcdef'; - let right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf'; + const left = 'abcdef'; + const right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf'; // We use a long non-matching portion at the end of the right-side string, so the backwards tracking logic // doesn't get there first. @@ -155,7 +155,7 @@ suite('Diff - Ported from VS', () => { diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) { assert(longestMatchSoFar <= 2); // We never see a match of length > 2 - let hitYet = hitSecondMatch; + const hitYet = hitSecondMatch; hitSecondMatch = longestMatchSoFar > 1; // Continue processing as long as there hasn't been a match made. return !hitYet; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 6a78700532..a979625dee 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -8,7 +8,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { errorHandler, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { AsyncEmitter, DebounceEmitter, Emitter, Event, EventBufferer, EventMultiplexer, IWaitUntil, MicrotaskEmitter, PauseableEmitter, Relay } from 'vs/base/common/event'; import { DisposableStore, IDisposable, isDisposable, setDisposableTracker, toDisposable } from 'vs/base/common/lifecycle'; -import { DisposableTracker } from 'vs/base/test/common/utils'; +import { observableValue, transaction } from 'vs/base/common/observable'; +import { DisposableTracker, ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; namespace Samples { @@ -49,7 +50,7 @@ suite('Event utils dispose', function () { const actualInstances = tracker.getTrackedDisposables(); assert.strictEqual(actualInstances.length, expected.length); - for (let item of actualInstances) { + for (const item of actualInstances) { assert.ok(instances.has(item)); } @@ -76,7 +77,7 @@ suite('Event utils dispose', function () { assertDisposablesCount(1); // snaphot only listen when `evens` is being listened on let all = 0; - let leaked = evens(n => all += n); + const leaked = evens(n => all += n); assert.ok(isDisposable(leaked)); assertDisposablesCount(3); @@ -92,7 +93,7 @@ suite('Event utils dispose', function () { assertDisposablesCount(1); // debounce only listens when `debounce` is being listened on let all = 0; - let leaked = debounced(n => all += n); + const leaked = debounced(n => all += n); assert.ok(isDisposable(leaked)); assertDisposablesCount(3); @@ -111,9 +112,9 @@ suite('Event', function () { test('Emitter plain', function () { - let doc = new Samples.Document3(); + const doc = new Samples.Document3(); - let subscription = doc.onDidChange(counter.onEvent, counter); + const subscription = doc.onDidChange(counter.onEvent, counter); doc.setText('far'); doc.setText('boo'); @@ -127,9 +128,9 @@ suite('Event', function () { test('Emitter, bucket', function () { - let bucket: IDisposable[] = []; - let doc = new Samples.Document3(); - let subscription = doc.onDidChange(counter.onEvent, counter, bucket); + const bucket: IDisposable[] = []; + const doc = new Samples.Document3(); + const subscription = doc.onDidChange(counter.onEvent, counter, bucket); doc.setText('far'); doc.setText('boo'); @@ -149,9 +150,9 @@ suite('Event', function () { test('Emitter, store', function () { - let bucket = new DisposableStore(); - let doc = new Samples.Document3(); - let subscription = doc.onDidChange(counter.onEvent, counter, bucket); + const bucket = new DisposableStore(); + const doc = new Samples.Document3(); + const subscription = doc.onDidChange(counter.onEvent, counter, bucket); doc.setText('far'); doc.setText('boo'); @@ -171,7 +172,7 @@ suite('Event', function () { let firstCount = 0; let lastCount = 0; - let a = new Emitter({ + const a = new Emitter({ onFirstListenerAdd() { firstCount += 1; }, onLastListenerRemove() { lastCount += 1; } }); @@ -197,7 +198,7 @@ suite('Event', function () { setUnexpectedErrorHandler(() => null); try { - let a = new Emitter(); + const a = new Emitter(); let hit = false; a.event(function () { // eslint-disable-next-line no-throw-literal @@ -221,9 +222,9 @@ suite('Event', function () { } const context = {}; - let emitter = new Emitter(); - let reg1 = emitter.event(listener, context); - let reg2 = emitter.event(listener, context); + const emitter = new Emitter(); + const reg1 = emitter.event(listener, context); + const reg2 = emitter.event(listener, context); emitter.fire(undefined); assert.strictEqual(counter, 2); @@ -238,9 +239,9 @@ suite('Event', function () { }); test('Debounce Event', function (done: () => void) { - let doc = new Samples.Document3(); + const doc = new Samples.Document3(); - let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[] | undefined, cur) => { + const onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[] | undefined, cur) => { if (!prev) { prev = [cur]; } else if (prev.indexOf(cur) < 0) { @@ -270,7 +271,7 @@ suite('Event', function () { test('Debounce Event - leading', async function () { const emitter = new Emitter(); - let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); + const debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); let calls = 0; debounced(() => { @@ -286,7 +287,7 @@ suite('Event', function () { test('Debounce Event - leading', async function () { const emitter = new Emitter(); - let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); + const debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); let calls = 0; debounced(() => { @@ -303,9 +304,9 @@ suite('Event', function () { test('Debounce Event - leading reset', async function () { const emitter = new Emitter(); - let debounced = Event.debounce(emitter.event, (l, e) => l ? l + 1 : 1, 0, /*leading=*/true); + const debounced = Event.debounce(emitter.event, (l, e) => l ? l + 1 : 1, 0, /*leading=*/true); - let calls: number[] = []; + const calls: number[] = []; debounced((e) => calls.push(e)); emitter.fire(1); @@ -396,7 +397,7 @@ suite('AsyncEmitter', function () { bar: number; } - let emitter = new AsyncEmitter(); + const emitter = new AsyncEmitter(); emitter.event(e => { assert.strictEqual(e.foo, true); @@ -415,7 +416,7 @@ suite('AsyncEmitter', function () { } let globalState = 0; - let emitter = new AsyncEmitter(); + const emitter = new AsyncEmitter(); emitter.event(e => { e.waitUntil(timeout(10).then(_ => { @@ -439,9 +440,9 @@ suite('AsyncEmitter', function () { interface E extends IWaitUntil { foo: number; } - let events: number[] = []; + const events: number[] = []; let done = false; - let emitter = new AsyncEmitter(); + const emitter = new AsyncEmitter(); // e1 emitter.event(e => { @@ -473,7 +474,7 @@ suite('AsyncEmitter', function () { } let globalState = 0; - let emitter = new AsyncEmitter(); + const emitter = new AsyncEmitter(); emitter.event(e => { globalState += 1; @@ -624,6 +625,33 @@ suite('PausableEmitter', function () { }); }); +suite('Event utils - ensureNoDisposablesAreLeakedInTestSuite', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('fromObservable', function () { + + const obs = observableValue('test', 12); + const event = Event.fromObservable(obs); + + const values: number[] = []; + const d = event(n => { values.push(n); }); + + obs.set(3, undefined); + obs.set(13, undefined); + obs.set(3, undefined); + obs.set(33, undefined); + obs.set(1, undefined); + + transaction(tx => { + obs.set(334, tx); + obs.set(99, tx); + }); + + assert.deepStrictEqual(values, ([3, 13, 3, 33, 1, 99])); + d.dispose(); + }); +}); + suite('Event utils', () => { suite('EventBufferer', () => { @@ -1033,7 +1061,7 @@ suite('Event utils', () => { const event = eventEmitter.event; let i = 0; - let log = new Array(); + const log = new Array(); const disposable = Event.runAndSubscribeWithStore(event, (e, disposables) => { const idx = i++; log.push({ label: 'handleEvent', data: e || null, idx }); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 4b6c0feee1..3c66087c54 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { anyScore, createMatches, fuzzyScore, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer, IFilter, IMatch, matchesCamelCase, matchesContiguousSubString, matchesPrefix, matchesStrictPrefix, matchesSubString, matchesWords, or } from 'vs/base/common/filters'; function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number }[]) { - let r = filter(word, wordToMatchAgainst); + const r = filter(word, wordToMatchAgainst); assert(r, `${word} didn't match ${wordToMatchAgainst}`); if (highlights) { assert.deepStrictEqual(r, highlights); @@ -21,7 +21,7 @@ suite('Filters', () => { test('or', () => { let filter: IFilter; let counters: number[]; - let newFilter = function (i: number, r: boolean): IFilter { + const newFilter = function (i: number, r: boolean): IFilter { return function (): IMatch[] { counters[i]++; return r as any; }; }; @@ -223,10 +223,10 @@ suite('Filters', () => { }); function assertMatches(pattern: string, word: string, decoratedWord: string | undefined, filter: FuzzyScorer, opts: { patternPos?: number; wordPos?: number; firstMatchCanBeWeak?: boolean } = {}) { - let r = filter(pattern, pattern.toLowerCase(), opts.patternPos || 0, word, word.toLowerCase(), opts.wordPos || 0, { firstMatchCanBeWeak: opts.firstMatchCanBeWeak ?? false, boostFullMatch: true }); + const r = filter(pattern, pattern.toLowerCase(), opts.patternPos || 0, word, word.toLowerCase(), opts.wordPos || 0, { firstMatchCanBeWeak: opts.firstMatchCanBeWeak ?? false, boostFullMatch: true }); assert.ok(!decoratedWord === !r); if (r) { - let matches = createMatches(r); + const matches = createMatches(r); let actualWord = ''; let pos = 0; for (const match of matches) { @@ -403,8 +403,8 @@ suite('Filters', () => { }); test('Cannot set property \'1\' of undefined, #26511', function () { - let word = new Array(123).join('a'); - let pattern = new Array(120).join('a'); + const word = new Array(123).join('a'); + const pattern = new Array(120).join('a'); fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0); assert.ok(true); // must not explode }); @@ -559,18 +559,18 @@ suite('Filters', () => { }); test('configurable full match boost', function () { - let prefix = 'create'; - let a = 'createModelServices'; - let b = 'create'; + const prefix = 'create'; + const a = 'createModelServices'; + const b = 'create'; - let aBoost = fuzzyScore(prefix, prefix, 0, a, a.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); - let bBoost = fuzzyScore(prefix, prefix, 0, b, b.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); + const aBoost = fuzzyScore(prefix, prefix, 0, a, a.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); + const bBoost = fuzzyScore(prefix, prefix, 0, b, b.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); assert.ok(aBoost); assert.ok(bBoost); assert.ok(aBoost[0] < bBoost[0]); - let aScore = fuzzyScore(prefix, prefix, 0, a, a.toLowerCase(), 0, { boostFullMatch: false, firstMatchCanBeWeak: true }); - let bScore = fuzzyScore(prefix, prefix, 0, b, b.toLowerCase(), 0, { boostFullMatch: false, firstMatchCanBeWeak: true }); + const aScore = fuzzyScore(prefix, prefix, 0, a, a.toLowerCase(), 0, { boostFullMatch: false, firstMatchCanBeWeak: true }); + const bScore = fuzzyScore(prefix, prefix, 0, b, b.toLowerCase(), 0, { boostFullMatch: false, firstMatchCanBeWeak: true }); assert.ok(aScore); assert.ok(bScore); assert.ok(aScore[0] === bScore[0]); diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index 182a42d9bb..ceb701fd14 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -119,7 +119,7 @@ suite('Fuzzy Scorer', () => { scores.push(_doScore(target, '4', true)); // no match // Assert scoring order - let sortedScores = scores.concat().sort((a, b) => b[0] - a[0]); + const sortedScores = scores.concat().sort((a, b) => b[0] - a[0]); assert.deepStrictEqual(scores, sortedScores); // Assert scoring positions @@ -228,7 +228,7 @@ suite('Fuzzy Scorer', () => { test('scoreItem - multiple', function () { const resource = URI.file('/xyz/some/path/someFile123.txt'); - let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor); + const res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor); assert.ok(res1.score); assert.strictEqual(res1.labelMatch?.length, 1); assert.strictEqual(res1.labelMatch![0].start, 0); @@ -237,7 +237,7 @@ suite('Fuzzy Scorer', () => { assert.strictEqual(res1.descriptionMatch![0].start, 1); assert.strictEqual(res1.descriptionMatch![0].end, 4); - let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor); + const res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor); assert.ok(res2.score); assert.strictEqual(res1.score, res2.score); assert.strictEqual(res2.labelMatch?.length, 1); @@ -247,7 +247,7 @@ suite('Fuzzy Scorer', () => { assert.strictEqual(res2.descriptionMatch![0].start, 1); assert.strictEqual(res2.descriptionMatch![0].end, 4); - let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor); + const res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor); assert.ok(res3.score); assert.ok(res3.score > res2.score); assert.strictEqual(res3.labelMatch?.length, 1); @@ -257,7 +257,7 @@ suite('Fuzzy Scorer', () => { assert.strictEqual(res3.descriptionMatch![0].start, 1); assert.strictEqual(res3.descriptionMatch![0].end, 4); - let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor); + const res4 = scoreItem(resource, 'path z y', true, ResourceAccessor); assert.ok(res4.score); assert.ok(res4.score < res2.score); assert.strictEqual(res4.labelMatch?.length, 0); @@ -271,11 +271,11 @@ suite('Fuzzy Scorer', () => { test('scoreItem - multiple with cache yields different results', function () { const resource = URI.file('/xyz/some/path/someFile123.txt'); const cache = {}; - let res1 = scoreItem(resource, 'xyz sm', true, ResourceAccessor, cache); + const res1 = scoreItem(resource, 'xyz sm', true, ResourceAccessor, cache); assert.ok(res1.score); // from the cache's perspective this should be a totally different query - let res2 = scoreItem(resource, 'xyz "sm"', true, ResourceAccessor, cache); + const res2 = scoreItem(resource, 'xyz "sm"', true, ResourceAccessor, cache); assert.ok(!res2.score); }); @@ -576,7 +576,7 @@ suite('Fuzzy Scorer', () => { const resourceC = URI.file('/unrelated/the/path/other/fileC.txt'); // Resource A part of path - let query = 'somepath'; + const query = 'somepath'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceA); @@ -595,7 +595,7 @@ suite('Fuzzy Scorer', () => { const resourceC = URI.file('/unrelated/the/path/other/fileC.txt'); // Resource A part of path - let query = 'file'; + const query = 'file'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceA); @@ -614,7 +614,7 @@ suite('Fuzzy Scorer', () => { const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); // Resource A part of path - let query = 'somepath'; + const query = 'somepath'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceA); @@ -632,9 +632,9 @@ suite('Fuzzy Scorer', () => { const resourceB = URI.file('config/test.js'); const resourceC = URI.file('config/test/t2.js'); - let query = 'co/te'; + const query = 'co/te'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + const res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); assert.strictEqual(res[1], resourceA); assert.strictEqual(res[2], resourceC); @@ -644,9 +644,9 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('parts/quick/arrow-left-dark.svg'); const resourceB = URI.file('parts/quickopen/quickopen.ts'); - let query = 'partsquick'; + const query = 'partsquick'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + const res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); assert.strictEqual(res[1], resourceA); }); @@ -670,7 +670,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('config/test/openthisAnythingHandler.js'); const resourceB = URI.file('config/test/openthisisnotsorelevantforthequeryAnyHand.js'); - let query = 'AH'; + const query = 'AH'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -685,7 +685,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('config/test/examasdaple.js'); const resourceB = URI.file('config/test/exampleasdaasd.ts'); - let query = 'xp'; + const query = 'xp'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -700,7 +700,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('config/test/examasdaple/file.js'); const resourceB = URI.file('config/test/exampleasdaasd/file.ts'); - let query = 'xp'; + const query = 'xp'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -715,7 +715,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('config/example/thisfile.ts'); const resourceB = URI.file('config/24234243244/example/file.js'); - let query = 'exfile'; + const query = 'exfile'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -754,7 +754,7 @@ suite('Fuzzy Scorer', () => { const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js'); const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js'); - let query = 'StatVideoindex'; + const query = 'StatVideoindex'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceC); @@ -767,7 +767,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('src/build-helper/store/redux.ts'); const resourceB = URI.file('src/repository/store/redux.ts'); - let query = 'reproreduxts'; + const query = 'reproreduxts'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -781,7 +781,7 @@ suite('Fuzzy Scorer', () => { const resourceB = URI.file('photobook/src/components/ApprovalPageHeader/index.js'); const resourceC = URI.file('photobook/src/canvasComponents/BookPage/index.js'); - let query = 'bookpageIndex'; + const query = 'bookpageIndex'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceC); @@ -794,7 +794,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('ui/src/utils/constants.js'); const resourceB = URI.file('ui/src/ui/Icons/index.js'); - let query = isWindows ? 'ui\\icons' : 'ui/icons'; + const query = isWindows ? 'ui\\icons' : 'ui/icons'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -807,7 +807,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('ui/src/components/IDInput/index.js'); const resourceB = URI.file('ui/src/ui/Input/index.js'); - let query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; + const query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -820,7 +820,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo'); const resourceB = URI.file('django/core/signals.py'); - let query = 'djancosig'; + const query = 'djancosig'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -834,7 +834,7 @@ suite('Fuzzy Scorer', () => { const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php'); const resourceC = URI.file('duowanVideo/wap/protected/config.php'); - let query = 'protectedconfig.php'; + const query = 'protectedconfig.php'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceA); @@ -851,7 +851,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml'); const resourceB = URI.file('cmd/gradient/main.go'); - let query = 'gradientmain'; + const query = 'gradientmain'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -864,7 +864,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('alpha-beta-cappa.txt'); const resourceB = URI.file('abc.txt'); - let query = 'abc'; + const query = 'abc'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -877,7 +877,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('xerxes-yak-zubba/index.js'); const resourceB = URI.file('xyz/index.js'); - let query = 'xyz'; + const query = 'xyz'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -890,7 +890,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('AssymblyInfo.cs'); const resourceB = URI.file('IAsynchronousTask.java'); - let query = 'async'; + const query = 'async'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -903,7 +903,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js'); const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js'); - let query = 'partisettings'; + const query = 'partisettings'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -916,7 +916,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('Trilby.TrilbyTV.Web.Portal/Views/Systems/Index.cshtml'); const resourceB = URI.file('Trilby.TrilbyTV.Web.Portal/Areas/Admins/Views/Tips/Index.cshtml'); - let query = 'tipsindex.cshtml'; + const query = 'tipsindex.cshtml'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -929,7 +929,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('editor/core/components/tests/list-view-spec.js'); const resourceB = URI.file('editor/core/components/list-view.js'); - let query = 'listview'; + const query = 'listview'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -943,7 +943,7 @@ suite('Fuzzy Scorer', () => { const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts'); const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts'); - let query = 'filesexplorerview.ts'; + const query = 'filesexplorerview.ts'; let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -956,7 +956,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('lists.php'); const resourceB = URI.file('lib/Lists.php'); - let query = 'Lists.php'; + const query = 'Lists.php'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceB); @@ -1039,7 +1039,7 @@ suite('Fuzzy Scorer', () => { const resourceA = URI.file('test/smoke/src/main.ts'); const resourceB = URI.file('src/vs/editor/common/services/semantikTokensProviderStyling.ts'); - let query = 'smoke main.ts'; + const query = 'smoke main.ts'; let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); assert.strictEqual(res[0], resourceA); @@ -1120,7 +1120,7 @@ suite('Fuzzy Scorer', () => { assert.strictEqual(query.values?.[1].normalized, 'World'); assert.strictEqual(query.values?.[1].normalizedLowercase, 'World'.toLowerCase()); - let restoredQuery = pieceToQuery(query.values!); + const restoredQuery = pieceToQuery(query.values!); assert.strictEqual(restoredQuery.original, query.original); assert.strictEqual(restoredQuery.values?.length, query.values?.length); assert.strictEqual(restoredQuery.containsPathSeparator, query.containsPathSeparator); diff --git a/src/vs/base/test/common/glob.test.ts b/src/vs/base/test/common/glob.test.ts index 3253a1bdb8..2c009b3fef 100644 --- a/src/vs/base/test/common/glob.test.ts +++ b/src/vs/base/test/common/glob.test.ts @@ -203,7 +203,7 @@ suite('Glob', () => { }); test('file / folder match', function () { - let p = '**/node_modules/**'; + const p = '**/node_modules/**'; assertGlobMatch(p, 'node_modules'); assertGlobMatch(p, 'node_modules/'); @@ -469,8 +469,8 @@ suite('Glob', () => { }); test('expression support (single)', function () { - let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; - let hasSibling = (name: string) => siblings.indexOf(name) !== -1; + const siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; // { "**/*.js": { "when": "$(basename).ts" } } let expression: glob.IExpression = { @@ -505,11 +505,11 @@ suite('Glob', () => { }); test('expression support (multiple)', function () { - let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; - let hasSibling = (name: string) => siblings.indexOf(name) !== -1; + const siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; // { "**/*.js": { "when": "$(basename).ts" } } - let expression: glob.IExpression = { + const expression: glob.IExpression = { '**/*.js': { when: '$(basename).ts' }, '**/*.as': true, '**/*.foo': false, @@ -632,7 +632,7 @@ suite('Glob', () => { }); test('cached properly', function () { - let p = '**/*.js'; + const p = '**/*.js'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'testing/foo.js'); @@ -692,7 +692,7 @@ suite('Glob', () => { }); test('invalid glob', function () { - let p = '**/*(.js'; + const p = '**/*(.js'; assertNoGlobMatch(p, 'foo.js'); }); @@ -710,13 +710,13 @@ suite('Glob', () => { }); test('expression with disabled glob', function () { - let expr = { '**/*.js': false }; + const expr = { '**/*.js': false }; assert.strictEqual(glob.match(expr, 'foo.js'), null); }); test('expression with two non-trivia globs', function () { - let expr = { + const expr = { '**/*.j?': true, '**/*.t?': true }; @@ -726,7 +726,7 @@ suite('Glob', () => { }); test('expression with non-trivia glob (issue 144458)', function () { - let pattern = '**/p*'; + const pattern = '**/p*'; assert.strictEqual(glob.match(pattern, 'foo/barp'), false); assert.strictEqual(glob.match(pattern, 'foo/bar/ap'), false); @@ -751,19 +751,19 @@ suite('Glob', () => { }); test('expression with empty glob', function () { - let expr = { '': true }; + const expr = { '': true }; assert.strictEqual(glob.match(expr, 'foo.js'), null); }); test('expression with other falsy value', function () { - let expr = { '**/*.js': 0 } as any; + const expr = { '**/*.js': 0 } as any; assert.strictEqual(glob.match(expr, 'foo.js'), '**/*.js'); }); test('expression with two basename globs', function () { - let expr = { + const expr = { '**/bar': true, '**/baz': true }; @@ -776,14 +776,14 @@ suite('Glob', () => { }); test('expression with two basename globs and a siblings expression', function () { - let expr = { + const expr = { '**/bar': true, '**/baz': true, '**/*.js': { when: '$(basename).ts' } }; - let siblings = ['foo.ts', 'foo.js', 'foo', 'bar']; - let hasSibling = (name: string) => siblings.indexOf(name) !== -1; + const siblings = ['foo.ts', 'foo.js', 'foo', 'bar']; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; assert.strictEqual(glob.match(expr, 'bar', hasSibling), '**/bar'); assert.strictEqual(glob.match(expr, 'foo', hasSibling), null); @@ -798,7 +798,7 @@ suite('Glob', () => { }); test('expression with multipe basename globs', function () { - let expr = { + const expr = { '**/bar': true, '{**/baz,**/foo}': true }; @@ -838,9 +838,9 @@ suite('Glob', () => { assert.strictEqual(glob.parse('{**/baz,**/foo}')('baz/bar', 'bar'), false); assert.strictEqual(glob.parse('{**/baz,**/foo}')('baz/foo', 'foo'), true); - let expr = { '**/*.js': { when: '$(basename).ts' } }; - let siblings = ['foo.ts', 'foo.js']; - let hasSibling = (name: string) => siblings.indexOf(name) !== -1; + const expr = { '**/*.js': { when: '$(basename).ts' } }; + const siblings = ['foo.ts', 'foo.js']; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; assert.strictEqual(glob.parse(expr)('bar/baz.js', 'baz.js', hasSibling), null); assert.strictEqual(glob.parse(expr)('bar/foo.js', 'foo.js', hasSibling), '**/*.js'); @@ -990,7 +990,7 @@ suite('Glob', () => { ]); const siblings = ['baz', 'baz.zip', 'nope']; - let hasSibling = (name: string) => siblings.indexOf(name) !== -1; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; testOptimizationForPaths({ '**/foo/123/**': { when: '$(basename).zip' }, '**/bar/123/**': true @@ -1020,14 +1020,14 @@ suite('Glob', () => { test('relative pattern - glob star', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '**/*.cs' }; + const p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '**/*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\bar\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.ts'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\Program.cs'); assertNoGlobMatch(p, 'C:\\other\\DNXConsoleApp\\foo\\Program.ts'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '**/*.cs' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '**/*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); assertGlobMatch(p, '/DNXConsoleApp/foo/bar/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.ts'); @@ -1038,14 +1038,14 @@ suite('Glob', () => { test('relative pattern - single star', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '*.cs' }; + const p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\bar\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.ts'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\Program.cs'); assertNoGlobMatch(p, 'C:\\other\\DNXConsoleApp\\foo\\Program.ts'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '*.cs' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/bar/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.ts'); @@ -1056,11 +1056,11 @@ suite('Glob', () => { test('relative pattern - single star with path', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs' }; + const p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\something\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/something/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } @@ -1068,11 +1068,11 @@ suite('Glob', () => { test('relative pattern - single star alone', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo\\something\\Program.cs', pattern: '*' }; + const p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo\\something\\Program.cs', pattern: '*' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\something\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo/something/Program.cs', pattern: '*' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo/something/Program.cs', pattern: '*' }; assertGlobMatch(p, '/DNXConsoleApp/foo/something/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } @@ -1080,13 +1080,13 @@ suite('Glob', () => { test('relative pattern - ignores case on macOS/Windows', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs' }; + const p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\something\\Program.cs'.toLowerCase()); } else if (isMacintosh) { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/something/Program.cs'.toLowerCase()); } else if (isLinux) { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; assertNoGlobMatch(p, '/DNXConsoleApp/foo/something/Program.cs'.toLowerCase()); } }); @@ -1097,27 +1097,27 @@ suite('Glob', () => { test('relative pattern - #57475', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'styles/style.css' }; + const p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'styles/style.css' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\styles\\style.css'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'styles/style.css' }; + const p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'styles/style.css' }; assertGlobMatch(p, '/DNXConsoleApp/foo/styles/style.css'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } }); test('URI match', () => { - let p = 'scheme:/**/*.md'; + const p = 'scheme:/**/*.md'; assertGlobMatch(p, URI.file('super/duper/long/some/file.md').with({ scheme: 'scheme' }).toString()); }); test('expression fails when siblings use promises (https://github.com/microsoft/vscode/issues/146294)', async function () { - let siblings = ['test.html', 'test.txt', 'test.ts']; - let hasSibling = (name: string) => Promise.resolve(siblings.indexOf(name) !== -1); + const siblings = ['test.html', 'test.txt', 'test.ts']; + const hasSibling = (name: string) => Promise.resolve(siblings.indexOf(name) !== -1); // { "**/*.js": { "when": "$(basename).ts" } } - let expression: glob.IExpression = { + const expression: glob.IExpression = { '**/test.js': { when: '$(basename).js' }, '**/*.js': { when: '$(basename).ts' } }; diff --git a/src/vs/base/test/common/history.test.ts b/src/vs/base/test/common/history.test.ts index 1252c81f8f..929d7a7631 100644 --- a/src/vs/base/test/common/history.test.ts +++ b/src/vs/base/test/common/history.test.ts @@ -148,7 +148,7 @@ suite('History Navigator', () => { }); function toArray(historyNavigator: HistoryNavigator): Array { - let result: Array = []; + const result: Array = []; historyNavigator.first(); if (historyNavigator.current()) { do { diff --git a/src/vs/base/test/common/iconLabels.test.ts b/src/vs/base/test/common/iconLabels.test.ts index 8bde354333..9f6c5795a9 100644 --- a/src/vs/base/test/common/iconLabels.test.ts +++ b/src/vs/base/test/common/iconLabels.test.ts @@ -13,7 +13,7 @@ export interface IIconFilter { } function filterOk(filter: IIconFilter, word: string, target: IParsedLabelWithIcons, highlights?: { start: number; end: number }[]) { - let r = filter(word, target); + const r = filter(word, target); assert(r); if (highlights) { assert.deepStrictEqual(r, highlights); diff --git a/src/vs/base/test/common/json.test.ts b/src/vs/base/test/common/json.test.ts index f4081e0292..8f66b752a3 100644 --- a/src/vs/base/test/common/json.test.ts +++ b/src/vs/base/test/common/json.test.ts @@ -7,7 +7,7 @@ import { createScanner, Node, parse, ParseError, ParseErrorCode, ParseOptions, p import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; function assertKinds(text: string, ...kinds: SyntaxKind[]): void { - let scanner = createScanner(text); + const scanner = createScanner(text); let kind: SyntaxKind; while ((kind = scanner.scan()) !== SyntaxKind.EOF) { assert.strictEqual(kind, kinds.shift()); @@ -15,15 +15,15 @@ function assertKinds(text: string, ...kinds: SyntaxKind[]): void { assert.strictEqual(kinds.length, 0); } function assertScanError(text: string, expectedKind: SyntaxKind, scanError: ScanError): void { - let scanner = createScanner(text); + const scanner = createScanner(text); scanner.scan(); assert.strictEqual(scanner.getToken(), expectedKind); assert.strictEqual(scanner.getTokenError(), scanError); } function assertValidParse(input: string, expected: any, options?: ParseOptions): void { - let errors: ParseError[] = []; - let actual = parse(input, errors, options); + const errors: ParseError[] = []; + const actual = parse(input, errors, options); if (errors.length !== 0) { assert(false, getParseErrorMessage(errors[0].error)); @@ -32,21 +32,21 @@ function assertValidParse(input: string, expected: any, options?: ParseOptions): } function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void { - let errors: ParseError[] = []; - let actual = parse(input, errors, options); + const errors: ParseError[] = []; + const actual = parse(input, errors, options); assert(errors.length > 0); assert.deepStrictEqual(actual, expected); } function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void { - let errors: ParseError[] = []; - let actual = parseTree(input, errors, options); + const errors: ParseError[] = []; + const actual = parseTree(input, errors, options); assert.deepStrictEqual(errors.map(e => e.error, expected), expectedErrors); - let checkParent = (node: Node) => { + const checkParent = (node: Node) => { if (node.children) { - for (let child of node.children) { + for (const child of node.children) { assert.strictEqual(node, child.parent); delete (child).parent; // delete to avoid recursion in deep equal checkParent(child); @@ -216,7 +216,7 @@ suite('JSON', () => { }); test('parse: disallow commments', () => { - let options = { disallowComments: true }; + const options = { disallowComments: true }; assertValidParse('[ 1, 2, null, "foo" ]', [1, 2, null, 'foo'], options); assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options); diff --git a/src/vs/base/test/common/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index a308efb6ca..d14326cbef 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -12,7 +12,7 @@ suite('JSON - edits', () => { assert(edits); let lastEditOffset = content.length; for (let i = edits.length - 1; i >= 0; i--) { - let edit = edits[i]; + const edit = edits[i]; assert(edit.offset >= 0 && edit.length >= 0 && edit.offset + edit.length <= content.length); assert(typeof edit.content === 'string'); assert(lastEditOffset >= edit.offset + edit.length); // make sure all edits are ordered @@ -22,7 +22,7 @@ suite('JSON - edits', () => { assert.strictEqual(content, expected); } - let formatterOptions: FormattingOptions = { + const formatterOptions: FormattingOptions = { insertSpaces: true, tabSize: 2, eol: '\n' @@ -119,74 +119,74 @@ suite('JSON - edits', () => { }); test('insert item at 0', () => { - let content = '[\n 2,\n 3\n]'; - let edits = setProperty(content, [0], 1, formatterOptions); + const content = '[\n 2,\n 3\n]'; + const edits = setProperty(content, [0], 1, formatterOptions); assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]'); }); test('insert item at 0 in empty array', () => { - let content = '[\n]'; - let edits = setProperty(content, [0], 1, formatterOptions); + const content = '[\n]'; + const edits = setProperty(content, [0], 1, formatterOptions); assertEdit(content, edits, '[\n 1\n]'); }); test('insert item at an index', () => { - let content = '[\n 1,\n 3\n]'; - let edits = setProperty(content, [1], 2, formatterOptions); + const content = '[\n 1,\n 3\n]'; + const edits = setProperty(content, [1], 2, formatterOptions); assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]'); }); test('insert item at an index im empty array', () => { - let content = '[\n]'; - let edits = setProperty(content, [1], 1, formatterOptions); + const content = '[\n]'; + const edits = setProperty(content, [1], 1, formatterOptions); assertEdit(content, edits, '[\n 1\n]'); }); test('insert item at end index', () => { - let content = '[\n 1,\n 2\n]'; - let edits = setProperty(content, [2], 3, formatterOptions); + const content = '[\n 1,\n 2\n]'; + const edits = setProperty(content, [2], 3, formatterOptions); assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]'); }); test('insert item at end to empty array', () => { - let content = '[\n]'; - let edits = setProperty(content, [-1], 'bar', formatterOptions); + const content = '[\n]'; + const edits = setProperty(content, [-1], 'bar', formatterOptions); assertEdit(content, edits, '[\n "bar"\n]'); }); test('insert item at end', () => { - let content = '[\n 1,\n 2\n]'; - let edits = setProperty(content, [-1], 'bar', formatterOptions); + const content = '[\n 1,\n 2\n]'; + const edits = setProperty(content, [-1], 'bar', formatterOptions); assertEdit(content, edits, '[\n 1,\n 2,\n "bar"\n]'); }); test('remove item in array with one item', () => { - let content = '[\n 1\n]'; - let edits = setProperty(content, [0], undefined, formatterOptions); + const content = '[\n 1\n]'; + const edits = setProperty(content, [0], undefined, formatterOptions); assertEdit(content, edits, '[]'); }); test('remove item in the middle of the array', () => { - let content = '[\n 1,\n 2,\n 3\n]'; - let edits = setProperty(content, [1], undefined, formatterOptions); + const content = '[\n 1,\n 2,\n 3\n]'; + const edits = setProperty(content, [1], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n 3\n]'); }); test('remove last item in the array', () => { - let content = '[\n 1,\n 2,\n "bar"\n]'; - let edits = setProperty(content, [2], undefined, formatterOptions); + const content = '[\n 1,\n 2,\n "bar"\n]'; + const edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n 2\n]'); }); test('remove last item in the array if ends with comma', () => { - let content = '[\n 1,\n "foo",\n "bar",\n]'; - let edits = setProperty(content, [2], undefined, formatterOptions); + const content = '[\n 1,\n "foo",\n "bar",\n]'; + const edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n "foo"\n]'); }); test('remove last item in the array if there is a comment in the beginning', () => { - let content = '// This is a comment\n[\n 1,\n "foo",\n "bar"\n]'; - let edits = setProperty(content, [2], undefined, formatterOptions); + const content = '// This is a comment\n[\n 1,\n "foo",\n "bar"\n]'; + const edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '// This is a comment\n[\n 1,\n "foo"\n]'); }); diff --git a/src/vs/base/test/common/jsonFormatter.test.ts b/src/vs/base/test/common/jsonFormatter.test.ts index 677dd24a92..ad17e96770 100644 --- a/src/vs/base/test/common/jsonFormatter.test.ts +++ b/src/vs/base/test/common/jsonFormatter.test.ts @@ -20,7 +20,7 @@ suite('JSON - formatter', () => { let lastEditOffset = content.length; for (let i = edits.length - 1; i >= 0; i--) { - let edit = edits[i]; + const edit = edits[i]; assert(edit.offset >= 0 && edit.length >= 0 && edit.offset + edit.length <= content.length); assert(typeof edit.content === 'string'); assert(lastEditOffset >= edit.offset + edit.length); // make sure all edits are ordered diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts index c9c0bb4cf8..22755f5b84 100644 --- a/src/vs/base/test/common/labels.test.ts +++ b/src/vs/base/test/common/labels.test.ts @@ -139,20 +139,6 @@ suite('Labels', () => { assert.strictEqual(labels.template(t, { dirty: '* ', activeEditorShort: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), '* somefile.txt - monaco - Visual Studio Code'); }); - (isWindows ? test.skip : test)('getBaseLabel - unix', () => { - assert.strictEqual(labels.getBaseLabel('/some/folder/file.txt'), 'file.txt'); - assert.strictEqual(labels.getBaseLabel('/some/folder'), 'folder'); - assert.strictEqual(labels.getBaseLabel('/'), '/'); - }); - - (!isWindows ? test.skip : test)('getBaseLabel - windows', () => { - assert.strictEqual(labels.getBaseLabel('c:'), 'C:'); - assert.strictEqual(labels.getBaseLabel('c:\\'), 'C:'); - assert.strictEqual(labels.getBaseLabel('c:\\some\\folder\\file.txt'), 'file.txt'); - assert.strictEqual(labels.getBaseLabel('c:\\some\\folder'), 'folder'); - assert.strictEqual(labels.getBaseLabel('c:\\some\\f:older'), 'f:older'); // https://github.com/microsoft/vscode-remote-release/issues/4227 - }); - test('mnemonicButtonLabel', () => { assert.strictEqual(labels.mnemonicButtonLabel('Hello World'), 'Hello World'); assert.strictEqual(labels.mnemonicButtonLabel(''), ''); diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 91f636f9bd..8f31c050df 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -95,16 +95,16 @@ suite('Lifecycle', () => { }); test('Action bar has broken accessibility #100273', function () { - let array = [{ dispose() { } }, { dispose() { } }]; - let array2 = dispose(array); + const array = [{ dispose() { } }, { dispose() { } }]; + const array2 = dispose(array); assert.strictEqual(array.length, 2); assert.strictEqual(array2.length, 0); assert.ok(array !== array2); - let set = new Set([{ dispose() { } }, { dispose() { } }]); - let setValues = set.values(); - let setValues2 = dispose(setValues); + const set = new Set([{ dispose() { } }, { dispose() { } }]); + const setValues = set.values(); + const setValues2 = dispose(setValues); assert.ok(setValues === setValues2); }); diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index 3b9c2d62f5..876b6649fd 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -66,7 +66,7 @@ suite('LinkedList', function () { }); test('Push/toArray', () => { - let list = new LinkedList(); + const list = new LinkedList(); list.push('foo'); list.push('bar'); list.push('far'); @@ -107,7 +107,7 @@ suite('LinkedList', function () { }); test('unshift/toArray', () => { - let list = new LinkedList(); + const list = new LinkedList(); list.unshift('foo'); list.unshift('bar'); list.unshift('far'); @@ -116,20 +116,20 @@ suite('LinkedList', function () { }); test('pop/unshift', function () { - let list = new LinkedList(); + const list = new LinkedList(); list.push('a'); list.push('b'); assertElements(list, 'a', 'b'); - let a = list.shift(); + const a = list.shift(); assert.strictEqual(a, 'a'); assertElements(list, 'b'); list.unshift('a'); assertElements(list, 'a', 'b'); - let b = list.pop(); + const b = list.pop(); assert.strictEqual(b, 'b'); assertElements(list, 'a'); }); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 4b9dbf548a..3f4a18d423 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri'; suite('Map', () => { test('LinkedMap - Simple', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('bk', 'bv'); assert.deepStrictEqual([...map.keys()], ['ak', 'bk']); @@ -24,7 +24,7 @@ suite('Map', () => { }); test('LinkedMap - Touch Old one', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('ak', 'av', Touch.AsOld); assert.deepStrictEqual([...map.keys()], ['ak']); @@ -32,7 +32,7 @@ suite('Map', () => { }); test('LinkedMap - Touch New one', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('ak', 'av', Touch.AsNew); assert.deepStrictEqual([...map.keys()], ['ak']); @@ -40,7 +40,7 @@ suite('Map', () => { }); test('LinkedMap - Touch Old two', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('bk', 'bv'); map.set('bk', 'bv', Touch.AsOld); @@ -49,7 +49,7 @@ suite('Map', () => { }); test('LinkedMap - Touch New two', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('bk', 'bv'); map.set('ak', 'av', Touch.AsNew); @@ -58,7 +58,7 @@ suite('Map', () => { }); test('LinkedMap - Touch Old from middle', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('bk', 'bv'); map.set('ck', 'cv'); @@ -68,7 +68,7 @@ suite('Map', () => { }); test('LinkedMap - Touch New from middle', () => { - let map = new LinkedMap(); + const map = new LinkedMap(); map.set('ak', 'av'); map.set('bk', 'bv'); map.set('ck', 'cv'); @@ -199,7 +199,7 @@ suite('Map', () => { cache.set(7, 7); assert.strictEqual(cache.size, 5); assert.deepStrictEqual([...cache.keys()], [3, 4, 5, 6, 7]); - let values: number[] = []; + const values: number[] = []; [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [3, 4, 5, 6, 7]); }); @@ -214,7 +214,7 @@ suite('Map', () => { assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]); cache.peek(4); assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]); - let values: number[] = []; + const values: number[] = []; [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [1, 2, 3, 4, 5]); }); @@ -235,7 +235,7 @@ suite('Map', () => { cache.set(i, i); } assert.deepStrictEqual(cache.size, 15); - let values: number[] = []; + const values: number[] = []; for (let i = 6; i <= 20; i++) { values.push(cache.get(i)!); assert.strictEqual(cache.get(i), i); @@ -253,7 +253,7 @@ suite('Map', () => { cache.set(11, 11); assert.strictEqual(cache.size, 5); assert.deepStrictEqual([...cache.keys()], [7, 8, 9, 10, 11]); - let values: number[] = []; + const values: number[] = []; [...cache.keys()].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [7, 8, 9, 10, 11]); assert.deepStrictEqual([...cache.values()], values); @@ -486,7 +486,7 @@ suite('Map', () => { assert.ok(trie._isBalanced(), 'TST is not balanced'); let i = 0; - for (let [key, value] of trie) { + for (const [key, value] of trie) { const expected = elements[i++]; assert.ok(expected); assert.strictEqual(key, expected[0]); @@ -513,7 +513,7 @@ suite('Map', () => { // iterator let iterCount = 0; - for (let [key, value] of trie) { + for (const [key, value] of trie) { assert.strictEqual(value, map.get(key)); iterCount++; } @@ -557,7 +557,7 @@ suite('Map', () => { test('TernarySearchTree - findLongestMatch', function () { - let trie = TernarySearchTree.forStrings(); + const trie = TernarySearchTree.forStrings(); trie.set('foo', 1); trie.set('foobar', 2); trie.set('foobaz', 3); @@ -573,7 +573,7 @@ suite('Map', () => { }); test('TernarySearchTree - basics', function () { - let trie = new TernarySearchTree(new StringIterator()); + const trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('bar', 2); @@ -641,7 +641,7 @@ suite('Map', () => { }); test('TernarySearchTree (PathSegments) - basics', function () { - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/user/foo/bar', 1); trie.set('/user/foo', 2); @@ -666,7 +666,7 @@ suite('Map', () => { test('TernarySearchTree - (AVL) set', function () { { // rotate left - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/fileA', 1); trie.set('/fileB', 2); trie.set('/fileC', 3); @@ -675,7 +675,7 @@ suite('Map', () => { { // rotate left (inside middle) - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/foo/fileA', 1); trie.set('/foo/fileB', 2); trie.set('/foo/fileC', 3); @@ -684,7 +684,7 @@ suite('Map', () => { { // rotate right - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/fileC', 3); trie.set('/fileB', 2); trie.set('/fileA', 1); @@ -693,7 +693,7 @@ suite('Map', () => { { // rotate right (inside middle) - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/mid/fileC', 3); trie.set('/mid/fileB', 2); trie.set('/mid/fileA', 1); @@ -702,7 +702,7 @@ suite('Map', () => { { // rotate right, left - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/fileD', 7); trie.set('/fileB', 2); trie.set('/fileG', 42); @@ -714,7 +714,7 @@ suite('Map', () => { { // rotate left, right - let trie = new TernarySearchTree(new PathIterator()); + const trie = new TernarySearchTree(new PathIterator()); trie.set('/fileJ', 42); trie.set('/fileZ', 73); trie.set('/fileE', 15); @@ -727,7 +727,7 @@ suite('Map', () => { test('TernarySearchTree - (BST) delete', function () { - let trie = new TernarySearchTree(new StringIterator()); + const trie = new TernarySearchTree(new StringIterator()); // delete root trie.set('d', 1); @@ -757,7 +757,7 @@ suite('Map', () => { test('TernarySearchTree - (AVL) delete', function () { - let trie = new TernarySearchTree(new StringIterator()); + const trie = new TernarySearchTree(new StringIterator()); trie.clear(); trie.set('d', 1); @@ -811,7 +811,7 @@ suite('Map', () => { const tst = TernarySearchTree.forUris(); - for (let item of keys) { + for (const item of keys) { tst.set(item, true); } @@ -824,7 +824,7 @@ suite('Map', () => { const keys = ['C', 'A', 'D', 'B',]; const tst = TernarySearchTree.forStrings(); - for (let item of keys) { + for (const item of keys) { tst.set(item, true); } assertTstDfs(tst, ['A', true], ['B', true], ['C', true], ['D', true]); @@ -850,14 +850,14 @@ suite('Map', () => { } const tst = TernarySearchTree.forUris(); - for (let item of keys) { + for (const item of keys) { tst.set(item, true); - assert.ok(tst._isBalanced()); + assert.ok(tst._isBalanced(), `SET${item}|${keys.map(String).join()}`); } - for (let item of keys) { + for (const item of keys) { tst.delete(item); - assert.ok(tst._isBalanced()); + assert.ok(tst._isBalanced(), `DEL${item}|${keys.map(String).join()}`); } } }); @@ -953,7 +953,7 @@ suite('Map', () => { }); test('TernarySearchTree (URI) - basics', function () { - let trie = new TernarySearchTree(new UriIterator(() => false, () => false)); + const trie = new TernarySearchTree(new UriIterator(() => false, () => false)); trie.set(URI.file('/user/foo/bar'), 1); trie.set(URI.file('/user/foo'), 2); @@ -972,7 +972,7 @@ suite('Map', () => { }); test('TernarySearchTree (URI) - query parameters', function () { - let trie = new TernarySearchTree(new UriIterator(() => false, () => true)); + const trie = new TernarySearchTree(new UriIterator(() => false, () => true)); const root = URI.parse('memfs:/?param=1'); trie.set(root, 1); @@ -1066,7 +1066,7 @@ suite('Map', () => { }); test('TernarySearchTree (ConfigKeySegments) - basics', function () { - let trie = new TernarySearchTree(new ConfigKeysIterator()); + const trie = new TernarySearchTree(new ConfigKeysIterator()); trie.set('config.foo.bar', 1); trie.set('config.foo', 2); @@ -1107,7 +1107,7 @@ suite('Map', () => { map.set('boo', 4); let item: IteratorResult<[string, number]>; - let iter = map.findSuperstr('config'); + const iter = map.findSuperstr('config'); item = iter!.next(); assert.strictEqual(item.value[1], 2); @@ -1171,7 +1171,7 @@ suite('Map', () => { Object.freeze(keys); tst.fill(true, keys); - for (let key of keys) { + for (const key of keys) { assert.ok(tst.get(key), key); } }); @@ -1188,7 +1188,7 @@ suite('Map', () => { assert.strictEqual(map.size, 0); - let res = map.set(resource1, 1); + const res = map.set(resource1, 1); assert.ok(res === map); map.set(resource2, '2'); map.set(resource3, true); @@ -1343,7 +1343,7 @@ suite.skip('TST, perf', function () { const uris: URI[] = []; function randomWord(): string { let result = ''; - let length = 4 + Math.floor(Math.random() * 4); + const length = 4 + Math.floor(Math.random() * 4); for (let i = 0; i < length; i++) { result += (Math.random() * 26 + 65).toString(36); } @@ -1360,7 +1360,7 @@ suite.skip('TST, perf', function () { let len = 4 + Math.floor(Math.random() * 4); - let segments: string[] = []; + const segments: string[] = []; for (; len >= 0; len--) { segments.push(words[Math.floor(Math.random() * words.length)]); } @@ -1384,7 +1384,7 @@ suite.skip('TST, perf', function () { setup(() => { tree = TernarySearchTree.forUris(); - for (let uri of sampleUris) { + for (const uri of sampleUris) { tree.set(uri, true); } }); @@ -1406,15 +1406,15 @@ suite.skip('TST, perf', function () { }); perfTest('TST, insert', function () { - let insertTree = TernarySearchTree.forUris(); - for (let uri of sampleUris) { + const insertTree = TernarySearchTree.forUris(); + for (const uri of sampleUris) { insertTree.set(uri, true); } }); perfTest('TST, lookup', function () { let match = 0; - for (let candidate of candidates) { + for (const candidate of candidates) { if (tree.has(candidate)) { match += 1; } @@ -1424,7 +1424,7 @@ suite.skip('TST, perf', function () { perfTest('TST, substr', function () { let match = 0; - for (let candidate of candidates) { + for (const candidate of candidates) { if (tree.findSubstr(candidate)) { match += 1; } @@ -1433,7 +1433,7 @@ suite.skip('TST, perf', function () { }); perfTest('TST, superstr', function () { - for (let candidate of candidates) { + for (const candidate of candidates) { tree.findSuperstr(candidate); } }); diff --git a/src/vs/base/test/common/marshalling.test.ts b/src/vs/base/test/common/marshalling.test.ts index 890a44f520..c0851b7c9d 100644 --- a/src/vs/base/test/common/marshalling.test.ts +++ b/src/vs/base/test/common/marshalling.test.ts @@ -9,9 +9,9 @@ import { URI } from 'vs/base/common/uri'; suite('Marshalling', () => { test('RegExp', () => { - let value = /foo/img; - let raw = stringify(value); - let clone = parse(raw); + const value = /foo/img; + const raw = stringify(value); + const clone = parse(raw); assert.strictEqual(value.source, clone.source); assert.strictEqual(value.global, clone.global); diff --git a/src/vs/base/test/common/network.test.ts b/src/vs/base/test/common/network.test.ts index fe16ecabfe..f395bb6765 100644 --- a/src/vs/base/test/common/network.test.ts +++ b/src/vs/base/test/common/network.test.ts @@ -38,19 +38,19 @@ suite('network', () => { }); (isWeb ? test.skip : test)('FileAccess: query and fragment is dropped (native)', () => { - let originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); - let browserUri = FileAccess.asBrowserUri(originalFileUri); + const originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); + const browserUri = FileAccess.asBrowserUri(originalFileUri); assert.strictEqual(browserUri.query, ''); assert.strictEqual(browserUri.fragment, ''); }); (isWeb ? test.skip : test)('FileAccess: query and fragment is kept if URI is already of same scheme (native)', () => { - let originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); - let browserUri = FileAccess.asBrowserUri(originalFileUri.with({ scheme: Schemas.vscodeFileResource })); + const originalFileUri = URI.file('network.test.ts').with({ query: 'foo=bar', fragment: 'something' }); + const browserUri = FileAccess.asBrowserUri(originalFileUri.with({ scheme: Schemas.vscodeFileResource })); assert.strictEqual(browserUri.query, 'foo=bar'); assert.strictEqual(browserUri.fragment, 'something'); - let fileUri = FileAccess.asFileUri(originalFileUri); + const fileUri = FileAccess.asFileUri(originalFileUri); assert.strictEqual(fileUri.query, 'foo=bar'); assert.strictEqual(fileUri.fragment, 'something'); }); diff --git a/src/vs/base/test/common/objects.test.ts b/src/vs/base/test/common/objects.test.ts index 689427c27c..eb0bb86ad3 100644 --- a/src/vs/base/test/common/objects.test.ts +++ b/src/vs/base/test/common/objects.test.ts @@ -5,12 +5,12 @@ import * as assert from 'assert'; import * as objects from 'vs/base/common/objects'; -let check = (one: any, other: any, msg: string) => { +const check = (one: any, other: any, msg: string) => { assert(objects.equals(one, other), msg); assert(objects.equals(other, one), '[reverse] ' + msg); }; -let checkNot = (one: any, other: any, msg: string) => { +const checkNot = (one: any, other: any, msg: string) => { assert(!objects.equals(one, other), msg); assert(!objects.equals(other, one), '[reverse] ' + msg); }; @@ -55,7 +55,7 @@ suite('Objects', () => { test('mixin - array', function () { - let foo: any = {}; + const foo: any = {}; objects.mixin(foo, { bar: [1, 2, 3] }); assert(foo.bar); @@ -67,11 +67,11 @@ suite('Objects', () => { }); test('mixin - no overwrite', function () { - let foo: any = { + const foo: any = { bar: '123' }; - let bar: any = { + const bar: any = { bar: '456' }; @@ -81,8 +81,8 @@ suite('Objects', () => { }); test('cloneAndChange', () => { - let o1 = { something: 'hello' }; - let o = { + const o1 = { something: 'hello' }; + const o = { o1: o1, o2: o1 }; @@ -90,21 +90,21 @@ suite('Objects', () => { }); test('safeStringify', () => { - let obj1: any = { + const obj1: any = { friend: null }; - let obj2: any = { + const obj2: any = { friend: null }; obj1.friend = obj2; obj2.friend = obj1; - let arr: any = [1]; + const arr: any = [1]; arr.push(arr); - let circular: any = { + const circular: any = { a: 42, b: null, c: [ @@ -119,7 +119,7 @@ suite('Objects', () => { circular.b = circular; circular.d = arr; - let result = objects.safeStringify(circular); + const result = objects.safeStringify(circular); assert.deepStrictEqual(JSON.parse(result), { a: 42, @@ -137,7 +137,7 @@ suite('Objects', () => { }); test('distinct', () => { - let base = { + const base = { one: 'one', two: 2, three: { diff --git a/src/vs/base/test/common/observable.test.ts b/src/vs/base/test/common/observable.test.ts new file mode 100644 index 0000000000..7f3120fc58 --- /dev/null +++ b/src/vs/base/test/common/observable.test.ts @@ -0,0 +1,507 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Emitter } from 'vs/base/common/event'; +import { ISettableObservable, autorun, derived, ITransaction, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable'; +import { BaseObservable, IObservable, IObserver } from 'vs/base/common/observableImpl/base'; + +suite('observable integration', () => { + test('basic observable + autorun', () => { + const log = new Log(); + const observable = observableValue('MyObservableValue', 0); + + autorun('MyAutorun', (reader) => { + log.log(`value: ${observable.read(reader)}`); + }); + assert.deepStrictEqual(log.getAndClearEntries(), ['value: 0']); + + observable.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), ['value: 1']); + + observable.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + transaction((tx) => { + observable.set(2, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + observable.set(3, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + }); + + assert.deepStrictEqual(log.getAndClearEntries(), ['value: 3']); + }); + + test('basic computed + autorun', () => { + const log = new Log(); + const observable1 = observableValue('MyObservableValue1', 0); + const observable2 = observableValue('MyObservableValue2', 0); + + const computed = derived('computed', (reader) => { + const value1 = observable1.read(reader); + const value2 = observable2.read(reader); + const sum = value1 + value2; + log.log(`recompute: ${value1} + ${value2} = ${sum}`); + return sum; + }); + + autorun('MyAutorun', (reader) => { + log.log(`value: ${computed.read(reader)}`); + }); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: 0 + 0 = 0', + 'value: 0', + ]); + + observable1.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: 1 + 0 = 1', + 'value: 1', + ]); + + observable2.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: 1 + 1 = 2', + 'value: 2', + ]); + + transaction((tx) => { + observable1.set(5, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + observable2.set(5, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + }); + + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: 5 + 5 = 10', + 'value: 10', + ]); + + transaction((tx) => { + observable1.set(6, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + observable2.set(4, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + }); + + assert.deepStrictEqual(log.getAndClearEntries(), ['recompute: 6 + 4 = 10']); + }); + + test('read during transaction', () => { + const log = new Log(); + const observable1 = observableValue('MyObservableValue1', 0); + const observable2 = observableValue('MyObservableValue2', 0); + + const computed = derived('computed', (reader) => { + const value1 = observable1.read(reader); + const value2 = observable2.read(reader); + const sum = value1 + value2; + log.log(`recompute: ${value1} + ${value2} = ${sum}`); + return sum; + }); + + autorun('MyAutorun', (reader) => { + log.log(`value: ${computed.read(reader)}`); + }); + + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: 0 + 0 = 0', + 'value: 0', + ]); + + log.log(`computed is ${computed.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), ['computed is 0']); + + transaction((tx) => { + observable1.set(-1, tx); + log.log(`computed is ${computed.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: -1 + 0 = -1', + 'computed is -1', + ]); + + log.log(`computed is ${computed.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), ['computed is -1']); + + observable2.set(1, tx); + assert.deepStrictEqual(log.getAndClearEntries(), []); + }); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute: -1 + 1 = 0', + 'value: 0', + ]); + }); + + test('topological order', () => { + const log = new Log(); + const observable1 = observableValue('MyObservableValue1', 0); + const observable2 = observableValue('MyObservableValue2', 0); + + const computed1 = derived('computed1', (reader) => { + const value1 = observable1.read(reader); + const value2 = observable2.read(reader); + const sum = value1 + value2; + log.log(`recompute1: ${value1} + ${value2} = ${sum}`); + return sum; + }); + + const computed2 = derived('computed2', (reader) => { + const value1 = computed1.read(reader); + const value2 = observable1.read(reader); + const value3 = observable2.read(reader); + const sum = value1 + value2 + value3; + log.log(`recompute2: ${value1} + ${value2} + ${value3} = ${sum}`); + return sum; + }); + + const computed3 = derived('computed3', (reader) => { + const value1 = computed2.read(reader); + const value2 = observable1.read(reader); + const value3 = observable2.read(reader); + const sum = value1 + value2 + value3; + log.log(`recompute3: ${value1} + ${value2} + ${value3} = ${sum}`); + return sum; + }); + + autorun('MyAutorun', (reader) => { + log.log(`value: ${computed3.read(reader)}`); + }); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute1: 0 + 0 = 0', + 'recompute2: 0 + 0 + 0 = 0', + 'recompute3: 0 + 0 + 0 = 0', + 'value: 0', + ]); + + observable1.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute1: 1 + 0 = 1', + 'recompute2: 1 + 1 + 0 = 2', + 'recompute3: 2 + 1 + 0 = 3', + 'value: 3', + ]); + + transaction((tx) => { + observable1.set(2, tx); + log.log(`computed2: ${computed2.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute1: 2 + 0 = 2', + 'recompute2: 2 + 2 + 0 = 4', + 'computed2: 4', + ]); + + observable1.set(3, tx); + log.log(`computed2: ${computed2.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute1: 3 + 0 = 3', + 'recompute2: 3 + 3 + 0 = 6', + 'computed2: 6', + ]); + }); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute3: 6 + 3 + 0 = 9', + 'value: 9', + ]); + }); + + test('self-disposing autorun', () => { + const log = new Log(); + + const observable1 = new LoggingObservableValue('MyObservableValue1', 0, log); + const observable2 = new LoggingObservableValue('MyObservableValue2', 0, log); + const observable3 = new LoggingObservableValue('MyObservableValue3', 0, log); + + const d = autorun('autorun', (reader) => { + if (observable1.read(reader) >= 2) { + observable2.read(reader); + d.dispose(); + observable3.read(reader); + } + }); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'MyObservableValue1.firstObserverAdded', + 'MyObservableValue1.get', + ]); + + observable1.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'MyObservableValue1.set (value 1)', + 'MyObservableValue1.get', + ]); + + observable1.set(2, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'MyObservableValue1.set (value 2)', + 'MyObservableValue1.get', + 'MyObservableValue2.firstObserverAdded', + 'MyObservableValue2.get', + 'MyObservableValue1.lastObserverRemoved', + 'MyObservableValue2.lastObserverRemoved', + 'MyObservableValue3.get', + ]); + }); + + test('from event', () => { + const log = new Log(); + + let value = 0; + const eventEmitter = new Emitter(); + + let id = 0; + const observable = observableFromEvent( + (handler) => { + const curId = id++; + log.log(`subscribed handler ${curId}`); + const disposable = eventEmitter.event(handler); + + return { + dispose: () => { + log.log(`unsubscribed handler ${curId}`); + disposable.dispose(); + }, + }; + }, + () => { + log.log(`compute value ${value}`); + return value; + } + ); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + log.log(`get value: ${observable.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'compute value 0', + 'get value: 0', + ]); + + log.log(`get value: ${observable.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'compute value 0', + 'get value: 0', + ]); + + const shouldReadObservable = observableValue('shouldReadObservable', true); + + const autorunDisposable = autorun('MyAutorun', (reader) => { + if (shouldReadObservable.read(reader)) { + observable.read(reader); + log.log( + `autorun, should read: true, value: ${observable.read(reader)}` + ); + } else { + log.log(`autorun, should read: false`); + } + }); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'subscribed handler 0', + 'compute value 0', + 'autorun, should read: true, value: 0', + ]); + + log.log(`get value: ${observable.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), ['get value: 0']); + + value = 1; + eventEmitter.fire(); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'compute value 1', + 'autorun, should read: true, value: 1', + ]); + + shouldReadObservable.set(false, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'autorun, should read: false', + 'unsubscribed handler 0', + ]); + + shouldReadObservable.set(true, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'subscribed handler 1', + 'compute value 1', + 'autorun, should read: true, value: 1', + ]); + + autorunDisposable.dispose(); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'unsubscribed handler 1', + ]); + }); + + test('get without observers', () => { + // Maybe this scenario should not be supported. + + const log = new Log(); + const observable1 = observableValue('MyObservableValue1', 0); + const computed1 = derived('computed', (reader) => { + const value1 = observable1.read(reader); + const result = value1 % 3; + log.log(`recompute1: ${value1} % 3 = ${result}`); + return result; + }); + const computed2 = derived('computed', (reader) => { + const value1 = computed1.read(reader); + + const result = value1 * 2; + log.log(`recompute2: ${value1} * 2 = ${result}`); + return result; + }); + const computed3 = derived('computed', (reader) => { + const value1 = computed1.read(reader); + + const result = value1 * 3; + log.log(`recompute3: ${value1} * 3 = ${result}`); + return result; + }); + const computedSum = derived('computed', (reader) => { + const value1 = computed2.read(reader); + const value2 = computed3.read(reader); + + const result = value1 + value2; + log.log(`recompute4: ${value1} + ${value2} = ${result}`); + return result; + }); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + observable1.set(1, undefined); + assert.deepStrictEqual(log.getAndClearEntries(), []); + + log.log(`value: ${computedSum.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute1: 1 % 3 = 1', + 'recompute2: 1 * 2 = 2', + 'recompute3: 1 * 3 = 3', + 'recompute4: 2 + 3 = 5', + 'value: 5', + ]); + + log.log(`value: ${computedSum.get()}`); + assert.deepStrictEqual(log.getAndClearEntries(), [ + 'recompute1: 1 % 3 = 1', + 'recompute2: 1 * 2 = 2', + 'recompute3: 1 * 3 = 3', + 'recompute4: 2 + 3 = 5', + 'value: 5', + ]); + }); +}); + +suite('observable details', () => { + test('1', () => { + const log = new Log(); + + const shouldReadObservable = observableValue('shouldReadObservable', true); + const observable = new LoggingObservableValue('observable', 0, log); + const computed = derived('test', reader => { + if (shouldReadObservable.read(reader)) { + return observable.read(reader) * 2; + } + return 1; + }); + autorun('test', reader => { + const value = computed.read(reader); + log.log(`autorun: ${value}`); + }); + + assert.deepStrictEqual(log.getAndClearEntries(), (["observable.firstObserverAdded", "observable.get", "autorun: 0"])); + + transaction(tx => { + observable.set(1, tx); + assert.deepStrictEqual(log.getAndClearEntries(), (["observable.set (value 1)"])); + + shouldReadObservable.set(false, tx); + assert.deepStrictEqual(log.getAndClearEntries(), ([])); + + computed.get(); + assert.deepStrictEqual(log.getAndClearEntries(), (["observable.lastObserverRemoved"])); + }); + assert.deepStrictEqual(log.getAndClearEntries(), (["autorun: 1"])); + }); +}); + +export class LoggingObserver implements IObserver { + private count = 0; + + constructor(public readonly debugName: string, private readonly log: Log) { + } + + beginUpdate(observable: IObservable): void { + this.count++; + this.log.log(`${this.debugName}.beginUpdate (count ${this.count})`); + } + handleChange(observable: IObservable, change: TChange): void { + this.log.log(`${this.debugName}.handleChange (count ${this.count})`); + } + endUpdate(observable: IObservable): void { + this.log.log(`${this.debugName}.endUpdate (count ${this.count})`); + this.count--; + } +} + +export class LoggingObservableValue + extends BaseObservable + implements ISettableObservable +{ + private value: T; + + constructor(public readonly debugName: string, initialValue: T, private readonly log: Log) { + super(); + this.value = initialValue; + } + + protected override onFirstObserverAdded(): void { + this.log.log(`${this.debugName}.firstObserverAdded`); + } + + protected override onLastObserverRemoved(): void { + this.log.log(`${this.debugName}.lastObserverRemoved`); + } + + public get(): T { + this.log.log(`${this.debugName}.get`); + return this.value; + } + + public set(value: T, tx: ITransaction | undefined, change: TChange): void { + if (this.value === value) { + return; + } + + if (!tx) { + transaction((tx) => { + this.set(value, tx, change); + }, () => `Setting ${this.debugName}`); + return; + } + + this.log.log(`${this.debugName}.set (value ${value})`); + + this.value = value; + + for (const observer of this.observers) { + tx.updateObserver(observer, this); + observer.handleChange(this, change); + } + } + + override toString(): string { + return `${this.debugName}: ${this.value}`; + } +} + +class Log { + private readonly entries: string[] = []; + public log(message: string): void { + this.entries.push(message); + } + + public getAndClearEntries(): string[] { + const entries = [...this.entries]; + this.entries.length = 0; + return entries; + } +} diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts index c2dc9f61c1..8d9badfa6c 100644 --- a/src/vs/base/test/common/processes.test.ts +++ b/src/vs/base/test/common/processes.test.ts @@ -8,7 +8,7 @@ import * as processes from 'vs/base/common/processes'; suite('Processes', () => { test('sanitizeProcessEnvironment', () => { - let env = { + const env = { FOO: 'bar', ELECTRON_ENABLE_STACK_DUMPING: 'x', ELECTRON_ENABLE_LOGGING: 'x', diff --git a/src/vs/base/test/common/resourceTree.test.ts b/src/vs/base/test/common/resourceTree.test.ts index e6da572269..92ac13f1bb 100644 --- a/src/vs/base/test/common/resourceTree.test.ts +++ b/src/vs/base/test/common/resourceTree.test.ts @@ -19,11 +19,11 @@ suite('ResourceTree', function () { tree.add(URI.file('/foo/bar.txt'), 'bar contents'); assert.strictEqual(tree.root.childrenCount, 1); - let foo = tree.root.get('foo')!; + const foo = tree.root.get('foo')!; assert(foo); assert.strictEqual(foo.childrenCount, 1); - let bar = foo.get('bar.txt')!; + const bar = foo.get('bar.txt')!; assert(bar); assert.strictEqual(bar.element, 'bar contents'); diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index ec48ad8e83..998060cc41 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -235,14 +235,14 @@ suite('Resources', () => { }); function assertEqualURI(actual: URI, expected: URI, message?: string, ignoreCase?: boolean) { - let util = ignoreCase ? extUriIgnorePathCase : extUri; + const util = ignoreCase ? extUriIgnorePathCase : extUri; if (!util.isEqual(expected, actual)) { assert.strictEqual(actual.toString(), expected.toString(), message); } } function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean, ignoreCase?: boolean) { - let util = ignoreCase ? extUriIgnorePathCase : extUri; + const util = ignoreCase ? extUriIgnorePathCase : extUri; assert.strictEqual(util.relativePath(u1, u2), expectedPath, `from ${u1.toString()} to ${u2.toString()}`); if (expectedPath !== undefined && !ignoreJoin) { @@ -350,7 +350,7 @@ suite('Resources', () => { function assertIsEqual(u1: URI, u2: URI, ignoreCase: boolean | undefined, expected: boolean) { - let util = ignoreCase ? extUriIgnorePathCase : extUri; + const util = ignoreCase ? extUriIgnorePathCase : extUri; assert.strictEqual(util.isEqual(u1, u2), expected, `${u1.toString()}${expected ? '===' : '!=='}${u2.toString()}`); assert.strictEqual(util.compare(u1, u2) === 0, expected); @@ -363,16 +363,16 @@ suite('Resources', () => { test('isEqual', () => { - let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); - let fileURI2 = isWindows ? URI.file('C:\\foo\\Bar') : URI.file('/foo/Bar'); + const fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); + const fileURI2 = isWindows ? URI.file('C:\\foo\\Bar') : URI.file('/foo/Bar'); assertIsEqual(fileURI, fileURI, true, true); assertIsEqual(fileURI, fileURI, false, true); assertIsEqual(fileURI, fileURI, undefined, true); assertIsEqual(fileURI, fileURI2, true, true); assertIsEqual(fileURI, fileURI2, false, false); - let fileURI3 = URI.parse('foo://server:453/foo/bar'); - let fileURI4 = URI.parse('foo://server:453/foo/Bar'); + const fileURI3 = URI.parse('foo://server:453/foo/bar'); + const fileURI4 = URI.parse('foo://server:453/foo/Bar'); assertIsEqual(fileURI3, fileURI3, true, true); assertIsEqual(fileURI3, fileURI3, false, true); assertIsEqual(fileURI3, fileURI3, undefined, true); @@ -387,8 +387,8 @@ suite('Resources', () => { assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo/'), true, false); assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo?'), true, true); - let fileURI5 = URI.parse('foo://server:453/foo/bar?q=1'); - let fileURI6 = URI.parse('foo://server:453/foo/bar#xy'); + const fileURI5 = URI.parse('foo://server:453/foo/bar?q=1'); + const fileURI6 = URI.parse('foo://server:453/foo/bar#xy'); assertIsEqual(fileURI5, fileURI5, true, true); assertIsEqual(fileURI5, fileURI3, true, false); @@ -399,9 +399,9 @@ suite('Resources', () => { test('isEqualOrParent', () => { - let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); - let fileURI2 = isWindows ? URI.file('c:\\foo') : URI.file('/foo'); - let fileURI2b = isWindows ? URI.file('C:\\Foo\\') : URI.file('/Foo/'); + const fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); + const fileURI2 = isWindows ? URI.file('c:\\foo') : URI.file('/foo'); + const fileURI2b = isWindows ? URI.file('C:\\Foo\\') : URI.file('/Foo/'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI), true, '1'); assert.strictEqual(extUri.isEqualOrParent(fileURI, fileURI), true, '2'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2), true, '3'); @@ -412,9 +412,9 @@ suite('Resources', () => { assert.strictEqual(extUri.isEqualOrParent(fileURI2, fileURI), false, '7'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI2b, fileURI2), true, '8'); - let fileURI3 = URI.parse('foo://server:453/foo/bar/goo'); - let fileURI4 = URI.parse('foo://server:453/foo/'); - let fileURI5 = URI.parse('foo://server:453/foo'); + const fileURI3 = URI.parse('foo://server:453/foo/bar/goo'); + const fileURI4 = URI.parse('foo://server:453/foo/'); + const fileURI5 = URI.parse('foo://server:453/foo'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI3, true), true, '11'); assert.strictEqual(extUri.isEqualOrParent(fileURI3, fileURI3), true, '12'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI4, true), true, '13'); @@ -422,8 +422,8 @@ suite('Resources', () => { assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI, true), false, '15'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI5, fileURI5, true), true, '16'); - let fileURI6 = URI.parse('foo://server:453/foo?q=1'); - let fileURI7 = URI.parse('foo://server:453/foo/bar?q=1'); + const fileURI6 = URI.parse('foo://server:453/foo?q=1'); + const fileURI7 = URI.parse('foo://server:453/foo/bar?q=1'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI5), false, '17'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI6), true, '18'); assert.strictEqual(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI6), true, '19'); diff --git a/src/vs/base/test/common/scrollable.test.ts b/src/vs/base/test/common/scrollable.test.ts index 4607555375..2314330145 100644 --- a/src/vs/base/test/common/scrollable.test.ts +++ b/src/vs/base/test/common/scrollable.test.ts @@ -33,8 +33,8 @@ suite('SmoothScrollingOperation', () => { const LINE_HEIGHT = 20; function extractLines(scrollable: TestSmoothScrollingOperation, now: number): [number, number] { - let scrollTop = scrollable.testTick(now).scrollTop; - let scrollBottom = scrollTop + VIEWPORT_HEIGHT; + const scrollTop = scrollable.testTick(now).scrollTop; + const scrollBottom = scrollTop + VIEWPORT_HEIGHT; const startLineNumber = Math.floor(scrollTop / LINE_HEIGHT); const endLineNumber = Math.ceil(scrollBottom / LINE_HEIGHT); @@ -45,7 +45,8 @@ suite('SmoothScrollingOperation', () => { function simulateSmoothScroll(from: number, to: number): [number, number][] { const scrollable = new TestSmoothScrollingOperation(from, to, VIEWPORT_HEIGHT, 0, ANIMATION_DURATION); - let result: [number, number][] = [], resultLen = 0; + const result: [number, number][] = []; + let resultLen = 0; result[resultLen++] = extractLines(scrollable, 0); result[resultLen++] = extractLines(scrollable, 25); result[resultLen++] = extractLines(scrollable, 50); diff --git a/src/vs/base/test/common/skipList.test.ts b/src/vs/base/test/common/skipList.test.ts index f0ce03ffd9..528d2eeb24 100644 --- a/src/vs/base/test/common/skipList.test.ts +++ b/src/vs/base/test/common/skipList.test.ts @@ -15,10 +15,10 @@ suite('SkipList', function () { assert.strictEqual(list.size, expected.length); assert.deepStrictEqual([...list.values()], expected); - let valuesFromEntries = [...list.entries()].map(entry => entry[1]); + const valuesFromEntries = [...list.entries()].map(entry => entry[1]); assert.deepStrictEqual(valuesFromEntries, expected); - let valuesFromIter = [...list].map(entry => entry[1]); + const valuesFromIter = [...list].map(entry => entry[1]); assert.deepStrictEqual(valuesFromIter, expected); let i = 0; @@ -32,10 +32,10 @@ suite('SkipList', function () { assert.strictEqual(list.size, expected.length); assert.deepStrictEqual([...list.keys()], expected); - let keysFromEntries = [...list.entries()].map(entry => entry[0]); + const keysFromEntries = [...list.entries()].map(entry => entry[0]); assert.deepStrictEqual(keysFromEntries, expected); - let keysFromIter = [...list].map(entry => entry[0]); + const keysFromIter = [...list].map(entry => entry[0]); assert.deepStrictEqual(keysFromIter, expected); let i = 0; @@ -46,7 +46,7 @@ suite('SkipList', function () { } test('set/get/delete', function () { - let list = new SkipList((a, b) => a - b); + const list = new SkipList((a, b) => a - b); assert.strictEqual(list.get(3), undefined); list.set(3, 1); @@ -74,7 +74,7 @@ suite('SkipList', function () { }); test('Figure 3', function () { - let list = new SkipList((a, b) => a - b); + const list = new SkipList((a, b) => a - b); list.set(3, true); list.set(6, true); list.set(7, true); @@ -92,7 +92,7 @@ suite('SkipList', function () { }); test('capacity max', function () { - let list = new SkipList((a, b) => a - b, 10); + const list = new SkipList((a, b) => a - b, 10); list.set(1, true); list.set(2, true); list.set(3, true); @@ -132,7 +132,7 @@ suite('SkipList', function () { } function delArraySorted(array: number[], element: number) { - let idx = binarySearch(array, element, cmp); + const idx = binarySearch(array, element, cmp); if (idx >= 0) { // array = array.slice(0, idx).concat(array.slice(idx)); array.splice(idx, 1); @@ -147,13 +147,13 @@ suite('SkipList', function () { const max = 2 ** 16; const values = new Set(); for (let i = 0; i < max; i++) { - let value = Math.floor(Math.random() * max); + const value = Math.floor(Math.random() * max); values.add(value); } console.log(values.size); // init - let list = new SkipList(cmp, max); + const list = new SkipList(cmp, max); let sw = new StopWatch(true); values.forEach(value => list.set(value, true)); sw.stop(); @@ -166,9 +166,9 @@ suite('SkipList', function () { // get sw = new StopWatch(true); - let someValues = [...values].slice(0, values.size / 4); + const someValues = [...values].slice(0, values.size / 4); someValues.forEach(key => { - let value = list.get(key); // find + const value = list.get(key); // find console.assert(value, '[LIST] must have ' + key); list.get(-key); // miss }); @@ -176,7 +176,7 @@ suite('SkipList', function () { console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`); sw = new StopWatch(true); someValues.forEach(key => { - let idx = binarySearch(array, key, cmp); // find + const idx = binarySearch(array, key, cmp); // find console.assert(idx >= 0, '[ARRAY] must have ' + key); binarySearch(array, -key, cmp); // miss }); diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index fe4ff668b8..82d923caa3 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -155,10 +155,10 @@ suite('Stream', () => { res = stream.write('3'); assert.ok(!res); - let promise1 = stream.write('4'); + const promise1 = stream.write('4'); assert.ok(promise1 instanceof Promise); - let promise2 = stream.write('5'); + const promise2 = stream.write('5'); assert.ok(promise2 instanceof Promise); let drained1 = false; diff --git a/src/vs/base/test/common/stripComments.test.ts b/src/vs/base/test/common/stripComments.test.ts index f557f7e3a3..38f160e371 100644 --- a/src/vs/base/test/common/stripComments.test.ts +++ b/src/vs/base/test/common/stripComments.test.ts @@ -122,4 +122,26 @@ suite('Strip Comments', () => { ].join('\n'); assert.strictEqual(stripComments(content), expected); }); + test('Trailing comma in object', () => { + const content: string = [ + "{", + ` "a": 10,`, + "}" + ].join('\n'); + const expected: string = [ + "{", + ` "a": 10`, + "}" + ].join('\n'); + assert.strictEqual(stripComments(content), expected); + }); + test('Trailing comma in array', () => { + const content: string = [ + `[ "a", "b", "c", ]` + ].join('\n'); + const expected: string = [ + `[ "a", "b", "c" ]` + ].join('\n'); + assert.strictEqual(stripComments(content), expected); + }); }); diff --git a/src/vs/base/test/common/timeTravelScheduler.ts b/src/vs/base/test/common/timeTravelScheduler.ts index 298bd13fe3..ea6dd99a97 100644 --- a/src/vs/base/test/common/timeTravelScheduler.ts +++ b/src/vs/base/test/common/timeTravelScheduler.ts @@ -5,6 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { setTimeout0, setTimeout0IsFaster } from 'vs/base/common/platform'; interface PriorityQueue { length: number; @@ -177,6 +178,8 @@ export class AsyncSchedulerProcessor extends Disposable { Promise.resolve().then(() => { if (this.useSetImmediate) { originalGlobalValues.setImmediate(() => this.process()); + } else if (setTimeout0IsFaster) { + setTimeout0(() => this.process()); } else { originalGlobalValues.setTimeout(() => this.process()); } @@ -190,7 +193,7 @@ export class AsyncSchedulerProcessor extends Disposable { if (this.history.length >= this.maxTaskCount && this.scheduler.hasScheduledTasks) { const lastTasks = this._history.slice(Math.max(0, this.history.length - 10)).map(h => `${h.source.toString()}: ${h.source.stackTrace}`); - let e = new Error(`Queue did not get empty after processing ${this.history.length} items. These are the last ${lastTasks.length} scheduled tasks:\n${lastTasks.join('\n\n\n')}`); + const e = new Error(`Queue did not get empty after processing ${this.history.length} items. These are the last ${lastTasks.length} scheduled tasks:\n${lastTasks.join('\n\n\n')}`); this.lastError = e; throw e; } @@ -368,7 +371,7 @@ function createDateClass(scheduler: Scheduler): DateConstructor { return new (OriginalDate as any)(...args); } - for (let prop in OriginalDate) { + for (const prop in OriginalDate) { if (OriginalDate.hasOwnProperty(prop)) { (SchedulerDate as any)[prop] = (OriginalDate as any)[prop]; } diff --git a/src/vs/base/test/common/troubleshooting.ts b/src/vs/base/test/common/troubleshooting.ts index 1aa4798ed4..e9296e442c 100644 --- a/src/vs/base/test/common/troubleshooting.ts +++ b/src/vs/base/test/common/troubleshooting.ts @@ -47,13 +47,9 @@ export function endTrackingDisposables(): void { } export function beginLoggingFS(withStacks: boolean = false): void { - if ((self).beginLoggingFS) { - (self).beginLoggingFS(withStacks); - } + (self).beginLoggingFS?.(withStacks); } export function endLoggingFS(): void { - if ((self).endLoggingFS) { - (self).endLoggingFS(); - } + (self).endLoggingFS?.(); } diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 11e5076f7f..7c9b808f90 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -88,7 +88,7 @@ suite('URI', () => { }); test('with, identity', () => { - let uri = URI.parse('foo:bar/path'); + const uri = URI.parse('foo:bar/path'); let uri2 = uri.with(null!); assert.ok(uri === uri2); @@ -121,7 +121,7 @@ suite('URI', () => { }); test('with, validation', () => { - let uri = URI.parse('foo:bar/path'); + const uri = URI.parse('foo:bar/path'); assert.throws(() => uri.with({ scheme: 'fai:l' })); assert.throws(() => uri.with({ scheme: 'fäil' })); assert.throws(() => uri.with({ authority: 'fail' })); @@ -281,14 +281,14 @@ suite('URI', () => { }); test('VSCode URI module\'s driveLetterPath regex is incorrect, #32961', function () { - let uri = URI.parse('file:///_:/path'); + const uri = URI.parse('file:///_:/path'); assert.strictEqual(uri.fsPath, isWindows ? '\\_:\\path' : '/_:/path'); }); test('URI#file, no path-is-uri check', () => { // we don't complain here - let value = URI.file('file://path/to/file'); + const value = URI.file('file://path/to/file'); assert.strictEqual(value.scheme, 'file'); assert.strictEqual(value.authority, ''); assert.strictEqual(value.path, '/file://path/to/file'); @@ -475,10 +475,10 @@ suite('URI', () => { }); test.skip('Links in markdown are broken if url contains encoded parameters #79474', function () { - let strIn = 'https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'; - let uri1 = URI.parse(strIn); - let strOut = uri1.toString(); - let uri2 = URI.parse(strOut); + const strIn = 'https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom'; + const uri1 = URI.parse(strIn); + const strOut = uri1.toString(); + const uri2 = URI.parse(strOut); assert.strictEqual(uri1.scheme, uri2.scheme); assert.strictEqual(uri1.authority, uri2.authority); @@ -489,10 +489,10 @@ suite('URI', () => { }); test.skip('Uri#parse can break path-component #45515', function () { - let strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'; - let uri1 = URI.parse(strIn); - let strOut = uri1.toString(); - let uri2 = URI.parse(strOut); + const strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437'; + const uri1 = URI.parse(strIn); + const strOut = uri1.toString(); + const uri2 = URI.parse(strOut); assert.strictEqual(uri1.scheme, uri2.scheme); assert.strictEqual(uri1.authority, uri2.authority); @@ -516,9 +516,9 @@ suite('URI', () => { // console.profile(); // let c = 100000; // while (c-- > 0) { - for (let value of values) { - let data = value.toJSON() as UriComponents; - let clone = URI.revive(data); + for (const value of values) { + const data = value.toJSON() as UriComponents; + const clone = URI.revive(data); assert.strictEqual(clone.scheme, value.scheme); assert.strictEqual(clone.authority, value.authority); diff --git a/src/vs/base/test/node/css.build.test.ts b/src/vs/base/test/node/css.build.test.ts new file mode 100644 index 0000000000..65231061d3 --- /dev/null +++ b/src/vs/base/test/node/css.build.test.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { CSSPluginUtilities, rewriteUrls } from 'vs/css.build'; + +suite('CSSPlugin', () => { + + test('Utilities.pathOf', () => { + assert.strictEqual(CSSPluginUtilities.pathOf(''), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('/a'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a/b/c.css'), 'a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a'), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('a.com/a.css'), 'a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a.css'), 'http://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a.css'), 'https://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a/b/c.css'), 'http://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a/b/c.css'), 'https://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a.css'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a/b/c.css'), '/a/b/'); + }); + + test('Utilities.joinPaths', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.joinPaths(a, b), expected, '<' + a + '> + <' + b + '> = <' + expected + '>'); + } + mytest('', 'a.css', 'a.css'); + mytest('', './a.css', 'a.css'); + mytest('', '././././a.css', 'a.css'); + mytest('', './../a.css', '../a.css'); + mytest('', '../../a.css', '../../a.css'); + mytest('', '../../a/b/c.css', '../../a/b/c.css'); + mytest('/', 'a.css', '/a.css'); + mytest('/', './a.css', '/a.css'); + mytest('/', '././././a.css', '/a.css'); + mytest('/', './../a.css', '/a.css'); + mytest('/', '../../a.css', '/a.css'); + mytest('/', '../../a/b/c.css', '/a/b/c.css'); + mytest('x/y/z/', 'a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './a.css', 'x/y/z/a.css'); + mytest('x/y/z/', '././././a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './../a.css', 'x/y/a.css'); + mytest('x/y/z/', '../../a.css', 'x/a.css'); + mytest('x/y/z/', '../../a/b/c.css', 'x/a/b/c.css'); + + mytest('//a.com/', 'a.css', '//a.com/a.css'); + mytest('//a.com/', './a.css', '//a.com/a.css'); + mytest('//a.com/', '././././a.css', '//a.com/a.css'); + mytest('//a.com/', './../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a/b/c.css', '//a.com/a/b/c.css'); + mytest('//a.com/x/y/z/', 'a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', '././././a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './../a.css', '//a.com/x/y/a.css'); + mytest('//a.com/x/y/z/', '../../a.css', '//a.com/x/a.css'); + mytest('//a.com/x/y/z/', '../../a/b/c.css', '//a.com/x/a/b/c.css'); + + mytest('http://a.com/', 'a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '././././a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a/b/c.css', 'http://a.com/a/b/c.css'); + mytest('http://a.com/x/y/z/', 'a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', '././././a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './../a.css', 'http://a.com/x/y/a.css'); + mytest('http://a.com/x/y/z/', '../../a.css', 'http://a.com/x/a.css'); + mytest('http://a.com/x/y/z/', '../../a/b/c.css', 'http://a.com/x/a/b/c.css'); + + mytest('https://a.com/', 'a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '././././a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a/b/c.css', 'https://a.com/a/b/c.css'); + mytest('https://a.com/x/y/z/', 'a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', '././././a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './../a.css', 'https://a.com/x/y/a.css'); + mytest('https://a.com/x/y/z/', '../../a.css', 'https://a.com/x/a.css'); + mytest('https://a.com/x/y/z/', '../../a/b/c.css', 'https://a.com/x/a/b/c.css'); + }); + + test('Utilities.commonPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonPrefix(a, b), expected, 'prefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonPrefix(b, a), expected, 'prefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaa'); + }); + + test('Utilities.commonFolderPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(a, b), expected, 'folderPrefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(b, a), expected, 'folderPrefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', ''); + mytest('aaaa', 'aaaa', ''); + mytest('aaaaxyz', 'aaaa', ''); + mytest('aaaaxyz', 'aaaatuv', ''); + mytest('/', '/', '/'); + mytest('x/', '', ''); + mytest('x/', 'x/', 'x/'); + mytest('aaaa/', 'aaaa/', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/a', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/atuv', 'aaaa/'); + }); + + test('Utilities.relativePath', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.relativePath(a, b), expected, 'relativePath(<' + a + '>, <' + b + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaatuv'); + + mytest('x/y/aaaaxyz', 'x/aaaatuv', '../aaaatuv'); + mytest('x/y/aaaaxyz', 'x/y/aaaatuv', 'aaaatuv'); + mytest('z/t/aaaaxyz', 'x/y/aaaatuv', '../../x/y/aaaatuv'); + mytest('aaaaxyz', 'x/y/aaaatuv', 'x/y/aaaatuv'); + + mytest('a', '/a', '/a'); + mytest('/', '/a', '/a'); + mytest('/a/b/c', '/a/b/c', '/a/b/c'); + mytest('/a/b', '/a/b/c/d', '/a/b/c/d'); + + mytest('a', 'http://a', 'http://a'); + mytest('/', 'http://a', 'http://a'); + mytest('/a/b/c', 'http://a/b/c', 'http://a/b/c'); + mytest('/a/b', 'http://a/b/c/d', 'http://a/b/c/d'); + + mytest('a', 'https://a', 'https://a'); + mytest('/', 'https://a', 'https://a'); + mytest('/a/b/c', 'https://a/b/c', 'https://a/b/c'); + mytest('/a/b', 'https://a/b/c/d', 'https://a/b/c/d'); + + mytest('x/', '', '../'); + mytest('x/', '', '../'); + mytest('x/', 'x/', ''); + mytest('x/a', 'x/a', 'a'); + }); + + test('Utilities.rewriteUrls', () => { + function mytest(originalFile: string, newFile: string, url: string, expected: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\'' + url + '\'); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\"' + url + '\"); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + url + '); }'), 'sel { background:url(' + expected + '); }'); + } + + // img/img.png + mytest('a.css', 'b.css', 'img/img.png', 'img/img.png'); + mytest('a.css', 't/b.css', 'img/img.png', '../img/img.png'); + mytest('a.css', 'x/y/b.css', 'img/img.png', '../../img/img.png'); + mytest('x/a.css', 'b.css', 'img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 'b.css', 'img/img.png', 'x/y/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'img/img.png', '../../x/y/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'img/img.png', '../y/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'img/img.png', 'img/img.png'); + mytest('/a.css', 'b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'https://www.example.com/x/y/img/img.png'); + + // ../img/img.png + mytest('a.css', 'b.css', '../img/img.png', '../img/img.png'); + mytest('a.css', 't/b.css', '../img/img.png', '../../img/img.png'); + mytest('a.css', 'x/y/b.css', '../img/img.png', '../../../img/img.png'); + mytest('x/a.css', 'b.css', '../img/img.png', 'img/img.png'); + mytest('x/y/a.css', 'b.css', '../img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '../img/img.png', '../../x/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '../img/img.png', '../img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '../img/img.png', '../img/img.png'); + mytest('/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '../img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'https://www.example.com/x/img/img.png'); + + // /img/img.png + mytest('a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 't/b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '/img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'https://www.example.com/img/img.png'); + + // http://example.com/img/img.png + mytest('a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 't/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + + + }); + + test('Utilities.rewriteUrls - quotes and spaces', () => { + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\t\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'\t); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\' ); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \t \'../img/img.png\' \t); }'), 'sel { background:url(../../x/img/img.png); }'); + }); + + test('Bug 9601 - css should ignore data urls', () => { + const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIEltYWdlUmVhZHk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ClC8oVQAAAGnSURBVDiNrZMxTxNxGMZ///9dZWns9a4dTHSABFiuCU5dGt2d9BsQls6GD2LCd2AiQQfixKIJE0ObdKIUSvDa5uLZihP0Sh+HOw3ipOUZ3zzvL2+e932NJBaRe7/Q8Uw5eMRrzXllDU8A5mJkLB+/TflQ+67JXb+5O0FUNS9deLckns/tn2A7hxtDawZvn37Vp78AX8rmxZLDewf89HGJ+fgKCrkrBeuXKPy44hbGN7e8eTbRZwALcFE2nuOy48j6zmaTYP8Qtxaia9A1uLWQYP8QZ7OJI+s7LjsXZeMBIIlLn61xgEbLnqadtiQp7Z0orq8rrq8r7Z1IkqadtkbLnsYBuvTZkpQBhgF7SRVFJRQ3QqW9bgY5P1V6fpoDu4oboaISSqpoGLD3GzAIOEqqaFBBURHF9TWlZxlEktKzruL6mqJi5kmqaBBwJIl7Wf+7LICBIYBSKGyE+LsHuCurzPo9Zv0e7soq/u4BhY0Qpfn68p6HCbHv4Q0qtBPfarLd1LR1nAVWzDNphJq2jjXZbirxrQYV2n0PT9Lih/Rwp/xLCz3T/+gnd2VVRJs/vngAAAAASUVORK5CYII='; + + function mytest(originalFile: string, newFile: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + dataUrl + '); }'), 'sel { background:url(' + dataUrl + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url( \t' + dataUrl + '\t ); }'), 'sel { background:url(' + dataUrl + '); }'); + } + + mytest('a.css', 'b.css'); + mytest('a.css', 't/b.css'); + mytest('a.css', 'x/y/b.css'); + mytest('x/a.css', 'b.css'); + mytest('x/y/a.css', 'b.css'); + mytest('x/y/a.css', 't/u/b.css'); + mytest('x/y/a.css', 'x/u/b.css'); + mytest('x/y/a.css', 'x/y/b.css'); + mytest('/a.css', 'b.css'); + mytest('/a.css', 'x/b.css'); + mytest('/a.css', 'x/y/b.css'); + mytest('/x/a.css', 'b.css'); + mytest('/x/a.css', 'x/b.css'); + mytest('/x/a.css', 'x/y/b.css'); + mytest('/x/y/a.css', 'b.css'); + mytest('/x/y/a.css', 'x/b.css'); + mytest('/x/y/a.css', 'x/y/b.css'); + mytest('/a.css', '/b.css'); + mytest('/a.css', '/b.css'); + mytest('/x/a.css', '/b.css'); + mytest('/x/a.css', '/x/b.css'); + mytest('http://www.example.com/x/y/a.css', 'b.css'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css'); + mytest('https://www.example.com/x/y/a.css', 'b.css'); + }); +}); diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index 2eb00a5b98..37d6e5bf85 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -11,9 +11,11 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { randomPath } from 'vs/base/common/extpath'; import { join, sep } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; -import { Promises, RimRafMode, rimrafSync, SymlinkSupport, writeFileSync } from 'vs/base/node/pfs'; +import { configureFlushOnWrite, Promises, RimRafMode, rimrafSync, SymlinkSupport, writeFileSync } from 'vs/base/node/pfs'; import { flakySuite, getPathFromAmdModule, getRandomTestPath } from 'vs/base/test/node/testUtils'; +configureFlushOnWrite(false); // speed up all unit tests by disabling flush on write + flakySuite('PFS', function () { let testDir: string; @@ -368,24 +370,36 @@ flakySuite('PFS', function () { const smallData = 'Hello World'; const bigData = (new Array(100 * 1024)).join('Large String\n'); - return testWriteFileAndFlush(smallData, smallData, bigData, bigData); + return testWriteFile(smallData, smallData, bigData, bigData); + }); + + test('writeFile (string) - flush on write', async () => { + configureFlushOnWrite(true); + try { + const smallData = 'Hello World'; + const bigData = (new Array(100 * 1024)).join('Large String\n'); + + return await testWriteFile(smallData, smallData, bigData, bigData); + } finally { + configureFlushOnWrite(false); + } }); test('writeFile (Buffer)', async () => { const smallData = 'Hello World'; const bigData = (new Array(100 * 1024)).join('Large String\n'); - return testWriteFileAndFlush(Buffer.from(smallData), smallData, Buffer.from(bigData), bigData); + return testWriteFile(Buffer.from(smallData), smallData, Buffer.from(bigData), bigData); }); test('writeFile (UInt8Array)', async () => { const smallData = 'Hello World'; const bigData = (new Array(100 * 1024)).join('Large String\n'); - return testWriteFileAndFlush(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData); + return testWriteFile(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData); }); - async function testWriteFileAndFlush( + async function testWriteFile( smallData: string | Buffer | Uint8Array, smallDataValue: string, bigData: string | Buffer | Uint8Array, diff --git a/src/vs/base/test/node/uri.test.perf.ts b/src/vs/base/test/node/uri.test.perf.ts index bfe0d811ba..fa5a63fa2e 100644 --- a/src/vs/base/test/node/uri.test.perf.ts +++ b/src/vs/base/test/node/uri.test.perf.ts @@ -13,18 +13,18 @@ suite('URI - perf', function () { let manyFileUris: URI[]; setup(function () { manyFileUris = []; - let data = readFileSync(getPathFromAmdModule(require, './uri.test.data.txt')).toString(); - let lines = data.split('\n'); - for (let line of lines) { + const data = readFileSync(getPathFromAmdModule(require, './uri.test.data.txt')).toString(); + const lines = data.split('\n'); + for (const line of lines) { manyFileUris.push(URI.file(line)); } }); function perfTest(name: string, callback: Function) { test(name, _done => { - let t1 = Date.now(); + const t1 = Date.now(); callback(); - let d = Date.now() - t1; + const d = Date.now() - t1; console.log(`${name} took ${d}ms (${(d / manyFileUris.length).toPrecision(3)} ms/uri)`); _done(); }); @@ -32,28 +32,28 @@ suite('URI - perf', function () { perfTest('toString', function () { for (const uri of manyFileUris) { - let data = uri.toString(); + const data = uri.toString(); assert.ok(data); } }); perfTest('toString(skipEncoding)', function () { for (const uri of manyFileUris) { - let data = uri.toString(true); + const data = uri.toString(true); assert.ok(data); } }); perfTest('fsPath', function () { for (const uri of manyFileUris) { - let data = uri.fsPath; + const data = uri.fsPath; assert.ok(data); } }); perfTest('toJSON', function () { for (const uri of manyFileUris) { - let data = uri.toJSON(); + const data = uri.toJSON(); assert.ok(data); } }); diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 7984d75123..f08e487085 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -76,17 +76,21 @@ }); } - const loadCode = function (moduleId: string) { + function configureAMDLoader() { + require.config({ + baseUrl: monacoBaseUrl, + catchError: true, + trustedTypesPolicy, + amdModulesPattern: /^vs\// + }); + } + + function loadCode(moduleId: string) { loadAMDLoader().then(() => { - require.config({ - baseUrl: monacoBaseUrl, - catchError: true, - trustedTypesPolicy, - amdModulesPattern: /^vs\// - }); + configureAMDLoader(); require([moduleId], function (ws) { setTimeout(function () { - let messageHandler = ws.create((msg: any, transfer?: Transferable[]) => { + const messageHandler = ws.create((msg: any, transfer?: Transferable[]) => { (self).postMessage(msg, transfer); }, null); @@ -97,10 +101,17 @@ }, 0); }); }); - }; + } + + // If the loader is already defined, configure it immediately + // This helps in the bundled case, where we must load nls files + // and they need a correct baseUrl to be loaded. + if (typeof (self).define === 'function' && (self).define.amd) { + configureAMDLoader(); + } let isFirstMessage = true; - let beforeReadyMessages: MessageEvent[] = []; + const beforeReadyMessages: MessageEvent[] = []; self.onmessage = (message: MessageEvent) => { if (!isFirstMessage) { beforeReadyMessages.push(message); diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 2b3f7f4c65..ca282de3be 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -52,6 +52,19 @@ Object.keys(self.webPackagePaths).map(function (key, index) { self.webPackagePaths[key] = `${baseUrl}/node_modules/${key}/${self.webPackagePaths[key]}`; }); + + // Set up nls if the user is not using the default language (English) + const nlsConfig = {}; + const locale = window.localStorage.getItem('vscode.nls.locale') || navigator.language; + if (!locale.startsWith('en')) { + nlsConfig['vs/nls'] = { + availableLanguages: { + '*': locale + }, + translationServiceUrl: '{{WORKBENCH_NLS_BASE_URL}}' + }; + } + require.config({ baseUrl: `${baseUrl}/out`, recordStats: true, @@ -104,7 +117,7 @@ 'plotly.js-dist-min': `${window.location.origin}/static/node_modules/plotly.js-dist-min/plotly.min.js`, 'azdataGraph': `${window.location.origin}/static/node_modules/azdataGraph/dist/build.js` } - }; + }); diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts index 9390654784..a2c2a76f55 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts @@ -3,27 +3,186 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IExtensionGalleryService, IExtensionIdentifier, IGlobalExtensionEnablementService, ServerDidUninstallExtensionEvent, ServerInstallExtensionResult, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getIdAndVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { migrateUnsupportedExtensions } from 'vs/platform/extensionManagement/common/unsupportedExtensionsMigration'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { INativeServerExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { DidChangeProfilesEvent, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; + +const uninstalOptions: UninstallOptions = { versionOnly: true, donotIncludePack: true, donotCheckDependents: true }; export class ExtensionsCleaner extends Disposable { constructor( - @IExtensionManagementService extensionManagementService: ExtensionManagementService, + @INativeServerExtensionManagementService extensionManagementService: INativeServerExtensionManagementService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, @IExtensionStorageService extensionStorageService: IExtensionStorageService, @IGlobalExtensionEnablementService extensionEnablementService: IGlobalExtensionEnablementService, + @IInstantiationService instantiationService: IInstantiationService, @IStorageService storageService: IStorageService, @ILogService logService: ILogService, ) { super(); - extensionManagementService.removeDeprecatedExtensions(); + + extensionManagementService.removeUninstalledExtensions(this.userDataProfilesService.profiles.length === 1); migrateUnsupportedExtensions(extensionManagementService, extensionGalleryService, extensionStorageService, extensionEnablementService, logService); ExtensionStorageService.removeOutdatedExtensionVersions(extensionManagementService, storageService); + this._register(instantiationService.createInstance(ProfileExtensionsCleaner)); } + +} + +class ProfileExtensionsCleaner extends Disposable { + + private profileExtensionsLocations = new Map(); // {{SQL CARBON EDIT}} lewissanchez - Added parenthesis + + private readonly profileModeDisposables = this._register(new MutableDisposable()); + + constructor( + @INativeServerExtensionManagementService private readonly extensionManagementService: INativeServerExtensionManagementService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @ILogService private readonly logService: ILogService, + ) { + super(); + this.onDidChangeProfiles({ added: this.userDataProfilesService.profiles, removed: [], all: this.userDataProfilesService.profiles }); + } + + private async onDidChangeProfiles({ added, removed, all }: Omit): Promise { + try { + await Promise.all(removed.map(profile => profile.extensionsResource ? this.removeExtensionsFromProfile(profile.extensionsResource) : Promise.resolve())); + } catch (error) { + this.logService.error(error); + } + + if (all.length === 1) { + // Exit profile mode + this.profileModeDisposables.clear(); + // Listen for entering into profile mode + const disposable = this._register(this.userDataProfilesService.onDidChangeProfiles(() => { + disposable.dispose(); + this.onDidChangeProfiles({ added: this.userDataProfilesService.profiles, removed: [], all: this.userDataProfilesService.profiles }); + })); + return; + } + + try { + if (added.length) { + await Promise.all(added.map(profile => profile.extensionsResource ? this.populateExtensionsFromProfile(profile.extensionsResource) : Promise.resolve())); + // Enter profile mode + if (!this.profileModeDisposables.value) { + this.profileModeDisposables.value = new DisposableStore(); + this.profileModeDisposables.value.add(toDisposable(() => this.profileExtensionsLocations.clear())); + this.profileModeDisposables.value.add(this.userDataProfilesService.onDidChangeProfiles(e => this.onDidChangeProfiles(e))); + this.profileModeDisposables.value.add(this.extensionManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e))); + this.profileModeDisposables.value.add(this.extensionManagementService.onDidUninstallExtension(e => this.onDidUninstallExtension(e))); + await this.uninstallExtensionsNotInProfiles(); + } + } + } catch (error) { + this.logService.error(error); + } + } + + private async uninstallExtensionsNotInProfiles(): Promise { + const installed = await this.extensionManagementService.getAllUserInstalled(); + const toUninstall = installed.filter(installedExtension => !this.profileExtensionsLocations.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); + if (toUninstall.length) { + await Promise.all(toUninstall.map(extension => this.extensionManagementService.uninstall(extension, uninstalOptions))); + } + } + + private async onDidInstallExtensions(installedExtensions: readonly ServerInstallExtensionResult[]): Promise { + for (const { local, profileLocation } of installedExtensions) { + if (!local || !profileLocation) { + continue; + } + this.addExtensionWithKey(this.getKey(local.identifier, local.manifest.version), profileLocation); + } + } + + private async onDidUninstallExtension(e: ServerDidUninstallExtensionEvent): Promise { + if (!e.profileLocation || !e.version) { + return; + } + if (this.removeExtensionWithKey(this.getKey(e.identifier, e.version), e.profileLocation)) { + await this.uninstallExtensions([{ identifier: e.identifier, version: e.version }]); + } + } + + private async populateExtensionsFromProfile(extensionsProfileLocation: URI): Promise { + const extensions = await this.extensionsProfileScannerService.scanProfileExtensions(extensionsProfileLocation); + for (const extension of extensions) { + this.addExtensionWithKey(this.getKey(extension.identifier, extension.version), extensionsProfileLocation); + } + } + + private async removeExtensionsFromProfile(removedProfile: URI): Promise { + const extensionsToRemove: { identifier: IExtensionIdentifier; version: string }[] = []; + for (const key of [...this.profileExtensionsLocations.keys()]) { + if (!this.removeExtensionWithKey(key, removedProfile)) { + continue; + } + const extensionToRemove = this.fromKey(key); + if (extensionToRemove) { + extensionsToRemove.push(extensionToRemove); + } + } + if (extensionsToRemove.length) { + await this.uninstallExtensions(extensionsToRemove); + } + } + + private addExtensionWithKey(key: string, extensionsProfileLocation: URI): void { + let locations = this.profileExtensionsLocations.get(key); + if (!locations) { + locations = []; + this.profileExtensionsLocations.set(key, locations); + } + locations.push(extensionsProfileLocation); + } + + private removeExtensionWithKey(key: string, profileLocation: URI): boolean { + const profiles = this.profileExtensionsLocations.get(key); + if (profiles) { + const index = profiles.findIndex(profile => this.uriIdentityService.extUri.isEqual(profile, profileLocation)); + if (index > -1) { + profiles.splice(index, 1); + } + } + if (!profiles?.length) { + this.profileExtensionsLocations.delete(key); + return true; + } + return false; + } + + private async uninstallExtensions(extensionsToRemove: { identifier: IExtensionIdentifier; version: string }[]): Promise { + const installed = await this.extensionManagementService.getAllUserInstalled(); + const toUninstall = installed.filter(installedExtension => extensionsToRemove.some(e => this.getKey(installedExtension.identifier, installedExtension.manifest.version) === this.getKey(e.identifier, e.version))); + if (toUninstall.length) { + await Promise.all(toUninstall.map(extension => this.extensionManagementService.uninstall(extension, uninstalOptions))); + } + } + + private getKey(identifier: IExtensionIdentifier, version: string): string { + return `${ExtensionIdentifier.toKey(identifier.id)}@${version}`; + } + + private fromKey(key: string): { identifier: IExtensionIdentifier; version: string } | undefined { + const [id, version] = getIdAndVersion(key); + return version ? { identifier: { id }, version } : undefined; + } + } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index f7a9bf641d..3266cdda6a 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -59,7 +59,7 @@ export class LanguagePackCachedDataCleaner extends Disposable { try { const installed: IStringDictionary = Object.create(null); const metaData: ILanguagePackFile = JSON.parse(await Promises.readFile(join(this.environmentService.userDataPath, 'languagepacks.json'), 'utf8')); - for (let locale of Object.keys(metaData)) { + for (const locale of Object.keys(metaData)) { const entry = metaData[locale]; installed[`${entry.hash}.${locale}`] = true; } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts b/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts index 382b802081..d4f354c3c6 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; export class LocalizationsUpdater extends Disposable { constructor( - @ILocalizationsService private readonly localizationsService: LocalizationsService + @ILanguagePackService private readonly localizationsService: NativeLanguagePackService ) { super(); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index b99d17e8a8..f11c1d9751 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -31,10 +31,10 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro import { SharedProcessEnvironmentService } from 'vs/platform/sharedProcess/node/sharedProcessEnvironmentService'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; -import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IDefaultExtensionsProfileInitService, IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannel, ExtensionTipsChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/electron-sandbox/extensionTipsService'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionManagementService, INativeServerExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { ExtensionRecommendationNotificationServiceChannelClient } from 'vs/platform/extensionRecommendations/electron-sandbox/extensionRecommendationsIpc'; import { IFileService } from 'vs/platform/files/common/files'; @@ -46,14 +46,13 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { MessagePortMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { FollowerLogService, LoggerChannelClient, LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; -import { RequestService } from 'vs/platform/request/browser/requestService'; import { IRequestService } from 'vs/platform/request/common/request'; import { ISharedProcessConfiguration } from 'vs/platform/sharedProcess/node/sharedProcess'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -63,7 +62,7 @@ import { ICustomEndpointTelemetryService, ITelemetryService } from 'vs/platform/ import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { supportsTelemetry, ITelemetryAppender, NullAppender, NullTelemetryService, isInternalTelemetry, getPiiPathsFromEnvironment } from 'vs/platform/telemetry/common/telemetryUtils'; +import { supportsTelemetry, ITelemetryAppender, NullAppender, NullTelemetryService, getPiiPathsFromEnvironment, isInternalTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { CustomEndpointTelemetryService } from 'vs/platform/telemetry/node/customEndpointTelemetryService'; import { LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; @@ -90,7 +89,7 @@ import { ipcSharedProcessTunnelChannelName, ISharedProcessTunnelService } from ' import { SharedProcessTunnelService } from 'vs/platform/tunnel/node/sharedProcessTunnelService'; import { ipcSharedProcessWorkerChannelName, ISharedProcessWorkerConfiguration, ISharedProcessWorkerService } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService'; import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService'; -import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; +// import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; // {{SQL CARBON EDIT}} - Unused import import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { isLinux } from 'vs/base/common/platform'; @@ -100,6 +99,14 @@ import { InspectProfilingService as V8InspectProfilingService } from 'vs/platfor import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profiling'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; +import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; +import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile'; +import { OneDataSystemWebAppender } from 'vs/platform/telemetry/browser/1dsAppender'; +import { DefaultExtensionsProfileInitService } from 'vs/platform/extensionManagement/electron-sandbox/defaultExtensionsProfileInit'; +import { SharedProcessRequestService } from 'vs/platform/request/electron-browser/sharedProcessRequestService'; class SharedProcessMain extends Disposable { @@ -180,6 +187,10 @@ class SharedProcessMain extends Disposable { const mainProcessService = new MessagePortMainProcessService(this.server, mainRouter); services.set(IMainProcessService, mainProcessService); + // Policies + const policyService = this.configuration.policiesData ? new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy')) : new NullPolicyService(); + services.set(IPolicyService, policyService); + // Environment const environmentService = new SharedProcessEnvironmentService(this.configuration.args, productService); services.set(INativeEnvironmentService, environmentService); @@ -221,12 +232,16 @@ class SharedProcessMain extends Disposable { )); fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); + // User Data Profiles + const userDataProfilesService = this._register(new UserDataProfilesNativeService(this.configuration.profiles, mainProcessService, environmentService)); + services.set(IUserDataProfilesService, userDataProfilesService); + // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService, policyService, logService)); services.set(IConfigurationService, configurationService); // Storage (global access only) - const storageService = new NativeStorageService(undefined, mainProcessService, environmentService); + const storageService = new NativeStorageService(undefined, { defaultProfile: userDataProfilesService.defaultProfile, currentProfile: userDataProfilesService.defaultProfile }, mainProcessService, environmentService); services.set(IStorageService, storageService); this._register(toDisposable(() => storageService.flush())); @@ -240,7 +255,7 @@ class SharedProcessMain extends Disposable { services.set(IUriIdentityService, new UriIdentityService(fileService)); // Request - services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IRequestService, new SharedProcessRequestService(mainProcessService, configurationService, logService)); // Checksum services.set(IChecksumService, new SyncDescriptor(ChecksumService)); @@ -269,14 +284,14 @@ class SharedProcessMain extends Disposable { appenders.push(logAppender); const { installSourcePath } = environmentService; if (productService.aiConfig?.ariaKey) { - const collectorAppender = new OneDataSystemAppender(internalTelemetry, 'adsworkbench', null, productService.aiConfig.ariaKey); // {{SQL CARBON EDIT}} Use our own event prefix + const collectorAppender = new OneDataSystemWebAppender(internalTelemetry, 'adsworkbench', null, productService.aiConfig.ariaKey); // {{SQL CARBON EDIT}} Use our own event prefix this._register(toDisposable(() => collectorAppender.flush())); // Ensure the 1DS appender is disposed so that it flushes remaining data appenders.push(collectorAppender); } telemetryService = new TelemetryService({ appenders, - commonProperties: resolveCommonProperties(fileService, release(), hostname(), process.arch, productService.commit, productService.version, this.configuration.machineId, productService.msftInternalDomains, installSourcePath), + commonProperties: resolveCommonProperties(fileService, release(), hostname(), process.arch, productService.commit, productService.version, this.configuration.machineId, internalTelemetry, installSourcePath), sendErrorTelemetry: true, piiPaths: getPiiPathsFromEnvironment(environmentService), }, configurationService, productService); @@ -294,8 +309,10 @@ class SharedProcessMain extends Disposable { services.set(ICustomEndpointTelemetryService, customEndpointTelemetryService); // Extension Management + services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IDefaultExtensionsProfileInitService, new SyncDescriptor(DefaultExtensionsProfileInitService)); // Extension Gallery services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); @@ -304,7 +321,7 @@ class SharedProcessMain extends Disposable { services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); // Localizations - services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(ILanguagePackService, new SyncDescriptor(NativeLanguagePackService)); // Diagnostics services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); @@ -353,9 +370,9 @@ class SharedProcessMain extends Disposable { const channel = new ExtensionManagementChannel(accessor.get(IExtensionManagementService), () => null); this.server.registerChannel('extensions', channel); - // Localizations - const localizationsChannel = ProxyChannel.fromService(accessor.get(ILocalizationsService)); - this.server.registerChannel('localizations', localizationsChannel); + // Language Packs + const languagePacksChannel = ProxyChannel.fromService(accessor.get(ILanguagePackService)); + this.server.registerChannel('languagePacks', languagePacksChannel); // Diagnostics const diagnosticsChannel = ProxyChannel.fromService(accessor.get(IDiagnosticsService)); @@ -406,6 +423,9 @@ class SharedProcessMain extends Disposable { // Worker const sharedProcessWorkerChannel = ProxyChannel.fromService(accessor.get(ISharedProcessWorkerService)); this.server.registerChannel(ipcSharedProcessWorkerChannelName, sharedProcessWorkerChannel); + + // Default Extensions Profile Init + this.server.registerChannel('IDefaultExtensionsProfileInitService', ProxyChannel.fromService(accessor.get(IDefaultExtensionsProfileInitService))); } private registerErrorHandler(logService: ILogService): void { diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html deleted file mode 100644 index 2be94cec24..0000000000 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js deleted file mode 100644 index 077777556b..0000000000 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ /dev/null @@ -1,214 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// - -//@ts-check -(function () { - 'use strict'; - - const bootstrapWindow = bootstrapWindowLib(); - - // Add a perf entry right from the top - performance.mark('code/didStartRenderer'); - - // Load workbench main JS, CSS and NLS all in parallel. This is an - // optimization to prevent a waterfall of loading to happen, because - // we know for a fact that workbench.desktop.main will depend on - // the related CSS and NLS counterparts. - bootstrapWindow.load([ - 'sql/setup', // {{SQL CARBON EDIT}} - 'vs/workbench/workbench.desktop.main', - 'vs/nls!vs/workbench/workbench.desktop.main', - 'vs/css!vs/workbench/workbench.desktop.main' - ], - function (_, configuration) { - - // Mark start of workbench - performance.mark('code/didLoadWorkbenchMain'); - - // @ts-ignore - return require('vs/workbench/electron-sandbox/desktop.main').main(configuration); - }, - { - configureDeveloperSettings: function (windowConfig) { - return { - // disable automated devtools opening on error when running extension tests - // as this can lead to nondeterministic test execution (devtools steals focus) - forceDisableShowDevtoolsOnError: typeof windowConfig.extensionTestsPath === 'string', - // enable devtools keybindings in extension development window - forceEnableDeveloperKeybindings: Array.isArray(windowConfig.extensionDevelopmentPath) && windowConfig.extensionDevelopmentPath.length > 0, - removeDeveloperKeybindingsAfterLoad: true - }; - }, - canModifyDOM: function (windowConfig) { - showSplash(windowConfig); - }, - beforeLoaderConfig: function (loaderConfig) { - loaderConfig.recordStats = true; - }, - beforeRequire: function () { - performance.mark('code/willLoadWorkbenchMain'); - - // It looks like browsers only lazily enable - // the element when needed. Since we - // leverage canvas elements in our code in many - // locations, we try to help the browser to - // initialize canvas when it is idle, right - // before we wait for the scripts to be loaded. - // @ts-ignore - window.requestIdleCallback(() => { - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - context.clearRect(0, 0, canvas.width, canvas.height); - canvas.remove(); - }, { timeout: 50 }); - } - } - ); - - //#region Helpers - - /** - * @typedef {import('../../../platform/window/common/window').INativeWindowConfiguration} INativeWindowConfiguration - * @typedef {import('../../../platform/environment/common/argv').NativeParsedArgs} NativeParsedArgs - * - * @returns {{ - * load: ( - * modules: string[], - * resultCallback: (result, configuration: INativeWindowConfiguration & NativeParsedArgs) => unknown, - * options?: { - * configureDeveloperSettings?: (config: INativeWindowConfiguration & NativeParsedArgs) => { - * forceDisableShowDevtoolsOnError?: boolean, - * forceEnableDeveloperKeybindings?: boolean, - * disallowReloadKeybinding?: boolean, - * removeDeveloperKeybindingsAfterLoad?: boolean - * }, - * canModifyDOM?: (config: INativeWindowConfiguration & NativeParsedArgs) => void, - * beforeLoaderConfig?: (loaderConfig: object) => void, - * beforeRequire?: () => void - * } - * ) => Promise - * }} - */ - function bootstrapWindowLib() { - // @ts-ignore (defined in bootstrap-window.js) - return window.MonacoBootstrapWindow; - } - - /** - * @param {INativeWindowConfiguration & NativeParsedArgs} configuration - */ - function showSplash(configuration) { - performance.mark('code/willShowPartsSplash'); - - let data = configuration.partsSplash; - - if (data) { - // high contrast mode has been turned by the OS -> ignore stored colors and layouts - if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) { - if ((configuration.colorScheme.dark && data.baseTheme !== 'hc-black') || (!configuration.colorScheme.dark && data.baseTheme !== 'hc-light')) { - data = undefined; - } - } else if (configuration.autoDetectColorScheme) { - // OS color scheme is tracked and has changed - if ((configuration.colorScheme.dark && data.baseTheme !== 'vs-dark') || (!configuration.colorScheme.dark && data.baseTheme !== 'vs')) { - data = undefined; - } - } - } - - // developing an extension -> ignore stored layouts - if (data && configuration.extensionDevelopmentPath) { - data.layoutInfo = undefined; - } - - // minimal color configuration (works with or without persisted data) - let baseTheme, shellBackground, shellForeground; - if (data) { - baseTheme = data.baseTheme; - shellBackground = data.colorInfo.editorBackground; - shellForeground = data.colorInfo.foreground; - } else if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) { - if (configuration.colorScheme.dark) { - baseTheme = 'hc-black'; - shellBackground = '#000000'; - shellForeground = '#FFFFFF'; - } else { - baseTheme = 'hc-light'; - shellBackground = '#FFFFFF'; - shellForeground = '#000000'; - } - } else if (configuration.autoDetectColorScheme) { - if (configuration.colorScheme.dark) { - baseTheme = 'vs-dark'; - shellBackground = '#1E1E1E'; - shellForeground = '#CCCCCC'; - } else { - baseTheme = 'vs'; - shellBackground = '#FFFFFF'; - shellForeground = '#000000'; - } - } - - const style = document.createElement('style'); - style.className = 'initialShellColors'; - document.head.appendChild(style); - style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; - - // restore parts if possible (we might not always store layout info) - if (data?.layoutInfo) { - const { layoutInfo, colorInfo } = data; - - const splash = document.createElement('div'); - splash.id = 'monaco-parts-splash'; - splash.className = baseTheme; - - if (layoutInfo.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); - - if (layoutInfo.windowBorderRadius) { - splash.style.borderRadius = layoutInfo.windowBorderRadius; - } - } - - // ensure there is enough space - layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth)); - - // part: title - const titleDiv = document.createElement('div'); - titleDiv.setAttribute('style', `position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground}; -webkit-app-region: drag;`); - splash.appendChild(titleDiv); - - // part: activity bar - const activityDiv = document.createElement('div'); - activityDiv.setAttribute('style', `position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};`); - splash.appendChild(activityDiv); - - // part: side bar (only when opening workspace/folder) - // folder or workspace -> status bar color, sidebar - if (configuration.workspace) { - const sideDiv = document.createElement('div'); - sideDiv.setAttribute('style', `position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: ${layoutInfo.activityBarWidth}px; width: ${layoutInfo.sideBarWidth}px; background-color: ${colorInfo.sideBarBackground};`); - splash.appendChild(sideDiv); - } - - // part: statusbar - const statusDiv = document.createElement('div'); - statusDiv.setAttribute('style', `position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${configuration.workspace ? colorInfo.statusBarBackground : colorInfo.statusBarNoFolderBackground};`); - splash.appendChild(statusDiv); - - document.body.appendChild(splash); - } - - performance.mark('code/didShowPartsSplash'); - } - - //#endregion -}()); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 95990da305..cae819f54d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -46,7 +46,7 @@ import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; -import { WorkerMainProcessExtensionHostStarter } from 'vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter'; +import { ExtensionHostStarter } from 'vs/platform/extensions/electron-main/extensionHostStarter'; import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/common/externalTerminal'; import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService'; import { LOCAL_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/files/common/diskFileSystemProviderClient'; @@ -70,12 +70,12 @@ import { SharedProcess } from 'vs/platform/sharedProcess/electron-main/sharedPro import { ISignService } from 'vs/platform/sign/common/sign'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc'; -import { GlobalStorageMainService, IGlobalStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { ApplicationStorageMainService, IApplicationStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; import { ITelemetryService, machineIdKey, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { getPiiPathsFromEnvironment, getTelemetryLevel, NullTelemetryService, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { getPiiPathsFromEnvironment, getTelemetryLevel, isInternalTelemetry, NullTelemetryService, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateChannel } from 'vs/platform/update/common/updateIpc'; import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin'; @@ -99,6 +99,13 @@ import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/ import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { CredentialsNativeMainService } from 'vs/platform/credentials/electron-main/credentialsMainService'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { PolicyChannel } from 'vs/platform/policy/common/policyIpc'; +import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; +import { IDefaultExtensionsProfileInitService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { DefaultExtensionsProfileInitHandler } from 'vs/platform/extensionManagement/electron-main/defaultExtensionsProfileInit'; +import { RequestChannel } from 'vs/platform/request/common/requestIpc'; +import { IRequestService } from 'vs/platform/request/common/request'; /** * The main VS Code application. There will only ever be one instance, @@ -263,7 +270,7 @@ export class CodeApplication extends Disposable { // remote extension schemes have the following format // http://127.0.0.1:/vscode-remote-resource?path= - if (!uri.path.includes(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) { + if (!uri.path.endsWith(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) { return callback({ cancel: !isSvgRequestFromSafeContext(details) }); } } @@ -522,6 +529,9 @@ export class CodeApplication extends Disposable { // Services const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady); + // Setup Handlers + this.setUpHandlers(appInstantiationService); + // Setup Auth Handler this._register(appInstantiationService.createInstance(ProxyAuthHandler)); // {{SQL CARBON EDIT}} Cast here to avoid compilation error (not finding constructor?) @@ -540,6 +550,14 @@ export class CodeApplication extends Disposable { } } + private setUpHandlers(instantiationService: IInstantiationService): void { + // Auth Handler + this._register(instantiationService.createInstance(ProxyAuthHandler)); + + // Default Extensions Profile Init Handler + this._register(instantiationService.createInstance(DefaultExtensionsProfileInitHandler)); + } + private async resolveMachineId(): Promise { // We cache the machineId for faster lookups on startup @@ -641,11 +659,11 @@ export class CodeApplication extends Disposable { services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); // Extension Host Starter - services.set(IExtensionHostStarter, new SyncDescriptor(WorkerMainProcessExtensionHostStarter)); + services.set(IExtensionHostStarter, new SyncDescriptor(ExtensionHostStarter)); // Storage services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); - services.set(IGlobalStorageMainService, new SyncDescriptor(GlobalStorageMainService)); + services.set(IApplicationStorageMainService, new SyncDescriptor(ApplicationStorageMainService)); // External terminal if (isWindows) { @@ -665,9 +683,10 @@ export class CodeApplication extends Disposable { // Telemetry if (supportsTelemetry(this.productService, this.environmentMainService)) { + const isInternal = isInternalTelemetry(this.productService, this.configurationService); const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); const appender = new TelemetryAppenderClient(channel); - const commonProperties = resolveCommonProperties(this.fileService, release(), hostname(), process.arch, this.productService.commit, this.productService.version, machineId, this.productService.msftInternalDomains, this.environmentMainService.installSourcePath); + const commonProperties = resolveCommonProperties(this.fileService, release(), hostname(), process.arch, this.productService.commit, this.productService.version, machineId, isInternal, this.environmentMainService.installSourcePath); const piiPaths = getPiiPathsFromEnvironment(this.environmentMainService); const config: ITelemetryServiceConfig = { appenders: [appender], commonProperties, piiPaths, sendErrorTelemetry: true }; @@ -676,6 +695,9 @@ export class CodeApplication extends Disposable { services.set(ITelemetryService, NullTelemetryService); } + // Default Extensions Profile Init + services.set(IDefaultExtensionsProfileInitService, ProxyChannel.toService(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('IDefaultExtensionsProfileInitService'))))); + // Init services that require it await backupMainService.initialize(); @@ -695,6 +717,11 @@ export class CodeApplication extends Disposable { const diagnosticsChannel = ProxyChannel.fromService(accessor.get(IDiagnosticsMainService), { disableMarshalling: true }); this.mainProcessNodeIpcServer.registerChannel('diagnostics', diagnosticsChannel); + // Policies (main & shared process) + const policyChannel = new PolicyChannel(accessor.get(IPolicyService)); + mainProcessElectronServer.registerChannel('policy', policyChannel); + sharedProcessClient.then(client => client.registerChannel('policy', policyChannel)); + // Local Files const diskFileSystemProvider = this.fileService.getProvider(Schemas.file); assertType(diskFileSystemProvider instanceof DiskFileSystemProvider); @@ -702,6 +729,15 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel); sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel)); + // User Data Profiles + const userDataProfilesService = ProxyChannel.fromService(accessor.get(IUserDataProfilesMainService)); + mainProcessElectronServer.registerChannel('userDataProfiles', userDataProfilesService); + sharedProcessClient.then(client => client.registerChannel('userDataProfiles', userDataProfilesService)); + + // Request + const requestService = new RequestChannel(accessor.get(IRequestService)); + sharedProcessClient.then(client => client.registerChannel('request', requestService)); + // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); mainProcessElectronServer.registerChannel('update', updateChannel); @@ -864,6 +900,13 @@ export class CodeApplication extends Disposable { // or if no window is open (macOS only) shouldOpenInNewWindow ||= isMacintosh && windowsMainService.getWindowCount() === 0; + // Pass along edit session id + if (params.get('editSessionId') !== null) { + environmentService.editSessionId = params.get('editSessionId') ?? undefined; + params.delete('editSessionId'); + uri = uri.with({ query: params.toString() }); + } + // Check for URIs to open in window const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri); logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink); @@ -973,6 +1016,7 @@ export class CodeApplication extends Disposable { cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, + mergeMode: args.merge, noRecentEntry, waitMarkerFileURI, gotoLineMode: args.goto, @@ -1098,7 +1142,7 @@ export class CodeApplication extends Disposable { code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The type of shared process crash to understand the nature of the crash better.' }; visible: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether shared process window was visible or not.' }; shuttingdown: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether the application is shutting down when the crash happens.' }; - owner: 'bpaser'; + owner: 'bpasero'; comment: 'Event which fires whenever an error occurs in the shared process'; }; diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index a2835de9b4..54af9e0983 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -62,6 +62,14 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; +import { IUserDataProfilesMainService, UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; +import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { PROFILES_ENABLEMENT_CONFIG } from 'vs/platform/userDataProfile/common/userDataProfile'; /** * The main VS Code entry point. @@ -89,13 +97,13 @@ class CodeMain { setUnexpectedErrorHandler(err => console.error(err)); // Create services - const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService] = this.createServices(); + const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesMainService] = this.createServices(); try { // Init services try { - await this.initServices(environmentMainService, configurationService, stateMainService); + await this.initServices(environmentMainService, userDataProfilesMainService, configurationService, stateMainService); } catch (error) { // Show a dialog for errors that can be resolved by the user @@ -137,8 +145,10 @@ class CodeMain { } } - private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService] { + private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService, UserDataProfilesMainService] { const services = new ServiceCollection(); + const disposables = new DisposableStore(); + process.once('exit', () => disposables.dispose()); // Product const productService = { _serviceBrand: undefined, ...product }; @@ -153,8 +163,7 @@ class CodeMain { // we are the only instance running, otherwise we'll have concurrent // log file access on Windows (https://github.com/microsoft/vscode/issues/41218) const bufferLogService = new BufferLogService(); - const logService = new MultiplexLogService([new ConsoleMainLogger(getLogLevel(environmentMainService)), bufferLogService]); - process.once('exit', () => logService.dispose()); + const logService = disposables.add(new MultiplexLogService([new ConsoleMainLogger(getLogLevel(environmentMainService)), bufferLogService])); services.set(ILogService, logService); // Files @@ -163,20 +172,34 @@ class CodeMain { const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // URI Identity + const uriIdentityService = new UriIdentityService(fileService); + services.set(IUriIdentityService, uriIdentityService); + // Logger services.set(ILoggerService, new LoggerService(logService, fileService)); - // Configuration - const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService); - services.set(IConfigurationService, configurationService); - - // Lifecycle - services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService)); - // State const stateMainService = new StateMainService(environmentMainService, logService, fileService); services.set(IStateMainService, stateMainService); + // User Data Profiles + const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, uriIdentityService, environmentMainService, fileService, logService); + services.set(IUserDataProfilesMainService, userDataProfilesMainService); + + // Policy + const policyService = isWindows && productService.win32RegValueName ? disposables.add(new NativePolicyService(productService.win32RegValueName)) + : environmentMainService.policyFile ? disposables.add(new FilePolicyService(environmentMainService.policyFile, fileService, logService)) + : new NullPolicyService(); + services.set(IPolicyService, policyService); + + // Configuration + const configurationService = new ConfigurationService(userDataProfilesMainService.defaultProfile.settingsResource, fileService, policyService, logService); + services.set(IConfigurationService, configurationService); + + // Lifecycle + services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService)); + // Request services.set(IRequestService, new SyncDescriptor(RequestMainService)); @@ -192,7 +215,7 @@ class CodeMain { // Protocol services.set(IProtocolMainService, new SyncDescriptor(ProtocolMainService)); - return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService]; + return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesMainService]; } private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment { @@ -212,26 +235,28 @@ class CodeMain { return instanceEnvironment; } - private initServices(environmentMainService: IEnvironmentMainService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { - return Promises.settled([ + private async initServices(environmentMainService: IEnvironmentMainService, userDataProfilesMainService: UserDataProfilesMainService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { + await Promises.settled([ // Environment service (paths) Promise.all([ environmentMainService.extensionsPath, environmentMainService.codeCachePath, environmentMainService.logsPath, - environmentMainService.globalStorageHome.fsPath, + userDataProfilesMainService.defaultProfile.globalStorageHome.fsPath, environmentMainService.workspaceStorageHome.fsPath, environmentMainService.localHistoryHome.fsPath, environmentMainService.backupHome ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), - // Configuration service - configurationService.initialize(), - // State service - stateMainService.init() + stateMainService.init(), + + // Configuration service + configurationService.initialize() ]); + + userDataProfilesMainService.setEnablement(!!configurationService.getValue(PROFILES_ENABLEMENT_CONFIG)); } private async claimInstance(logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, productService: IProductService, retry: boolean): Promise { diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index 98e9947198..6bd8da6322 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -142,6 +142,7 @@ export class IssueReporter extends Disposable { this.updateExperimentsInfo(configuration.data.experiments); this.updateRestrictedMode(configuration.data.restrictedMode); this.updatePreviewFeaturesEnabled(configuration.data.previewFeaturesEnabled); // {{SQL CARBON EDIT}} Add preview features flag + this.updateUnsupportedMode(configuration.data.isUnsupported); } render(): void { @@ -1161,6 +1162,10 @@ export class IssueReporter extends Disposable { this.issueReporterModel.update({ previewFeaturesEnabled }); } + private updateUnsupportedMode(isUnsupported: boolean) { + this.issueReporterModel.update({ isUnsupported }); + } + private updateExperimentsInfo(experimentInfo: string | undefined) { this.issueReporterModel.update({ experimentInfo }); const target = document.querySelector('.block-experiments .block-info'); @@ -1204,21 +1209,15 @@ export class IssueReporter extends Disposable { private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { const element = this.getElementById(elementId); - if (element) { - element.addEventListener(eventType, handler); - } + element?.addEventListener(eventType, handler); } } // helper functions function hide(el: Element | undefined | null) { - if (el) { - el.classList.add('hidden'); - } + el?.classList.add('hidden'); } function show(el: Element | undefined | null) { - if (el) { - el.classList.remove('hidden'); - } + el?.classList.remove('hidden'); } diff --git a/src/vs/code/electron-sandbox/issue/issueReporterModel.ts b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts index 02922c15d7..1c7a2434cc 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterModel.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterModel.ts @@ -34,6 +34,7 @@ export interface IssueReporterData { experimentInfo?: string; restrictedMode?: boolean; previewFeaturesEnabled?: boolean; // {{SQL CARBON EDIT}} Add preview features flag + isUnsupported?: boolean; } export class IssueReporterModel { @@ -63,8 +64,15 @@ export class IssueReporterModel { // {{SQL CARBON EDIT}} serialize(): string { + const modes = []; + if (this._data.restrictedMode) { + modes.push('Restricted'); + } + if (this._data.isUnsupported) { + modes.push('Unsupported'); + } return ` -Issue Type: ${this.getIssueTypeTitle()} +Type: ${this.getIssueTypeTitle()} ${this._data.issueDescription} ${this.getExtensionVersion()} @@ -72,6 +80,7 @@ Azure Data Studio version: ${this._data.versionInfo && this._data.versionInfo.vs OS version: ${this._data.versionInfo && this._data.versionInfo.os} Restricted Mode: ${this._data.restrictedMode ? 'Yes' : 'No'} Preview Features: ${this._data.previewFeaturesEnabled ? 'Enabled' : 'Disabled'} +Modes:${modes.length ? ' ' + modes.join(', ') : ''} ${this.getRemoteOSes()} ${this.getInfos()} `; diff --git a/src/vs/code/electron-sandbox/workbench/workbench.html b/src/vs/code/electron-sandbox/workbench/workbench.html index 2a18556fce..f34f6f6cd2 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.html +++ b/src/vs/code/electron-sandbox/workbench/workbench.html @@ -4,7 +4,7 @@ - + diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index b77bc66b0b..ca0af36024 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -16,10 +16,11 @@ // Load workbench main JS, CSS and NLS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because - // we know for a fact that workbench.desktop.sandbox.main will depend on + // we know for a fact that workbench.desktop.main will depend on // the related CSS and NLS counterparts. bootstrapWindow.load([ - 'vs/workbench/workbench.desktop.sandbox.main', + 'sql/setup', // {{SQL CARBON EDIT}} + 'vs/workbench/workbench.desktop.main', 'vs/nls!vs/workbench/workbench.desktop.main', 'vs/css!vs/workbench/workbench.desktop.main' ], @@ -61,7 +62,7 @@ window.requestIdleCallback(() => { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); - context.clearRect(0, 0, canvas.width, canvas.height); + context?.clearRect(0, 0, canvas.width, canvas.height); canvas.remove(); }, { timeout: 50 }); } diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 94ba236f85..2cb321ab30 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -8,7 +8,7 @@ import { chmodSync, existsSync, readFileSync, statSync, truncateSync, unlinkSync import { homedir, release, tmpdir } from 'os'; import type { ProfilingSession, Target } from 'v8-inspect-profiler'; import { Event } from 'vs/base/common/event'; -import { isAbsolute, resolve } from 'vs/base/common/path'; +import { isAbsolute, resolve, join } from 'vs/base/common/path'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; import { randomPort } from 'vs/base/common/ports'; import { isString } from 'vs/base/common/types'; @@ -24,6 +24,8 @@ import product from 'vs/platform/product/common/product'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { randomPath } from 'vs/base/common/extpath'; import { Utils } from 'vs/platform/profiling/common/profiling'; +import { dirname } from 'vs/base/common/resources'; +import { FileAccess } from 'vs/base/common/network'; function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { return !!argv['install-source'] @@ -59,6 +61,21 @@ export async function main(argv: string[]): Promise { console.log(buildVersionMessage(product.version, product.commit)); } + // Shell integration + else if (args['locate-shell-integration-path']) { + let file: string; + switch (args['locate-shell-integration-path']) { + // Usage: `[[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path bash)"` + case 'bash': file = 'shellIntegration-bash.sh'; break; + // Usage: `if ($env:TERM_PROGRAM -eq "vscode") { . "$(code --locate-shell-integration-path pwsh)" }` + case 'pwsh': file = 'shellIntegration.ps1'; break; + // Usage: `[[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path zsh)"` + case 'zsh': file = 'shellIntegration-rc.zsh'; break; + default: throw new Error('Error using --locate-shell-integration-path: Invalid shell type'); + } + console.log(join(dirname(FileAccess.asFileUri('', require)).fsPath, 'out', 'vs', 'workbench', 'contrib', 'terminal', 'browser', 'media', file)); + } + // Extensions Management else if (shouldSpawnCliProcess(args)) { const cli = await new Promise((resolve, reject) => require(['vs/code/node/cliProcessMain'], resolve, reject)); @@ -286,7 +303,7 @@ export async function main(argv: string[]): Promise { return; } let suffix = ''; - let result = await session.stop(); + const result = await session.stop(); if (!process.env['VSCODE_DEV']) { // when running from a not-development-build we remove // absolute filenames because we don't want to reveal anything diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 57634bf038..f1f28d0042 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -11,8 +11,8 @@ import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/err import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isAbsolute, join } from 'vs/base/common/path'; +import { isWindows } from 'vs/base/common/platform'; import { cwd } from 'vs/base/common/process'; -import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Promises } from 'vs/base/node/pfs'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -23,10 +23,11 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ExtensionGalleryServiceWithNoStorageService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; -import { IExtensionGalleryService, IExtensionManagementCLIService, IExtensionManagementService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IExtensionManagementCLIService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionManagementService, INativeServerExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -35,22 +36,29 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; +import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; +import { IStateService } from 'vs/platform/state/node/state'; +import { StateService } from 'vs/platform/state/node/stateService'; import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; import { ITelemetryService, machineIdKey } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { supportsTelemetry, NullTelemetryService, isInternalTelemetry, getPiiPathsFromEnvironment } from 'vs/platform/telemetry/common/telemetryUtils'; +import { supportsTelemetry, NullTelemetryService, getPiiPathsFromEnvironment, isInternalTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; import { buildTelemetryMessage } from 'vs/platform/telemetry/node/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { IUserDataProfilesService, PROFILES_ENABLEMENT_CONFIG } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfilesService } from 'vs/platform/userDataProfile/node/userDataProfile'; class CliMain extends Disposable { @@ -128,12 +136,35 @@ class CliMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // State + const stateService = new StateService(environmentService, logService, fileService); + services.set(IStateService, stateService); + + // Uri Identity + const uriIdentityService = new UriIdentityService(fileService); + services.set(IUriIdentityService, uriIdentityService); + + // User Data Profiles + const userDataProfilesService = new UserDataProfilesService(stateService, uriIdentityService, environmentService, fileService, logService); + services.set(IUserDataProfilesService, userDataProfilesService); + + // Policy + const policyService = isWindows && productService.win32RegValueName ? this._register(new NativePolicyService(productService.win32RegValueName)) + : environmentService.policyFile ? this._register(new FilePolicyService(environmentService.policyFile, fileService, logService)) + : new NullPolicyService(); + services.set(IPolicyService, policyService); + // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService, policyService, logService)); services.set(IConfigurationService, configurationService); - // Init config - await configurationService.initialize(); + // Initialize + await Promise.all([ + stateService.init(), + configurationService.initialize() + ]); + + userDataProfilesService.setEnablement(!!configurationService.getValue(PROFILES_ENABLEMENT_CONFIG)); // URI Identity services.set(IUriIdentityService, new UriIdentityService(fileService)); @@ -145,13 +176,14 @@ class CliMain extends Disposable { services.set(IDownloadService, new SyncDescriptor(DownloadService)); // Extensions + services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService)); services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService)); // Localizations - services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(ILanguagePackService, new SyncDescriptor(NativeLanguagePackService)); // Telemetry const appenders: OneDataSystemAppender[] = []; @@ -169,7 +201,7 @@ class CliMain extends Disposable { commonProperties: (async () => { let machineId: string | undefined = undefined; try { - const storageContents = await Promises.readFile(joinPath(environmentService.globalStorageHome, 'storage.json').fsPath); + const storageContents = await Promises.readFile(environmentService.stateResource.fsPath); machineId = JSON.parse(storageContents.toString())[machineIdKey]; } catch (error) { if (error.code !== 'ENOENT') { @@ -177,7 +209,7 @@ class CliMain extends Disposable { } } - return resolveCommonProperties(fileService, release(), hostname(), process.arch, productService.commit, productService.version, machineId, productService.msftInternalDomains, installSourcePath); + return resolveCommonProperties(fileService, release(), hostname(), process.arch, productService.commit, productService.version, machineId, isInternal, installSourcePath); })(), piiPaths: getPiiPathsFromEnvironment(environmentService) }; diff --git a/src/vs/code/test/electron-sandbox/issue/testReporterModel.test.ts b/src/vs/code/test/electron-sandbox/issue/testReporterModel.test.ts index 4c83f7de53..ce0eb14849 100644 --- a/src/vs/code/test/electron-sandbox/issue/testReporterModel.test.ts +++ b/src/vs/code/test/electron-sandbox/issue/testReporterModel.test.ts @@ -27,7 +27,7 @@ suite('IssueReporter', () => { const issueReporterModel = new IssueReporterModel({}); assert.strictEqual(issueReporterModel.serialize(), ` -Issue Type: Bug +Type: Bug undefined @@ -35,6 +35,7 @@ Azure Data Studio version: undefined OS version: undefined Restricted Mode: No Preview Features: Disabled +Modes: Extensions: none `); @@ -59,7 +60,7 @@ Extensions: none }); assert.strictEqual(issueReporterModel.serialize(), ` -Issue Type: Bug +Type: Bug undefined @@ -67,6 +68,7 @@ Azure Data Studio version: undefined OS version: undefined Restricted Mode: No Preview Features: Disabled +Modes:
    System Info @@ -104,13 +106,13 @@ Preview Features: Disabled }); assert.strictEqual(issueReporterModel.serialize(), ` -Issue Type: Bug +Type: Bug undefined VS Code version: undefined OS version: undefined -Restricted Mode: No +Modes:
    System Info @@ -159,13 +161,13 @@ vsins829:30139715 }); assert.strictEqual(issueReporterModel.serialize(), ` -Issue Type: Bug +Type: Bug undefined VS Code version: undefined OS version: undefined -Restricted Mode: No +Modes:
    System Info @@ -216,13 +218,13 @@ Restricted Mode: No }); assert.strictEqual(issueReporterModel.serialize(), ` -Issue Type: Bug +Type: Bug undefined VS Code version: undefined OS version: undefined -Restricted Mode: No +Modes: Remote OS version: Linux x64 4.18.0
    @@ -265,13 +267,13 @@ Remote OS version: Linux x64 4.18.0 }); assert.strictEqual(issueReporterModel.serialize(), ` -Issue Type: Bug +Type: Bug undefined VS Code version: undefined OS version: undefined -Restricted Mode: No +Modes:
    System Info @@ -289,6 +291,26 @@ Restricted Mode: No `); }); + test('should supply mode if applicable', () => { // {{SQL CARBON EDIT}} Modifies test for ADS + const issueReporterModel = new IssueReporterModel({ + isUnsupported: true, + restrictedMode: true + }); + assert.strictEqual(issueReporterModel.serialize(), + ` +Type: Bug + +undefined + +Azure Data Studio version: undefined +OS version: undefined +Restricted Mode: Yes +Preview Features: Disabled +Modes: Restricted, Unsupported + +Extensions: none +`); + }); test('should normalize GitHub urls', () => { [ 'https://github.com/repo', diff --git a/src/vs/css.build.ts b/src/vs/css.build.ts new file mode 100644 index 0000000000..45a11cc042 --- /dev/null +++ b/src/vs/css.build.ts @@ -0,0 +1,304 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface ICSSPluginConfig { + inlineResources?: boolean | 'base64'; + inlineResourcesLimit?: number; +} + +interface ICSSEntryPointData { + moduleName: string; + contents: string; + fsPath: string; +} + +// This file gets compiled also with the standalone editor, +// so we cannot depend on types from node.d.ts +interface INodeFS { + readFileSync(path: string, encoding: 'utf8'): string; + readFileSync(path: string): INodeBuffer; +} +interface INodeBuffer { + length: number; + toString(encoding?: 'base64'): string; +} +interface INodePath { + dirname(p: string): string; + join(...paths: string[]): string; +} + +const nodeReq = (module: string): T | undefined => { + if (typeof (require).__$__nodeRequire === 'function') { + return (require).__$__nodeRequire(module); + } + return undefined; +}; + +const fs = nodeReq('fs'); +const path = nodeReq('path'); + +let inlineResources: boolean | 'base64' = false; +let inlineResourcesLimit: number = 5000; + +const contentsMap: { [moduleName: string]: string } = {}; +const pathMap: { [moduleName: string]: string } = {}; +const entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; +const inlinedResources: string[] = []; + +/** + * Invoked by the loader at build-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions | { isBuild?: boolean }): void { // {{SQL CARBON EDIT}} Added type to config + if (!fs) { + throw new Error(`Cannot load files without 'fs'!`); + } + config = config || {}; + const myConfig = (config['vs/css'] || {}); + inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); + inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); + const cssUrl = req.toUrl(name + '.css'); + let contents = fs.readFileSync(cssUrl, 'utf8'); + if (contents.charCodeAt(0) === 65279 /* BOM */) { + // Remove BOM + contents = contents.substring(1); + } + if (config.isBuild) { + contentsMap[name] = contents; + pathMap[name] = cssUrl; + } + load({}); +} + +/** + * Invoked by the loader at build-time + */ +export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); + + entryPoints[entryPoint] = entryPoints[entryPoint] || []; + entryPoints[entryPoint].push({ + moduleName: moduleName, + contents: contentsMap[moduleName], + fsPath: pathMap[moduleName], + }); + + write.asModule(pluginName + '!' + moduleName, + 'define([\'vs/css!' + entryPoint + '\'], {});' + ); +} + +/** + * Invoked by the loader at build-time + */ +export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (entryPoints && entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.css'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = entryPoints[moduleName]; + for (let i = 0; i < entries.length; i++) { + if (inlineResources) { + contents.push(rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, inlineResources === 'base64', inlineResourcesLimit)); + } else { + contents.push(rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); + } + } + write(fileName, contents.join('\r\n')); + } +} + +export function getInlinedResources(): string[] { + return inlinedResources || []; +} + +function rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + if (!fs || !path) { + throw new Error(`Cannot rewrite or inline urls without 'fs' or 'path'!`); + } + return CSSPluginUtilities.replaceURL(contents, (url) => { + if (/\.(svg|png)$/.test(url)) { + const fsPath = path.join(path.dirname(originalFileFSPath), url); + const fileContents = fs.readFileSync(fsPath); + + if (fileContents.length < inlineByteLimit) { + const normalizedFSPath = fsPath.replace(/\\/g, '/'); + inlinedResources.push(normalizedFSPath); + + const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; + let DATA = ';base64,' + fileContents.toString('base64'); + + if (!forceBase64 && /\.svg$/.test(url)) { + // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris + const newText = fileContents.toString() + .replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(//g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); + const encodedData = ',' + newText; + if (encodedData.length < DATA.length) { + DATA = encodedData; + } + } + return '"data:' + MIME + DATA + '"'; + } + } + + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} + +export function rewriteUrls(originalFile: string, newFile: string, contents: string): string { + return CSSPluginUtilities.replaceURL(contents, (url) => { + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} + +export class CSSPluginUtilities { + + public static startsWith(haystack: string, needle: string): boolean { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + } + + /** + * Find the path of a file. + */ + public static pathOf(filename: string): string { + const lastSlash = filename.lastIndexOf('/'); + if (lastSlash !== -1) { + return filename.substr(0, lastSlash + 1); + } else { + return ''; + } + } + + /** + * A conceptual a + b for paths. + * Takes into account if `a` contains a protocol. + * Also normalizes the result: e.g.: a/b/ + ../c => a/c + */ + public static joinPaths(a: string, b: string): string { + + function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { + if (CSSPluginUtilities.startsWith(haystack, prefix)) { + return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); + } + return 0; + } + + let aPathStartIndex = 0; + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); + + function pushPiece(pieces: string[], piece: string): void { + if (piece === './') { + // Ignore + return; + } + if (piece === '../') { + const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); + if (prevPiece && prevPiece === '/') { + // Ignore + return; + } + if (prevPiece && prevPiece !== '../') { + // Pop + pieces.pop(); + return; + } + } + // Push + pieces.push(piece); + } + + function push(pieces: string[], path: string): void { + while (path.length > 0) { + const slashIndex = path.indexOf('/'); + const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); + path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); + pushPiece(pieces, piece); + } + } + + let pieces: string[] = []; + push(pieces, a.substr(aPathStartIndex)); + if (b.length > 0 && b.charAt(0) === '/') { + pieces = []; + } + push(pieces, b); + + return a.substring(0, aPathStartIndex) + pieces.join(''); + } + + public static commonPrefix(str1: string, str2: string): string { + const len = Math.min(str1.length, str2.length); + for (let i = 0; i < len; i++) { + if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { + return str1.substring(0, i); + } + } + return str1.substring(0, len); + } + + public static commonFolderPrefix(fromPath: string, toPath: string): string { + const prefix = CSSPluginUtilities.commonPrefix(fromPath, toPath); + const slashIndex = prefix.lastIndexOf('/'); + if (slashIndex === -1) { + return ''; + } + return prefix.substring(0, slashIndex + 1); + } + + public static relativePath(fromPath: string, toPath: string): string { + if (CSSPluginUtilities.startsWith(toPath, '/') || CSSPluginUtilities.startsWith(toPath, 'http://') || CSSPluginUtilities.startsWith(toPath, 'https://')) { + return toPath; + } + + // Ignore common folder prefix + const prefix = CSSPluginUtilities.commonFolderPrefix(fromPath, toPath); + fromPath = fromPath.substr(prefix.length); + toPath = toPath.substr(prefix.length); + + const upCount = fromPath.split('/').length; + let result = ''; + for (let i = 1; i < upCount; i++) { + result += '../'; + } + return result + toPath; + } + + public static replaceURL(contents: string, replacer: (url: string) => string): string { + // Use ")" as the terminator as quotes are oftentimes not used at all + return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { + let url = matches[0]; + // Eliminate starting quotes (the initial whitespace is not captured) + if (url.charAt(0) === '"' || url.charAt(0) === '\'') { + url = url.substring(1); + } + // The ending whitespace is captured + while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { + url = url.substring(0, url.length - 1); + } + // Eliminate ending quotes + if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { + url = url.substring(0, url.length - 1); + } + + if (!CSSPluginUtilities.startsWith(url, 'data:') && !CSSPluginUtilities.startsWith(url, 'http://') && !CSSPluginUtilities.startsWith(url, 'https://')) { + url = replacer(url); + } + + return 'url(' + url + ')'; + }); + } +} diff --git a/src/vs/css.js b/src/vs/css.js index 6c64a99dba..ad8d37febd 100644 --- a/src/vs/css.js +++ b/src/vs/css.js @@ -16,10 +16,6 @@ 'use strict'; var CSSLoaderPlugin; (function (CSSLoaderPlugin) { - /** - * Known issue: - * - In IE there is no way to know if the CSS file loaded successfully or not. - */ var BrowserCSSLoader = /** @class */ (function () { function BrowserCSSLoader() { this._pendingLoads = 0; diff --git a/src/vs/css.ts b/src/vs/css.ts new file mode 100644 index 0000000000..0cf0e755ea --- /dev/null +++ b/src/vs/css.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface ICSSPluginConfig { + disabled?: boolean; +} + +/** + * Invoked by the loader at run-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions | { isBuild?: boolean }): void { // {{SQL CARBON EDIT}} Added type to config + config = config || {}; + const cssConfig = (config['vs/css'] || {}); + + if (cssConfig.disabled) { + // the plugin is asked to not create any style sheets + load({}); + return; + } + + const cssUrl = req.toUrl(name + '.css'); + loadCSS(name, cssUrl, () => { + load({}); + }, (err: any) => { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + '.'); + } + }); +} + +function loadCSS(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + if (linkTagExists(name, cssUrl)) { + callback(); + return; + } + createLinkTag(name, cssUrl, callback, errorback); +} + +function linkTagExists(name: string, cssUrl: string): boolean { + const links = document.getElementsByTagName('link'); + for (let i = 0, len = links.length; i < len; i++) { + const nameAttr = links[i].getAttribute('data-name'); + const hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; + } + } + return false; +} + +function createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + const linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); + + attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); + + const head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(linkNode); +} + +function attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { + const unbind = () => { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + const loadEventListener = (e: any) => { + unbind(); + callback(); + }; + const errorEventListener = (e: any) => { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); +} diff --git a/src/vs/editor/browser/config/domFontInfo.ts b/src/vs/editor/browser/config/domFontInfo.ts index 85c93bfce8..8b63846129 100644 --- a/src/vs/editor/browser/config/domFontInfo.ts +++ b/src/vs/editor/browser/config/domFontInfo.ts @@ -3,21 +3,19 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as browser from 'vs/base/browser/browser'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; -import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; export function applyFontInfo(domNode: FastDomNode | HTMLElement, fontInfo: BareFontInfo): void { if (domNode instanceof FastDomNode) { - domNode.setFontFamily(fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null)); + domNode.setFontFamily(fontInfo.getMassagedFontFamily()); domNode.setFontWeight(fontInfo.fontWeight); domNode.setFontSize(fontInfo.fontSize); domNode.setFontFeatureSettings(fontInfo.fontFeatureSettings); domNode.setLineHeight(fontInfo.lineHeight); domNode.setLetterSpacing(fontInfo.letterSpacing); } else { - domNode.style.fontFamily = fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null); + domNode.style.fontFamily = fontInfo.getMassagedFontFamily(); domNode.style.fontWeight = fontInfo.fontWeight; domNode.style.fontSize = fontInfo.fontSize + 'px'; domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts index 353d5aac1e..89ee977c55 100644 --- a/src/vs/editor/browser/config/editorConfiguration.ts +++ b/src/vs/editor/browser/config/editorConfiguration.ts @@ -30,12 +30,6 @@ export interface IEditorConstructionOptions extends IEditorOptions { * Defaults to an internal DOM node. */ overflowWidgetsDomNode?: HTMLElement; - /** - * Enables dropping into the editor. - * - * This shows a preview of the drop location and triggers an `onDropIntoEditor` event. - */ - enableDropIntoEditor?: boolean; } export class EditorConfiguration extends Disposable implements IEditorConfiguration { diff --git a/src/vs/editor/browser/config/fontMeasurements.ts b/src/vs/editor/browser/config/fontMeasurements.ts index 424668ebb8..f2d25f4f42 100644 --- a/src/vs/editor/browser/config/fontMeasurements.ts +++ b/src/vs/editor/browser/config/fontMeasurements.ts @@ -149,9 +149,7 @@ class FontMeasurementsImpl extends Disposable { private _createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[] | null): CharWidthRequest { const result = new CharWidthRequest(chr, type); all.push(result); - if (monospace) { - monospace.push(result); - } + monospace?.push(result); return result; } diff --git a/src/vs/editor/browser/config/migrateOptions.ts b/src/vs/editor/browser/config/migrateOptions.ts index 1b519aa3c3..723a082497 100644 --- a/src/vs/editor/browser/config/migrateOptions.ts +++ b/src/vs/editor/browser/config/migrateOptions.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { forEach } from 'vs/base/common/collections'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; export interface ISettingsReader { @@ -152,14 +151,22 @@ const suggestFilteredTypesMapping: Record = { registerEditorSettingMigration('suggest.filteredTypes', (value, read, write) => { if (value && typeof value === 'object') { - forEach(suggestFilteredTypesMapping, entry => { - const v = value[entry.key]; + for (const entry of Object.entries(suggestFilteredTypesMapping)) { + const v = value[entry[0]]; if (v === false) { - if (typeof read(`suggest.${entry.value}`) === 'undefined') { - write(`suggest.${entry.value}`, false); + if (typeof read(`suggest.${entry[1]}`) === 'undefined') { + write(`suggest.${entry[1]}`, false); } } - }); + } write('suggest.filteredTypes', undefined); } }); + +registerEditorSettingMigration('quickSuggestions', (input, read, write) => { + if (typeof input === 'boolean') { + const value = input ? 'on' : 'off'; + const newValue = { comments: value, strings: value, other: value }; + write('quickSuggestions', newValue); + } +}); diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 8c5e924b78..ac5875a20c 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { TimeoutTimer } from 'vs/base/common/async'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { HitTestContext, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; import { IMouseTarget, IMouseTargetViewZoneData, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -21,22 +21,6 @@ import * as viewEvents from 'vs/editor/common/viewEvents'; import { ViewEventHandler } from 'vs/editor/common/viewEventHandler'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -/** - * Merges mouse events when mouse move events are throttled - */ -export function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) { - return function (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent { - let targetIsWidget = false; - if (mouseTargetFactory) { - targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent); - } - if (!targetIsWidget) { - currentEvent.preventDefault(); - } - return currentEvent; - }; -} - export interface IPointerHandlerHelper { viewDomNode: HTMLElement; linesContentDomNode: HTMLElement; @@ -64,8 +48,6 @@ export interface IPointerHandlerHelper { export class MouseHandler extends ViewEventHandler { - static readonly MOUSE_MOVE_MINIMUM_TIME = 100; // ms - protected _context: ViewContext; protected viewController: ViewController; protected viewHelper: IPointerHandlerHelper; @@ -73,6 +55,7 @@ export class MouseHandler extends ViewEventHandler { protected readonly _mouseDownOperation: MouseDownOperation; private lastMouseLeaveTime: number; private _height: number; + private _mouseLeaveMonitor: IDisposable | null = null; constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { super(); @@ -97,9 +80,25 @@ export class MouseHandler extends ViewEventHandler { this._register(mouseEvents.onContextMenu(this.viewHelper.viewDomNode, (e) => this._onContextMenu(e, true))); - this._register(mouseEvents.onMouseMoveThrottled(this.viewHelper.viewDomNode, - (e) => this._onMouseMove(e), - createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME)); + this._register(mouseEvents.onMouseMove(this.viewHelper.viewDomNode, (e) => { + this._onMouseMove(e); + + // See https://github.com/microsoft/vscode/issues/138789 + // When moving the mouse really quickly, the browser sometimes forgets to + // send us a `mouseleave` or `mouseout` event. We therefore install here + // a global `mousemove` listener to manually recover if the mouse goes outside + // the editor. As soon as the mouse leaves outside of the editor, we + // remove this listener + + if (!this._mouseLeaveMonitor) { + this._mouseLeaveMonitor = dom.addDisposableListener(document, 'mousemove', (e) => { + if (!this.viewHelper.viewDomNode.contains(e.target as Node | null)) { + // went outside the editor! + this._onMouseLeave(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode)); + } + }); + } + })); this._register(mouseEvents.onMouseUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e))); @@ -109,11 +108,9 @@ export class MouseHandler extends ViewEventHandler { // because their `e.detail` is always 0. // We will therefore save the pointer id for the mouse and then reuse it in the `mousedown` event // for `element.setPointerCapture`. - let mousePointerId: number = 0; - this._register(mouseEvents.onPointerDown(this.viewHelper.viewDomNode, (e, pointerType, pointerId) => { - if (pointerType === 'mouse') { - mousePointerId = pointerId; - } + let capturePointerId: number = 0; + this._register(mouseEvents.onPointerDown(this.viewHelper.viewDomNode, (e, pointerId) => { + capturePointerId = pointerId; })); // The `pointerup` listener registered by `GlobalEditorPointerMoveMonitor` does not get invoked 100% of the times. // I speculate that this is because the `pointerup` listener is only registered during the `mousedown` event, and perhaps @@ -123,7 +120,7 @@ export class MouseHandler extends ViewEventHandler { this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, dom.EventType.POINTER_UP, (e: PointerEvent) => { this._mouseDownOperation.onPointerUp(); })); - this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e, mousePointerId))); + this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e, capturePointerId))); const onMouseWheel = (browserEvent: IMouseWheelEvent) => { this.viewController.emitMouseWheel(browserEvent); @@ -154,6 +151,10 @@ export class MouseHandler extends ViewEventHandler { public override dispose(): void { this._context.removeEventHandler(this); + if (this._mouseLeaveMonitor) { + this._mouseLeaveMonitor.dispose(); + this._mouseLeaveMonitor = null; + } super.dispose(); } @@ -220,6 +221,11 @@ export class MouseHandler extends ViewEventHandler { } public _onMouseMove(e: EditorMouseEvent): void { + const targetIsWidget = this.mouseTargetFactory.mouseTargetIsWidget(e); + if (!targetIsWidget) { + e.preventDefault(); + } + if (this._mouseDownOperation.isActive()) { // In selection/drag operation return; @@ -237,6 +243,10 @@ export class MouseHandler extends ViewEventHandler { } public _onMouseLeave(e: EditorMouseEvent): void { + if (this._mouseLeaveMonitor) { + this._mouseLeaveMonitor.dispose(); + this._mouseLeaveMonitor = null; + } this.lastMouseLeaveTime = (new Date()).getTime(); this.viewController.emitMouseLeave({ event: e, @@ -404,7 +414,6 @@ class MouseDownOperation extends Disposable { this._viewHelper.viewLinesDomNode, pointerId, e.buttons, - createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), (browserEvent?: MouseEvent | KeyboardEvent) => { const position = this._findMousePosition(this._lastMouseEvent!, false); @@ -435,7 +444,6 @@ class MouseDownOperation extends Disposable { this._viewHelper.viewLinesDomNode, pointerId, e.buttons, - createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), () => this._stop() ); diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 46fcbb24f5..1c265289f9 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -823,7 +823,21 @@ export class MouseTargetFactory { const curr = points[i]; if (prev.offset <= request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset <= curr.offset) { const rng = new EditorRange(lineNumber, prev.column, lineNumber, curr.column); - return request.fulfillContentText(pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); + + // See https://github.com/microsoft/vscode/issues/152819 + // Due to the use of zwj, the browser's hit test result is skewed towards the left + // Here we try to correct that if the mouse horizontal offset is closer to the right than the left + + const prevDelta = Math.abs(prev.offset - request.mouseContentHorizontalOffset); + const nextDelta = Math.abs(curr.offset - request.mouseContentHorizontalOffset); + + const resultPos = ( + prevDelta < nextDelta + ? new Position(lineNumber, prev.column) + : new Position(lineNumber, curr.column) + ); + + return request.fulfillContentText(resultPos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); } } return request.fulfillContentText(pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 81be5b6433..e52b61d5fa 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from 'vs/editor/browser/controller/mouseHandler'; +import { IPointerHandlerHelper, MouseHandler } from 'vs/editor/browser/controller/mouseHandler'; import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; @@ -45,9 +45,7 @@ export class PointerEventHandler extends MouseHandler { // PonterEvents const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode); - this._register(pointerEvents.onPointerMoveThrottled(this.viewHelper.viewDomNode, - (e) => this._onMouseMove(e), - createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME)); + this._register(pointerEvents.onPointerMove(this.viewHelper.viewDomNode, (e) => this._onMouseMove(e))); this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e))); this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e))); this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e, pointerId) => this._onMouseDown(e, pointerId))); diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 2bb33282d9..c352bf4985 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -31,7 +31,8 @@ import * as viewEvents from 'vs/editor/common/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { ColorId, ITokenPresentation, TokenizationRegistry } from 'vs/editor/common/languages'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { ColorId, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes'; import { Color } from 'vs/base/common/color'; export interface IVisibleRangeProvider { @@ -521,7 +522,7 @@ export class TextAreaHandler extends ViewPart { this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); const accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); if (this._accessibilitySupport === AccessibilitySupport.Enabled && accessibilityPageSize === EditorOptions.accessibilityPageSize.defaultValue) { - // If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 500 for a better experience + // If a screen reader is attached and the default value is not set we should automatically increase the page size to 500 for a better experience this._accessibilityPageSize = 500; } else { this._accessibilityPageSize = accessibilityPageSize; @@ -633,9 +634,7 @@ export class TextAreaHandler extends ViewPart { public prepareRender(ctx: RenderingContext): void { this._primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(this._primaryCursorPosition); - if (this._visibleTextArea) { - this._visibleTextArea.prepareRender(ctx); - } + this._visibleTextArea?.prepareRender(ctx); } public render(ctx: RestrictedRenderingContext): void { diff --git a/src/vs/editor/browser/coreCommands.ts b/src/vs/editor/browser/coreCommands.ts index 228b1c6e5f..37a6088f9c 100644 --- a/src/vs/editor/browser/coreCommands.ts +++ b/src/vs/editor/browser/coreCommands.ts @@ -84,7 +84,7 @@ export namespace EditorScroll_ { \`\`\` * 'by': Unit to move. Default is computed based on 'to' value. \`\`\` - 'line', 'wrappedLine', 'page', 'halfPage' + 'line', 'wrappedLine', 'page', 'halfPage', 'editor' \`\`\` * 'value': Number of units to move. Default is '1'. * 'revealCursor': If 'true' reveals the cursor if it is outside view port. @@ -100,7 +100,7 @@ export namespace EditorScroll_ { }, 'by': { 'type': 'string', - 'enum': ['line', 'wrappedLine', 'page', 'halfPage'] + 'enum': ['line', 'wrappedLine', 'page', 'halfPage', 'editor'] }, 'value': { 'type': 'number', @@ -130,7 +130,8 @@ export namespace EditorScroll_ { Line: 'line', WrappedLine: 'wrappedLine', Page: 'page', - HalfPage: 'halfPage' + HalfPage: 'halfPage', + Editor: 'editor' }; /** @@ -172,6 +173,9 @@ export namespace EditorScroll_ { case RawUnit.HalfPage: unit = Unit.HalfPage; break; + case RawUnit.Editor: + unit = Unit.Editor; + break; default: unit = Unit.WrappedLine; } @@ -205,7 +209,8 @@ export namespace EditorScroll_ { Line = 1, WrappedLine = 2, Page = 3, - HalfPage = 4 + HalfPage = 4, + Editor = 5 } } @@ -1279,6 +1284,14 @@ export namespace CoreNavigationCommands { return viewModel.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber); } + if (args.unit === EditorScroll_.Unit.Editor) { + let desiredTopModelLineNumber = 0; + if (args.direction === EditorScroll_.Direction.Down) { + desiredTopModelLineNumber = viewModel.model.getLineCount() - viewModel.cursorConfig.pageSize; + } + return viewModel.viewLayout.getVerticalOffsetForLineNumber(desiredTopModelLineNumber); + } + let noOfLines: number; if (args.unit === EditorScroll_.Unit.Page) { noOfLines = viewModel.cursorConfig.pageSize * args.value; @@ -1345,6 +1358,29 @@ export namespace CoreNavigationCommands { } }); + export const ScrollEditorTop: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'scrollEditorTop', + precondition: undefined, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textInputFocus, + } + }); + } + + runCoreEditorCommand(viewModel: IViewModel, args: any): void { + EditorScroll._runEditorScroll(viewModel, args.source, { + direction: EditorScroll_.Direction.Up, + unit: EditorScroll_.Unit.Editor, + value: 1, + revealCursor: false, + select: false + }); + } + }); + export const ScrollLineDown: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { constructor() { super({ @@ -1396,6 +1432,29 @@ export namespace CoreNavigationCommands { } }); + export const ScrollEditorBottom: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand { + constructor() { + super({ + id: 'scrollEditorBottom', + precondition: undefined, + kbOpts: { + weight: CORE_WEIGHT, + kbExpr: EditorContextKeys.textInputFocus, + } + }); + } + + runCoreEditorCommand(viewModel: IViewModel, args: any): void { + EditorScroll._runEditorScroll(viewModel, args.source, { + direction: EditorScroll_.Direction.Down, + unit: EditorScroll_.Unit.Editor, + value: 1, + revealCursor: false, + select: false + }); + } + }); + class WordCommand extends CoreEditorCommand { private readonly _inSelectionMode: boolean; diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts new file mode 100644 index 0000000000..542a05e64f --- /dev/null +++ b/src/vs/editor/browser/dnd.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DataTransfers } from 'vs/base/browser/dnd'; +import { distinct } from 'vs/base/common/arrays'; +import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { Mimes } from 'vs/base/common/mime'; +import { URI } from 'vs/base/common/uri'; +import { CodeDataTransfers, extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; + + +export function toVSDataTransfer(dataTransfer: DataTransfer) { + const vsDataTransfer = new VSDataTransfer(); + for (const item of dataTransfer.items) { + const type = item.type; + if (item.kind === 'string') { + const asStringValue = new Promise(resolve => item.getAsString(resolve)); + vsDataTransfer.append(type, createStringDataTransferItem(asStringValue)); + } else if (item.kind === 'file') { + const file = item.getAsFile(); + if (file) { + vsDataTransfer.append(type, createFileDataTransferItemFromFile(file)); + } + } + } + return vsDataTransfer; +} + +export function createFileDataTransferItemFromFile(file: File): IDataTransferItem { + const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; + return createFileDataTransferItem(file.name, uri, async () => { + return new Uint8Array(await file.arrayBuffer()); + }); +} + +const INTERNAL_DND_MIME_TYPES = Object.freeze([ + CodeDataTransfers.EDITORS, + CodeDataTransfers.FILES, + DataTransfers.RESOURCES, +]); + +export function addExternalEditorsDropData(dataTransfer: VSDataTransfer, dragEvent: DragEvent, overwriteUriList = false) { + if (dragEvent.dataTransfer && (overwriteUriList || !dataTransfer.has(Mimes.uriList))) { + const editorData = extractEditorsDropData(dragEvent) + .filter(input => input.resource) + .map(input => input.resource!.toString()); + + // Also add in the files + for (const item of dragEvent.dataTransfer?.items) { + const file = item.getAsFile(); + if (file) { + editorData.push((file as FileAdditionalNativeProperties).path ? URI.file((file as FileAdditionalNativeProperties).path!).toString() : file.name); + } + } + + if (editorData.length) { + dataTransfer.replace(Mimes.uriList, createStringDataTransferItem(UriList.create(editorData))); + } + } + + for (const internal of INTERNAL_DND_MIME_TYPES) { + dataTransfer.delete(internal); + } +} + +export const UriList = Object.freeze({ + // http://amundsen.com/hypermedia/urilist/ + create: (entries: ReadonlyArray): string => { + return distinct(entries.map(x => x.toString())).join('\r\n'); + }, + parse: (str: string): string[] => { + return str.split('\r\n').filter(value => !value.startsWith('#')); + } +}); diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 570ecbdec7..cb63b590ae 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -681,7 +681,7 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * Restores the view state of the editor from a serializable object generated by `saveViewState`. */ - restoreViewState(state: editorCommon.ICodeEditorViewState): void; + restoreViewState(state: editorCommon.ICodeEditorViewState | null): void; /** * Returns true if the text inside this editor or an editor widget has focus. @@ -852,24 +852,29 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * All decorations added through this call will get the ownerId of this editor. - * @see {@link ITextModel.deltaDecorations} + * @deprecated */ deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[]; /** - * @internal + * Remove previously added decorations. */ - setDecorations(description: string, decorationTypeKey: string, ranges: editorCommon.IDecorationOptions[]): void; + removeDecorations(decorationIds: string[]): void; /** * @internal */ - setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void; + setDecorationsByType(description: string, decorationTypeKey: string, ranges: editorCommon.IDecorationOptions[]): void; /** * @internal */ - removeDecorations(decorationTypeKey: string): void; + setDecorationsByTypeFast(decorationTypeKey: string, ranges: IRange[]): void; + + /** + * @internal + */ + removeDecorationsByType(decorationTypeKey: string): void; /** * Get the layout info for the editor. @@ -894,10 +899,15 @@ export interface ICodeEditor extends editorCommon.IEditor { getWhitespaces(): IEditorWhitespace[]; /** - * Get the vertical position (top offset) for the line w.r.t. to the first line. + * Get the vertical position (top offset) for the line's top w.r.t. to the first line. */ getTopForLineNumber(lineNumber: number): number; + /** + * Get the vertical position (top offset) for the line's bottom w.r.t. to the first line. + */ + getBottomForLineNumber(lineNumber: number): number; + /** * Get the vertical position (top offset) for the position w.r.t. to the first line. */ @@ -1116,7 +1126,7 @@ export interface IDiffEditor extends editorCommon.IEditor { /** * Restores the view state of the editor from a serializable object generated by `saveViewState`. */ - restoreViewState(state: editorCommon.IDiffEditorViewState): void; + restoreViewState(state: editorCommon.IDiffEditorViewState | null): void; /** * Type the getModel() of IEditor. diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index fa1e28f19f..916a7ea9fa 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -136,10 +136,6 @@ export class EditorMouseEvent extends StandardMouseEvent { } } -export interface EditorMouseEventMerger { - (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent; -} - export class EditorMouseEventFactory { private readonly _editorViewDomNode: HTMLElement; @@ -170,23 +166,20 @@ export class EditorMouseEventFactory { }); } - public onPointerDown(target: HTMLElement, callback: (e: EditorMouseEvent, pointerType: string, pointerId: number) => void): IDisposable { + public onPointerDown(target: HTMLElement, callback: (e: EditorMouseEvent, pointerId: number) => void): IDisposable { return dom.addDisposableListener(target, dom.EventType.POINTER_DOWN, (e: PointerEvent) => { - callback(this._create(e), e.pointerType, e.pointerId); + callback(this._create(e), e.pointerId); }); } public onMouseLeave(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { - return dom.addDisposableNonBubblingMouseOutListener(target, (e: MouseEvent) => { + return dom.addDisposableListener(target, dom.EventType.MOUSE_LEAVE, (e: MouseEvent) => { callback(this._create(e)); }); } - public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { - return merger(lastEvent, this._create(currentEvent)); - }; - return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); + public onMouseMove(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'mousemove', (e) => callback(this._create(e))); } } @@ -215,29 +208,26 @@ export class EditorPointerEventFactory { } public onPointerLeave(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { - return dom.addDisposableNonBubblingPointerOutListener(target, (e: MouseEvent) => { + return dom.addDisposableListener(target, dom.EventType.POINTER_LEAVE, (e: MouseEvent) => { callback(this._create(e)); }); } - public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { - return merger(lastEvent, this._create(currentEvent)); - }; - return dom.addDisposableThrottledListener(target, 'pointermove', callback, myMerger, minimumTimeMs); + public onPointerMove(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'pointermove', (e) => callback(this._create(e))); } } export class GlobalEditorPointerMoveMonitor extends Disposable { private readonly _editorViewDomNode: HTMLElement; - private readonly _globalPointerMoveMonitor: GlobalPointerMoveMonitor; + private readonly _globalPointerMoveMonitor: GlobalPointerMoveMonitor; private _keydownListener: IDisposable | null; constructor(editorViewDomNode: HTMLElement) { super(); this._editorViewDomNode = editorViewDomNode; - this._globalPointerMoveMonitor = this._register(new GlobalPointerMoveMonitor()); + this._globalPointerMoveMonitor = this._register(new GlobalPointerMoveMonitor()); this._keydownListener = null; } @@ -245,7 +235,6 @@ export class GlobalEditorPointerMoveMonitor extends Disposable { initialElement: Element, pointerId: number, initialButtons: number, - merger: EditorMouseEventMerger, pointerMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: (browserEvent?: PointerEvent | KeyboardEvent) => void ): void { @@ -261,14 +250,18 @@ export class GlobalEditorPointerMoveMonitor extends Disposable { this._globalPointerMoveMonitor.stopMonitoring(true, e.browserEvent); }, true); - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: PointerEvent): EditorMouseEvent => { - return merger(lastEvent, new EditorMouseEvent(currentEvent, true, this._editorViewDomNode)); - }; - - this._globalPointerMoveMonitor.startMonitoring(initialElement, pointerId, initialButtons, myMerger, pointerMoveCallback, (e) => { - this._keydownListener!.dispose(); - onStopCallback(e); - }); + this._globalPointerMoveMonitor.startMonitoring( + initialElement, + pointerId, + initialButtons, + (e) => { + pointerMoveCallback(new EditorMouseEvent(e, true, this._editorViewDomNode)); + }, + (e) => { + this._keydownListener!.dispose(); + onStopCallback(e); + } + ); } public stopMonitoring(): void { diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index afc18bd15f..83f10f46d7 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -338,8 +338,10 @@ export abstract class EditorAction extends EditorCommand { protected reportTelemetry(accessor: ServicesAccessor, editor: ICodeEditor) { type EditorActionInvokedClassification = { - name: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; - id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; + owner: 'alexdima'; + comment: 'An editor action has been invoked.'; + name: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The label of the action that was invoked.' }; + id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the action that was invoked.' }; }; type EditorActionInvokedEvent = { name: string; diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 9b488fb02b..510f67c4f8 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -5,11 +5,12 @@ import * as dom from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { LinkedList } from 'vs/base/common/linkedList'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { ICodeEditorOpenHandler, ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IContentDecorationRenderOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions, isThemeColor } from 'vs/editor/common/editorCommon'; import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, InjectedTextOptions, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; @@ -42,6 +43,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC protected _globalStyleSheet: GlobalStyleSheet | null; private readonly _decorationOptionProviders = new Map(); private readonly _editorStyleSheets = new Map(); + private readonly _codeEditorOpenHandlers = new LinkedList(); constructor( @IThemeService private readonly _themeService: IThemeService, @@ -161,7 +163,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC if (provider.refCount <= 0) { this._decorationOptionProviders.delete(key); provider.dispose(); - this.listCodeEditors().forEach((ed) => ed.removeDecorations(key)); + this.listCodeEditors().forEach((ed) => ed.removeDecorationsByType(key)); } } } @@ -247,7 +249,21 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC } abstract getActiveCodeEditor(): ICodeEditor | null; - abstract openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; + + async openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { + for (const handler of this._codeEditorOpenHandlers) { + const candidate = await handler(input, source, sideBySide); + if (candidate !== null) { + return candidate; + } + } + return null; + } + + registerCodeEditorOpenHandler(handler: ICodeEditorOpenHandler): IDisposable { + const rm = this._codeEditorOpenHandlers.unshift(handler); + return toDisposable(rm); + } } export class ModelTransientSettingWatcher { @@ -776,7 +792,7 @@ class DecorationCSSRules { private collectCSSText(opts: any, properties: string[], cssTextArr: string[]): boolean { const lenBefore = cssTextArr.length; - for (let property of properties) { + for (const property of properties) { const value = this.resolveValue(opts[property]); if (typeof value === 'string') { cssTextArr.push(strings.format(_CSS_MAP[property], value)); diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 019f4e2463..5c8bd18041 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, WorkspaceFileEdit, WorkspaceFileEditOptions, WorkspaceTextEdit } from 'vs/editor/common/languages'; +import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, IWorkspaceFileEdit, WorkspaceFileEditOptions, IWorkspaceTextEdit } from 'vs/editor/common/languages'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -15,49 +15,77 @@ import { CancellationToken } from 'vs/base/common/cancellation'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); -function isWorkspaceFileEdit(thing: any): thing is WorkspaceFileEdit { - return isObject(thing) && (Boolean((thing).newUri) || Boolean((thing).oldUri)); -} - -function isWorkspaceTextEdit(thing: any): thing is WorkspaceTextEdit { - return isObject(thing) && URI.isUri((thing).resource) && isObject((thing).edit); -} - export class ResourceEdit { protected constructor(readonly metadata?: WorkspaceEditMetadata) { } static convert(edit: WorkspaceEdit): ResourceEdit[] { - return edit.edits.map(edit => { - if (isWorkspaceTextEdit(edit)) { - return new ResourceTextEdit(edit.resource, edit.edit, edit.modelVersionId, edit.metadata); + if (ResourceTextEdit.is(edit)) { + return ResourceTextEdit.lift(edit); } - if (isWorkspaceFileEdit(edit)) { - return new ResourceFileEdit(edit.oldUri, edit.newUri, edit.options, edit.metadata); + + if (ResourceFileEdit.is(edit)) { + return ResourceFileEdit.lift(edit); } throw new Error('Unsupported edit'); }); } } -export class ResourceTextEdit extends ResourceEdit { +export class ResourceTextEdit extends ResourceEdit implements IWorkspaceTextEdit { + + static is(candidate: any): candidate is IWorkspaceTextEdit { + if (candidate instanceof ResourceTextEdit) { + return true; + } + return isObject(candidate) + && URI.isUri((candidate).resource) + && isObject((candidate).textEdit); + } + + static lift(edit: IWorkspaceTextEdit): ResourceTextEdit { + if (edit instanceof ResourceTextEdit) { + return edit; + } else { + return new ResourceTextEdit(edit.resource, edit.textEdit, edit.versionId, edit.metadata); + } + } + constructor( readonly resource: URI, - readonly textEdit: TextEdit, - readonly versionId?: number, - metadata?: WorkspaceEditMetadata + readonly textEdit: TextEdit & { insertAsSnippet?: boolean }, + readonly versionId: number | undefined = undefined, + metadata?: WorkspaceEditMetadata, ) { super(metadata); } } -export class ResourceFileEdit extends ResourceEdit { +export class ResourceFileEdit extends ResourceEdit implements IWorkspaceFileEdit { + + static is(candidate: any): candidate is IWorkspaceFileEdit { + if (candidate instanceof ResourceFileEdit) { + return true; + } else { + return isObject(candidate) + && (Boolean((candidate).newResource) || Boolean((candidate).oldResource)); + } + } + + static lift(edit: IWorkspaceFileEdit): ResourceFileEdit { + if (edit instanceof ResourceFileEdit) { + return edit; + } else { + return new ResourceFileEdit(edit.oldResource, edit.newResource, edit.options, edit.metadata); + } + } + constructor( readonly oldResource: URI | undefined, readonly newResource: URI | undefined, - readonly options?: WorkspaceFileEditOptions, + readonly options: WorkspaceFileEditOptions = {}, metadata?: WorkspaceEditMetadata ) { super(metadata); diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 356f5b040e..bf194117e5 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -10,6 +10,7 @@ import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; export const ICodeEditorService = createDecorator('codeEditorService'); @@ -53,4 +54,9 @@ export interface ICodeEditorService { getActiveCodeEditor(): ICodeEditor | null; openCodeEditor(input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; + registerCodeEditorOpenHandler(handler: ICodeEditorOpenHandler): IDisposable; +} + +export interface ICodeEditorOpenHandler { + (input: ITextResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; } diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index d05542ded2..697f7281b5 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -303,7 +303,7 @@ class EditorModelManager extends Disposable { } public override dispose(): void { - for (let modelUrl in this._syncedModels) { + for (const modelUrl in this._syncedModels) { dispose(this._syncedModels[modelUrl]); } this._syncedModels = Object.create(null); @@ -328,7 +328,7 @@ class EditorModelManager extends Disposable { const currentTime = (new Date()).getTime(); const toRemove: string[] = []; - for (let modelUrl in this._syncedModelsLastUsedTime) { + for (const modelUrl in this._syncedModelsLastUsedTime) { const elapsedTime = currentTime - this._syncedModelsLastUsedTime[modelUrl]; if (elapsedTime > STOP_SYNC_MODEL_DELTA_TIME_MS) { toRemove.push(modelUrl); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 72b297ae79..74f729565f 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -163,7 +163,7 @@ export class OpenerService implements IOpenerService { // validate against the original URI that this URI resolves to, if one exists const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target; for (const validator of this._validators) { - if (!(await validator.shouldOpen(validationTarget))) { + if (!(await validator.shouldOpen(validationTarget, options))) { return false; } } diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index e6136ab871..f74e8822af 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -49,6 +49,7 @@ import { IViewModel } from 'vs/editor/common/viewModel'; import { getThemeTypeSelector, IColorTheme } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; +import { BlockDecorations } from 'vs/editor/browser/viewParts/blockDecorations/blockDecorations'; export interface IContentWidgetData { @@ -180,6 +181,9 @@ export class View extends ViewEventHandler { const rulers = new Rulers(this._context); this._viewParts.push(rulers); + const blockOutline = new BlockDecorations(this._context); + this._viewParts.push(blockOutline); + const minimap = new Minimap(this._context); this._viewParts.push(minimap); @@ -192,6 +196,7 @@ export class View extends ViewEventHandler { this._linesContent.appendChild(contentViewOverlays.getDomNode()); this._linesContent.appendChild(rulers.domNode); + this._linesContent.appendChild(blockOutline.domNode); this._linesContent.appendChild(this._viewZones.domNode); this._linesContent.appendChild(this._viewLines.getDomNode()); this._linesContent.appendChild(this._contentWidgets.domNode); diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts index 5912efe308..114e4144a4 100644 --- a/src/vs/editor/browser/view/viewUserInputEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -33,69 +33,47 @@ export class ViewUserInputEvents { } public emitKeyDown(e: IKeyboardEvent): void { - if (this.onKeyDown) { - this.onKeyDown(e); - } + this.onKeyDown?.(e); } public emitKeyUp(e: IKeyboardEvent): void { - if (this.onKeyUp) { - this.onKeyUp(e); - } + this.onKeyUp?.(e); } public emitContextMenu(e: IEditorMouseEvent): void { - if (this.onContextMenu) { - this.onContextMenu(this._convertViewToModelMouseEvent(e)); - } + this.onContextMenu?.(this._convertViewToModelMouseEvent(e)); } public emitMouseMove(e: IEditorMouseEvent): void { - if (this.onMouseMove) { - this.onMouseMove(this._convertViewToModelMouseEvent(e)); - } + this.onMouseMove?.(this._convertViewToModelMouseEvent(e)); } public emitMouseLeave(e: IPartialEditorMouseEvent): void { - if (this.onMouseLeave) { - this.onMouseLeave(this._convertViewToModelMouseEvent(e)); - } + this.onMouseLeave?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDown(e: IEditorMouseEvent): void { - if (this.onMouseDown) { - this.onMouseDown(this._convertViewToModelMouseEvent(e)); - } + this.onMouseDown?.(this._convertViewToModelMouseEvent(e)); } public emitMouseUp(e: IEditorMouseEvent): void { - if (this.onMouseUp) { - this.onMouseUp(this._convertViewToModelMouseEvent(e)); - } + this.onMouseUp?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDrag(e: IEditorMouseEvent): void { - if (this.onMouseDrag) { - this.onMouseDrag(this._convertViewToModelMouseEvent(e)); - } + this.onMouseDrag?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDrop(e: IPartialEditorMouseEvent): void { - if (this.onMouseDrop) { - this.onMouseDrop(this._convertViewToModelMouseEvent(e)); - } + this.onMouseDrop?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDropCanceled(): void { - if (this.onMouseDropCanceled) { - this.onMouseDropCanceled(); - } + this.onMouseDropCanceled?.(); } public emitMouseWheel(e: IMouseWheelEvent): void { - if (this.onMouseWheel) { - this.onMouseWheel(e); - } + this.onMouseWheel?.(e); } private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent; diff --git a/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.css b/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.css new file mode 100644 index 0000000000..f2100bd484 --- /dev/null +++ b/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.css @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .blockDecorations-container { + position: absolute; + top: 0; +} + +.monaco-editor .blockDecorations-block { + position: absolute; + box-sizing: border-box; +} diff --git a/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts b/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts new file mode 100644 index 0000000000..d7f72cfa80 --- /dev/null +++ b/src/vs/editor/browser/viewParts/blockDecorations/blockDecorations.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode'; +import 'vs/css!./blockDecorations'; +import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/browser/view/renderingContext'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import * as viewEvents from 'vs/editor/common/viewEvents'; +import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; + +export class BlockDecorations extends ViewPart { + + public domNode: FastDomNode; + + private readonly blocks: FastDomNode[] = []; + + private contentWidth: number = -1; + + constructor(context: ViewContext) { + super(context); + + this.domNode = createFastDomNode(document.createElement('div')); + this.domNode.setAttribute('role', 'presentation'); + this.domNode.setAttribute('aria-hidden', 'true'); + this.domNode.setClassName('blockDecorations-container'); + + this.update(); + } + + private update(): boolean { + let didChange = false; + const options = this._context.configuration.options; + const layoutInfo = options.get(EditorOption.layoutInfo); + const newContentWidth = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth; + + if (this.contentWidth !== newContentWidth) { + this.contentWidth = newContentWidth; + didChange = true; + } + + return didChange; + } + + public override dispose(): void { + super.dispose(); + } + + // --- begin event handlers + + public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return this.update(); + } + public override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged || e.scrollLeftChanged; + } + public override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return true; + } + + public override onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + + // --- end event handlers + public prepareRender(ctx: RenderingContext): void { + // Nothing to read + } + + public render(ctx: RestrictedRenderingContext): void { + let count = 0; + const decorations = ctx.getDecorationsInViewport(); + for (const decoration of decorations) { + if (!decoration.options.blockClassName) { + continue; + } + + let block = this.blocks[count]; + if (!block) { + block = this.blocks[count] = createFastDomNode(document.createElement('div')); + this.domNode.appendChild(block); + } + const top = ctx.getVerticalOffsetForLineNumber(decoration.range.startLineNumber); + // See https://github.com/microsoft/vscode/pull/152740#discussion_r902661546 + const bottom = ctx.getVerticalOffsetForLineNumber(decoration.range.endLineNumber + 1); + + block.setClassName('blockDecorations-block ' + decoration.options.blockClassName); + block.setLeft(ctx.scrollLeft); + block.setWidth(this.contentWidth); + block.setTop(top); + block.setHeight(bottom - top); + + count++; + } + + for (let i = count; i < this.blocks.length; i++) { + this.blocks[i].domNode.remove(); + } + this.blocks.length = count; + } +} diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index dcb3381798..31c6eb8eb1 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -27,6 +27,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { private _lineNumbersLeft!: number; private _lineNumbersWidth!: number; private _lastCursorModelPosition: Position; + private _lastCursorViewPosition: Position; private _renderResult: string[] | null; private _activeLineNumber: number; @@ -37,6 +38,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { this._readConfig(); this._lastCursorModelPosition = new Position(1, 1); + this._lastCursorViewPosition = new Position(1, 1); this._renderResult = null; this._activeLineNumber = 1; this._context.addEventHandler(this); @@ -68,6 +70,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { } public override onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { const primaryViewPosition = e.selections[0].getPosition(); + this._lastCursorViewPosition = primaryViewPosition; this._lastCursorModelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(primaryViewPosition); let shouldRender = false; @@ -112,14 +115,6 @@ export class LineNumbersOverlay extends DynamicViewOverlay { return this._renderCustomLineNumbers(modelLineNumber); } - if (this._renderLineNumbers === RenderLineNumbersType.Relative) { - const diff = Math.abs(this._lastCursorModelPosition.lineNumber - modelLineNumber); - if (diff === 0) { - return '' + modelLineNumber + ''; - } - return String(diff); - } - if (this._renderLineNumbers === RenderLineNumbersType.Interval) { if (this._lastCursorModelPosition.lineNumber === modelLineNumber) { return String(modelLineNumber); @@ -144,6 +139,45 @@ export class LineNumbersOverlay extends DynamicViewOverlay { const visibleEndLineNumber = ctx.visibleRange.endLineNumber; const common = '
    '; + let relativeLineNumbers: number[] | null = null; + if (this._renderLineNumbers === RenderLineNumbersType.Relative) { + relativeLineNumbers = new Array(visibleEndLineNumber - visibleStartLineNumber + 1); + + if (this._lastCursorViewPosition.lineNumber >= visibleStartLineNumber && this._lastCursorViewPosition.lineNumber <= visibleEndLineNumber) { + relativeLineNumbers[this._lastCursorViewPosition.lineNumber - visibleStartLineNumber] = this._lastCursorModelPosition.lineNumber; + } + + // Iterate up to compute relative line numbers + { + let value = 0; + for (let lineNumber = this._lastCursorViewPosition.lineNumber + 1; lineNumber <= visibleEndLineNumber; lineNumber++) { + const modelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, 1)); + const isWrappedLine = (modelPosition.column !== 1); + if (!isWrappedLine) { + value++; + } + if (lineNumber >= visibleStartLineNumber) { + relativeLineNumbers[lineNumber - visibleStartLineNumber] = isWrappedLine ? 0 : value; + } + } + } + + // Iterate down to compute relative line numbers + { + let value = 0; + for (let lineNumber = this._lastCursorViewPosition.lineNumber - 1; lineNumber >= visibleStartLineNumber; lineNumber--) { + const modelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, 1)); + const isWrappedLine = (modelPosition.column !== 1); + if (!isWrappedLine) { + value++; + } + if (lineNumber <= visibleEndLineNumber) { + relativeLineNumbers[lineNumber - visibleStartLineNumber] = isWrappedLine ? 0 : value; + } + } + } + } + const lineCount = this._context.viewModel.getLineCount(); const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { @@ -157,7 +191,20 @@ export class LineNumbersOverlay extends DynamicViewOverlay { } } - const renderLineNumber = this._getLineRenderLineNumber(lineNumber); + let renderLineNumber: string; + if (relativeLineNumbers) { + const relativeLineNumber = relativeLineNumbers[lineIndex]; + if (this._lastCursorViewPosition.lineNumber === lineNumber) { + // current line! + renderLineNumber = `${relativeLineNumber}`; + } else if (relativeLineNumber) { + renderLineNumber = String(relativeLineNumber); + } else { + renderLineNumber = ''; + } + } else { + renderLineNumber = this._getLineRenderLineNumber(lineNumber); + } if (renderLineNumber) { if (lineNumber === this._activeLineNumber) { diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 5bb20137b0..77ef37357b 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -446,8 +446,8 @@ class FastRenderedViewLine implements IRenderedViewLine { } private _getCharPosition(column: number): number { - const charOffset = this._characterMapping.getAbsoluteOffset(column); - return this._charWidth * charOffset; + const horizontalOffset = this._characterMapping.getHorizontalOffset(column); + return this._charWidth * horizontalOffset; } public getColumnOfNodeOffset(lineNumber: number, spanNode: HTMLElement, offset: number): number { @@ -625,8 +625,8 @@ class RenderedViewLine implements IRenderedViewLine { } const result = r[0].left; if (this.input.isBasicASCII) { - const charOffset = this._characterMapping.getAbsoluteOffset(column); - const expectedResult = Math.round(this.input.spaceWidth * charOffset); + const horizontalOffset = this._characterMapping.getHorizontalOffset(column); + const expectedResult = Math.round(this.input.spaceWidth * horizontalOffset); if (Math.abs(expectedResult - result) <= 1) { return expectedResult; } diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index a3e7a59e9f..96951fec6b 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -412,7 +412,8 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return null; } - let visibleRanges: LineVisibleRanges[] = [], visibleRangesLen = 0; + const visibleRanges: LineVisibleRanges[] = []; + let visibleRangesLen = 0; const domReadingContext = new DomReadingContext(this.domNode.domNode, this._textRangeRestingSpot); let nextLineModelLineNumber: number = 0; diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.css b/src/vs/editor/browser/viewParts/minimap/minimap.css index cbfa190288..034d0dbf16 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.css +++ b/src/vs/editor/browser/viewParts/minimap/minimap.css @@ -30,3 +30,12 @@ left: -1px; width: 1px; } + +/* 0.5s fade in/out for the minimap */ +.minimap.autohide { + opacity: 0.0; + transition: opacity 0.5s; +} +.minimap.autohide:hover { + opacity: 1.0; +} diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 1e356e9956..d8502d8f7e 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -6,7 +6,7 @@ import 'vs/css!./minimap'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { GlobalPointerMoveMonitor, standardPointerMoveMerger } from 'vs/base/browser/globalPointerMoveMonitor'; +import { GlobalPointerMoveMonitor } from 'vs/base/browser/globalPointerMoveMonitor'; import { CharCode } from 'vs/base/common/charCode'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; @@ -18,7 +18,7 @@ import { Range } from 'vs/editor/common/core/range'; import { RGBA8 } from 'vs/editor/common/core/rgba'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration'; -import { ColorId } from 'vs/editor/common/languages'; +import { ColorId } from 'vs/editor/common/encodedTokenAttributes'; import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimapCharRenderer'; import { Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker'; @@ -56,6 +56,8 @@ class MinimapOptions { public readonly showSlider: 'always' | 'mouseover'; + public readonly autohide: boolean; + public readonly pixelRatio: number; public readonly typicalHalfwidthCharacterWidth: number; @@ -120,6 +122,7 @@ class MinimapOptions { this.minimapHeightIsEditorHeight = minimapLayout.minimapHeightIsEditorHeight; this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); this.showSlider = minimapOpts.showSlider; + this.autohide = minimapOpts.autohide; this.pixelRatio = pixelRatio; this.typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; this.lineHeight = options.get(EditorOption.lineHeight); @@ -166,6 +169,7 @@ class MinimapOptions { && this.minimapHeightIsEditorHeight === other.minimapHeightIsEditorHeight && this.scrollBeyondLastLine === other.scrollBeyondLastLine && this.showSlider === other.showSlider + && this.autohide === other.autohide && this.pixelRatio === other.pixelRatio && this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth && this.lineHeight === other.lineHeight @@ -1230,7 +1234,6 @@ class InnerMinimap extends Disposable { e.target, e.pointerId, e.buttons, - standardPointerMoveMerger, pointerMoveData => handlePointerMove(pointerMoveData.pageY, pointerMoveData.pageX), () => { this._slider.toggleClassName('active', false); @@ -1256,10 +1259,17 @@ class InnerMinimap extends Disposable { } private _getMinimapDomNodeClassName(): string { + const class_ = ['minimap']; if (this._model.options.showSlider === 'always') { - return 'minimap slider-always'; + class_.push('slider-always'); + } else { + class_.push('slider-mouseover'); } - return 'minimap slider-mouseover'; + if (this._model.options.autohide) { + class_.push('autohide'); + } + + return class_.join(' '); } public getDomNode(): FastDomNode { @@ -1326,15 +1336,11 @@ class InnerMinimap extends Disposable { return false; } public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): boolean { - if (this._lastRenderData) { - this._lastRenderData.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); - } + this._lastRenderData?.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); return true; } public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): boolean { - if (this._lastRenderData) { - this._lastRenderData.onLinesInserted(insertFromLineNumber, insertToLineNumber); - } + this._lastRenderData?.onLinesInserted(insertFromLineNumber, insertToLineNumber); return true; } public onScrollChanged(): boolean { diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 408457f88b..514b83eae0 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -62,9 +62,15 @@ class Settings { const minimapOpts = options.get(EditorOption.minimap); const minimapEnabled = minimapOpts.enabled; const minimapSide = minimapOpts.side; - const backgroundColor = minimapEnabled - ? theme.getColor(editorOverviewRulerBackground) || TokenizationRegistry.getDefaultBackground() - : null; + const themeColor = theme.getColor(editorOverviewRulerBackground); + const defaultBackground = TokenizationRegistry.getDefaultBackground(); + let backgroundColor: Color | null = null; + + if (themeColor !== undefined) { + backgroundColor = themeColor; + } else if (minimapEnabled) { + backgroundColor = defaultBackground; + } if (backgroundColor === null || minimapSide === 'left') { this.backgroundColor = null; diff --git a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts index 0ec70bea1f..44cfada860 100644 --- a/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts +++ b/src/vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts @@ -61,7 +61,7 @@ export class ScrollDecorationViewPart extends ViewPart { if (layoutInfo.minimap.renderMinimap === 0 || (layoutInfo.minimap.minimapWidth > 0 && layoutInfo.minimap.minimapLeft === 0)) { this._width = layoutInfo.width; } else { - this._width = layoutInfo.width - layoutInfo.minimap.minimapWidth - layoutInfo.verticalScrollbarWidth; + this._width = layoutInfo.width - layoutInfo.verticalScrollbarWidth; } } diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index 65b67f29ec..d08f3f7648 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -172,7 +172,13 @@ export class ViewCursor { } const range = firstVisibleRangeForCharacter.ranges[0]; - const width = range.width < 1 ? this._typicalHalfwidthCharacterWidth : range.width; + const width = ( + nextGrapheme === '\t' + ? this._typicalHalfwidthCharacterWidth + : (range.width < 1 + ? this._typicalHalfwidthCharacterWidth + : range.width) + ); let textContentClassName = ''; if (this._cursorStyle === TextEditorCursorStyle.Block) { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index fcdb2fd9a7..633920bbeb 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -14,7 +14,7 @@ import { Color } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, EmitterOptions, Event, EventDeliveryQueue } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { EditorConfiguration, IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; @@ -267,7 +267,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private _bannerDomNode: HTMLElement | null = null; - private _dropIntoEditorDecorationIds: string[] = []; + private _dropIntoEditorDecorations: EditorDecorationsCollection = this.createDecorationsCollection(); constructor( domElement: HTMLElement, @@ -368,35 +368,47 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._actions[internalAction.id] = internalAction; }); - if (_options.enableDropIntoEditor) { - this._register(new dom.DragAndDropObserver(this._domElement, { - onDragEnter: () => undefined, - onDragOver: e => { - const target = this.getTargetAtClientPoint(e.clientX, e.clientY); - if (target?.position) { - this.showDropIndicatorAt(target.position); - } - }, - onDrop: async e => { - this.removeDropIndicator(); + const isDropIntoEnabled = () => { + return !this._configuration.options.get(EditorOption.readOnly) + && this._configuration.options.get(EditorOption.dropIntoEditor).enabled; + }; - if (!e.dataTransfer) { - return; - } + this._register(new dom.DragAndDropObserver(this._domElement, { + onDragEnter: () => undefined, + onDragOver: e => { + if (!isDropIntoEnabled()) { + return; + } + + const target = this.getTargetAtClientPoint(e.clientX, e.clientY); + if (target?.position) { + this.showDropIndicatorAt(target.position); + } + }, + onDrop: async e => { + if (!isDropIntoEnabled()) { + return; + } + + this.removeDropIndicator(); + + if (!e.dataTransfer) { + return; + } + + const target = this.getTargetAtClientPoint(e.clientX, e.clientY); + if (target?.position) { + this._onDropIntoEditor.fire({ position: target.position, event: e }); + } + }, + onDragLeave: () => { + this.removeDropIndicator(); + }, + onDragEnd: () => { + this.removeDropIndicator(); + }, + })); - const target = this.getTargetAtClientPoint(e.clientX, e.clientY); - if (target?.position) { - this._onDropIntoEditor.fire({ position: target.position, event: e }); - } - }, - onDragLeave: () => { - this.removeDropIndicator(); - }, - onDragEnd: () => { - this.removeDropIndicator(); - }, - })); - } this._codeEditorService.addCodeEditor(this); } @@ -526,9 +538,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private _removeDecorationTypes(): void { this._decorationTypeKeysToIds = {}; if (this._decorationTypeSubtypes) { - for (let decorationType in this._decorationTypeSubtypes) { + for (const decorationType in this._decorationTypeSubtypes) { const subTypes = this._decorationTypeSubtypes[decorationType]; - for (let subType in subTypes) { + for (const subType in subTypes) { this._removeDecorationType(decorationType + '-' + subType); } } @@ -557,33 +569,47 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.viewModel.viewLayout.getWhitespaces(); } - private static _getVerticalOffsetForPosition(modelData: ModelData, modelLineNumber: number, modelColumn: number): number { + private static _getVerticalOffsetAfterPosition(modelData: ModelData, modelLineNumber: number, modelColumn: number, includeViewZones: boolean): number { const modelPosition = modelData.model.validatePosition({ lineNumber: modelLineNumber, column: modelColumn }); const viewPosition = modelData.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); - return modelData.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber); + return modelData.viewModel.viewLayout.getVerticalOffsetAfterLineNumber(viewPosition.lineNumber, includeViewZones); } - public getTopForLineNumber(lineNumber: number): number { + public getTopForLineNumber(lineNumber: number, includeViewZones: boolean = false): number { if (!this._modelData) { return -1; } - return CodeEditorWidget._getVerticalOffsetForPosition(this._modelData, lineNumber, 1); + return CodeEditorWidget._getVerticalOffsetForPosition(this._modelData, lineNumber, 1, includeViewZones); } public getTopForPosition(lineNumber: number, column: number): number { if (!this._modelData) { return -1; } - return CodeEditorWidget._getVerticalOffsetForPosition(this._modelData, lineNumber, column); + return CodeEditorWidget._getVerticalOffsetForPosition(this._modelData, lineNumber, column, false); + } + + private static _getVerticalOffsetForPosition(modelData: ModelData, modelLineNumber: number, modelColumn: number, includeViewZones: boolean = false): number { + const modelPosition = modelData.model.validatePosition({ + lineNumber: modelLineNumber, + column: modelColumn + }); + const viewPosition = modelData.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); + return modelData.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber, includeViewZones); + } + + public getBottomForLineNumber(lineNumber: number, includeViewZones: boolean = false): number { + if (!this._modelData) { + return -1; + } + return CodeEditorWidget._getVerticalOffsetAfterPosition(this._modelData, lineNumber, 1, includeViewZones); } public setHiddenAreas(ranges: IRange[]): void { - if (this._modelData) { - this._modelData.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); - } + this._modelData?.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); } public getVisibleColumnFromPosition(rawPosition: IPosition): number { @@ -1156,9 +1182,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData || text.length === 0) { return; } - const startPosition = this._modelData.viewModel.getSelection().getStartPosition(); - this._modelData.viewModel.paste(text, pasteOnNewLine, multicursorText, source); - const endPosition = this._modelData.viewModel.getSelection().getStartPosition(); + const viewModel = this._modelData.viewModel; + const startPosition = viewModel.getSelection().getStartPosition(); + viewModel.paste(text, pasteOnNewLine, multicursorText, source); + const endPosition = viewModel.getSelection().getStartPosition(); if (source === 'keyboard') { this._onDidPaste.fire({ range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), @@ -1255,6 +1282,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData.viewModel.executeCommands(commands, source); } + public createDecorationsCollection(decorations?: IModelDeltaDecoration[]): EditorDecorationsCollection { + return new EditorDecorationsCollection(this, decorations); + } + public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { if (!this._modelData) { // callback will not be called @@ -1277,6 +1308,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.model.getDecorationsInRange(range, this._id, filterValidationDecorations(this._configuration.options)); } + /** + * @deprecated + */ public deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] { if (!this._modelData) { return []; @@ -1289,7 +1323,17 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.model.deltaDecorations(oldDecorations, newDecorations, this._id); } - public setDecorations(description: string, decorationTypeKey: string, decorationOptions: editorCommon.IDecorationOptions[]): void { + public removeDecorations(decorationIds: string[]): void { + if (!this._modelData || decorationIds.length === 0) { + return; + } + + this._modelData.model.changeDecorations((changeAccessor) => { + changeAccessor.deltaDecorations(decorationIds, []); + }); + } + + public setDecorationsByType(description: string, decorationTypeKey: string, decorationOptions: editorCommon.IDecorationOptions[]): void { const newDecorationsSubTypes: { [key: string]: boolean } = {}; const oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; @@ -1297,7 +1341,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE const newModelDecorations: IModelDeltaDecoration[] = []; - for (let decorationOption of decorationOptions) { + for (const decorationOption of decorationOptions) { let typeKey = decorationTypeKey; if (decorationOption.renderOptions) { // identify custom reder options by a hash code over all keys and values @@ -1320,7 +1364,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } // remove decoration sub types that are no longer used, deregister decoration type if necessary - for (let subType in oldDecorationsSubTypes) { + for (const subType in oldDecorationsSubTypes) { if (!newDecorationsSubTypes[subType]) { this._removeDecorationType(decorationTypeKey + '-' + subType); } @@ -1331,11 +1375,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); } - public setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void { + public setDecorationsByTypeFast(decorationTypeKey: string, ranges: IRange[]): void { // remove decoration sub types that are no longer used, deregister decoration type if necessary const oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; - for (let subType in oldDecorationsSubTypes) { + for (const subType in oldDecorationsSubTypes) { this._removeDecorationType(decorationTypeKey + '-' + subType); } this._decorationTypeSubtypes[decorationTypeKey] = {}; @@ -1351,7 +1395,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); } - public removeDecorations(decorationTypeKey: string): void { + public removeDecorationsByType(decorationTypeKey: string): void { // remove decorations for type and sub type const oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey]; if (oldDecorationsIds) { @@ -1775,9 +1819,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } protected _postDetachModelCleanup(detachedModel: ITextModel | null): void { - if (detachedModel) { - detachedModel.removeAllDecorationsWithOwnerId(this._id); - } + detachedModel?.removeAllDecorationsWithOwnerId(this._id); } private _detachModel(): ITextModel | null { @@ -1822,17 +1864,17 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } private showDropIndicatorAt(position: Position): void { - let newDecorations: IModelDeltaDecoration[] = [{ + const newDecorations: IModelDeltaDecoration[] = [{ range: new Range(position.lineNumber, position.column, position.lineNumber, position.column), options: CodeEditorWidget.dropIntoEditorDecorationOptions }]; - this._dropIntoEditorDecorationIds = this.deltaDecorations(this._dropIntoEditorDecorationIds, newDecorations); + this._dropIntoEditorDecorations.set(newDecorations); this.revealPosition(position, editorCommon.ScrollType.Immediate); } private removeDropIndicator(): void { - this._dropIntoEditorDecorationIds = this.deltaDecorations(this._dropIntoEditorDecorationIds, []); + this._dropIntoEditorDecorations.clear(); } } @@ -2125,8 +2167,82 @@ class CodeEditorWidgetFocusTracker extends Disposable { } public refreshState(): void { - if (this._domFocusTracker.refreshState) { - this._domFocusTracker.refreshState(); + this._domFocusTracker.refreshState?.(); + } +} + +class EditorDecorationsCollection implements editorCommon.IEditorDecorationsCollection { + + private _decorationIds: string[] = []; + private _isChangingDecorations: boolean = false; + + public get length(): number { + return this._decorationIds.length; + } + + constructor( + private readonly _editor: editorBrowser.ICodeEditor, + decorations: IModelDeltaDecoration[] | undefined + ) { + if (Array.isArray(decorations) && decorations.length > 0) { + this.set(decorations); + } + } + + public onDidChange(listener: (e: IModelDecorationsChangedEvent) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable { + return this._editor.onDidChangeModelDecorations((e) => { + if (this._isChangingDecorations) { + return; + } + listener.call(thisArgs, e); + }, disposables); + } + + public getRange(index: number): Range | null { + if (!this._editor.hasModel()) { + return null; + } + if (index >= this._decorationIds.length) { + return null; + } + return this._editor.getModel().getDecorationRange(this._decorationIds[index]); + } + + public getRanges(): Range[] { + if (!this._editor.hasModel()) { + return []; + } + const model = this._editor.getModel(); + const result: Range[] = []; + for (const decorationId of this._decorationIds) { + const range = model.getDecorationRange(decorationId); + if (range) { + result.push(range); + } + } + return result; + } + + public has(decoration: IModelDecoration): boolean { + return this._decorationIds.includes(decoration.id); + } + + public clear(): void { + if (this._decorationIds.length === 0) { + // nothing to do + return; + } + this.set([]); + } + + public set(newDecorations: IModelDeltaDecoration[]): void { + try { + this._isChangingDecorations = true; + this._editor.changeDecorations((accessor) => { + this._decorationIds = accessor.deltaDecorations(this._decorationIds, newDecorations); + }); + } finally { + this._isChangingDecorations = false; } } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 5ab48860e4..acd38d7162 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -6,6 +6,7 @@ import 'vs/css!./media/diffEditor'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import * as assert from 'vs/base/common/assert'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState, Orientation } from 'vs/base/browser/ui/sash/sash'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -52,7 +53,7 @@ import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ILineBreaksComputer } from 'vs/editor/common/modelLineProjectionData'; -import { IChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/diffComputer'; +import { IChange, ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/diffComputer'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IDimension } from 'vs/editor/common/core/dimension'; import { isHighContrast } from 'vs/platform/theme/common/theme'; @@ -114,7 +115,9 @@ class VisualEditorState { this._zonesMap = {}; // (2) Model decorations - this._decorations = editor.deltaDecorations(this._decorations, []); + editor.changeDecorations((changeAccessor) => { + this._decorations = changeAccessor.deltaDecorations(this._decorations, []); + }); } public apply(editor: CodeEditorWidget, overviewRuler: editorBrowser.IOverviewRuler | null, newDecorations: IEditorDiffDecorationsWithZones, restoreScrollState: boolean): void { @@ -149,17 +152,15 @@ class VisualEditorState { } }); - if (scrollState) { - scrollState.restore(editor); - } + scrollState?.restore(editor); // decorations - this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations); + editor.changeDecorations((changeAccessor) => { + this._decorations = changeAccessor.deltaDecorations(this._decorations, newDecorations.decorations); + }); // overview ruler - if (overviewRuler) { - overviewRuler.setZones(newDecorations.overviewZones); - } + overviewRuler?.setZones(newDecorations.overviewZones); } } @@ -263,6 +264,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE let diffOptions: any = { enableSplitViewResizing: true, renderSideBySide: true, + renderMarginRevertIcon: true, maxComputationTime: 5000, maxFileSize: 50, ignoreTrimWhitespace: true, @@ -430,24 +432,30 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return result; } - private _recreateOverviewRulers(): void { + private _disposeOverviewRulers(): void { + if (this._originalOverviewRuler) { + this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); + this._originalOverviewRuler.dispose(); + this._originalOverviewRuler = null; + } + if (this._modifiedOverviewRuler) { + this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); + this._modifiedOverviewRuler.dispose(); + this._modifiedOverviewRuler = null; + } + } + + private _createOverviewRulers(): void { if (!this._options.renderOverviewRuler) { return; } - if (this._originalOverviewRuler) { - this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); - this._originalOverviewRuler.dispose(); - } + assert.ok(!this._originalOverviewRuler && !this._modifiedOverviewRuler); + if (this._originalEditor.hasModel()) { this._originalOverviewRuler = this._originalEditor.createOverviewRuler('original diffOverviewRuler')!; this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); } - - if (this._modifiedOverviewRuler) { - this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); - this._modifiedOverviewRuler.dispose(); - } if (this._modifiedEditor.hasModel()) { this._modifiedOverviewRuler = this._modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); @@ -593,11 +601,77 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE }); })); + // Revert change when an arrow is clicked. + this._register(editor.onMouseDown(event => { + if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) { + const lineNumber = event.target.position.lineNumber; + const change = this._diffComputationResult?.changes.find(c => c.modifiedStartLineNumber === lineNumber - 1 || c.modifiedStartLineNumber === lineNumber); + if (change) { + this.revertChange(change); + } + event.event.stopPropagation(); + this._updateDecorations(); + return; + } + })); + return editor; } + /** + * Reverts a change in the modified editor. + */ + revertChange(change: IChange) { + const editor = this._modifiedEditor; + const original = this._originalEditor.getModel(); + const modified = this._modifiedEditor.getModel(); + if (!original || !modified || !editor) { + return; + } + + const originalRange = change.originalEndLineNumber > 0 ? new Range(change.originalStartLineNumber, 1, change.originalEndLineNumber, original.getLineMaxColumn(change.originalEndLineNumber)) : null; + const originalContent = originalRange ? original.getValueInRange(originalRange) : null; + + const newRange = change.modifiedEndLineNumber > 0 ? new Range(change.modifiedStartLineNumber, 1, change.modifiedEndLineNumber, modified.getLineMaxColumn(change.modifiedEndLineNumber)) : null; + + const eol = modified.getEOL(); + + if (change.originalEndLineNumber === 0 && newRange) { + // Insert change. + // To revert: delete the new content and a linebreak (if possible) + + let range = newRange; + if (change.modifiedStartLineNumber > 1) { + // Try to include a linebreak from before. + range = newRange.setStartPosition(change.modifiedStartLineNumber - 1, modified.getLineMaxColumn(change.modifiedStartLineNumber - 1)); + } else if (change.modifiedEndLineNumber < modified.getLineCount()) { + // Try to include the linebreak from after. + range = newRange.setEndPosition(change.modifiedEndLineNumber + 1, 1); + } + editor.executeEdits('diffEditor', [{ + range, + text: '', + }]); + } else if (change.modifiedEndLineNumber === 0 && originalContent !== null) { + // Delete change. + // To revert: insert the old content and a linebreak. + + const insertAt = change.modifiedStartLineNumber < modified.getLineCount() ? new Position(change.modifiedStartLineNumber + 1, 1) : new Position(change.modifiedStartLineNumber, modified.getLineMaxColumn(change.modifiedStartLineNumber)); + editor.executeEdits('diffEditor', [{ + range: Range.fromPositions(insertAt, insertAt), + text: change.modifiedStartLineNumber < modified.getLineCount() ? originalContent + eol : eol + originalContent, + }]); + } else if (newRange && originalContent !== null) { + // Modified change. + editor.executeEdits('diffEditor', [{ + range: newRange, + text: originalContent, + }]); + } + } + protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - return instantiationService.createInstance(CodeEditorWidget, container, { enableDropIntoEditor: true, ...options }, editorWidgetOptions); + return instantiationService.createInstance(CodeEditorWidget, container, options, editorWidgetOptions); } public override dispose(): void { @@ -677,7 +751,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE const changed = changedDiffEditorOptions(this._options, newOptions); this._options = newOptions; - const beginUpdateDecorations = (changed.ignoreTrimWhitespace || changed.renderIndicators); + const beginUpdateDecorations = (changed.ignoreTrimWhitespace || changed.renderIndicators || changed.renderMarginRevertIcon); const beginUpdateDecorationsSoon = (this._isVisible && (changed.maxComputationTime || changed.maxFileSize)); if (beginUpdateDecorations) { @@ -729,6 +803,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE // Remove all view zones & decorations this._cleanViewZonesAndDecorations(); + this._disposeOverviewRulers(); + // Update code editor models this._originalEditor.setModel(model ? model.original : null); this._modifiedEditor.setModel(model ? model.modified : null); @@ -747,7 +823,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._setState(editorBrowser.DiffEditorState.Idle); if (model) { - this._recreateOverviewRulers(); + this._createOverviewRulers(); // Begin comparing this._beginUpdateDecorations(); @@ -921,6 +997,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._modifiedEditor.trigger(source, handlerId, payload); } + public createDecorationsCollection(decorations?: IModelDeltaDecoration[]): editorCommon.IEditorDecorationsCollection { + return this._modifiedEditor.createDecorationsCollection(decorations); + } + public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { return this._modifiedEditor.changeDecorations(callback); } @@ -1070,7 +1150,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE const foreignModified = this._modifiedEditorState.getForeignViewZones(this._modifiedEditor.getWhitespaces()); // {{SQL CARBON EDIT}} - const diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._options.ignoreTrimWhitespace, this._options.renderIndicators, foreignOriginal, foreignModified, this._originalEditor, this._modifiedEditor, this._options.reverse); + // const diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._options.ignoreTrimWhitespace, this._options.renderIndicators, foreignOriginal, foreignModified, this._originalEditor, this._modifiedEditor, this._options.reverse); // {{ SQL CARBON TODO }} - The last 3 args are different in the updated version below. + const diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._options.ignoreTrimWhitespace, this._options.renderIndicators, this._options.renderMarginRevertIcon, foreignOriginal, foreignModified); try { this._currentlyChangingViewZones = true; @@ -1111,6 +1192,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE result.ariaLabel = options.originalAriaLabel; } result.readOnly = !this._options.originalEditable; + result.dropIntoEditor = { enabled: !result.readOnly }; result.extraEditorClassName = 'original-in-monaco-diff-editor'; return { ...result, @@ -1386,7 +1468,7 @@ abstract class DiffEditorWidgetStyle extends Disposable { } // {{SQL CARBON EDIT}} - add reverse parameter - public getEditorsDiffDecorations(lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, reverse?: boolean): IEditorsDiffDecorationsWithZones { + public getEditorsDiffDecorations(lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], reverse?: boolean): IEditorsDiffDecorationsWithZones { // Get view zones modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; @@ -1398,7 +1480,7 @@ abstract class DiffEditorWidgetStyle extends Disposable { // Get decorations & overview ruler zones let originalDecorations = this._getOriginalEditorDecorations(zones, lineChanges, ignoreTrimWhitespace, renderIndicators); - let modifiedDecorations = this._getModifiedEditorDecorations(zones, lineChanges, ignoreTrimWhitespace, renderIndicators); + const modifiedDecorations = this._getModifiedEditorDecorations(zones, lineChanges, ignoreTrimWhitespace, renderIndicators, renderMarginRevertIcon); // {{SQL CARBON EDIT}} - reverse decorations if (reverse) { @@ -1425,7 +1507,7 @@ abstract class DiffEditorWidgetStyle extends Disposable { protected abstract _getViewZones(lineChanges: ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones; protected abstract _getOriginalEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; - protected abstract _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; + protected abstract _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean): IEditorDiffDecorations; public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; public abstract layout(): number; @@ -1741,6 +1823,11 @@ function createDecoration(startLineNumber: number, startColumn: number, endLineN const DECORATIONS = { + arrowRevertChange: ModelDecorationOptions.register({ + description: 'diff-editor-arrow-revert-change', + glyphMarginClassName: 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight), + }), + charDelete: ModelDecorationOptions.register({ description: 'diff-editor-char-delete', className: 'char-delete' @@ -1995,7 +2082,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti if (lineChange.charChanges) { for (const charChange of lineChange.charChanges) { - if (isChangeOrDelete(charChange)) { + if (isCharChangeOrDelete(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { let startColumn: number; @@ -2024,7 +2111,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti return result; } - protected _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + protected _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean): IEditorDiffDecorations { const modifiedEditor = this._dataSource.getModifiedEditor(); const overviewZoneColor = String(this._insertColor); @@ -2038,6 +2125,21 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti for (const lineChange of lineChanges) { + // Arrows for reverting changes. + if (renderMarginRevertIcon) { + if (lineChange.modifiedEndLineNumber > 0) { + result.decorations.push({ + range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedStartLineNumber, 1), + options: DECORATIONS.arrowRevertChange + }); + } else { + const viewZone = zones.modified.find(z => z.afterLineNumber === lineChange.modifiedStartLineNumber); + if (viewZone) { + viewZone.marginDomNode = createViewZoneMarginArrow(); + } + } + } + if (isChangeOrInsert(lineChange)) { result.decorations.push({ @@ -2053,7 +2155,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti if (lineChange.charChanges) { for (const charChange of lineChange.charChanges) { - if (isChangeOrInsert(charChange)) { + if (isCharChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { let startColumn: number; @@ -2202,7 +2304,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle { return result; } - protected _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { + protected _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean): IEditorDiffDecorations { const modifiedEditor = this._dataSource.getModifiedEditor(); const overviewZoneColor = String(this._insertColor); @@ -2228,7 +2330,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle { if (lineChange.charChanges) { for (const charChange of lineChange.charChanges) { - if (isChangeOrInsert(charChange)) { + if (isCharChangeOrInsert(charChange)) { if (ignoreTrimWhitespace) { for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { let startColumn: number; @@ -2390,7 +2492,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { const decorations: InlineDecoration[] = []; if (lineChange.charChanges) { for (const charChange of lineChange.charChanges) { - if (isChangeOrDelete(charChange)) { + if (isCharChangeOrDelete(charChange)) { decorations.push(new InlineDecoration( new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), 'char-delete', @@ -2565,7 +2667,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { marginDomNode.appendChild(marginElement); } - return output.characterMapping.getAbsoluteOffset(output.characterMapping.length); + return output.characterMapping.getHorizontalOffset(output.characterMapping.length); } } @@ -2573,20 +2675,40 @@ function validateDiffWordWrap(value: 'off' | 'on' | 'inherit' | undefined, defau return validateStringSetOption<'off' | 'on' | 'inherit'>(value, defaultValue, ['off', 'on', 'inherit']); } -function isChangeOrInsert(lineChange: IChange): boolean { +function isChangeOrInsert(lineChange: ILineChange): boolean { return lineChange.modifiedEndLineNumber > 0; } -function isChangeOrDelete(lineChange: IChange): boolean { +function isChangeOrDelete(lineChange: ILineChange): boolean { return lineChange.originalEndLineNumber > 0; } +function isCharChangeOrInsert(charChange: ICharChange): boolean { + if (charChange.modifiedStartLineNumber === charChange.modifiedEndLineNumber) { + return charChange.modifiedEndColumn - charChange.modifiedStartColumn > 0; + } + return charChange.modifiedEndLineNumber - charChange.modifiedStartLineNumber > 0; +} + +function isCharChangeOrDelete(charChange: ICharChange): boolean { + if (charChange.originalStartLineNumber === charChange.originalEndLineNumber) { + return charChange.originalEndColumn - charChange.originalStartColumn > 0; + } + return charChange.originalEndLineNumber - charChange.originalStartLineNumber > 0; +} + function createFakeLinesDiv(): HTMLElement { const r = document.createElement('div'); r.className = 'diagonal-fill'; return r; } +function createViewZoneMarginArrow(): HTMLElement { + const arrow = document.createElement('div'); + arrow.className = 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight); + return dom.$('div', {}, arrow); +} + function getViewRange(model: ITextModel, viewModel: IViewModel, startLineNumber: number, endLineNumber: number): Range { const lineCount = model.getLineCount(); startLineNumber = Math.min(lineCount, Math.max(1, startLineNumber)); @@ -2601,6 +2723,7 @@ function validateDiffEditorOptions(options: Readonly, defaul let outOptions: any = { enableSplitViewResizing: validateBooleanOption(options.enableSplitViewResizing, defaults.enableSplitViewResizing), renderSideBySide: validateBooleanOption(options.renderSideBySide, defaults.renderSideBySide), + renderMarginRevertIcon: validateBooleanOption(options.renderMarginRevertIcon, defaults.renderMarginRevertIcon), maxComputationTime: clampedInt(options.maxComputationTime, defaults.maxComputationTime, 0, Constants.MAX_SAFE_SMALL_INTEGER), maxFileSize: clampedInt(options.maxFileSize, defaults.maxFileSize, 0, Constants.MAX_SAFE_SMALL_INTEGER), ignoreTrimWhitespace: validateBooleanOption(options.ignoreTrimWhitespace, defaults.ignoreTrimWhitespace), @@ -2618,6 +2741,7 @@ function changedDiffEditorOptions(a: ValidDiffEditorBaseOptions, b: ValidDiffEdi return { enableSplitViewResizing: (a.enableSplitViewResizing !== b.enableSplitViewResizing), renderSideBySide: (a.renderSideBySide !== b.renderSideBySide), + renderMarginRevertIcon: (a.renderMarginRevertIcon !== b.renderMarginRevertIcon), maxComputationTime: (a.maxComputationTime !== b.maxComputationTime), maxFileSize: (a.maxFileSize !== b.maxFileSize), ignoreTrimWhitespace: (a.ignoreTrimWhitespace !== b.ignoreTrimWhitespace), diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css index e1a6a8001b..b681cdf62b 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -57,6 +57,15 @@ text-align: right; } +.monaco-editor .arrow-revert-change { + z-index: 10; + position: absolute; +} + +.monaco-editor .arrow-revert-change:hover { + cursor: pointer; +} + /* ---------- Inline Diff ---------- */ .monaco-editor .view-zones .view-lines .view-line span { diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index e4cbd5abb2..c320bcc755 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -146,6 +146,11 @@ const editorConfiguration: IConfigurationNode = { default: true, description: nls.localize('sideBySide', "Controls whether the diff editor shows the diff side by side or inline.") }, + 'diffEditor.renderMarginRevertIcon': { + type: 'boolean', + default: true, + description: nls.localize('renderMarginRevertIcon', "When enabled, the diff editor shows arrows in its glyph margin to revert changes.") + }, 'diffEditor.ignoreTrimWhitespace': { type: 'boolean', default: true, diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 7d485cc521..89b0ce356b 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -169,6 +169,10 @@ export interface IEditorOptions { * Control the behavior and rendering of the scrollbars. */ scrollbar?: IEditorScrollbarOptions; + /** + * Control the behavior of experimental options + */ + experimental?: IEditorExperimentalOptions; /** * Control the behavior and rendering of the minimap. */ @@ -563,7 +567,7 @@ export interface IEditorOptions { * Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter. * Defaults to 'mouseover'. */ - showFoldingControls?: 'always' | 'mouseover'; + showFoldingControls?: 'always' | 'never' | 'mouseover'; /** * Controls whether clicking on the empty content after a folded line will unfold the line. * Defaults to false. @@ -659,6 +663,13 @@ export interface IEditorOptions { * Configures bracket pair colorization (disabled by default). */ bracketPairColorization?: IBracketPairColorizationOptions; + + /** + * Controls dropping into the editor from an external source. + * + * When enabled, this shows a preview of the drop location and triggers an `onDropIntoEditor` event. + */ + dropIntoEditor?: IDropIntoEditorOptions; } /** @@ -698,6 +709,11 @@ export interface IDiffEditorBaseOptions { * Defaults to true. */ renderIndicators?: boolean; + /** + * Shows icons in the glyph margin to revert changes. + * Default to true. + */ + renderMarginRevertIcon?: boolean; /** * Original model should be editable? * Defaults to false. @@ -816,7 +832,7 @@ export interface IEditorOption { /** * Might modify `value`. */ - applyUpdate(value: V, update: V): ApplyUpdateResult; + applyUpdate(value: V | undefined, update: V): ApplyUpdateResult; } /** @@ -845,7 +861,7 @@ abstract class BaseEditorOption implements IEditor this.schema = schema; } - public applyUpdate(value: V, update: V): ApplyUpdateResult { + public applyUpdate(value: V | undefined, update: V): ApplyUpdateResult { return applyUpdate(value, update); } @@ -863,13 +879,13 @@ export class ApplyUpdateResult { ) { } } -function applyUpdate(value: T, update: T): ApplyUpdateResult { +function applyUpdate(value: T | undefined, update: T): ApplyUpdateResult { if (typeof value !== 'object' || typeof update !== 'object' || !value || !update) { return new ApplyUpdateResult(update, value !== update); } if (Array.isArray(value) || Array.isArray(update)) { const arrayEquals = Array.isArray(value) && Array.isArray(update) && arrays.equals(value, update); - return new ApplyUpdateResult(update, arrayEquals); + return new ApplyUpdateResult(update, !arrayEquals); } let didChange = false; for (const key in update) { @@ -900,7 +916,7 @@ abstract class ComputedEditorOption implements IEdito this.defaultValue = undefined; } - public applyUpdate(value: V, update: V): ApplyUpdateResult { + public applyUpdate(value: V | undefined, update: V): ApplyUpdateResult { return applyUpdate(value, update); } @@ -925,7 +941,7 @@ class SimpleEditorOption implements IEditorOption { + public applyUpdate(value: V | undefined, update: V): ApplyUpdateResult { return applyUpdate(value, update); } @@ -2324,6 +2340,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption { + + constructor() { + const defaults: EditorExperimentalOptions = { stickyScroll: { enabled: false } }; + super( + EditorOption.experimental, 'experimental', defaults, + { + 'editor.experimental.stickyScroll.enabled': { + type: 'boolean', + default: defaults.stickyScroll.enabled, + description: nls.localize('editor.experimental.stickyScroll', "Shows the nested current scopes during the scroll at the top of the editor.") + }, + } + ); + } + + public validate(_input: any): EditorExperimentalOptions { + if (!_input || typeof _input !== 'object') { + return this.defaultValue; + } + const input = _input as IEditorExperimentalOptions; + return { + stickyScroll: { + enabled: boolean(input.stickyScroll?.enabled, this.defaultValue.stickyScroll.enabled) + } + }; + } +} + +//#endregion + //#region inlayHints /** @@ -2521,11 +2589,10 @@ export interface IEditorInlayHintsOptions { fontFamily?: string; /** - * The display style to render inlay hints with. - * Compact mode disables the borders and padding around the inlay hint. - * Defaults to 'standard'. + * Enables the padding around the inlay hint. + * Defaults to false. */ - displayStyle: 'standard' | 'compact'; + padding?: boolean; } /** @@ -2536,7 +2603,7 @@ export type EditorInlayHintsOptions = Readonly { constructor() { - const defaults: EditorInlayHintsOptions = { enabled: 'on', fontSize: 0, fontFamily: '', displayStyle: 'compact' }; + const defaults: EditorInlayHintsOptions = { enabled: 'on', fontSize: 0, fontFamily: '', padding: false }; super( EditorOption.inlayHints, 'inlayHints', defaults, { @@ -2555,23 +2622,18 @@ class EditorInlayHints extends BaseEditorOption(input.enabled, this.defaultValue.enabled, ['on', 'off', 'offUnlessPressed', 'onUnlessPressed']), fontSize: EditorIntOption.clampedInt(input.fontSize, this.defaultValue.fontSize, 0, 100), fontFamily: EditorStringOption.string(input.fontFamily, this.defaultValue.fontFamily), - displayStyle: stringSet<'standard' | 'compact'>(input.displayStyle, this.defaultValue.displayStyle, ['standard', 'compact']) + padding: boolean(input.padding, this.defaultValue.padding) }; } } @@ -2629,6 +2691,10 @@ export interface IEditorMinimapOptions { * Defaults to true. */ enabled?: boolean; + /** + * Control the rendering of minimap. + */ + autohide?: boolean; /** * Control the side of the minimap in editor. * Defaults to 'right'. @@ -2673,6 +2739,7 @@ class EditorMinimap extends BaseEditorOption