From 0b7e7ddbf96bf76e2acc774ae560a06de064b4f5 Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Mon, 15 Jul 2019 22:35:46 -0700 Subject: [PATCH] Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381) * Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check --- .gitignore | 9 +- .vscode/extensions.json | 1 + .vscode/launch.json | 2 +- .vscode/settings.json | 7 +- .yarnrc | 2 +- CODE_OF_CONDUCT.md | 1 - azure-pipelines-linux-mac.yml | 6 +- azure-pipelines-windows.yml | 6 +- build/.cachesalt | 1 + build/.nativeignore | 38 +- .../common/extract-telemetry.sh | 30 + build/azure-pipelines/common/installDistro.ts | 20 - build/azure-pipelines/common/publish.ts | 15 +- build/azure-pipelines/common/release.ts | 109 + build/azure-pipelines/common/symbols.ts | 15 +- build/azure-pipelines/common/sync-mooncake.ts | 5 - build/azure-pipelines/darwin/build.sh | 5 - .../darwin/continuous-build-darwin.yml | 21 +- .../darwin/product-build-darwin.yml | 71 +- build/azure-pipelines/darwin/publish.sh | 2 +- build/azure-pipelines/distro-build.yml | 8 +- build/azure-pipelines/linux/build.sh | 7 - .../linux/continuous-build-linux.yml | 21 +- .../linux/multiarch/alpine/build.sh | 3 + .../linux/multiarch/alpine/prebuild.sh | 3 + .../linux/multiarch/alpine/publish.sh | 3 + .../linux/multiarch/armhf/build.sh | 3 + .../linux/multiarch/armhf/prebuild.sh | 3 + .../linux/multiarch/armhf/publish.sh | 3 + .../linux/product-build-linux-multiarch.yml | 115 + .../linux/product-build-linux.yml | 88 +- build/azure-pipelines/linux/publish.sh | 38 +- .../linux/snap-build-linux.yml | 11 +- build/azure-pipelines/mixin.js | 41 + build/azure-pipelines/product-build.yml | 98 +- build/azure-pipelines/product-compile.yml | 124 + build/azure-pipelines/release.yml | 22 + build/azure-pipelines/upload-sourcemaps.js | 39 + .../azure-pipelines/web/product-build-web.yml | 96 + build/azure-pipelines/web/publish.sh | 15 + build/azure-pipelines/win32/build.ps1 | 5 - .../win32/continuous-build-win32.yml | 27 +- .../win32/product-build-win32.yml | 82 +- build/builtin/main.js | 2 +- build/download/download.js | 91 - build/download/download.ts | 111 - build/gulpfile.compile.js | 8 +- build/gulpfile.extensions.js | 23 +- build/gulpfile.hygiene.js | 16 + build/gulpfile.reh.js | 129 +- build/gulpfile.vscode.js | 114 +- build/gulpfile.vscode.linux.js | 5 +- build/gulpfile.vscode.web.js | 151 + build/gulpfile.vscode.win32.js | 2 +- build/lib/compilation.js | 26 +- build/lib/compilation.ts | 28 +- build/lib/electron.js | 2 +- build/lib/extensions.js | 233 +- build/lib/extensions.ts | 256 +- build/lib/i18n.resources.json | 4 - build/lib/i18n.ts | 2 +- build/lib/node.js | 15 + build/lib/node.ts | 16 + build/lib/optimize.js | 7 +- build/lib/optimize.ts | 10 +- build/lib/test/i18n.test.js | 4 +- build/lib/test/i18n.test.ts | 4 +- build/lib/util.js | 42 +- build/lib/util.ts | 43 +- build/lib/watch/package.json | 3 +- build/lib/watch/yarn.lock | 1491 ++++---- build/npm/postinstall.js | 11 +- build/npm/update-distro.js | 18 + build/npm/update-grammar.js | 8 +- build/package.json | 3 +- build/win32/Cargo.lock | 4 +- build/win32/code.iss | 2 +- build/win32/i18n/messages.en.isl | 2 +- build/win32/inno_updater.exe | Bin 386560 -> 396800 bytes build/yarn.lock | 8 +- cglicenses.json | 1430 ++++---- cgmanifest.json | 29 +- extensions/admin-tool-ext-win/.vscodeignore | 4 +- extensions/bat/package.json | 1 + extensions/configuration-editing/package.json | 3 +- .../schemas/devContainer.schema.json | 52 +- extensions/configuration-editing/yarn.lock | 8 +- .../server/src/test/links.test.ts | 79 + .../server/test/linksTestFixtures/.gitignore | 1 + .../node_modules/foo/package.json | 0 extensions/docker/package.json | 1 + extensions/extension-editing/package.json | 3 +- extensions/extension-editing/yarn.lock | 8 +- extensions/git-ui/.vscodeignore | 8 + extensions/git-ui/README.md | 7 + extensions/git-ui/cgmanifest.json | 4 + .../git-ui/extension.webpack.config.js | 17 +- extensions/git-ui/package.json | 28 + extensions/git-ui/package.nls.json | 4 + extensions/git-ui/resources/icons/git.png | Bin 0 -> 2383 bytes extensions/git-ui/src/main.ts | 57 + extensions/git-ui/src/typings/refs.d.ts | 8 + extensions/git-ui/tsconfig.json | 13 + extensions/git-ui/yarn.lock | 8 + extensions/git/cgmanifest.json | 2 +- extensions/git/package.json | 15 +- extensions/git/package.nls.json | 4 +- extensions/git/resources/icons/dark/check.svg | 4 +- extensions/git/resources/icons/dark/clean.svg | 4 +- extensions/git/resources/icons/dark/git.svg | 4 +- .../git/resources/icons/dark/open-change.svg | 4 +- .../resources/icons/dark/open-file-mono.svg | 1 - .../git/resources/icons/dark/open-file.svg | 4 +- .../git/resources/icons/dark/refresh.svg | 5 +- extensions/git/resources/icons/dark/stage.svg | 4 +- .../git/resources/icons/dark/unstage.svg | 4 +- .../git/resources/icons/light/check.svg | 4 +- .../git/resources/icons/light/clean.svg | 4 +- extensions/git/resources/icons/light/git.svg | 4 +- .../git/resources/icons/light/open-change.svg | 4 +- .../resources/icons/light/open-file-mono.svg | 1 - .../git/resources/icons/light/open-file.svg | 4 +- .../git/resources/icons/light/refresh.svg | 5 +- .../git/resources/icons/light/stage.svg | 4 +- .../git/resources/icons/light/unstage.svg | 4 +- extensions/git/src/api/git.d.ts | 2 +- extensions/git/src/commands.ts | 52 +- extensions/git/src/git.ts | 43 +- extensions/git/src/model.ts | 5 +- extensions/git/src/repository.ts | 177 +- extensions/git/src/test/git.test.ts | 11 + extensions/git/src/util.ts | 22 +- .../git/syntaxes/git-rebase.tmLanguage.json | 11 +- extensions/git/yarn.lock | 13 +- .../client/src/jsonMain.ts | 11 +- .../json-language-features/package.json | 9 +- .../json-language-features/server/README.md | 9 +- .../server/package.json | 12 +- .../server/src/jsonServerMain.ts | 6 +- .../json-language-features/server/yarn.lock | 86 +- extensions/json-language-features/yarn.lock | 51 +- extensions/json/package.json | 6 +- extensions/markdown-basics/package.json | 1 + .../syntaxes/markdown.tmLanguage.json | 2 +- .../cgmanifest.json | 32 - .../media/Preview.svg | 1 - .../media/PreviewOnRightPane_16x.svg | 1 - .../media/PreviewOnRightPane_16x_dark.svg | 1 - .../media/Preview_inverse.svg | 1 - .../media/ViewSource.svg | 3 - .../media/ViewSource_inverse.svg | 1 - .../markdown-language-features/media/index.js | 9 +- .../media/markdown.css | 11 +- .../markdown-language-features/media/pre.js | 2 +- .../media/preview-dark.svg | 3 + .../media/preview-light.svg | 3 + .../media/preview-right-dark.svg | 4 + .../media/preview-right-light.svg | 4 + .../media/view-source-dark.svg | 3 + .../media/view-source-light.svg | 3 + .../markdown-language-features/package.json | 24 +- .../preview-src/index.ts | 10 +- .../preview-src/settings.ts | 17 +- .../src/features/documentLinkProvider.ts | 34 +- .../src/features/preview.ts | 48 +- .../src/features/previewContentProvider.ts | 124 +- .../src/markdownEngine.ts | 3 +- .../src/markdownExtensions.ts | 3 +- .../src/test/documentLinkProvider.test.ts | 8 + .../src/util/resources.ts | 33 + .../markdown-language-features/yarn.lock | 16 +- extensions/merge-conflict/package.json | 18 +- extensions/merge-conflict/package.nls.json | 6 +- .../merge-conflict/src/commandHandler.ts | 82 +- .../merge-conflict/src/contentProvider.ts | 22 +- .../src/documentMergeConflict.ts | 16 +- extensions/merge-conflict/src/interfaces.ts | 2 +- extensions/merge-conflict/yarn.lock | 8 +- extensions/mssql/.vscodeignore | 1 + .../objective-c/build/update-grammars.js | 11 + .../test/colorize-fixtures/test.mm | 52 + .../test/colorize-results/test_mm.json | 3093 +++++++++++++++++ extensions/package.json | 2 +- extensions/powershell/package.json | 1 + extensions/python/.vscodeignore | 2 + extensions/python/extension.webpack.config.js | 17 + extensions/python/package.json | 4 +- extensions/r/package.json | 1 + extensions/shared.webpack.config.js | 14 +- extensions/sql/cgmanifest.json | 4 +- extensions/sql/package.json | 1 + extensions/sql/syntaxes/sql.tmLanguage.json | 2 +- extensions/theme-abyss/package.json | 1 + .../theme-abyss/themes/abyss-color-theme.json | 4 +- extensions/theme-defaults/package.json | 1 + .../theme-defaults/themes/dark_plus.json | 6 +- .../theme-defaults/themes/hc_black.json | 8 +- .../theme-defaults/themes/light_plus.json | 8 +- extensions/theme-kimbie-dark/package.json | 1 + .../themes/kimbie-dark-color-theme.json | 4 +- extensions/theme-monokai-dimmed/package.json | 1 + .../themes/dimmed-monokai-color-theme.json | 4 +- extensions/theme-monokai/package.json | 1 + .../themes/monokai-color-theme.json | 2 +- extensions/theme-quietlight/package.json | 1 + .../themes/quietlight-color-theme.json | 2 + extensions/theme-red/package.json | 1 + .../theme-seti/icons/vs-seti-icon-theme.json | 8 +- extensions/theme-seti/package.json | 1 + extensions/theme-solarized-dark/package.json | 1 + .../themes/solarized-dark-color-theme.json | 4 +- extensions/theme-solarized-light/package.json | 1 + .../themes/solarized-light-color-theme.json | 12 +- .../theme-tomorrow-night-blue/package.json | 1 + .../themes/tomorrow-night-blue-theme.json | 2 +- .../src/tsServer/serverError.ts | 66 + .../src/tsServer/spawner.ts | 229 ++ .../src/singlefolder-tests/terminal.test.ts | 343 ++ .../singlefolder-tests/workspace.fs.test.ts | 141 + extensions/vscode-colorize-tests/package.json | 3 +- extensions/vscode-colorize-tests/yarn.lock | 8 +- extensions/vscode-test-resolver/package.json | 36 +- .../scripts/terminateProcess.sh | 12 + .../vscode-test-resolver/src/extension.ts | 196 +- .../src/util/processes.ts | 37 + extensions/vscode-test-resolver/yarn.lock | 8 +- .../xml-language-features/.vscodeignore | 1 + extensions/xml/package.json | 1 + extensions/yaml/.gitignore | 1 - extensions/yaml/package.json | 1 + extensions/yarn.lock | 8 +- gulpfile.js | 2 +- package.json | 58 +- remote/.yarnrc | 2 +- remote/installDevModules.sh | 20 - remote/installDevPackagesAsRoot.sh | 16 - remote/launchDevMode.sh | 17 - remote/package.json | 25 +- remote/web/.yarnrc | 3 + remote/web/package.json | 11 + remote/web/yarn.lock | 42 + remote/yarn.lock | 1356 ++++---- resources/completions/bash/code | 2 +- resources/completions/zsh/_code | 2 +- resources/linux/rpm/dependencies.json | 78 - resources/linux/snap/snapcraft.yaml | 1 + resources/win32/bin/code.sh | 17 +- scripts/code-web.js | 24 - scripts/code.sh | 2 +- scripts/env.ps1 | 3 - scripts/env.sh | 6 - scripts/test-integration.bat | 16 +- scripts/test-integration.sh | 16 +- src/buildfile.js | 10 +- src/main.js | 31 +- src/sql/azdata.proposed.d.ts | 4 +- src/sql/base/browser/ui/button/button.ts | 2 +- .../browser/ui/dropdownList/dropdownList.ts | 6 +- .../scrollableSplitview.ts | 12 +- .../table/plugins/autoSizeColumns.plugin.ts | 2 +- src/sql/base/browser/ui/taskbar/actionbar.ts | 5 +- .../accounts/browser/accountDialog.ts | 2 +- .../browser/accountListStatusbarItem.ts | 70 - .../browser/accountManagement.contribution.ts | 16 - .../electron-browser/clipboardService.ts | 10 +- .../common/connectionManagementService.ts | 8 +- .../query/common/queryModelService.ts | 30 - .../extensionHost.contribution.common.ts | 23 + .../mainThreadAccountManagement.ts | 0 .../mainThreadBackgroundTaskManagement.ts | 0 .../mainThreadConnectionManagement.ts | 0 .../mainThreadCredentialManagement.ts | 0 .../mainThreadDashboard.ts | 0 .../mainThreadDashboardWebview.ts | 0 .../mainThreadDataProtocol.ts | 0 .../mainThreadExtensionManagement.ts | 4 +- .../mainThreadModalDialog.ts | 0 .../{node => browser}/mainThreadModelView.ts | 0 .../mainThreadModelViewDialog.ts | 0 .../{node => browser}/mainThreadNotebook.ts | 0 .../mainThreadNotebookDocumentsAndEditors.ts | 0 .../mainThreadObjectExplorer.ts | 0 .../mainThreadQueryEditor.ts | 0 .../mainThreadResourceProvider.ts | 0 .../mainThreadSerializationProvider.ts | 0 .../mainThreadTasks.ts | 0 .../extensionHost.contribution.ts} | 2 +- .../api/node/extHostModelViewTree.ts | 4 +- .../workbench/api/node/sqlExtHost.api.impl.ts | 8 +- .../api/node/sqlExtHost.contribution.ts | 46 - .../browser/parts/views/customView.ts | 18 +- .../modelComponents/diffeditor.component.ts | 6 +- .../modelComponents/editor.component.ts | 4 +- .../accounts/browser/accounts.contribution.ts | 40 + .../browser/connection.contribution.ts | 13 +- .../connection/browser/connectionStatus.ts | 71 +- .../common/connectionGlobalStatus.ts | 12 +- .../parts/dashboard/common/actions.ts | 2 +- .../widgets/explorer/explorerTree.ts | 14 +- .../explorer/explorerWidget.component.ts | 7 +- .../browser/connectionViewletPanel.ts | 1 - .../electron-browser/nodeCommands.ts | 6 +- .../parts/editData/common/editDataInput.ts | 8 +- .../notebook/cellViews/code.component.ts | 4 +- .../parts/notebook/notebookStyles.ts | 4 +- .../notebook/outputs/notebookMarkdown.ts | 2 +- .../browser/treeSelectionHandler.ts | 24 +- .../objectExplorerViewTreeShimActions.ts | 16 +- .../parts/profiler/browser/profilerActions.ts | 6 +- .../profiler/browser/profilerTableEditor.ts | 2 +- .../parts/query/browser/flavorStatus.ts | 75 +- .../parts/query/browser/query.contribution.ts | 12 + .../parts/query/browser/queryEditor.ts | 2 +- .../parts/query/browser/queryStatus.ts | 117 - .../parts/query/browser/rowCountStatus.ts | 99 - .../parts/query/browser/statusBarItems.ts | 238 ++ .../parts/query/browser/timeElapsedStatus.ts | 112 - .../parts/query/common/queryInput.ts | 7 + .../browser/accountManagementService.ts | 12 - .../commandLine/common/commandLineService.ts | 20 +- .../browser/connectionDialogWidget.ts | 4 - .../connection/browser/connectionWidget.ts | 4 +- .../test/common/insightsUtils.test.ts | 18 +- src/sqltest/common/telemetryUtilities.test.ts | 10 +- .../commandLine/commandLineService.test.ts | 10 +- .../connectionManagementService.test.ts | 12 +- .../connectionStatusManager.test.ts | 7 +- .../connection/objectExplorerService.test.ts | 4 +- .../propertiesWidget.component.test.ts | 4 +- .../notebook/model/notebookModel.test.ts | 5 +- .../connectionManagementService.test.ts | 4 +- src/sqltest/stubs/editorGroupService.ts | 22 +- src/sqltest/stubs/storageTestService.ts | 3 + src/sqltest/stubs/telemetryServiceStub.ts | 5 + .../api/extHostAccountManagement.test.ts | 2 +- .../api/extHostCredentialManagement.test.ts | 2 +- ...mainThreadBackgroundTaskManagement.test.ts | 2 +- .../api/mainThreadModelViewDialog.test.ts | 2 +- .../workbench/api/mainThreadNotebook.test.ts | 2 +- src/tsconfig.json | 3 +- src/typings/chokidar.d.ts | 242 +- src/typings/electron.d.ts | 1109 ++++-- src/typings/gc-signals.d.ts | 19 - src/typings/lib.array-ext.d.ts | 2 + src/typings/{vscode-nsfw.d.ts => nsfw.d.ts} | 3 +- src/typings/onigasm-umd.d.ts | 33 + src/typings/require.d.ts | 7 +- src/typings/vscode-sqlite3.d.ts | 8 +- src/typings/vscode-textmate.d.ts | 6 +- .../vscode-windows-ca-certs.d.ts} | 4 +- .../vsda.d.ts} | 6 +- src/typings/xterm-addon-search.d.ts | 69 + src/typings/xterm-addon-web-links.d.ts | 71 + src/typings/{vscode-xterm.d.ts => xterm.d.ts} | 280 +- src/vs/base/browser/browser.ts | 1 + src/vs/base/browser/contextmenu.ts | 2 +- src/vs/base/browser/dom.ts | 114 +- src/vs/base/browser/globalMouseMoveMonitor.ts | 40 +- src/vs/base/browser/hash.ts | 15 - src/vs/base/browser/htmlContentRenderer.ts | 39 +- src/vs/base/browser/ui/actionbar/actionbar.ts | 18 +- .../ui/breadcrumbs/breadcrumbsWidget.css | 8 +- .../ui/breadcrumbs/breadcrumbsWidget.ts | 27 +- .../base/browser/ui/breadcrumbs/collapsed.svg | 1 - .../browser/ui/breadcrumbs/collpased-dark.svg | 1 - .../ui/breadcrumbs/tree-collapsed-dark.svg | 3 + .../ui/breadcrumbs/tree-collapsed-hc.svg | 3 + .../ui/breadcrumbs/tree-collapsed-light.svg | 3 + .../browser/ui/centered/centeredViewLayout.ts | 14 +- src/vs/base/browser/ui/checkbox/checkbox.ts | 17 +- .../browser/ui/contextview/contextview.ts | 19 +- .../base/browser/ui/countBadge/countBadge.css | 11 +- src/vs/base/browser/ui/dialog/close-dark.svg | 3 + .../base/browser/ui/dialog/close-inverse.svg | 1 - src/vs/base/browser/ui/dialog/close-light.svg | 3 + src/vs/base/browser/ui/dialog/close.svg | 1 - src/vs/base/browser/ui/dialog/dialog.css | 24 +- src/vs/base/browser/ui/dialog/dialog.ts | 41 +- src/vs/base/browser/ui/dialog/error-dark.svg | 3 + .../base/browser/ui/dialog/error-inverse.svg | 26 - src/vs/base/browser/ui/dialog/error-light.svg | 3 + src/vs/base/browser/ui/dialog/error.svg | 25 - src/vs/base/browser/ui/dialog/info-dark.svg | 3 + .../base/browser/ui/dialog/info-inverse.svg | 17 - src/vs/base/browser/ui/dialog/info-light.svg | 4 + src/vs/base/browser/ui/dialog/info.svg | 17 - .../base/browser/ui/dialog/warning-dark.svg | 3 + .../browser/ui/dialog/warning-inverse.svg | 15 - .../base/browser/ui/dialog/warning-light.svg | 4 + src/vs/base/browser/ui/dialog/warning.svg | 15 - src/vs/base/browser/ui/dropdown/dropdown.ts | 16 +- .../ui/findinput/case-sensitive-dark.svg | 4 +- .../ui/findinput/case-sensitive-hc.svg | 3 + .../ui/findinput/case-sensitive-light.svg | 3 + .../browser/ui/findinput/case-sensitive.svg | 1 - .../ui/findinput/findInputCheckboxes.css | 34 +- .../base/browser/ui/findinput/regex-dark.svg | 4 +- src/vs/base/browser/ui/findinput/regex-hc.svg | 3 + .../base/browser/ui/findinput/regex-light.svg | 3 + src/vs/base/browser/ui/findinput/regex.svg | 1 - .../browser/ui/findinput/whole-word-dark.svg | 4 +- .../browser/ui/findinput/whole-word-hc.svg | 3 + .../browser/ui/findinput/whole-word-light.svg | 3 + .../base/browser/ui/findinput/whole-word.svg | 1 - src/vs/base/browser/ui/grid/grid.ts | 127 +- src/vs/base/browser/ui/grid/gridview.ts | 89 +- src/vs/base/browser/ui/inputbox/inputBox.ts | 22 +- src/vs/base/browser/ui/list/list.css | 4 +- src/vs/base/browser/ui/list/listView.ts | 21 +- src/vs/base/browser/ui/list/listWidget.ts | 57 +- src/vs/base/browser/ui/menu/check.svg | 4 +- src/vs/base/browser/ui/menu/ellipsis.svg | 6 +- src/vs/base/browser/ui/menu/menu.css | 2 +- src/vs/base/browser/ui/menu/menu.ts | 42 +- src/vs/base/browser/ui/menu/menubar.ts | 48 +- .../ui/octiconLabel/octicons/octicons.css | 22 +- .../ui/octiconLabel/octicons/octicons.svg | 38 +- .../ui/octiconLabel/octicons/octicons.ttf | Bin 36908 -> 37504 bytes src/vs/base/browser/ui/sash/sash.ts | 30 +- .../browser/ui/scrollbar/abstractScrollbar.ts | 6 + src/vs/base/browser/ui/selectBox/selectBox.ts | 5 +- .../browser/ui/selectBox/selectBoxCustom.ts | 65 +- .../browser/ui/selectBox/selectBoxNative.ts | 18 +- .../ui/splitview/arrow-collapse-dark.svg | 1 - .../browser/ui/splitview/arrow-collapse.svg | 1 - .../ui/splitview/arrow-expand-dark.svg | 1 - .../browser/ui/splitview/arrow-expand.svg | 1 - .../base/browser/ui/splitview/panelview.css | 16 +- src/vs/base/browser/ui/splitview/panelview.ts | 51 +- .../base/browser/ui/splitview/splitview.css | 4 + src/vs/base/browser/ui/splitview/splitview.ts | 329 +- .../ui/splitview/tree-collapsed-dark.svg | 3 + .../ui/splitview/tree-collapsed-hc.svg | 3 + .../ui/splitview/tree-collapsed-light.svg | 3 + .../ui/splitview/tree-expanded-dark.svg | 3 + .../browser/ui/splitview/tree-expanded-hc.svg | 3 + .../ui/splitview/tree-expanded-light.svg | 3 + .../base/browser/ui/toolbar/ellipsis-dark.svg | 5 + .../base/browser/ui/toolbar/ellipsis-hc.svg | 5 + .../browser/ui/toolbar/ellipsis-inverse.svg | 1 - .../browser/ui/toolbar/ellipsis-light.svg | 5 + src/vs/base/browser/ui/toolbar/ellipsis.svg | 1 - src/vs/base/browser/ui/toolbar/toolbar.css | 9 +- src/vs/base/browser/ui/toolbar/toolbar.ts | 38 +- src/vs/base/browser/ui/tree/abstractTree.ts | 204 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 69 +- src/vs/base/browser/ui/tree/dataTree.ts | 7 +- .../browser/ui/tree/media/collapsed-dark.svg | 1 - .../browser/ui/tree/media/collapsed-hc.svg | 1 - .../base/browser/ui/tree/media/collapsed.svg | 1 - .../browser/ui/tree/media/expanded-dark.svg | 1 - .../browser/ui/tree/media/expanded-hc.svg | 1 - .../base/browser/ui/tree/media/expanded.svg | 1 - .../ui/tree/media/tree-collapsed-dark.svg | 3 + .../ui/tree/media/tree-collapsed-hc.svg | 3 + .../ui/tree/media/tree-collapsed-light.svg | 3 + .../ui/tree/media/tree-expanded-dark.svg | 3 + .../ui/tree/media/tree-expanded-hc.svg | 3 + .../ui/tree/media/tree-expanded-light.svg | 3 + src/vs/base/browser/ui/tree/media/tree.css | 38 +- src/vs/base/browser/ui/tree/objectTree.ts | 5 +- .../base/browser/ui/tree/objectTreeModel.ts | 31 +- src/vs/base/browser/ui/tree/tree.ts | 7 + src/vs/base/browser/ui/tree/treeDefaults.ts | 25 + src/vs/base/common/actions.ts | 68 +- src/vs/base/common/arrays.ts | 8 +- src/vs/base/common/async.ts | 32 +- src/vs/base/common/buffer.ts | 46 +- src/vs/base/common/cache.ts | 4 +- src/vs/base/common/collections.ts | 44 +- src/vs/base/common/comparers.ts | 81 +- src/vs/base/common/console.ts | 5 +- src/vs/base/common/errorMessage.ts | 10 +- src/vs/base/common/errorsWithActions.ts | 4 +- src/vs/base/common/event.ts | 23 +- src/vs/base/common/filters.ts | 15 +- src/vs/base/common/glob.ts | 6 +- src/vs/base/common/htmlContent.ts | 22 + src/vs/base/common/lifecycle.ts | 208 +- src/vs/base/common/mime.ts | 83 +- src/vs/base/common/network.ts | 2 + src/vs/base/common/objects.ts | 8 +- src/vs/base/common/parsers.ts | 23 - src/vs/base/common/path.ts | 2 +- src/vs/base/common/resources.ts | 17 +- src/vs/base/common/types.ts | 25 + src/vs/base/common/uri.ts | 7 +- src/vs/base/common/uriIpc.ts | 49 + src/vs/base/common/worker/simpleWorker.ts | 120 +- src/vs/base/node/config.ts | 23 +- src/vs/base/node/encoding.ts | 2 +- src/vs/base/node/id.ts | 51 +- src/vs/base/node/macAddress.ts | 69 + src/vs/base/node/pfs.ts | 38 +- src/vs/base/node/processes.ts | 9 +- src/vs/base/node/request.ts | 188 - src/vs/base/parts/ipc/common/ipc.net.ts | 7 +- src/vs/base/parts/ipc/common/ipc.ts | 70 +- src/vs/base/parts/ipc/node/ipc.net.ts | 197 +- .../test/common/quickOpenScorer.test.ts | 20 +- src/vs/base/parts/storage/common/storage.ts | 318 ++ .../base/{ => parts/storage}/node/storage.ts | 343 +- .../storage/test/node}/storage.test.ts | 3 +- .../base/parts/tree/browser/CollapseAll.svg | 1 - .../tree/browser/CollapseAll_inverse.svg | 1 - .../parts/tree/browser/collapse-all-dark.svg | 4 + .../parts/tree/browser/collapse-all-hc.svg | 4 + .../parts/tree/browser/collapse-all-light.svg | 4 + .../parts/tree/browser/collapsed-dark.svg | 1 - .../base/parts/tree/browser/collapsed-hc.svg | 1 - src/vs/base/parts/tree/browser/collapsed.svg | 1 - .../base/parts/tree/browser/expanded-dark.svg | 1 - .../base/parts/tree/browser/expanded-hc.svg | 1 - src/vs/base/parts/tree/browser/expanded.svg | 1 - .../tree/browser/tree-collapsed-dark.svg | 3 + .../parts/tree/browser/tree-collapsed-hc.svg | 3 + .../tree/browser/tree-collapsed-light.svg | 3 + .../parts/tree/browser/tree-expanded-dark.svg | 3 + .../parts/tree/browser/tree-expanded-hc.svg | 3 + .../tree/browser/tree-expanded-light.svg | 3 + src/vs/base/parts/tree/browser/tree.css | 25 +- src/vs/base/parts/tree/browser/tree.ts | 6 +- src/vs/base/parts/tree/browser/treeModel.ts | 4 +- src/vs/base/parts/tree/browser/treeView.ts | 6 +- src/vs/base/test/browser/comparers.test.ts | 23 +- src/vs/base/test/browser/hash.test.ts | 16 - src/vs/base/test/browser/htmlContent.test.ts | 15 +- .../test/browser/ui/grid/gridview.test.ts | 4 +- .../browser/ui/tree/asyncDataTree.test.ts | 53 + src/vs/base/test/common/decorators.test.ts | 2 +- src/vs/base/test/common/event.test.ts | 23 +- src/vs/base/test/common/filters.test.ts | 21 +- src/vs/base/test/common/lifecycle.test.ts | 3 +- src/vs/base/test/common/mime.test.ts | 55 +- src/vs/base/test/common/resources.test.ts | 9 +- src/vs/base/test/common/uri.test.ts | 13 + .../base/test/{common => node}/buffer.test.ts | 38 + src/vs/base/test/node/id.test.ts | 6 +- src/vs/base/test/node/pfs/pfs.test.ts | 26 + .../base/test/node/stream/fixtures/file.css | 40 - src/vs/base/test/node/stream/stream.test.ts | 27 - src/vs/base/test/node/utils.ts | 4 +- src/vs/code/browser/workbench/workbench.html | 28 +- src/vs/code/browser/workbench/workbench.js | 40 +- .../issue/issueReporterMain.ts | 59 +- .../processExplorer/processExplorerMain.ts | 10 +- .../contrib/languagePackCachedDataCleaner.ts | 21 +- .../contrib/nodeCachedDataCleaner.ts | 8 +- .../sharedProcess/sharedProcessMain.ts | 72 +- .../electron-browser/workbench/workbench.html | 2 +- .../electron-browser/workbench/workbench.js | 11 +- src/vs/code/electron-main/app.ts | 553 +-- src/vs/code/electron-main/auth.ts | 6 +- src/vs/code/electron-main/keyboard.ts | 32 - src/vs/code/electron-main/logUploader.ts | 153 - src/vs/code/electron-main/main.ts | 601 ++-- src/vs/code/electron-main/sharedProcess.ts | 18 +- src/vs/code/electron-main/theme.ts | 44 - src/vs/code/electron-main/window.ts | 158 +- src/vs/code/electron-main/windows.ts | 143 +- src/vs/code/node/cli.ts | 24 +- src/vs/code/node/cliProcessMain.ts | 101 +- src/vs/code/node/shellEnv.ts | 12 +- .../test/electron-main/nativeHelpers.test.ts | 28 + src/vs/code/test/node/argv.test.ts | 27 +- src/vs/css.build.js | 4 +- src/vs/editor/browser/config/configuration.ts | 28 +- .../browser/controller/pointerHandler.ts | 22 +- src/vs/editor/browser/core/editorState.ts | 20 +- .../browser/services/bulkEditService.ts | 4 +- .../browser/services/codeEditorServiceImpl.ts | 28 +- src/vs/editor/browser/view/viewImpl.ts | 4 +- .../browser/viewParts/lines/rangeUtil.ts | 2 +- .../browser/viewParts/minimap/minimap.ts | 143 +- .../editor/browser/widget/codeEditorWidget.ts | 3 +- src/vs/editor/browser/widget/diffNavigator.ts | 21 +- .../browser/widget/media/addition-dark.svg} | 2 +- .../browser/widget/media/addition-inverse.svg | 1 - .../browser/widget/media/addition-light.svg} | 2 +- .../editor/browser/widget/media/addition.svg | 1 - .../browser/widget/media/close-dark.svg | 3 + .../browser/widget/media/close-inverse.svg | 1 - .../browser/widget/media/close-light.svg | 3 + .../browser/widget/media/deletion-dark.svg | 3 + .../browser/widget/media/deletion-inverse.svg | 1 - .../browser/widget/media/deletion-light.svg | 3 + .../editor/browser/widget/media/deletion.svg | 1 - .../browser/widget/media/diffEditor.css | 11 +- .../browser/widget/media/diffReview.css | 4 +- .../common/config/commonEditorConfig.ts | 6 +- src/vs/editor/common/core/position.ts | 2 +- src/vs/editor/common/core/uint.ts | 6 +- src/vs/editor/common/model.ts | 40 +- .../editor/common/model/indentationGuesser.ts | 30 +- .../pieceTreeTextBuffer/pieceTreeBase.ts | 18 +- src/vs/editor/common/model/textModel.ts | 396 +-- src/vs/editor/common/model/textModelTokens.ts | 801 +++-- src/vs/editor/common/model/tokensStore.ts | 330 ++ src/vs/editor/common/modes.ts | 127 +- .../common/services/editorSimpleWorker.ts | 136 +- .../services/editorWorkerServiceImpl.ts | 65 +- .../editor/common/services/getIconClasses.ts | 14 +- .../common/services/languagesRegistry.ts | 6 +- .../services/markerDecorationsServiceImpl.ts | 10 +- src/vs/editor/common/services/modeService.ts | 4 +- .../editor/common/services/modeServiceImpl.ts | 8 +- .../common/services/modelServiceImpl.ts | 11 +- .../common/services/resourceConfiguration.ts | 2 +- .../services/resourceConfigurationImpl.ts | 2 +- src/vs/editor/common/services/webWorker.ts | 23 +- .../common/standalone/standaloneEnums.ts | 7 + .../editor/common/view/editorColorRegistry.ts | 18 +- .../common/viewModel/splitLinesCollection.ts | 5 +- .../editor/contrib/codeAction/codeAction.ts | 31 +- .../contrib/codeAction/codeActionCommands.ts | 176 +- .../contrib/codeAction/codeActionModel.ts | 150 +- .../contrib/codeAction/codeActionTrigger.ts | 5 + .../editor/contrib/codeAction/codeActionUi.ts | 105 + .../contrib/codeAction/codeActionWidget.ts | 38 +- .../contrib/codeAction/lightBulbWidget.css | 4 +- .../contrib/codeAction/lightBulbWidget.ts | 143 +- .../codeAction/lightbulb-autofix-dark.svg | 10 +- .../codeAction/lightbulb-autofix-light.svg | 4 + .../contrib/codeAction/lightbulb-autofix.svg | 10 - .../contrib/codeAction/lightbulb-dark.svg | 4 +- .../contrib/codeAction/lightbulb-light.svg | 4 + .../editor/contrib/codeAction/lightbulb.svg | 1 - .../codeAction/test/codeAction.test.ts | 106 +- .../codeAction/test/codeActionModel.test.ts | 81 +- .../editor/contrib/codelens/codeLensCache.ts | 55 +- src/vs/editor/contrib/codelens/codelens.ts | 59 +- .../contrib/codelens/codelensController.ts | 144 +- .../editor/contrib/codelens/codelensWidget.ts | 20 +- .../contrib/colorPicker/colorDetector.ts | 36 +- .../contrib/colorPicker/colorPicker.css | 6 +- .../comment/test/lineCommentCommand.test.ts | 3 +- .../editor/contrib/contextmenu/contextmenu.ts | 16 +- src/vs/editor/contrib/dnd/dnd.ts | 23 +- .../editor/contrib/dnd/dragAndDropCommand.ts | 2 +- .../documentSymbols/media/BooleanData_16x.svg | 1 - .../media/BooleanData_16x_darkp.svg | 1 - .../documentSymbols/media/Class_16x.svg | 1 - .../documentSymbols/media/Class_16x_darkp.svg | 1 - .../media/ColorPalette_ColorPalette_16x.svg | 1 - .../ColorPalette_ColorPalette_16x_darkp.svg | 1 - .../documentSymbols/media/Constant_16x.svg | 1 - .../media/Constant_16x_inverse.svg | 1 - .../documentSymbols/media/Document_16x.svg | 1 - .../media/Document_16x_darkp.svg | 1 - .../documentSymbols/media/EnumItem_16x.svg | 1 - .../media/EnumItem_inverse_16x.svg | 1 - .../documentSymbols/media/Enumerator_16x.svg | 1 - .../media/Enumerator_inverse_16x.svg | 1 - .../media/Event_16x_vscode.svg | 1 - .../media/Event_16x_vscode_inverse.svg | 1 - .../documentSymbols/media/Field_16x.svg | 1 - .../documentSymbols/media/Field_16x_darkp.svg | 1 - .../documentSymbols/media/Indexer_16x.svg | 1 - .../media/Indexer_16x_darkp.svg | 1 - .../media/IntelliSenseKeyword_16x.svg | 1 - .../media/IntelliSenseKeyword_16x_darkp.svg | 1 - .../documentSymbols/media/Interface_16x.svg | 1 - .../media/Interface_16x_darkp.svg | 1 - .../media/LocalVariable_16x_vscode.svg | 1 - .../LocalVariable_16x_vscode_inverse.svg | 1 - .../documentSymbols/media/Method_16x.svg | 1 - .../media/Method_16x_darkp.svg | 1 - .../documentSymbols/media/Namespace_16x.svg | 1 - .../media/Namespace_16x_darkp.svg | 1 - .../documentSymbols/media/Numeric_16x.svg | 1 - .../media/Numeric_16x_darkp.svg | 1 - .../media/Operator_16x_vscode.svg | 1 - .../media/Operator_16x_vscode_inverse.svg | 1 - .../documentSymbols/media/Property_16x.svg | 1 - .../media/Property_16x_darkp.svg | 1 - .../documentSymbols/media/Snippet_16x.svg | 1 - .../media/Snippet_16x_darkp.svg | 1 - .../documentSymbols/media/String_16x.svg | 1 - .../media/String_16x_darkp.svg | 1 - .../media/Structure_16x_vscode.svg | 1 - .../media/Structure_16x_vscode_inverse.svg | 1 - .../media/Template_16x_vscode.svg | 1 - .../media/Template_16x_vscode_inverse.svg | 1 - .../documentSymbols/media/boolean-dark.svg | 3 + .../documentSymbols/media/boolean-light.svg | 3 + .../documentSymbols/media/class-dark.svg | 3 + .../documentSymbols/media/class-light.svg | 3 + .../documentSymbols/media/constant-dark.svg | 4 + .../documentSymbols/media/constant-light.svg | 4 + .../documentSymbols/media/enumerator-dark.svg | 3 + .../media/enumerator-item-dark.svg | 3 + .../media/enumerator-item-light.svg | 3 + .../media/enumerator-light.svg | 3 + .../documentSymbols/media/event-dark.svg | 3 + .../documentSymbols/media/event-light.svg | 3 + .../documentSymbols/media/field-dark.svg | 3 + .../documentSymbols/media/field-light.svg | 3 + .../documentSymbols/media/file-dark.svg | 3 + .../documentSymbols/media/file-light.svg | 3 + .../documentSymbols/media/indexer-dark.svg | 3 + .../documentSymbols/media/indexer-light.svg | 3 + .../documentSymbols/media/interface-dark.svg | 3 + .../documentSymbols/media/interface-light.svg | 3 + .../documentSymbols/media/keyword-dark.svg | 3 + .../documentSymbols/media/keyword-light.svg | 3 + .../documentSymbols/media/method-dark.svg | 3 + .../documentSymbols/media/method-light.svg | 3 + .../documentSymbols/media/namespace-dark.svg | 3 + .../documentSymbols/media/namespace-light.svg | 3 + .../documentSymbols/media/numeric-dark.svg | 3 + .../documentSymbols/media/numeric-light.svg | 3 + .../documentSymbols/media/operator-dark.svg | 3 + .../documentSymbols/media/operator-light.svg | 3 + .../documentSymbols/media/property-dark.svg | 3 + .../documentSymbols/media/property-light.svg | 3 + .../documentSymbols/media/snippet-dark.svg | 3 + .../documentSymbols/media/snippet-light.svg | 3 + .../documentSymbols/media/string-dark.svg | 3 + .../documentSymbols/media/string-light.svg | 3 + .../documentSymbols/media/structure-dark.svg | 3 + .../documentSymbols/media/structure-light.svg | 3 + .../documentSymbols/media/symbol-icons.css | 117 +- .../documentSymbols/media/template-dark.svg | 3 + .../documentSymbols/media/template-light.svg | 3 + .../documentSymbols/media/variable-dark.svg | 3 + .../documentSymbols/media/variable-light.svg | 3 + .../contrib/documentSymbols/outlineTree.ts | 16 +- src/vs/editor/contrib/find/findDecorations.ts | 12 +- src/vs/editor/contrib/find/findState.ts | 5 +- src/vs/editor/contrib/find/findWidget.css | 36 +- src/vs/editor/contrib/find/findWidget.ts | 7 +- .../images/cancelSelectionFind-inverse.svg | 8 - .../find/images/cancelSelectionFind.svg | 8 - .../contrib/find/images/chevron-next-dark.svg | 3 + .../find/images/chevron-next-light.svg | 3 + .../find/images/chevron-previous-dark.svg | 3 + .../find/images/chevron-previous-light.svg | 3 + .../editor/contrib/find/images/close-dark.svg | 4 +- .../contrib/find/images/close-light.svg | 3 + src/vs/editor/contrib/find/images/close.svg | 1 - .../find/images/expando-collapsed-dark.svg | 1 - .../contrib/find/images/expando-collapsed.svg | 1 - .../find/images/expando-expanded-dark.svg | 1 - .../contrib/find/images/expando-expanded.svg | 1 - .../find/images/find-selection-dark.svg | 3 + .../find/images/find-selection-light.svg | 3 + .../contrib/find/images/next-inverse.svg | 5 - src/vs/editor/contrib/find/images/next.svg | 5 - .../contrib/find/images/previous-inverse.svg | 5 - .../editor/contrib/find/images/previous.svg | 5 - .../contrib/find/images/replace-all-dark.svg | 3 + .../find/images/replace-all-inverse.svg | 11 - .../contrib/find/images/replace-all-light.svg | 3 + .../contrib/find/images/replace-all.svg | 11 - .../contrib/find/images/replace-dark.svg | 3 + .../contrib/find/images/replace-inverse.svg | 13 - .../contrib/find/images/replace-light.svg | 3 + src/vs/editor/contrib/find/images/replace.svg | 13 - .../find/images/tree-collapsed-dark.svg | 3 + .../find/images/tree-collapsed-light.svg | 3 + .../find/images/tree-expanded-dark.svg | 3 + .../find/images/tree-expanded-light.svg | 3 + .../editor/contrib/find/simpleFindWidget.css | 12 +- .../editor/contrib/find/simpleFindWidget.ts | 34 +- .../contrib/folding/arrow-collapse-dark.svg | 6 - .../editor/contrib/folding/arrow-collapse.svg | 6 - .../contrib/folding/arrow-expand-dark.svg | 4 - .../editor/contrib/folding/arrow-expand.svg | 4 - src/vs/editor/contrib/folding/folding.css | 17 +- src/vs/editor/contrib/folding/folding.ts | 75 +- src/vs/editor/contrib/folding/foldingModel.ts | 4 +- .../contrib/folding/tree-collapsed-dark.svg | 3 + .../contrib/folding/tree-collapsed-hc.svg | 3 + .../contrib/folding/tree-collapsed-light.svg | 3 + .../contrib/folding/tree-expanded-dark.svg | 3 + .../contrib/folding/tree-expanded-hc.svg | 3 + .../contrib/folding/tree-expanded-light.svg | 3 + src/vs/editor/contrib/format/formatActions.ts | 47 +- .../goToDefinition/goToDefinitionCommands.ts | 5 +- .../goToDefinition/goToDefinitionMouse.ts | 27 +- .../goToDefinitionResultsNavigation.ts | 44 +- src/vs/editor/contrib/gotoError/gotoError.ts | 31 +- .../contrib/gotoError/gotoErrorWidget.ts | 34 +- .../gotoError/media/gotoErrorWidget.css | 26 +- .../gotoError/media/status-error-inverse.svg | 1 - .../contrib/gotoError/media/status-error.svg | 1 - .../gotoError/media/status-info-inverse.svg | 1 - .../contrib/gotoError/media/status-info.svg | 1 - .../media/status-warning-inverse.svg | 1 - .../gotoError/media/status-warning.svg | 1 - src/vs/editor/contrib/hover/hover.css | 1 + src/vs/editor/contrib/hover/hover.ts | 35 +- src/vs/editor/contrib/hover/hoverWidgets.ts | 5 +- .../editor/contrib/hover/modesContentHover.ts | 116 +- .../editor/contrib/hover/modesGlyphHover.ts | 16 +- src/vs/editor/contrib/links/getLinks.ts | 16 +- src/vs/editor/contrib/links/links.ts | 58 +- .../contrib/markdown/markdownRenderer.ts | 13 +- .../contrib/message/messageController.ts | 33 +- .../editor/contrib/multicursor/multicursor.ts | 35 +- .../multicursor/test/multicursor.test.ts | 5 +- .../parameterHints/parameterHintsModel.ts | 18 +- .../parameterHints/parameterHintsWidget.ts | 64 +- .../parameterHints/provideSignatureHelp.ts | 24 +- .../test/parameterHintsModel.test.ts | 145 +- .../referenceSearch/media/chevron-down-hc.svg | 10 - .../media/chevron-down-inverse.svg | 10 - .../referenceSearch/media/chevron-down.svg | 10 - .../media/chevron-next-dark.svg | 3 + .../media/chevron-next-light.svg | 3 + .../media/chevron-previous-dark.svg | 3 + .../media/chevron-previous-light.svg | 3 + .../referenceSearch/media/chevron-up-hc.svg | 10 - .../media/chevron-up-inverse-hc.svg | 10 - .../media/chevron-up-inverse.svg | 10 - .../referenceSearch/media/chevron-up.svg | 10 - .../referenceSearch/media/close-dark.svg | 3 + .../referenceSearch/media/close-inverse.svg | 1 - .../referenceSearch/media/close-light.svg | 3 + .../contrib/referenceSearch/media/close.svg | 1 - .../referenceSearch/media/peekViewWidget.css | 33 +- .../contrib/referenceSearch/peekViewWidget.ts | 14 +- .../referenceSearch/referenceSearch.ts | 2 +- .../referenceSearch/referencesController.ts | 14 +- .../referenceSearch/referencesModel.ts | 27 +- .../contrib/referenceSearch/referencesTree.ts | 53 +- .../referenceSearch/referencesWidget.ts | 46 +- src/vs/editor/contrib/rename/rename.ts | 4 +- .../editor/contrib/rename/renameInputField.ts | 20 +- .../contrib/smartSelect/bracketSelections.ts | 12 +- .../editor/contrib/smartSelect/smartSelect.ts | 4 +- .../smartSelect/test/smartSelect.test.ts | 23 +- .../contrib/smartSelect/wordSelections.ts | 8 +- .../contrib/snippet/snippetController2.ts | 53 +- .../editor/contrib/snippet/snippetSession.ts | 71 +- .../contrib/snippet/snippetVariables.ts | 43 +- .../test/snippetController2.old.test.ts | 58 +- .../snippet/test/snippetController2.test.ts | 18 + .../snippet/test/snippetSession.test.ts | 4 +- .../snippet/test/snippetVariables.test.ts | 92 +- .../editor/contrib/suggest/completionModel.ts | 15 +- .../contrib/suggest/media/Class_16x.svg | 1 - .../suggest/media/Class_inverse_16x.svg | 1 - .../suggest/media/ColorPalette_16x.svg | 1 - .../media/ColorPalette_inverse_16x.svg | 1 - .../contrib/suggest/media/Constant_16x.svg | 1 - .../suggest/media/Constant_16x_inverse.svg | 1 - .../contrib/suggest/media/Document_16x.svg | 1 - .../suggest/media/Document_inverse_16x.svg | 1 - .../contrib/suggest/media/EnumItem_16x.svg | 1 - .../suggest/media/EnumItem_inverse_16x.svg | 1 - .../contrib/suggest/media/Enumerator_16x.svg | 1 - .../suggest/media/Enumerator_inverse_16x.svg | 1 - .../suggest/media/Event_16x_vscode.svg | 1 - .../media/Event_16x_vscode_inverse.svg | 1 - .../contrib/suggest/media/Field_16x.svg | 1 - .../suggest/media/Field_inverse_16x.svg | 1 - .../contrib/suggest/media/Folder_16x.svg | 1 - .../suggest/media/Folder_inverse_16x.svg | 1 - .../suggest/media/ImportFile_16x_vscode.svg | 1 - .../media/ImportFile_16x_vscode_inverse.svg | 1 - .../suggest/media/IntelliSenseKeyword_16x.svg | 1 - .../media/IntelliSenseKeyword_inverse_16x.svg | 1 - .../contrib/suggest/media/Interface_16x.svg | 1 - .../suggest/media/Interface_inverse_16x.svg | 1 - .../media/LocalVariable_16x_vscode.svg | 1 - .../LocalVariable_16x_vscode_inverse.svg | 1 - .../contrib/suggest/media/Method_16x.svg | 1 - .../suggest/media/Method_inverse_16x.svg | 1 - .../editor/contrib/suggest/media/Misc_16x.svg | 1 - .../suggest/media/Misc_inverse_16x.svg | 1 - .../contrib/suggest/media/Namespace_16x.svg | 1 - .../suggest/media/Namespace_inverse_16x.svg | 1 - .../suggest/media/Operator_16x_vscode.svg | 1 - .../media/Operator_16x_vscode_inverse.svg | 1 - .../contrib/suggest/media/Property_16x.svg | 1 - .../suggest/media/Property_inverse_16x.svg | 1 - .../contrib/suggest/media/Ruler_16x.svg | 1 - .../suggest/media/Ruler_inverse_16x.svg | 1 - .../contrib/suggest/media/Snippet_16x.svg | 47 - .../suggest/media/Snippet_inverse_16x.svg | 43 - .../contrib/suggest/media/String_16x.svg | 1 - .../suggest/media/String_inverse_16x.svg | 1 - .../suggest/media/Structure_16x_vscode.svg | 1 - .../media/Structure_16x_vscode_inverse.svg | 1 - .../suggest/media/Template_16x_vscode.svg | 1 - .../media/Template_16x_vscode_inverse.svg | 1 - .../contrib/suggest/media/class-dark.svg | 3 + .../contrib/suggest/media/class-light.svg | 3 + .../contrib/suggest/media/close-dark.svg | 4 +- .../contrib/suggest/media/close-light.svg | 3 + src/vs/editor/contrib/suggest/media/close.svg | 1 - .../contrib/suggest/media/color-dark.svg | 3 + .../contrib/suggest/media/color-light.svg | 3 + .../contrib/suggest/media/constant-dark.svg | 4 + .../contrib/suggest/media/constant-light.svg | 4 + .../contrib/suggest/media/enumerator-dark.svg | 3 + .../suggest/media/enumerator-item-dark.svg | 3 + .../suggest/media/enumerator-item-light.svg | 3 + .../suggest/media/enumerator-light.svg | 3 + .../contrib/suggest/media/event-dark.svg | 3 + .../contrib/suggest/media/event-light.svg | 3 + .../contrib/suggest/media/field-dark.svg | 3 + .../contrib/suggest/media/field-light.svg | 3 + .../contrib/suggest/media/file-dark.svg | 3 + .../contrib/suggest/media/file-light.svg | 3 + .../contrib/suggest/media/folder-dark.svg | 3 + .../contrib/suggest/media/folder-light.svg | 3 + .../contrib/suggest/media/info-dark.svg | 3 + .../contrib/suggest/media/info-light.svg | 3 + .../contrib/suggest/media/interface-dark.svg | 3 + .../contrib/suggest/media/interface-light.svg | 3 + .../contrib/suggest/media/keyword-dark.svg | 3 + .../contrib/suggest/media/keyword-light.svg | 3 + .../contrib/suggest/media/method-dark.svg | 3 + .../contrib/suggest/media/method-light.svg | 3 + .../contrib/suggest/media/namespace-dark.svg | 3 + .../contrib/suggest/media/namespace-light.svg | 3 + .../contrib/suggest/media/operator-dark.svg | 3 + .../contrib/suggest/media/operator-light.svg | 3 + .../contrib/suggest/media/property-dark.svg | 3 + .../contrib/suggest/media/property-light.svg | 3 + .../contrib/suggest/media/reference-dark.svg | 3 + .../contrib/suggest/media/reference-light.svg | 3 + .../contrib/suggest/media/ruler-dark.svg | 3 + .../contrib/suggest/media/ruler-light.svg | 3 + .../contrib/suggest/media/snippet-dark.svg | 3 + .../contrib/suggest/media/snippet-light.svg | 3 + .../contrib/suggest/media/string-dark.svg | 3 + .../contrib/suggest/media/string-light.svg | 3 + .../contrib/suggest/media/structure-dark.svg | 3 + .../contrib/suggest/media/structure-light.svg | 3 + .../editor/contrib/suggest/media/suggest.css | 99 +- .../contrib/suggest/media/template-dark.svg | 3 + .../contrib/suggest/media/template-light.svg | 3 + .../contrib/suggest/media/variable-dark.svg | 3 + .../contrib/suggest/media/variable-light.svg | 3 + src/vs/editor/contrib/suggest/suggest.ts | 45 +- .../suggest/suggestCommitCharacters.ts | 14 +- .../contrib/suggest/suggestController.ts | 71 +- .../editor/contrib/suggest/suggestMemory.ts | 8 +- src/vs/editor/contrib/suggest/suggestModel.ts | 44 +- .../editor/contrib/suggest/suggestWidget.ts | 157 +- .../contrib/suggest/test/suggestModel.test.ts | 9 +- .../editor/contrib/suggest/wordContextKey.ts | 11 +- .../contrib/tokenization/tokenization.ts | 50 +- .../wordHighlighter/wordHighlighter.ts | 13 +- .../editor/contrib/zoneWidget/zoneWidget.ts | 21 +- src/vs/editor/editor.worker.ts | 6 +- .../accessibilityHelp/accessibilityHelp.ts | 7 +- .../iPadShowKeyboard/iPadShowKeyboard.css | 4 +- .../iPadShowKeyboard/iPadShowKeyboard.ts | 22 +- .../iPadShowKeyboard/keyboard-dark.svg | 10 + .../iPadShowKeyboard/keyboard-inverse.svg | 1 - .../iPadShowKeyboard/keyboard-light.svg | 10 + .../browser/iPadShowKeyboard/keyboard.svg | 1 - .../standalone/browser/simpleServices.ts | 45 +- .../browser/standaloneCodeEditor.ts | 20 +- .../standalone/browser/standaloneEditor.ts | 5 +- .../standalone/browser/standaloneLanguages.ts | 13 +- .../standalone/browser/standaloneServices.ts | 18 +- .../browser/standaloneThemeServiceImpl.ts | 15 +- .../test/browser/controller/cursor.test.ts | 2 +- src/vs/editor/test/browser/testCommand.ts | 7 +- .../test/common/model/model.line.test.ts | 12 +- .../test/common/model/model.modes.test.ts | 68 +- .../test/common/model/textModel.test.ts | 51 +- .../services/editorSimpleWorker.test.ts | 7 +- src/vs/loader.js | 473 +-- src/vs/monaco.d.ts | 71 +- src/vs/nls.build.js | 4 +- src/vs/nls.js | 4 +- .../browser/menuEntryActionViewItem.ts | 61 +- src/vs/platform/actions/common/actions.ts | 55 +- src/vs/platform/actions/common/menuService.ts | 21 +- .../actions/test/common/menuService.test.ts | 40 +- .../backup/electron-main/backupMainService.ts | 20 +- .../clipboard/browser/clipboardService.ts | 49 + .../clipboard/common/clipboardService.ts | 6 +- .../electron-browser/clipboardService.ts | 8 +- src/vs/platform/commands/common/commands.ts | 16 +- .../platform/commands/test/commands.test.ts | 8 +- .../configuration/common/configuration.ts | 2 +- .../common/configurationModels.ts | 6 +- .../common/configurationRegistry.ts | 22 +- .../configuration/node/configuration.ts | 56 - .../node/configurationService.ts | 67 +- .../test/common/configuration.test.ts | 16 +- .../test/common/configurationModels.test.ts | 4 +- .../test/node/configurationService.test.ts | 36 +- .../contextkey/browser/contextKeyService.ts | 8 +- .../platform/contextkey/common/contextkeys.ts | 4 +- .../contextview/browser/contextMenuHandler.ts | 18 +- .../contextview/browser/contextMenuService.ts | 2 +- .../credentials/common/credentials.ts | 16 + .../credentials/node/credentialsService.ts | 41 + .../diagnostics/common/diagnosticsService.ts | 24 +- .../electron-main/diagnosticsService.ts | 389 --- .../diagnostics/node/diagnosticsIpc.ts | 58 + .../diagnostics/node/diagnosticsService.ts | 420 ++- .../platform/dialogs/browser/dialogService.ts | 13 +- src/vs/platform/dialogs/common/dialogs.ts | 7 +- .../platform/download/node/downloadService.ts | 9 +- .../driver/electron-browser/driver.ts | 2 +- .../platform/driver/electron-main/driver.ts | 2 +- src/vs/platform/editor/common/editor.ts | 10 +- .../environment/common/environment.ts | 39 +- src/vs/platform/environment/node/argv.ts | 42 +- .../environment/node/environmentService.ts | 47 +- .../extensionGalleryService.ts | 148 +- .../common/extensionManagement.ts | 8 +- .../common/extensionManagementUtil.ts | 21 +- .../node/extensionManagementIpc.ts | 14 +- .../node/extensionManagementService.ts | 121 +- .../node/extensionsManifestCache.ts | 6 +- .../test/node/extensionGalleryService.test.ts | 24 +- .../{node => common}/extensionValidator.ts | 3 +- .../platform/extensions/common/extensions.ts | 26 +- .../test/node/extensionValidator.test.ts | 2 +- .../files/common/fileService.ts | 171 +- src/vs/platform/files/common/files.ts | 15 +- .../diskFileSystemProvider.ts | 7 +- .../files/node/diskFileSystemProvider.ts | 158 +- .../node/watcher/nodejs/watcherService.ts | 19 +- .../node/watcher/nsfw/nsfwWatcherService.ts | 44 +- .../nsfw/test/nsfwWatcherService.test.ts | 4 +- .../files/node/watcher/nsfw/watcher.ts | 10 +- .../files/node/watcher/nsfw/watcherApp.ts | 4 +- .../files/node/watcher/nsfw/watcherIpc.ts | 11 +- .../files/node/watcher/nsfw/watcherService.ts | 38 +- .../watcher/unix/chokidarWatcherService.ts | 61 +- .../unix/test/chockidarWatcherService.test.ts | 17 +- .../files/node/watcher/unix/watcher.ts | 12 +- .../files/node/watcher/unix/watcherApp.ts | 4 +- .../files/node/watcher/unix/watcherIpc.ts | 11 +- .../files/node/watcher/unix/watcherService.ts | 38 +- .../files/node/watcher/watcher.ts | 5 + .../files/node/watcher/win32/CodeHelper.exe | Bin .../files/node/watcher/win32/CodeHelper.md | 0 .../watcher/win32/csharpWatcherService.ts | 28 +- .../node/watcher/win32/watcherService.ts | 50 +- .../files/test/browser/fileService.test.ts | 4 +- .../test/common/nullFileSystemProvider.ts | 33 + .../files/test/node/diskFileService.test.ts | 424 ++- .../fixtures/resolver/examples/company.js | 0 .../node/fixtures/resolver/examples/conway.js | 0 .../fixtures/resolver/examples/employee.js | 0 .../node/fixtures/resolver/examples/small.js | 0 .../test/node/fixtures/resolver/index.html | 0 .../fixtures/resolver/other/deep/company.js | 0 .../fixtures/resolver/other/deep/conway.js | 0 .../fixtures/resolver/other/deep/employee.js | 0 .../fixtures/resolver/other/deep/small.js | 0 .../test/node/fixtures/resolver/site.css | 0 .../test/node/fixtures/service/binary.txt | Bin .../node/fixtures/service/deep/company.js | 0 .../test/node/fixtures/service/deep/conway.js | 0 .../node/fixtures/service/deep/employee.js | 0 .../test/node/fixtures/service/deep/small.js | 0 .../test/node/fixtures/service/index.html | 0 .../test/node/fixtures/service/lorem.txt | 0 .../test/node/fixtures/service/small.txt | 0 .../node/fixtures/service/small_umlaut.txt | 0 .../node/fixtures/service/some_utf16le.css | Bin .../node/fixtures/service/some_utf8_bom.txt | 0 .../files/test/node/normalizer.test.ts | 2 +- src/vs/platform/history/common/history.ts | 6 +- .../historyStorage.ts | 50 +- .../electron-main/historyMainService.ts | 72 +- .../test/electron-main/historyStorage.test.ts | 13 +- .../instantiation/common/extensions.ts | 12 +- .../instantiation/common/instantiation.ts | 11 +- .../common/instantiationService.ts | 42 +- .../issue/electron-main/issueService.ts | 79 +- .../common/abstractKeybindingService.ts | 50 +- .../platform/keybinding/common/keybinding.ts | 1 + .../keybinding/common/keybindingResolver.ts | 5 +- .../common/abstractKeybindingService.test.ts | 27 +- .../test/common/mockKeybindingService.ts | 4 + .../platform/launch/common/launchService.ts | 21 + .../launch/electron-main/launchService.ts | 29 +- .../lifecycle/browser/lifecycleService.ts | 59 + src/vs/platform/lifecycle/common/lifecycle.ts | 14 +- .../lifecycle/common/lifecycleService.ts | 9 +- .../electron-browser/lifecycleService.ts | 13 +- .../lifecycle/electron-main/lifecycleMain.ts | 109 +- src/vs/platform/list/browser/listService.ts | 105 +- src/vs/platform/log/common/log.ts | 2 +- .../platform/log/{node => common}/logIpc.ts | 0 src/vs/platform/log/node/spdlogService.ts | 103 +- src/vs/platform/markers/common/markers.ts | 9 + .../platform/menubar/electron-main/menubar.ts | 23 +- .../notification/common/notification.ts | 46 +- .../test/common/testNotificationService.ts | 19 +- src/vs/platform/opener/common/opener.ts | 2 +- .../product/browser/productService.ts | 49 + src/vs/platform/product/common/product.ts | 119 +- src/vs/platform/product/node/product.ts | 93 +- .../platform/product/node/productService.ts | 31 +- src/vs/platform/progress/common/progress.ts | 76 +- src/vs/platform/registry/common/platform.ts | 14 +- .../browser/remoteAuthorityResolverService.ts | 3 - .../remote/common/remoteAgentConnection.ts | 27 +- .../common/remoteAgentFileSystemChannel.ts | 13 +- .../request/browser/requestService.ts | 96 + .../request/{node => common}/request.ts | 58 +- src/vs/platform/request/common/requestIpc.ts | 51 + .../electron-browser/requestService.ts | 105 - .../request/electron-main/requestService.ts | 6 +- .../{base => platform/request}/node/proxy.ts | 3 +- .../platform/request/node/requestService.ts | 121 +- .../severityIcon/common/severityIcon.ts | 73 + src/vs/platform/sign/browser/signService.ts | 16 + src/vs/platform/sign/common/sign.ts | 15 + src/vs/platform/sign/node/signService.ts | 32 + src/vs/platform/state/node/stateService.ts | 37 +- src/vs/platform/statusbar/common/statusbar.ts | 25 +- .../storage/browser/storageService.ts | 138 + src/vs/platform/storage/common/storage.ts | 130 +- src/vs/platform/storage/node/storageIpc.ts | 23 +- .../storage/node/storageMainService.ts | 21 +- .../platform/storage/node/storageService.ts | 93 +- .../storage/test/node/storage.test.ts | 92 + .../storage/test/node/storageService.test.ts | 2 +- .../telemetry/browser/errorTelemetry.ts | 2 +- .../telemetry/common/errorTelemetry.ts | 42 +- .../platform/telemetry/common/gdprTypings.ts | 26 + src/vs/platform/telemetry/common/telemetry.ts | 9 + .../telemetry/common/telemetryService.ts | 28 +- .../telemetry/common/telemetryUtils.ts | 59 +- src/vs/platform/telemetry/node/telemetry.ts | 44 + .../node/workbenchCommonProperties.ts | 31 +- src/vs/platform/theme/common/colorRegistry.ts | 22 +- src/vs/platform/theme/common/styler.ts | 10 +- .../theme/electron-main/themeMainService.ts | 67 + .../update/electron-browser/updateService.ts | 2 +- .../electron-main/abstractUpdateService.ts | 4 +- .../electron-main/updateService.darwin.ts | 2 +- .../electron-main/updateService.linux.ts | 3 +- .../electron-main/updateService.snap.ts | 2 +- .../electron-main/updateService.win32.ts | 14 +- .../update/node/update.config.contribution.ts | 6 +- src/vs/platform/windows/common/windows.ts | 33 +- .../electron-browser/windowsService.ts | 5 + .../platform/windows/electron-main/windows.ts | 1 - .../windows/electron-main/windowsService.ts | 69 +- src/vs/platform/windows/node/windowsIpc.ts | 1 + src/vs/platform/workspace/common/workspace.ts | 12 +- .../workspace/test/common/workspace.test.ts | 9 + .../electron-main/workspacesMainService.ts | 2 +- src/vs/vscode.d.ts | 337 +- src/vs/vscode.proposed.d.ts | 652 ++-- .../extensionHost.contribution.ts | 80 +- .../api/browser/mainThreadClipboard.ts | 7 +- .../api/browser/mainThreadCodeInsets.ts | 148 + .../api/browser/mainThreadCommands.ts | 21 +- .../api/browser/mainThreadComments.ts | 366 +- .../api/browser/mainThreadConfiguration.ts | 2 +- .../api/browser/mainThreadDebugService.ts | 25 +- .../mainThreadDocumentContentProviders.ts | 2 +- .../api/browser/mainThreadDocuments.ts | 36 +- .../browser/mainThreadDocumentsAndEditors.ts | 52 +- .../workbench/api/browser/mainThreadEditor.ts | 34 +- .../api/browser/mainThreadEditors.ts | 85 +- .../api/browser/mainThreadFileSystem.ts | 93 +- .../api/browser/mainThreadHeapService.ts | 25 - .../workbench/api/browser/mainThreadKeytar.ts | 36 +- .../api/browser/mainThreadLabelService.ts | 36 + .../api/browser/mainThreadLanguageFeatures.ts | 198 +- .../api/browser/mainThreadMessageService.ts | 3 +- .../api/browser/mainThreadProgress.ts | 6 +- .../api/browser/mainThreadQuickOpen.ts | 8 +- src/vs/workbench/api/browser/mainThreadSCM.ts | 60 +- .../api/browser/mainThreadSaveParticipant.ts | 10 +- .../workbench/api/browser/mainThreadSearch.ts | 8 +- .../api/browser/mainThreadStatusBar.ts | 9 +- .../workbench/api/browser/mainThreadTask.ts | 59 +- .../api/browser/mainThreadTelemetry.ts | 7 + .../api/browser/mainThreadTerminalService.ts | 224 +- .../api/browser/mainThreadTreeViews.ts | 7 +- .../mainThreadWebview.ts | 187 +- .../workbench/api/browser/mainThreadWindow.ts | 27 +- .../api/browser/mainThreadWorkspace.ts | 17 +- .../api/browser/viewsExtensionPoint.ts | 10 +- src/vs/workbench/api/common/apiCommands.ts | 10 +- .../api/common/configurationExtensionPoint.ts | 10 +- .../workbench/api/common/extHost.protocol.ts | 210 +- .../api/common/extHostApiCommands.ts | 21 +- .../workbench/api/common/extHostCodeInsets.ts | 143 + .../workbench/api/common/extHostCommands.ts | 35 +- .../workbench/api/common/extHostComments.ts | 552 +-- .../api/common/extHostConfiguration.ts | 12 +- .../api/common/extHostDiagnostics.ts | 22 +- .../common/extHostDocumentContentProviders.ts | 6 +- .../workbench/api/common/extHostDocuments.ts | 28 +- .../api/common/extHostExtensionActivator.ts | 18 +- .../workbench/api/common/extHostFileSystem.ts | 113 +- .../api/common/extHostHeapService.ts | 33 - .../api/common/extHostLabelService.ts | 27 + .../api/common/extHostLanguageFeatures.ts | 287 +- src/vs/workbench/api/common/extHostMemento.ts | 10 +- .../workbench/api/common/extHostQuickOpen.ts | 6 +- src/vs/workbench/api/common/extHostSCM.ts | 15 +- .../workbench/api/common/extHostStatusBar.ts | 20 +- .../workbench/api/common/extHostTextEditor.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 37 +- .../api/common/extHostTypeConverters.ts | 15 +- src/vs/workbench/api/common/extHostTypes.ts | 37 +- src/vs/workbench/api/common/extHostWebview.ts | 38 +- .../workbench/api/common/extHostWorkspace.ts | 3 +- .../api/common/menusExtensionPoint.ts | 50 +- src/vs/workbench/api/common/shared/tasks.ts | 6 +- src/vs/workbench/api/common/shared/webview.ts | 24 + src/vs/workbench/api/node/extHost.api.impl.ts | 140 +- src/vs/workbench/api/node/extHostCLIServer.ts | 2 +- .../workbench/api/node/extHostDebugService.ts | 7 +- .../api/node/extHostExtensionService.ts | 144 +- .../api/node/extHostOutputService.ts | 2 +- src/vs/workbench/api/node/extHostSearch.ts | 11 +- src/vs/workbench/api/node/extHostTask.ts | 135 +- .../api/node/extHostTerminalService.ts | 246 +- src/vs/workbench/browser/actions.ts | 13 +- .../browser/actions/developerActions.ts | 222 ++ .../browser/actions/layoutActions.ts | 75 +- .../workbench/browser/actions/listCommands.ts | 12 +- .../browser/actions/media/actions.css | 20 +- .../actions/media/editor-layout-inverse.svg | 1 - .../browser/actions/media/editor-layout.svg | 1 - .../browser/actions/media/layout-dark.svg | 3 + .../browser/actions/media/layout-hc.svg | 3 + .../browser/actions/media/layout-light.svg | 3 + .../browser/actions/media/remove-dark.svg | 3 + .../browser/actions/media/remove-light.svg | 3 + .../browser/actions/windowActions.ts | 300 ++ .../browser/actions/workspaceActions.ts | 84 +- src/vs/workbench/browser/composite.ts | 10 +- src/vs/workbench/browser/contextkeys.ts | 34 +- src/vs/workbench/browser/dnd.ts | 14 +- src/vs/workbench/browser/editor.ts | 69 +- src/vs/workbench/browser/labels.ts | 32 +- src/vs/workbench/browser/layout.ts | 271 +- src/vs/workbench/browser/legacyLayout.ts | 10 +- src/vs/workbench/browser/media/style.css | 2 + src/vs/workbench/browser/panel.ts | 7 + .../parts/activitybar/activitybarActions.ts | 42 +- .../parts/activitybar/activitybarPart.ts | 86 +- .../activitybar/media/activityaction.css | 11 +- .../activitybar/media/activitybarpart.css | 8 +- .../media/ellipsis-activity-bar.svg | 5 + .../activitybar/media/ellipsis-global.svg | 1 - .../media/settings-activity-bar.svg | 3 + .../workbench/browser/parts/compositeBar.ts | 3 +- .../browser/parts/compositeBarActions.ts | 17 +- .../workbench/browser/parts/compositePart.ts | 87 +- .../browser/parts/editor/baseEditor.ts | 3 +- .../browser/parts/editor/binaryEditor.ts | 20 +- .../browser/parts/editor/breadcrumbs.ts | 10 +- .../parts/editor/breadcrumbsControl.ts | 160 +- .../browser/parts/editor/breadcrumbsPicker.ts | 45 +- .../parts/editor/editor.contribution.ts | 89 +- .../browser/parts/editor/editorActions.ts | 12 +- .../browser/parts/editor/editorCommands.ts | 4 +- .../browser/parts/editor/editorControl.ts | 24 +- .../browser/parts/editor/editorDropTarget.ts | 8 +- .../browser/parts/editor/editorGroupView.ts | 133 +- .../browser/parts/editor/editorPart.ts | 35 +- .../browser/parts/editor/editorPicker.ts | 2 +- .../browser/parts/editor/editorStatus.ts | 815 ++--- .../browser/parts/editor/editorWidgets.ts | 2 +- .../parts/editor/media/close-all-dark.svg | 4 + .../parts/editor/media/close-all-light.svg | 4 + .../parts/editor/media/close-big-alt.svg | 1 - .../editor/media/close-big-inverse-alt.svg | 1 - .../parts/editor/media/close-big-inverse.svg | 1 - .../browser/parts/editor/media/close-big.svg | 1 - .../parts/editor/media/close-dark-alt.svg | 3 + .../browser/parts/editor/media/close-dark.svg | 3 + .../editor/media/close-dirty-dark-alt.svg | 3 + .../parts/editor/media/close-dirty-dark.svg | 3 + .../editor/media/close-dirty-light-alt.svg | 3 + .../parts/editor/media/close-dirty-light.svg | 3 + .../browser/parts/editor/media/close-hc.svg | 3 + .../parts/editor/media/close-inverse.svg | 1 - .../parts/editor/media/close-light-alt.svg | 3 + .../parts/editor/media/close-light.svg | 3 + .../editor/media/close-statusview-inverse.svg | 1 - .../parts/editor/media/close-statusview.svg | 1 - .../browser/parts/editor/media/close.svg | 1 - .../editor/media/closeall-editors-inverse.svg | 1 - .../parts/editor/media/closeall-editors.svg | 1 - .../parts/editor/media/editorgroupview.css | 4 +- .../parts/editor/media/editorstatus.css | 28 +- .../parts/editor/media/next-diff-dark.svg | 3 + .../parts/editor/media/next-diff-inverse.svg | 1 - .../parts/editor/media/next-diff-light.svg | 3 + .../browser/parts/editor/media/next-diff.svg | 1 - .../parts/editor/media/paragraph-dark.svg | 3 + .../editor/media/paragraph-disabled-dark.svg | 5 + .../media/paragraph-disabled-inverse.svg | 1 - .../editor/media/paragraph-disabled-light.svg | 5 + .../parts/editor/media/paragraph-disabled.svg | 1 - .../parts/editor/media/paragraph-inverse.svg | 1 - .../parts/editor/media/paragraph-light.svg | 3 + .../browser/parts/editor/media/paragraph.svg | 1 - .../parts/editor/media/previous-diff-dark.svg | 3 + .../editor/media/previous-diff-inverse.svg | 1 - .../editor/media/previous-diff-light.svg | 3 + .../parts/editor/media/previous-diff.svg | 1 - .../parts/editor/media/resourceviewer.css | 12 +- .../media/split-editor-horizontal-dark.svg | 3 + .../media/split-editor-horizontal-hc.svg | 3 + .../media/split-editor-horizontal-inverse.svg | 1 - .../media/split-editor-horizontal-light.svg | 7 + .../editor/media/split-editor-horizontal.svg | 1 - .../media/split-editor-vertical-dark.svg | 3 + .../editor/media/split-editor-vertical-hc.svg | 3 + .../media/split-editor-vertical-inverse.svg | 1 - .../media/split-editor-vertical-light.svg | 7 + .../editor/media/split-editor-vertical.svg | 1 - .../parts/editor/media/tabstitlecontrol.css | 17 +- .../parts/editor/media/titlecontrol.css | 11 +- .../parts/editor/noTabsTitleControl.ts | 2 +- .../browser/parts/editor/rangeDecorations.ts | 20 +- .../browser/parts/editor/resourceViewer.ts | 170 +- .../browser/parts/editor/sideBySideEditor.ts | 4 +- .../browser/parts/editor/tabsTitleControl.ts | 141 +- .../browser/parts/editor/textDiffEditor.ts | 20 +- .../parts/editor/textResourceEditor.ts | 2 +- .../browser/parts/editor/titleControl.ts | 53 +- .../notifications/media/close-all-dark.svg | 4 + .../notifications/media/close-all-light.svg | 4 + .../parts/notifications/media/close-dark.svg | 3 + .../notifications/media/close-inverse.svg | 1 - .../parts/notifications/media/close-light.svg | 3 + .../parts/notifications/media/close.svg | 1 - .../notifications/media/closeall-inverse.svg | 1 - .../parts/notifications/media/closeall.svg | 1 - .../notifications/media/configure-dark.svg | 6 + .../notifications/media/configure-inverse.svg | 1 - .../notifications/media/configure-light.svg | 6 + .../parts/notifications/media/configure.svg | 1 - .../notifications/media/down-inverse.svg | 1 - .../parts/notifications/media/down.svg | 1 - .../parts/notifications/media/error-dark.svg | 3 + .../notifications/media/error-inverse.svg | 26 - .../parts/notifications/media/error-light.svg | 3 + .../parts/notifications/media/error.svg | 25 - .../parts/notifications/media/info-dark.svg | 3 + .../notifications/media/info-inverse.svg | 17 - .../parts/notifications/media/info-light.svg | 4 + .../parts/notifications/media/info.svg | 17 - .../media/notificationsActions.css | 24 +- .../notifications/media/notificationsList.css | 12 +- .../media/tree-collapsed-dark.svg | 3 + .../media/tree-collapsed-light.svg | 3 + .../media/tree-expanded-dark.svg | 3 + .../media/tree-expanded-light.svg | 3 + .../parts/notifications/media/up-inverse.svg | 1 - .../browser/parts/notifications/media/up.svg | 1 - .../notifications/media/warning-dark.svg | 3 + .../notifications/media/warning-inverse.svg | 15 - .../notifications/media/warning-light.svg | 4 + .../parts/notifications/media/warning.svg | 15 - .../notifications/notificationsActions.ts | 19 +- .../notifications/notificationsCenter.ts | 2 +- .../notifications/notificationsCommands.ts | 8 +- .../parts/notifications/notificationsList.ts | 2 +- .../notifications/notificationsStatus.ts | 137 +- .../notifications/notificationsToasts.ts | 34 +- .../notifications/notificationsViewer.ts | 48 +- .../parts/panel/media/chevron-down-dark.svg | 3 + .../parts/panel/media/chevron-down-hc.svg | 3 + .../parts/panel/media/chevron-down-light.svg | 3 + .../parts/panel/media/chevron-left-dark.svg | 3 + .../parts/panel/media/chevron-left-hc.svg | 3 + .../parts/panel/media/chevron-left-light.svg | 3 + .../parts/panel/media/chevron-right-dark.svg | 3 + .../parts/panel/media/chevron-right-hc.svg | 3 + .../parts/panel/media/chevron-right-light.svg | 3 + .../parts/panel/media/chevron-up-dark.svg | 3 + .../parts/panel/media/chevron-up-hc.svg | 3 + .../parts/panel/media/chevron-up-light.svg | 3 + .../browser/parts/panel/media/close-dark.svg | 4 + .../browser/parts/panel/media/close-hc.svg | 4 + .../parts/panel/media/close-inverse.svg | 1 - .../browser/parts/panel/media/close-light.svg | 4 + .../browser/parts/panel/media/close.svg | 1 - .../parts/panel/media/down-inverse.svg | 1 - .../browser/parts/panel/media/down.svg | 1 - .../parts/panel/media/ellipsis-dark.svg | 5 + .../browser/parts/panel/media/ellipsis-hc.svg | 5 + .../parts/panel/media/ellipsis-inverse.svg | 1 - .../parts/panel/media/ellipsis-light.svg | 5 + .../browser/parts/panel/media/ellipsis.svg | 1 - .../parts/panel/media/left-inverse.svg | 1 - .../browser/parts/panel/media/left.svg | 1 - .../browser/parts/panel/media/panelpart.css | 116 +- .../parts/panel/media/right-inverse.svg | 1 - .../browser/parts/panel/media/right.svg | 1 - .../browser/parts/panel/media/up-inverse.svg | 1 - .../browser/parts/panel/media/up.svg | 1 - .../browser/parts/panel/panelActions.ts | 55 +- .../browser/parts/panel/panelPart.ts | 39 +- .../arrow-left.svg => arrow-left-dark.svg} | 0 .../arrow-left.svg => arrow-left-light.svg} | 0 .../quickinput/{ => media}/quickInput.css | 0 .../browser/parts/quickinput/quickInput.ts | 303 +- .../browser/parts/quickinput/quickInputBox.ts | 15 +- .../parts/quickinput/quickInputList.ts | 13 +- .../parts/quickinput/quickInputUtils.ts | 8 +- .../parts/quickopen/media/dirty-dark.svg | 3 + .../parts/quickopen/media/dirty-inverse.svg | 1 - .../parts/quickopen/media/dirty-light.svg | 3 + .../browser/parts/quickopen/media/dirty.svg | 1 - .../parts/quickopen/media/quickopen.css | 4 +- .../parts/quickopen/quickOpenController.ts | 83 +- .../browser/parts/sidebar/sidebarPart.ts | 20 +- .../parts/statusbar/media/statusbarpart.css | 73 +- .../browser/parts/statusbar/statusbar.ts | 54 - .../browser/parts/statusbar/statusbarPart.ts | 864 +++-- .../parts/titlebar/media/titlebarpart.css | 3 +- .../browser/parts/titlebar/menubarControl.ts | 1085 +++--- .../browser/parts/titlebar/titlebarPart.ts | 104 +- .../browser/parts/views/customView.ts | 409 ++- .../parts/views/media/collapsed-dark.svg | 1 - .../parts/views/media/collapsed-hc.svg | 1 - .../browser/parts/views/media/collapsed.svg | 1 - .../parts/views/media/expanded-dark.svg | 1 - .../browser/parts/views/media/expanded-hc.svg | 1 - .../browser/parts/views/media/expanded.svg | 1 - .../parts/views/media/tree-collapsed-dark.svg | 3 + .../parts/views/media/tree-collapsed-hc.svg | 3 + .../views/media/tree-collapsed-light.svg | 3 + .../parts/views/media/tree-expanded-dark.svg | 3 + .../parts/views/media/tree-expanded-hc.svg | 3 + .../parts/views/media/tree-expanded-light.svg | 3 + .../browser/parts/views/media/views.css | 40 +- .../browser/parts/views/panelViewlet.ts | 29 +- .../browser/parts/views/viewsViewlet.ts | 22 +- src/vs/workbench/browser/style.ts | 2 + src/vs/workbench/browser/web.main.ts | 184 +- .../workbench/browser/web.simpleservices.ts | 975 ++---- .../browser/workbench.contribution.ts | 50 +- src/vs/workbench/browser/workbench.ts | 69 +- src/vs/workbench/buildfile.js | 4 +- src/vs/workbench/common/actions.ts | 10 +- src/vs/workbench/common/activity.ts | 33 +- src/vs/workbench/common/component.ts | 4 +- src/vs/workbench/common/composite.ts | 6 +- src/vs/workbench/common/editor.ts | 52 +- .../common/editor/dataUriEditorInput.ts | 4 +- .../common/editor/diffEditorInput.ts | 2 +- src/vs/workbench/common/editor/editorGroup.ts | 42 +- .../common/editor/resourceEditorInput.ts | 6 +- .../common/editor/textEditorModel.ts | 18 +- .../common/editor/untitledEditorInput.ts | 16 +- .../common/editor/untitledEditorModel.ts | 10 +- src/vs/workbench/common/memento.ts | 26 +- src/vs/workbench/common/notifications.ts | 111 +- src/vs/workbench/common/panel.ts | 1 + src/vs/workbench/common/resources.ts | 6 +- src/vs/workbench/common/theme.ts | 37 +- src/vs/workbench/common/views.ts | 16 +- .../backup/common/backupModelTracker.ts | 2 - .../browser/callHierarchyPeek.ts | 31 +- .../callHierarchy/common/callHierarchy.ts | 2 +- .../contrib/cli/node/cli.contribution.ts | 4 +- .../browser/accessibility/accessibility.ts | 7 +- .../codeEditor/browser/inspectKeybindings.ts | 25 + .../suggestEnabledInput.ts | 24 +- .../codeEditor/browser/toggleMinimap.ts | 26 +- .../browser/toggleRenderControlCharacter.ts | 26 +- .../browser/toggleRenderWhitespace.ts | 26 +- .../contrib/codeinset/common/codeInset.ts | 70 - .../codeInset.contribution.ts | 353 -- .../electron-browser/codeInsetWidget.ts | 193 - .../comments/browser/commentFormActions.ts | 34 +- .../contrib/comments/browser/commentMenus.ts | 10 +- .../contrib/comments/browser/commentNode.ts | 381 +- .../comments/browser/commentService.ts | 196 +- .../comments/browser/commentThreadWidget.ts | 474 +-- .../browser/commentsEditorContribution.ts | 243 +- .../contrib/comments/browser/commentsPanel.ts | 68 +- .../comments/browser/commentsTreeViewer.ts | 8 +- .../comments/browser/media/close-dark.svg | 3 + .../comments/browser/media/close-hc.svg | 3 + .../comments/browser/media/close-light.svg | 3 + .../comments/browser/media/comment.svg | 1 - .../comments/browser/media/delete-dark.svg | 3 + .../comments/browser/media/delete-hc.svg | 3 + .../comments/browser/media/delete-light.svg | 3 + .../comments/browser/media/edit-dark.svg | 4 + .../comments/browser/media/edit-hc.svg | 4 + .../comments/browser/media/edit-light.svg | 4 + .../comments/browser/media/reaction-dark.svg | 7 +- .../comments/browser/media/reaction-hc.svg | 5 +- .../comments/browser/media/reaction-light.svg | 3 + .../comments/browser/media/reaction.svg | 4 - .../contrib/comments/browser/media/review.css | 130 +- .../contrib/comments/common/commentModel.ts | 2 +- .../contrib/debug/browser/baseDebugView.ts | 2 +- .../contrib/debug/browser/breakpointWidget.ts | 17 +- .../contrib/debug/browser/breakpointsView.ts | 17 +- .../contrib/debug/browser/callStackView.ts | 31 +- .../debug.contribution.ts | 39 +- .../debug/browser/debugANSIHandling.ts | 116 +- .../debug/browser/debugActionViewItems.ts | 8 +- .../contrib/debug/browser/debugActions.ts | 10 +- .../contrib/debug/browser/debugCommands.ts | 94 +- .../debugConfigurationManager.ts | 45 +- .../debug/browser/debugEditorActions.ts | 12 +- .../debug/browser/debugEditorModelManager.ts | 9 +- .../debug/browser/debugHelperService.ts | 21 + .../contrib/debug/browser/debugHover.ts | 4 +- .../debugService.ts | 109 +- .../debugSession.ts | 61 +- .../contrib/debug/browser/debugStatus.ts | 127 +- .../contrib/debug/browser/debugToolBar.ts | 35 +- .../contrib/debug/browser/debugViewlet.ts | 25 +- .../contrib/debug/browser/exceptionWidget.ts | 6 +- .../contrib/debug/browser/linkDetector.ts | 2 +- .../debug/browser/loadedScriptsView.ts | 56 +- .../contrib/debug/browser/media/add-dark.svg | 3 + .../contrib/debug/browser/media/add-focus.svg | 1 - .../contrib/debug/browser/media/add-hc.svg | 3 + .../debug/browser/media/add-inverse.svg | 1 - .../contrib/debug/browser/media/add-light.svg | 3 + .../contrib/debug/browser/media/add.svg | 1 - .../media/breakpoint-conditional-disabled.svg | 4 +- .../breakpoint-conditional-unverified.svg | 4 +- .../browser/media/breakpoint-conditional.svg | 4 +- .../browser/media/breakpoint-disabled.svg | 4 +- .../media/breakpoint-function-disabled.svg | 4 +- .../media/breakpoint-function-unverified.svg | 4 +- .../browser/media/breakpoint-function.svg | 4 +- .../debug/browser/media/breakpoint-hint.svg | 6 +- .../browser/media/breakpoint-log-disabled.svg | 4 +- .../media/breakpoint-log-unverified.svg | 4 +- .../debug/browser/media/breakpoint-log.svg | 4 +- .../browser/media/breakpoint-unsupported.svg | 4 +- .../browser/media/breakpoint-unverified.svg | 4 +- .../debug/browser/media/breakpoint.svg | 4 +- .../debug/browser/media/breakpointWidget.css | 3 +- .../media/breakpoints-activate-inverse.svg | 1 - .../browser/media/breakpoints-activate.svg | 1 - .../debug/browser/media/clear-dark.svg | 7 + .../contrib/debug/browser/media/clear-hc.svg | 7 + .../debug/browser/media/clear-light.svg | 7 + .../browser/media/clear-repl-inverse.svg | 1 - .../debug/browser/media/clear-repl.svg | 1 - .../debug/browser/media/close-all-dark.svg | 4 + .../debug/browser/media/close-all-hc.svg | 4 + .../debug/browser/media/close-all-light.svg | 4 + .../debug/browser/media/configure-dark.svg | 6 + .../debug/browser/media/configure-hc.svg | 6 + .../debug/browser/media/configure-inverse.svg | 1 - .../debug/browser/media/configure-light.svg | 6 + .../contrib/debug/browser/media/configure.svg | 1 - .../debug/browser/media/console-dark.svg | 3 + .../debug/browser/media/console-hc.svg | 3 + .../debug/browser/media/console-light.svg | 3 + .../debug/browser/media/continue-dark.svg | 3 + .../debug/browser/media/continue-inverse.svg | 1 - .../debug/browser/media/continue-light.svg | 3 + .../media/continue-tb.png | Bin .../media/continue-without-debugging-tb.png | Bin .../contrib/debug/browser/media/continue.svg | 1 - .../browser/media/current-and-breakpoint.svg | 5 +- .../debug/browser/media/current-arrow.svg | 4 +- .../browser/media/debug-activity-bar.svg | 5 + .../debug/browser/media/debug-dark.svg | 1 - .../browser/media/debug.contribution.css | 22 +- .../debug/browser/media/debugHover.css | 5 +- .../debug/browser/media/debugToolBar.css | 4 +- .../debug/browser/media/debugViewlet.css | 72 +- .../debug/browser/media/disconnect-dark.svg | 3 + .../browser/media/disconnect-inverse.svg | 1 - .../debug/browser/media/disconnect-light.svg | 3 + .../debug/browser/media/disconnect.svg | 1 - .../contrib/debug/browser/media/drag.svg | 9 +- .../debug/browser/media/pause-dark.svg | 3 + .../debug/browser/media/pause-inverse.svg | 1 - .../debug/browser/media/pause-light.svg | 3 + .../media/pause-tb.png | Bin .../contrib/debug/browser/media/pause.svg | 1 - .../browser/media/remove-all-inverse.svg | 1 - .../debug/browser/media/remove-all.svg | 1 - .../debug/browser/media/repl-inverse.svg | 1 - .../contrib/debug/browser/media/repl.css | 67 +- .../contrib/debug/browser/media/repl.svg | 1 - .../debug/browser/media/restart-dark.svg | 3 + .../debug/browser/media/restart-inverse.svg | 1 - .../debug/browser/media/restart-light.svg | 3 + .../media/restart-tb.png | Bin .../contrib/debug/browser/media/restart.svg | 1 - .../browser/media/reverse-continue-dark.svg | 3 + .../browser/media/reverse-continue-light.svg | 3 + .../media/stackframe-and-breakpoint.svg | 5 +- .../debug/browser/media/stackframe-arrow.svg | 4 +- .../debug/browser/media/start-dark.svg | 3 + .../contrib/debug/browser/media/start-hc.svg | 3 + .../debug/browser/media/start-inverse.svg | 1 - .../debug/browser/media/start-light.svg | 3 + .../contrib/debug/browser/media/start.svg | 1 - .../debug/browser/media/step-back-dark.svg | 3 + .../debug/browser/media/step-back-inverse.svg | 11 - .../debug/browser/media/step-back-light.svg | 3 + .../contrib/debug/browser/media/step-back.svg | 11 - .../debug/browser/media/step-into-dark.svg | 3 + .../debug/browser/media/step-into-inverse.svg | 1 - .../debug/browser/media/step-into-light.svg | 3 + .../contrib/debug/browser/media/step-into.svg | 1 - .../debug/browser/media/step-out-dark.svg | 3 + .../debug/browser/media/step-out-inverse.svg | 1 - .../debug/browser/media/step-out-light.svg | 3 + .../contrib/debug/browser/media/step-out.svg | 1 - .../debug/browser/media/step-over-dark.svg | 3 + .../debug/browser/media/step-over-inverse.svg | 1 - .../debug/browser/media/step-over-light.svg | 3 + .../contrib/debug/browser/media/step-over.svg | 1 - .../media/stepinto-tb.png | Bin .../media/stepout-tb.png | Bin .../media/stepover-tb.png | Bin .../contrib/debug/browser/media/stop-dark.svg | 3 + .../debug/browser/media/stop-inverse.svg | 1 - .../debug/browser/media/stop-light.svg | 3 + .../media/stop-tb.png | Bin .../contrib/debug/browser/media/stop.svg | 1 - .../browser/media/toggle-breakpoints-dark.svg | 3 + .../browser/media/toggle-breakpoints-hc.svg | 3 + .../media/toggle-breakpoints-light.svg | 3 + .../rawDebugSession.ts | 248 +- .../workbench/contrib/debug/browser/repl.ts | 114 +- .../debug/browser/statusbarColorProvider.ts | 11 +- .../contrib/debug/browser/variablesView.ts | 61 +- .../debug/browser/watchExpressionsView.ts | 15 +- .../debug/common/abstractDebugAdapter.ts | 5 +- .../workbench/contrib/debug/common/debug.ts | 34 +- .../debug/common/debugContentProvider.ts | 2 +- .../contrib/debug/common/debugModel.ts | 95 +- .../contrib/debug/common/debugProtocol.d.ts | 108 +- .../contrib/debug/common/debugSchemas.ts | 6 +- .../contrib/debug/common/debugSource.ts | 19 +- .../contrib/debug/common/debugViewModel.ts | 5 +- .../debug/{node => common}/debugger.ts | 26 +- .../debug/electron-browser/terminalSupport.ts | 73 - .../contrib/debug/node/debugHelperService.ts | 44 + .../workbench/contrib/debug/node/terminals.ts | 289 +- .../test/browser/debugANSIHandling.test.ts | 22 + .../debugModel.test.ts | 33 +- .../contrib/debug/test/common/mockDebug.ts | 11 +- .../contrib/debug/test/node/debugger.test.ts | 6 +- .../experiments/common/experimentService.ts | 59 + .../experimentService.ts | 76 +- .../electron-browser/experimentalPrompt.ts | 13 +- .../experiments.contribution.ts | 3 +- .../experimentService.test.ts | 6 +- .../experimentalPrompts.test.ts | 15 +- .../extensionEditor.ts | 120 +- .../extensionsActions.ts | 270 +- .../extensionsActivationProgress.ts | 4 +- .../extensionsDependencyChecker.ts | 0 .../extensionsList.ts | 12 +- .../extensions/browser/extensionsViewer.ts | 6 +- .../extensionsViewlet.ts | 65 +- .../extensionsViews.ts | 150 +- .../extensionsWidgets.ts | 30 +- .../extensions/browser/media/clear-dark.svg | 7 + .../extensions/browser/media/clear-hc.svg | 7 + .../media/clear-inverse.svg | 0 .../extensions/browser/media/clear-light.svg | 7 + .../browser/media/configure-dark.svg | 6 + .../extensions/browser/media/configure-hc.svg | 6 + .../browser/media/configure-light.svg | 6 + .../media/defaultIcon.png | Bin .../media/extensionActions.css | 34 +- .../media/extensionEditor.css | 2 +- .../browser/media/extensions-activity-bar.svg | 3 + .../media/extensions.css | 2 +- .../media/extensionsViewlet.css | 10 +- .../media/extensionsWidgets.css | 7 +- .../media/language-icon.svg | 0 .../media/loading.svg | 0 .../media/markdown.css | 0 .../browser/media/profile-start-dark.svg | 10 + .../browser/media/profile-start-light.svg | 10 + .../browser/media/profile-stop-dark.svg | 10 + .../browser/media/profile-stop-light.svg | 10 + .../extensions/browser/media/save-dark.svg | 3 + .../extensions/browser/media/save-light.svg | 3 + .../extensions/browser/media/star-empty.svg | 5 + .../extensions/browser/media/star-full.svg | 3 + .../extensions/browser/media/star-half.svg | 3 + .../extensions/browser/media/star-small.svg | 10 + .../extensions/browser/media/start-dark.svg | 3 + .../extensions/browser/media/start-light.svg | 3 + .../media/theme-icon.png | Bin .../extensions/common/extensionQuery.ts | 4 +- .../contrib/extensions/common/extensions.ts | 2 +- .../extensions/common/extensionsUtils.ts | 23 +- .../extensionProfileService.ts | 134 +- .../electron-browser/extensionTipsService.ts | 422 +-- .../extensions.contribution.ts | 86 +- .../electron-browser/extensionsSlowActions.ts | 3 +- .../electron-browser/media/EmptyStar.svg | 92 - .../electron-browser/media/FullStarLight.svg | 92 - .../electron-browser/media/HalfStarLight.svg | 91 - .../electron-browser/media/clear.svg | 1 - .../media/extensions-dark.svg | 1 - .../electron-browser/media/manage-inverse.svg | 1 - .../electron-browser/media/manage.svg | 1 - .../media/profile-start-inverse.svg | 1 - .../electron-browser/media/profile-start.svg | 1 - .../media/profile-stop-inverse.svg | 1 - .../electron-browser/media/profile-stop.svg | 1 - .../media/runtimeExtensionsEditor.css | 17 - .../electron-browser/media/save-inverse.svg | 1 - .../electron-browser/media/save.svg | 1 - .../electron-browser/media/start-inverse.svg | 1 - .../electron-browser/media/start.svg | 1 - .../media/status-info-inverse.svg | 1 - .../electron-browser/media/status-info.svg | 1 - .../media/status-warning-inverse.svg | 1 - .../electron-browser/media/status-warning.svg | 1 - .../node/extensionsWorkbenchService.ts | 148 +- .../extensionsActions.test.ts | 7 +- .../extensionsTipsService.test.ts | 14 +- .../electron-browser/extensionsViews.test.ts | 12 +- .../extensionsWorkbenchService.test.ts | 14 +- .../externalTerminal.contribution.ts | 151 +- .../common/externalTerminal.ts | 15 +- .../electron-browser/externalTerminal.ts | 47 - .../TerminalHelper.scpt | Bin .../externalTerminalService.ts | 210 +- .../iTermHelper.scpt | Bin .../externalTerminalService.test.ts | 7 +- .../feedback/browser/feedback.contribution.ts | 11 + .../{electron-browser => browser}/feedback.ts | 66 +- .../feedback/browser/feedbackStatusbarItem.ts | 95 + .../media/close-dark.svg | 0 .../contrib/feedback/browser}/media/close.svg | 0 .../media/feedback.css | 12 +- .../media/happy.svg | 0 .../contrib/feedback/browser}/media/info.svg | 0 .../media/sad.svg | 0 .../media/smiley.svg | 0 .../media/twitter.svg | 0 .../electron-browser/feedback.contribution.ts | 35 - .../electron-browser/feedbackStatusbarItem.ts | 172 - .../feedback/electron-browser/media/close.svg | 1 - .../feedback/electron-browser/media/info.svg | 1 - .../files/browser/editors/binaryFileEditor.ts | 9 +- .../browser/editors/fileEditorTracker.ts | 7 +- .../files/browser/editors/textFileEditor.ts | 13 +- .../contrib/files/browser/explorerViewlet.ts | 20 +- .../files/browser/fileActions.contribution.ts | 172 +- .../contrib/files/browser/fileActions.ts | 242 +- .../contrib/files/browser/fileCommands.ts | 18 +- .../files/browser/files.contribution.ts | 39 +- .../contrib/files/browser/media/AddFile.svg | 1 - .../files/browser/media/AddFile_inverse.svg | 3 - .../contrib/files/browser/media/AddFolder.svg | 1 - .../files/browser/media/AddFolder_inverse.svg | 3 - .../files/browser/media/CollapseAll.svg | 1 - .../browser/media/CollapseAll_inverse.svg | 1 - .../contrib/files/browser/media/Preview.svg | 1 - .../files/browser/media/Preview_inverse.svg | 1 - .../contrib/files/browser/media/Refresh.svg | 1 - .../files/browser/media/Refresh_inverse.svg | 1 - .../files/browser/media/action-close-dark.svg | 4 +- .../browser/media/action-close-light.svg | 3 + .../files/browser/media/add-file-dark.svg | 3 + .../files/browser/media/add-file-hc.svg | 3 + .../files/browser/media/add-file-light.svg | 3 + .../files/browser/media/add-folder-dark.svg | 3 + .../files/browser/media/add-folder-hc.svg | 3 + .../files/browser/media/add-folder-light.svg | 3 + .../files/browser/media/check-dark.svg | 3 + .../files/browser/media/check-inverse.svg | 1 - .../files/browser/media/check-light.svg | 3 + .../contrib/files/browser/media/check.svg | 1 - .../files/browser/media/close-all-dark.svg | 4 + .../files/browser/media/close-all-hc.svg | 4 + .../files/browser/media/close-all-light.svg | 4 + .../contrib/files/browser/media/closeall.svg | 1 - .../files/browser/media/closeall_inverse.svg | 1 - .../files/browser/media/collapse-all-dark.svg | 4 + .../files/browser/media/collapse-all-hc.svg | 4 + .../browser/media/collapse-all-light.svg | 4 + .../files/browser/media/explorerviewlet.css | 2 +- .../files/browser/media/fileactions.css | 94 +- .../browser/media/files-activity-bar.svg | 3 + .../files/browser/media/files-dark.svg | 1 - .../files/browser/media/preview-dark.svg | 4 + .../files/browser/media/preview-light.svg | 4 + .../files/browser/media/refresh-dark.svg | 4 + .../files/browser/media/refresh-hc.svg | 4 + .../files/browser/media/refresh-light.svg | 4 + .../files/browser/media/save-all-dark.svg | 3 + .../files/browser/media/save-all-hc.svg | 3 + .../files/browser/media/save-all-light.svg | 3 + .../files/browser/media/saveall_inverse.svg | 1 - .../media/split-editor-horizontal-dark.svg | 3 + .../media/split-editor-horizontal-hc.svg | 3 + .../media/split-editor-horizontal-inverse.svg | 1 - .../media/split-editor-horizontal-light.svg | 7 + .../browser/media/split-editor-horizontal.svg | 1 - .../media/split-editor-vertical-dark.svg | 3 + .../media/split-editor-vertical-hc.svg | 3 + .../media/split-editor-vertical-inverse.svg | 1 - .../media/split-editor-vertical-light.svg | 7 + .../browser/media/split-editor-vertical.svg | 1 - .../contrib/files/browser/media/undo-dark.svg | 3 + .../files/browser/media/undo-inverse.svg | 1 - .../files/browser/media/undo-light.svg | 3 + .../contrib/files/browser/media/undo.svg | 1 - .../contrib/files/browser/saveErrorHandler.ts | 36 +- .../contrib/files/browser/views/emptyView.ts | 27 +- .../views/explorerDecorationsProvider.ts | 14 +- .../files/browser/views/explorerView.ts | 115 +- .../files/browser/views/explorerViewer.ts | 124 +- .../files/browser/views/openEditorsView.ts | 67 +- .../contrib/files/common/dirtyFilesTracker.ts | 8 +- .../files/common/editors/fileEditorInput.ts | 28 +- .../contrib/files/common/explorerModel.ts | 29 +- .../contrib/files/common/explorerService.ts | 21 +- .../workbench/contrib/files/common/files.ts | 26 +- .../test/electron-browser/fileActions.test.ts | 218 +- .../format/browser/formatActionsMultiple.ts | 4 +- .../electron-browser/issue.contribution.ts | 6 +- .../browser/localizations.contribution.ts | 9 +- .../browser/localizationsActions.ts | 5 +- .../browser/minimalTranslations.ts | 5 +- .../contrib/logs/common/logs.contribution.ts | 48 +- .../electron-browser/logs.contribution.ts | 74 + .../markers/browser/markers.contribution.ts | 113 +- .../contrib/markers/browser/markers.ts | 6 +- .../contrib/markers/browser/markersModel.ts | 59 +- .../contrib/markers/browser/markersPanel.ts | 23 +- .../markers/browser/markersPanelActions.ts | 20 +- .../markers/browser/markersTreeViewer.ts | 50 +- .../browser/media/exclude-settings-dark.svg | 3 + .../browser/media/exclude-settings-hc.svg | 3 + .../browser/media/exclude-settings-light.svg | 3 + .../browser/media/excludeSettings-dark.svg | 1 - .../markers/browser/media/excludeSettings.svg | 1 - .../browser/media/lightbulb-autofix-dark.svg | 10 +- .../browser/media/lightbulb-autofix-hc.svg | 4 + .../browser/media/lightbulb-autofix-light.svg | 4 + .../browser/media/lightbulb-autofix.svg | 10 - .../markers/browser/media/lightbulb-dark.svg | 4 +- .../markers/browser/media/lightbulb-hc.svg | 3 + .../markers/browser/media/lightbulb-light.svg | 4 + .../markers/browser/media/lightbulb.svg | 1 - .../contrib/markers/browser/media/markers.css | 50 +- .../browser/media/status-error-inverse.svg | 1 - .../markers/browser/media/status-error.svg | 1 - .../browser/media/status-info-inverse.svg | 1 - .../markers/browser/media/status-info.svg | 1 - .../browser/media/status-warning-inverse.svg | 1 - .../markers/browser/media/status-warning.svg | 1 - .../contrib/markers/browser/messages.ts | 2 +- .../electron-browser/markersModel.test.ts | 29 +- .../contrib/outline/browser/outlinePanel.ts | 64 +- .../output/browser/media/clear-dark.svg | 7 + .../contrib/output/browser/media/clear-hc.svg | 7 + .../output/browser/media/clear-light.svg | 7 + .../output/browser/media/clear_output.svg | 1 - .../browser/media/clear_output_inverse.svg | 1 - .../output/browser/media/locked-dark.svg | 3 + .../output/browser/media/locked-hc.svg | 3 + .../output/browser/media/locked-light.svg | 3 + .../output/browser/media/open-file-dark.svg | 3 + .../output/browser/media/open-file-hc.svg | 3 + .../output/browser/media/open-file-light.svg | 3 + .../output/browser/media/open_log_file.svg | 1 - .../browser/media/open_log_file_inverse.svg | 1 - .../contrib/output/browser/media/output.css | 38 +- .../output/browser/media/output_lock.svg | 1 - .../browser/media/output_lock_inverse.svg | 1 - .../output/browser/media/output_unlock.svg | 1 - .../browser/media/output_unlock_inverse.svg | 1 - .../output/browser/media/unlocked-dark.svg | 3 + .../output/browser/media/unlocked-hc.svg | 3 + .../output/browser/media/unlocked-light.svg | 3 + .../contrib/output/browser/outputActions.ts | 19 +- .../contrib/output/browser/outputPanel.ts | 2 +- .../contrib/output/browser/outputServices.ts | 19 +- .../electron-browser/perfviewEditor.ts | 71 +- .../electron-browser/startupProfiler.ts | 18 +- .../preferences/browser/keybindingWidgets.ts | 9 +- .../preferences/browser/keybindingsEditor.ts | 32 +- .../browser/keybindingsEditorContribution.ts | 11 +- .../browser/keyboardLayoutPicker.ts | 181 + .../browser/media/action-remove-dark.svg | 1 - .../browser/media/action-remove.svg | 1 - .../preferences/browser/media/add-dark.svg | 3 + .../preferences/browser/media/add-light.svg | 3 + .../preferences/browser/media/check-dark.svg | 3 + .../preferences/browser/media/check-light.svg | 3 + .../preferences/browser/media/clear-dark.svg | 7 + .../browser/media/clear-inverse.svg | 1 - .../preferences/browser/media/clear-light.svg | 7 + .../preferences/browser/media/clear.svg | 1 - .../preferences/browser/media/collapseAll.svg | 1 - .../browser/media/collapseAll_inverse.svg | 1 - .../browser/media/configure-dark.svg | 6 + .../browser/media/configure-light.svg | 6 + .../preferences/browser/media/edit-dark.svg | 4 + .../browser/media/edit-json-dark.svg | 3 + .../browser/media/edit-json-light.svg | 3 + .../preferences/browser/media/edit-light.svg | 4 + .../preferences/browser/media/info.svg | 1 - .../preferences/browser/media/keybindings.css | 2 - .../browser/media/keybindingsEditor.css | 20 +- .../browser/media/preferences-editor-dark.svg | 3 + .../media/preferences-editor-light.svg | 3 + .../preferences/browser/media/preferences.css | 34 +- .../browser/media/record-keys-dark.svg | 3 + .../browser/media/record-keys-inverse.svg | 3 - .../browser/media/record-keys-light.svg | 3 + .../preferences/browser/media/record-keys.svg | 3 - .../preferences/browser/media/remove-dark.svg | 3 + .../browser/media/remove-light.svg | 3 + .../media/settingsEditor2.css | 10 +- .../browser/media/settingsWidgets.css | 56 +- .../browser/media/sort-precedence-dark.svg | 3 + .../browser/media/sort-precedence-light.svg | 3 + .../browser/media/sort_precedence.svg | 1 - .../browser/media/sort_precedence_inverse.svg | 1 - .../browser/media/status-error.svg | 1 - .../browser/media/tree-collapsed-dark.svg | 3 + .../browser/media/tree-collapsed-light.svg | 3 + .../browser/media/tree-expanded-dark.svg | 3 + .../browser/media/tree-expanded-light.svg | 3 + .../preferences.contribution.ts | 110 +- .../preferences/browser/preferencesActions.ts | 8 +- .../preferences/browser/preferencesEditor.ts | 21 +- .../browser/preferencesRenderers.ts | 34 +- .../preferencesSearch.ts | 28 +- .../preferences/browser/preferencesWidgets.ts | 23 +- .../settingsEditor2.ts | 22 +- .../preferences/browser/settingsTree.ts | 205 +- .../preferences/browser/settingsTreeModels.ts | 15 +- .../preferences/browser/settingsWidgets.ts | 213 +- .../contrib/preferences/browser/tocTree.ts | 18 +- .../contrib/preferences/common/preferences.ts | 6 +- .../common/preferencesContribution.ts | 11 +- .../electron-browser/media/check-inverse.svg | 1 - .../electron-browser/media/check.svg | 1 - .../media/configure-inverse.svg | 1 - .../electron-browser/media/configure.svg | 1 - .../media/edit-json-inverse.svg | 3 - .../electron-browser/media/edit-json.svg | 3 - .../media/preferences-editor-inverse.svg | 4 - .../media/preferences-editor.svg | 4 - .../quickopen/browser/commandsHandler.ts | 76 +- .../quickopen/browser/gotoSymbolHandler.ts | 10 +- .../browser/quickopen.contribution.ts | 9 + .../quickopen/browser/viewPickerHandler.ts | 3 +- .../relauncher.contribution.ts | 12 +- .../remote/common/remote.contribution.ts | 81 + .../electron-browser/remote.contribution.ts | 108 +- .../browser/resourceServiceWorker.ts | 106 + .../browser/resourceServiceWorkerClient.ts | 105 + .../browser/resourceServiceWorkerMain.ts | 26 + .../contrib/scm/browser/dirtydiffDecorator.ts | 110 +- .../contrib/scm/browser/media/icon-dark.svg | 1 - .../contrib/scm/browser/media/icon-light.svg | 12 - .../scm/browser/media/scm-activity-bar.svg | 3 + .../contrib/scm/browser/media/scmViewlet.css | 3 +- .../contrib/scm/browser/scmActivity.ts | 31 +- .../workbench/contrib/scm/browser/scmMenus.ts | 13 +- .../contrib/scm/browser/scmViewlet.ts | 133 +- .../contrib/scm/common/scmService.ts | 12 +- .../search/browser/media/CollapseAll.svg | 1 - .../browser/media/CollapseAll_inverse.svg | 1 - .../contrib/search/browser/media/Refresh.svg | 1 - .../search/browser/media/Refresh_inverse.svg | 1 - .../browser/media/action-remove-dark.svg | 1 - .../search/browser/media/action-remove.svg | 1 - .../search/browser/media/clear-dark.svg | 7 + .../contrib/search/browser/media/clear-hc.svg | 7 + .../search/browser/media/clear-light.svg | 7 + .../media/clear-search-results-dark.svg | 1 - .../browser/media/clear-search-results.svg | 1 - .../browser/media/collapse-all-dark.svg | 4 + .../search/browser/media/collapse-all-hc.svg | 4 + .../browser/media/collapse-all-light.svg | 4 + .../search/browser/media/ellipsis-dark.svg | 5 + .../search/browser/media/ellipsis-hc.svg | 5 + .../search/browser/media/ellipsis-inverse.svg | 1 - .../search/browser/media/ellipsis-light.svg | 5 + .../contrib/search/browser/media/ellipsis.svg | 1 - .../browser/media/exclude-settings-dark.svg | 3 + .../browser/media/exclude-settings-hc.svg | 3 + .../browser/media/exclude-settings-light.svg | 3 + .../browser/media/excludeSettings-dark.svg | 1 - .../search/browser/media/excludeSettings.svg | 1 - .../browser/media/expando-collapsed-dark.svg | 1 - .../browser/media/expando-collapsed.svg | 1 - .../browser/media/expando-expanded-dark.svg | 1 - .../search/browser/media/expando-expanded.svg | 1 - .../search/browser/media/refresh-dark.svg | 4 + .../search/browser/media/refresh-hc.svg | 4 + .../search/browser/media/refresh-light.svg | 4 + .../search/browser/media/remove-dark.svg | 3 + .../search/browser/media/remove-hc.svg | 3 + .../search/browser/media/remove-light.svg | 3 + .../search/browser/media/replace-all-dark.svg | 3 + .../search/browser/media/replace-all-hc.svg | 3 + .../browser/media/replace-all-inverse.svg | 11 - .../browser/media/replace-all-light.svg | 3 + .../search/browser/media/replace-all.svg | 11 - .../search/browser/media/replace-dark.svg | 3 + .../search/browser/media/replace-hc.svg | 3 + .../search/browser/media/replace-inverse.svg | 13 - .../search/browser/media/replace-light.svg | 3 + .../contrib/search/browser/media/replace.svg | 13 - .../browser/media/search-activity-bar.svg | 3 + .../search/browser/media/search-dark.svg | 1 - .../browser/media/search.contribution.css | 2 +- .../search/browser/media/searchview.css | 114 +- .../search/browser/media/stop-dark.svg | 3 + .../contrib/search/browser/media/stop-hc.svg | 3 + .../search/browser/media/stop-inverse.svg | 3 - .../search/browser/media/stop-light.svg | 3 + .../contrib/search/browser/media/stop.svg | 3 - .../browser/media/tree-collapsed-dark.svg | 3 + .../browser/media/tree-collapsed-hc.svg | 3 + .../browser/media/tree-collapsed-light.svg | 3 + .../browser/media/tree-expanded-dark.svg | 3 + .../search/browser/media/tree-expanded-hc.svg | 3 + .../browser/media/tree-expanded-light.svg | 3 + .../contrib/search/browser/openFileHandler.ts | 5 +- .../search/browser/openSymbolHandler.ts | 43 +- .../contrib/search/browser/replaceService.ts | 10 +- .../search/browser/search.contribution.ts | 5 +- .../contrib/search/browser/searchActions.ts | 20 +- .../contrib/search/browser/searchView.ts | 145 +- .../contrib/search/common/queryBuilder.ts | 2 +- .../contrib/search/common/replace.ts | 4 +- .../workbench/contrib/search/common/search.ts | 19 +- .../contrib/search/common/searchModel.ts | 29 +- .../snippets/browser/configureSnippets.ts | 29 +- .../contrib/snippets/browser/insertSnippet.ts | 10 +- .../browser/snippetCompletionProvider.ts | 24 +- .../snippets/browser/snippets.contribution.ts | 6 +- .../contrib/snippets/browser/snippetsFile.ts | 123 +- .../snippets/browser/snippetsService.ts | 46 +- .../contrib/snippets/browser/tabCompletion.ts | 4 +- .../snippets/test/browser/snippetFile.test.ts | 15 + .../test/browser/snippetsRewrite.test.ts | 8 +- .../partsSplash.contribution.ts | 25 +- .../stats.contribution.ts | 2 +- .../workspaceStats.ts | 9 +- .../contrib/stats/test/workspaceStats.test.ts | 2 +- .../languageSurveys.contribution.ts | 3 +- .../abstractTaskService.ts} | 718 +--- .../contrib/tasks/browser/quickOpen.ts | 2 +- .../runAutomaticTasks.ts | 0 .../tasks/browser/task.contribution.ts | 297 ++ .../contrib/tasks/browser/taskService.ts | 51 + .../terminalTaskSystem.ts | 367 +- .../contrib/tasks/common/jsonSchema_v2.ts | 17 +- .../tasks/common/media/configure-dark.svg | 6 + .../tasks/common/media/configure-inverse.svg | 1 - .../tasks/common/media/configure-light.svg | 6 + .../contrib/tasks/common/media/configure.svg | 1 - .../tasks/common/media/status-error.svg | 1 - .../tasks/common/media/status-info.svg | 1 - .../tasks/common/media/status-warning.svg | 1 - .../tasks/common/media/task.contribution.css | 78 +- .../contrib/tasks/common/media/task.svg | 1 - .../contrib/tasks/common/problemCollectors.ts | 66 +- .../contrib/tasks/common/problemMatcher.ts | 65 +- .../contrib/tasks/common/taskConfiguration.ts | 62 +- .../contrib/tasks/common/taskService.ts | 3 +- .../contrib/tasks/common/taskSystem.ts | 2 +- .../workbench/contrib/tasks/common/tasks.ts | 30 +- .../tasks/electron-browser/taskService.ts | 135 + .../tasks/node/processRunnerDetector.ts | 2 +- .../contrib/tasks/node/processTaskSystem.ts | 2 +- .../terminal/browser/media/configure-dark.svg | 6 + .../terminal/browser/media/configure-hc.svg | 6 + .../browser/media/configure-inverse.svg | 1 - .../browser/media/configure-light.svg | 6 + .../terminal/browser/media/configure.svg | 1 - .../terminal/browser/media/kill-dark.svg | 3 + .../terminal/browser/media/kill-hc.svg | 3 + .../terminal/browser/media/kill-inverse.svg | 10 - .../terminal/browser/media/kill-light.svg | 3 + .../contrib/terminal/browser/media/kill.svg | 10 - .../terminal/browser/media/new-dark.svg | 3 + .../contrib/terminal/browser/media/new-hc.svg | 3 + .../terminal/browser/media/new-inverse.svg | 1 - .../terminal/browser/media/new-light.svg | 3 + .../contrib/terminal/browser/media/new.svg | 1 - .../media/split-editor-horizontal-dark.svg | 3 + .../media/split-editor-horizontal-hc.svg | 3 + .../media/split-editor-horizontal-light.svg | 3 + .../media/split-editor-vertical-dark.svg | 3 + .../media/split-editor-vertical-hc.svg | 3 + .../media/split-editor-vertical-light.svg | 3 + .../media/split-horizontal-inverse.svg | 1 - .../browser/media/split-horizontal.svg | 1 - .../terminal/browser/media/split-inverse.svg | 1 - .../contrib/terminal/browser/media/split.svg | 1 - .../terminal/browser/media/terminal.css | 36 +- .../contrib/terminal/browser/media/xterm.css | 34 +- .../terminal/browser/terminal.contribution.ts | 52 +- .../contrib/terminal/browser/terminal.ts | 22 +- .../terminal/browser/terminalActions.ts | 31 +- .../browser/terminalCommandTracker.ts | 4 +- .../terminal/browser/terminalConfigHelper.ts | 23 +- .../terminal/browser/terminalFindWidget.ts | 3 +- .../terminal/browser/terminalInstance.ts | 186 +- .../browser/terminalInstanceService.ts | 62 + .../terminal/browser/terminalLinkHandler.ts | 82 +- .../terminal/browser/terminalNativeService.ts | 33 + .../contrib/terminal/browser/terminalPanel.ts | 8 +- .../browser/terminalProcessManager.ts | 108 +- .../terminal/browser/terminalService.ts | 50 +- .../contrib/terminal/browser/terminalTab.ts | 72 +- .../browser/terminalTypeAheadAddon.ts | 136 - .../terminal/browser/terminalWidgetManager.ts | 11 +- .../contrib/terminal/common/terminal.ts | 111 +- .../terminal/common/terminalColorRegistry.ts | 4 +- .../terminal/common/terminalCommands.ts | 1 + .../terminal/common/terminalEnvironment.ts | 48 +- .../common/terminalProcessExtHostProxy.ts | 86 +- .../terminal/common/terminalService.ts | 126 +- .../terminal/common/terminalShellConfig.ts | 45 + .../electron-browser/terminal.contribution.ts | 39 +- .../terminalInstanceService.ts | 82 +- .../electron-browser/terminalNativeService.ts | 81 + .../electron-browser/terminalService.ts | 203 -- .../contrib/terminal/node/terminal.ts | 103 +- .../terminal/node/terminalEnvironment.ts | 132 + .../contrib/terminal/node/terminalProcess.ts | 74 +- .../contrib/terminal/node/terminalRemote.ts | 49 + .../terminal/node/windowsShellHelper.ts | 2 +- .../terminalCommandTracker.test.ts | 2 +- .../terminalLinkHandler.test.ts | 60 +- .../test/node/terminalEnvironment.test.ts | 4 +- .../themes/browser/themes.contribution.ts | 22 +- .../themes.test.contribution.ts | 2 +- .../contrib/update/common/update.ts} | 2 +- .../update/electron-browser/media/update.svg | 1 - .../electron-browser/releaseNotesEditor.ts | 17 +- .../electron-browser/update.contribution.ts | 14 +- .../contrib/update/electron-browser/update.ts | 232 +- .../contrib/url/common/url.contribution.ts | 2 +- .../contrib/watermark/browser/watermark.ts | 18 +- .../contrib/webview/browser/pre/fake.html | 0 .../contrib/webview/browser/pre/host.js | 99 + .../contrib/webview/browser/pre/index.html | 19 + .../contrib/webview/browser/pre/main.js | 710 ++-- .../webview/browser/pre/service-worker.js | 276 ++ .../webview/browser/webview.contribution.ts | 78 +- .../webview/browser/webviewCommands.ts | 90 - .../contrib/webview/browser/webviewEditor.ts | 84 +- .../webview/browser/webviewEditorInput.ts | 56 +- .../browser/webviewEditorInputFactory.ts | 3 +- .../webview/browser/webviewEditorService.ts | 11 +- .../contrib/webview/browser/webviewElement.ts | 323 ++ .../webview/browser/webviewFindWidget.ts | 1 + .../contrib/webview/browser/webviewService.ts | 27 + .../contrib/webview/common/mimeTypes.ts | 26 + .../contrib/webview/common/portMapping.ts | 87 + .../contrib/webview/common/resourceLoader.ts | 80 + .../contrib/webview/common/themeing.ts | 63 + .../contrib/webview/common/webview.ts | 22 +- .../electron-browser/pre/electron-index.js | 50 +- .../electron-browser/webview.contribution.ts | 84 +- .../electron-browser/webviewCommands.ts | 98 + .../electron-browser/webviewElement.ts | 242 +- .../electron-browser/webviewProtocols.ts | 86 +- .../electron-browser/webviewService.ts | 5 +- .../electron-browser/telemetryOptOut.ts | 6 +- .../welcome/overlay/browser/welcomeOverlay.ts | 14 +- .../welcome/page/browser/welcomePage.ts | 29 +- .../walkThrough/browser/walkThroughPart.ts | 18 +- .../common/walkThroughContentProvider.ts | 2 +- .../walkThrough/common/walkThroughInput.ts | 6 +- .../actions/developerActions.ts | 183 - .../actions/media/remove-dark.svg | 1 - .../electron-browser/actions/media/remove.svg | 1 - .../electron-browser/actions/windowActions.ts | 189 +- .../electron-browser/main.contribution.ts | 183 +- src/vs/workbench/electron-browser/main.ts | 97 +- src/vs/workbench/electron-browser/window.ts | 52 +- .../services/backup/common/backup.ts | 16 +- .../backup/common/backupFileService.ts | 443 +++ .../services/backup/node/backupFileService.ts | 406 +-- .../test/node/backupFileService.test.ts | 62 +- .../services/broadcast/common/broadcast.ts | 30 - .../electron-browser/broadcastService.ts | 51 - .../bulkEdit/browser/bulkEditService.ts | 15 +- .../test/common/commandService.test.ts | 16 +- .../configuration/browser/configuration.ts | 131 +- .../browser/configurationCache.ts | 22 + .../browser/configurationService.ts | 54 +- .../configuration/common/configuration.ts | 20 +- .../common/configurationEditingService.ts | 2 +- .../common/jsonEditingService.ts | 35 +- .../configuration/node/configurationCache.ts | 3 +- .../node/configurationFileService.ts | 79 - .../configurationEditingService.test.ts | 56 +- .../configurationService.test.ts | 40 +- .../browser/configurationResolverService.ts | 7 +- .../common/variableResolver.ts | 2 +- .../electron-browser/contextmenuService.ts | 14 +- .../decorations/browser/decorationsService.ts | 35 +- .../dialogs/browser/fileDialogService.ts | 125 +- .../dialogs/browser/remoteFileDialog.ts | 252 +- .../services/editor/browser/editorService.ts | 47 +- .../editor/common/editorGroupsService.ts | 6 +- .../editor/test/browser/editorService.test.ts | 5 +- .../environment/browser/environmentService.ts | 154 + .../environment/common/environmentService.ts | 5 +- .../environment/node/environmentService.ts | 8 + .../node/extensionEnablementService.ts | 6 +- .../extensionEnablementService.test.ts | 5 +- .../extensions/browser/extensionService.ts | 101 + .../common/abstractExtensionService.ts | 498 +++ .../common/extensionHostProcessManager.ts | 8 +- .../common/extensionHostProtocol.ts | 1 + .../extensions/common/extensionPoints.ts | 63 + .../services/extensions/common/extensions.ts | 3 +- .../extensions/common/extensionsRegistry.ts | 15 +- .../{node => common}/extensionsUtil.ts | 6 +- .../common/inactiveExtensionUrlHandler.ts | 6 +- .../remoteExtensionHostClient.ts | 60 +- .../cachedExtensionScanner.ts | 26 +- .../electron-browser/extensionHost.ts | 82 +- .../extensionManagementServerService.ts | 14 +- .../electron-browser/extensionService.ts | 668 +--- .../remoteExtensionManagementIpc.ts | 143 + .../extensions/node/extensionHostMain.ts | 8 +- .../extensions/node/extensionHostProcess.ts | 7 +- .../node/extensionHostProcessSetup.ts | 99 +- .../extensions/node/extensionPoints.ts | 41 +- .../node/multiExtensionManagement.ts | 86 +- .../services/extensions/node/proxyResolver.ts | 78 +- .../services/files/common/workspaceWatcher.ts | 2 +- src/vs/workbench/services/heap/common/heap.ts | 33 - src/vs/workbench/services/heap/node/heap.ts | 80 - .../services/history/browser/history.ts | 65 +- .../keybindingService.ts | 413 +-- .../browser/keyboardLayouts/_.contribution.ts | 23 + .../browser/keyboardLayouts/cz.win.ts | 168 + .../browser/keyboardLayouts/de-swiss.win.ts | 169 + .../browser/keyboardLayouts/de.darwin.ts | 132 + .../browser/keyboardLayouts/de.linux.ts | 187 + .../browser/keyboardLayouts/de.win.ts | 169 + .../browser/keyboardLayouts/dk.win.ts | 170 + .../browser/keyboardLayouts/en-belgian.win.ts | 169 + .../browser/keyboardLayouts/en-ext.darwin.ts | 132 + .../browser/keyboardLayouts/en-in.win.ts | 169 + .../browser/keyboardLayouts/en-intl.darwin.ts | 132 + .../browser/keyboardLayouts/en-intl.win.ts | 169 + .../browser/keyboardLayouts/en-uk.darwin.ts | 131 + .../browser/keyboardLayouts/en-uk.win.ts | 170 + .../browser/keyboardLayouts/en.darwin.ts | 140 + .../browser/keyboardLayouts/en.linux.ts | 190 + .../browser/keyboardLayouts/en.win.ts | 174 + .../browser/keyboardLayouts/es-latin.win.ts | 170 + .../browser/keyboardLayouts/es.darwin.ts | 132 + .../browser/keyboardLayouts/es.linux.ts | 187 + .../browser/keyboardLayouts/es.win.ts | 169 + .../browser/keyboardLayouts/fr.darwin.ts | 132 + .../browser/keyboardLayouts/fr.linux.ts | 187 + .../browser/keyboardLayouts/fr.win.ts | 169 + .../browser/keyboardLayouts/hu.win.ts | 169 + .../browser/keyboardLayouts/it.darwin.ts | 132 + .../browser/keyboardLayouts/it.win.ts | 169 + .../keyboardLayouts/jp-roman.darwin.ts | 132 + .../browser/keyboardLayouts/jp.darwin.ts | 132 + .../browser/keyboardLayouts/ko.darwin.ts | 132 + .../layout.contribution.darwin.ts | 22 + .../layout.contribution.linux.ts | 12 + .../layout.contribution.win.ts | 29 + .../browser/keyboardLayouts/no.win.ts | 169 + .../browser/keyboardLayouts/pl.darwin.ts | 132 + .../browser/keyboardLayouts/pl.win.ts | 169 + .../browser/keyboardLayouts/pt-br.win.ts | 170 + .../browser/keyboardLayouts/pt.darwin.ts | 132 + .../browser/keyboardLayouts/pt.win.ts | 170 + .../browser/keyboardLayouts/ru.darwin.ts | 132 + .../browser/keyboardLayouts/ru.linux.ts | 187 + .../browser/keyboardLayouts/ru.win.ts | 169 + .../browser/keyboardLayouts/sv.darwin.ts | 132 + .../browser/keyboardLayouts/sv.win.ts | 171 + .../browser/keyboardLayouts/thai.win.ts | 168 + .../browser/keyboardLayouts/tr.win.ts | 168 + .../browser/keyboardLayouts/zh-hans.darwin.ts | 131 + .../keybinding/browser/keymapService.ts | 622 ++++ .../keybinding/common/dispatchConfig.ts | 17 + .../keybinding/common/keybindingEditing.ts | 6 +- .../services/keybinding/common/keymapInfo.ts | 342 ++ .../keybinding/common/navigatorKeyboard.ts} | 16 +- .../keybinding.contribution.ts | 29 + .../electron-browser/nativeKeymapService.ts | 169 + .../test/browserKeyboardMapper.test.ts | 143 + .../keybindingEditing.test.ts | 40 +- .../services/layout/browser/layoutService.ts | 20 + .../common/notificationService.ts | 42 +- .../output/common/outputChannelModel.ts | 18 +- .../output/node/outputChannelModelService.ts | 5 +- .../services/panel/common/panelService.ts | 19 +- .../preferences/browser/preferencesService.ts | 16 +- .../common/keybindingsEditorModel.ts | 8 +- .../preferences/common/preferences.ts | 2 + .../preferences/common/preferencesModels.ts | 7 +- .../common/keybindingsEditorModel.test.ts | 84 +- .../progress/browser/editorProgressService.ts | 13 + ...ogressService2.css => progressService.css} | 4 +- .../progress/browser/progressIndicator.ts | 300 ++ .../progress/browser/progressService.ts | 585 ++-- .../progress/browser/progressService2.ts | 344 -- ...vice.test.ts => progressIndicator.test.ts} | 119 +- .../remote/browser/remoteAgentServiceImpl.ts | 12 +- .../common/abstractRemoteAgentService.ts | 7 +- .../remoteAgentServiceImpl.ts | 6 +- .../services/remote/node/tunnelService.ts | 5 +- .../request/browser/requestService.ts | 48 + .../services/search/common/searchService.ts | 455 +++ .../search/node/ripgrepTextSearchEngine.ts | 44 +- .../services/search/node/searchService.ts | 423 +-- .../test/node/ripgrepTextSearchEngine.test.ts | 79 +- .../test/node/textSearch.integrationTest.ts | 20 +- .../electron-browser/telemetryService.ts | 7 +- .../browser/abstractTextMateService.ts | 433 +++ .../textMate/browser/textMateService.ts | 64 + .../textMate/common/TMGrammarFactory.ts | 147 + .../textMate/common/TMScopeRegistry.ts | 58 + .../cgmanifest.json | 0 .../electron-browser/textMateService.ts | 623 +--- .../electron-browser/textMateWorker.ts | 128 + .../textfile/browser/textFileService.ts | 38 + .../textfile/common/textFileEditorModel.ts | 182 +- .../common/textFileEditorModelManager.ts | 16 +- .../textfile/common/textFileService.ts | 73 +- .../textResourcePropertiesService.ts | 0 .../services/textfile/common/textfiles.ts | 9 +- .../services/textfile/node/textFileService.ts | 6 +- .../textfile/test/textFileService.io.test.ts | 19 +- .../common/textModelResolverService.ts | 4 +- .../{common => browser}/fileIconThemeData.ts | 9 +- .../{common => browser}/fileIconThemeStore.ts | 12 +- .../themes/browser/workbenchThemeService.ts | 93 +- .../services/themes/common/colorThemeData.ts | 11 +- .../services/themes/common/colorThemeStore.ts | 6 +- .../services/themes/common/plistParser.ts | 4 +- .../themes/common/themeCompatibility.ts | 3 +- .../themes/common/workbenchThemeService.ts | 2 + .../timer/electron-browser/timerService.ts | 34 +- .../untitled/common/untitledEditorService.ts | 38 +- .../userData/common/fileUserDataProvider.ts | 142 + .../common/inMemoryUserDataProvider.ts | 232 ++ .../fileUserDataProvider.test.ts | 478 +++ .../services/viewlet/browser/viewlet.ts | 13 +- .../window/electron-browser/windowService.ts | 12 +- .../workspaceEditingService.ts | 160 +- .../browser/parts/editor/baseEditor.test.ts | 8 +- .../test/common/notifications.test.ts | 94 +- .../api/extHostApiCommands.test.ts | 18 +- .../api/extHostCommands.test.ts | 4 +- .../api/extHostConfiguration.test.ts | 10 +- .../api/extHostDiagnostics.test.ts | 6 +- .../extHostDocumentSaveParticipant.test.ts | 2 +- .../api/extHostLanguageFeatures.test.ts | 24 +- .../api/extHostMessagerService.test.ts | 15 +- .../api/extHostSearch.test.ts | 14 +- .../api/extHostTreeViews.test.ts | 3 +- .../electron-browser/api/extHostTypes.test.ts | 2 + .../api/extHostWebview.test.ts | 91 +- .../api/mainThreadEditors.test.ts | 2 +- .../colorRegistry.releaseTest.ts | 7 +- .../quickopen.perf.integrationTest.ts | 9 +- .../textsearch.perf.integrationTest.ts | 9 +- .../workbench/test/workbenchTestServices.ts | 87 +- src/vs/workbench/workbench.main.ts | 88 +- src/vs/workbench/workbench.web.api.ts | 61 + src/vs/workbench/workbench.web.main.ts | 149 +- test/electron/index.js | 13 +- test/electron/renderer.js | 9 +- test/smoke/README.md | 5 +- test/smoke/package.json | 7 +- test/smoke/src/application.ts | 10 +- test/smoke/src/areas/editor/peek.ts | 2 +- .../src/areas/extensions/extensions.test.ts | 4 + test/smoke/src/areas/git/git.test.ts | 2 +- .../src/areas/multiroot/multiroot.test.ts | 3 +- test/smoke/src/areas/problems/problems.ts | 2 +- test/smoke/src/areas/statusbar/statusbar.ts | 14 +- .../src/areas/workbench/localization.test.ts | 2 +- test/smoke/src/main.ts | 7 +- test/smoke/src/vscode/code.ts | 16 +- test/smoke/yarn.lock | 1084 ++++-- test/splitview/package.json | 13 + test/splitview/public/index.html | 138 + test/splitview/server.js | 19 + test/splitview/yarn.lock | 341 ++ yarn.lock | 922 +++-- 2406 files changed, 59140 insertions(+), 35464 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 build/.cachesalt create mode 100755 build/azure-pipelines/common/extract-telemetry.sh delete mode 100644 build/azure-pipelines/common/installDistro.ts create mode 100644 build/azure-pipelines/common/release.ts delete mode 100755 build/azure-pipelines/darwin/build.sh delete mode 100755 build/azure-pipelines/linux/build.sh create mode 100755 build/azure-pipelines/linux/multiarch/alpine/build.sh create mode 100755 build/azure-pipelines/linux/multiarch/alpine/prebuild.sh create mode 100755 build/azure-pipelines/linux/multiarch/alpine/publish.sh create mode 100755 build/azure-pipelines/linux/multiarch/armhf/build.sh create mode 100755 build/azure-pipelines/linux/multiarch/armhf/prebuild.sh create mode 100755 build/azure-pipelines/linux/multiarch/armhf/publish.sh create mode 100644 build/azure-pipelines/linux/product-build-linux-multiarch.yml create mode 100644 build/azure-pipelines/mixin.js create mode 100644 build/azure-pipelines/product-compile.yml create mode 100644 build/azure-pipelines/release.yml create mode 100644 build/azure-pipelines/upload-sourcemaps.js create mode 100644 build/azure-pipelines/web/product-build-web.yml create mode 100755 build/azure-pipelines/web/publish.sh delete mode 100644 build/azure-pipelines/win32/build.ps1 delete mode 100644 build/download/download.js delete mode 100644 build/download/download.ts create mode 100644 build/gulpfile.vscode.web.js create mode 100644 build/lib/node.js create mode 100644 build/lib/node.ts create mode 100644 build/npm/update-distro.js create mode 100644 extensions/css-language-features/server/src/test/links.test.ts create mode 100644 extensions/css-language-features/server/test/linksTestFixtures/.gitignore rename src/vs/base/test/node/stream/fixtures/empty.txt => extensions/css-language-features/server/test/linksTestFixtures/node_modules/foo/package.json (100%) create mode 100644 extensions/git-ui/.vscodeignore create mode 100644 extensions/git-ui/README.md create mode 100644 extensions/git-ui/cgmanifest.json rename src/typings/getmac.d.ts => extensions/git-ui/extension.webpack.config.js (66%) create mode 100644 extensions/git-ui/package.json create mode 100644 extensions/git-ui/package.nls.json create mode 100644 extensions/git-ui/resources/icons/git.png create mode 100644 extensions/git-ui/src/main.ts create mode 100644 extensions/git-ui/src/typings/refs.d.ts create mode 100644 extensions/git-ui/tsconfig.json create mode 100644 extensions/git-ui/yarn.lock delete mode 100644 extensions/git/resources/icons/dark/open-file-mono.svg delete mode 100644 extensions/git/resources/icons/light/open-file-mono.svg delete mode 100644 extensions/markdown-language-features/cgmanifest.json delete mode 100644 extensions/markdown-language-features/media/Preview.svg delete mode 100644 extensions/markdown-language-features/media/PreviewOnRightPane_16x.svg delete mode 100644 extensions/markdown-language-features/media/PreviewOnRightPane_16x_dark.svg delete mode 100644 extensions/markdown-language-features/media/Preview_inverse.svg delete mode 100644 extensions/markdown-language-features/media/ViewSource.svg delete mode 100644 extensions/markdown-language-features/media/ViewSource_inverse.svg create mode 100644 extensions/markdown-language-features/media/preview-dark.svg create mode 100644 extensions/markdown-language-features/media/preview-light.svg create mode 100644 extensions/markdown-language-features/media/preview-right-dark.svg create mode 100644 extensions/markdown-language-features/media/preview-right-light.svg create mode 100644 extensions/markdown-language-features/media/view-source-dark.svg create mode 100644 extensions/markdown-language-features/media/view-source-light.svg create mode 100644 extensions/markdown-language-features/src/util/resources.ts create mode 100644 extensions/objective-c/build/update-grammars.js create mode 100644 extensions/objective-c/test/colorize-fixtures/test.mm create mode 100644 extensions/objective-c/test/colorize-results/test_mm.json create mode 100644 extensions/python/extension.webpack.config.js create mode 100644 extensions/typescript-language-features/src/tsServer/serverError.ts create mode 100644 extensions/typescript-language-features/src/tsServer/spawner.ts create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts create mode 100755 extensions/vscode-test-resolver/scripts/terminateProcess.sh create mode 100644 extensions/vscode-test-resolver/src/util/processes.ts delete mode 100644 extensions/yaml/.gitignore delete mode 100755 remote/installDevModules.sh delete mode 100755 remote/installDevPackagesAsRoot.sh delete mode 100755 remote/launchDevMode.sh create mode 100644 remote/web/.yarnrc create mode 100644 remote/web/package.json create mode 100644 remote/web/yarn.lock delete mode 100644 scripts/code-web.js delete mode 100644 scripts/env.ps1 delete mode 100755 scripts/env.sh delete mode 100644 src/sql/platform/accounts/browser/accountListStatusbarItem.ts create mode 100644 src/sql/workbench/api/browser/extensionHost.contribution.common.ts rename src/sql/workbench/api/{node => browser}/mainThreadAccountManagement.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadBackgroundTaskManagement.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadConnectionManagement.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadCredentialManagement.ts (100%) rename src/sql/workbench/api/{electron-browser => browser}/mainThreadDashboard.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadDashboardWebview.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadDataProtocol.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadExtensionManagement.ts (83%) rename src/sql/workbench/api/{electron-browser => browser}/mainThreadModalDialog.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadModelView.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadModelViewDialog.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadNotebook.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadNotebookDocumentsAndEditors.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadObjectExplorer.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadQueryEditor.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadResourceProvider.ts (100%) rename src/sql/workbench/api/{node => browser}/mainThreadSerializationProvider.ts (100%) rename src/sql/workbench/api/{electron-browser => browser}/mainThreadTasks.ts (100%) rename src/{vs/code/code.main.ts => sql/workbench/api/electron-browser/extensionHost.contribution.ts} (85%) delete mode 100644 src/sql/workbench/api/node/sqlExtHost.contribution.ts create mode 100644 src/sql/workbench/parts/accounts/browser/accounts.contribution.ts delete mode 100644 src/sql/workbench/parts/query/browser/queryStatus.ts delete mode 100644 src/sql/workbench/parts/query/browser/rowCountStatus.ts create mode 100644 src/sql/workbench/parts/query/browser/statusBarItems.ts delete mode 100644 src/sql/workbench/parts/query/browser/timeElapsedStatus.ts delete mode 100644 src/typings/gc-signals.d.ts rename src/typings/{vscode-nsfw.d.ts => nsfw.d.ts} (95%) create mode 100644 src/typings/onigasm-umd.d.ts rename src/{vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css => typings/vscode-windows-ca-certs.d.ts} (88%) rename src/{vs/workbench/contrib/update/electron-browser/media/update.contribution.css => typings/vsda.d.ts} (82%) create mode 100644 src/typings/xterm-addon-search.d.ts create mode 100644 src/typings/xterm-addon-web-links.d.ts rename src/typings/{vscode-xterm.d.ts => xterm.d.ts} (76%) delete mode 100644 src/vs/base/browser/hash.ts delete mode 100755 src/vs/base/browser/ui/breadcrumbs/collapsed.svg delete mode 100755 src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg create mode 100644 src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg create mode 100644 src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg create mode 100644 src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg create mode 100644 src/vs/base/browser/ui/dialog/close-dark.svg delete mode 100644 src/vs/base/browser/ui/dialog/close-inverse.svg create mode 100644 src/vs/base/browser/ui/dialog/close-light.svg delete mode 100644 src/vs/base/browser/ui/dialog/close.svg create mode 100644 src/vs/base/browser/ui/dialog/error-dark.svg delete mode 100644 src/vs/base/browser/ui/dialog/error-inverse.svg create mode 100644 src/vs/base/browser/ui/dialog/error-light.svg delete mode 100644 src/vs/base/browser/ui/dialog/error.svg create mode 100644 src/vs/base/browser/ui/dialog/info-dark.svg delete mode 100644 src/vs/base/browser/ui/dialog/info-inverse.svg create mode 100644 src/vs/base/browser/ui/dialog/info-light.svg delete mode 100644 src/vs/base/browser/ui/dialog/info.svg create mode 100644 src/vs/base/browser/ui/dialog/warning-dark.svg delete mode 100644 src/vs/base/browser/ui/dialog/warning-inverse.svg create mode 100644 src/vs/base/browser/ui/dialog/warning-light.svg delete mode 100644 src/vs/base/browser/ui/dialog/warning.svg create mode 100644 src/vs/base/browser/ui/findinput/case-sensitive-hc.svg create mode 100644 src/vs/base/browser/ui/findinput/case-sensitive-light.svg delete mode 100644 src/vs/base/browser/ui/findinput/case-sensitive.svg create mode 100644 src/vs/base/browser/ui/findinput/regex-hc.svg create mode 100644 src/vs/base/browser/ui/findinput/regex-light.svg delete mode 100644 src/vs/base/browser/ui/findinput/regex.svg create mode 100644 src/vs/base/browser/ui/findinput/whole-word-hc.svg create mode 100644 src/vs/base/browser/ui/findinput/whole-word-light.svg delete mode 100644 src/vs/base/browser/ui/findinput/whole-word.svg delete mode 100644 src/vs/base/browser/ui/splitview/arrow-collapse-dark.svg delete mode 100644 src/vs/base/browser/ui/splitview/arrow-collapse.svg delete mode 100644 src/vs/base/browser/ui/splitview/arrow-expand-dark.svg delete mode 100644 src/vs/base/browser/ui/splitview/arrow-expand.svg create mode 100644 src/vs/base/browser/ui/splitview/tree-collapsed-dark.svg create mode 100644 src/vs/base/browser/ui/splitview/tree-collapsed-hc.svg create mode 100644 src/vs/base/browser/ui/splitview/tree-collapsed-light.svg create mode 100644 src/vs/base/browser/ui/splitview/tree-expanded-dark.svg create mode 100644 src/vs/base/browser/ui/splitview/tree-expanded-hc.svg create mode 100644 src/vs/base/browser/ui/splitview/tree-expanded-light.svg create mode 100644 src/vs/base/browser/ui/toolbar/ellipsis-dark.svg create mode 100644 src/vs/base/browser/ui/toolbar/ellipsis-hc.svg delete mode 100644 src/vs/base/browser/ui/toolbar/ellipsis-inverse.svg create mode 100644 src/vs/base/browser/ui/toolbar/ellipsis-light.svg delete mode 100644 src/vs/base/browser/ui/toolbar/ellipsis.svg delete mode 100755 src/vs/base/browser/ui/tree/media/collapsed-dark.svg delete mode 100644 src/vs/base/browser/ui/tree/media/collapsed-hc.svg delete mode 100755 src/vs/base/browser/ui/tree/media/collapsed.svg delete mode 100755 src/vs/base/browser/ui/tree/media/expanded-dark.svg delete mode 100644 src/vs/base/browser/ui/tree/media/expanded-hc.svg delete mode 100755 src/vs/base/browser/ui/tree/media/expanded.svg create mode 100644 src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg create mode 100644 src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg create mode 100644 src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg create mode 100644 src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg create mode 100644 src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg create mode 100644 src/vs/base/browser/ui/tree/media/tree-expanded-light.svg create mode 100644 src/vs/base/browser/ui/tree/treeDefaults.ts create mode 100644 src/vs/base/node/macAddress.ts delete mode 100644 src/vs/base/node/request.ts create mode 100644 src/vs/base/parts/storage/common/storage.ts rename src/vs/base/{ => parts/storage}/node/storage.ts (61%) rename src/vs/base/{test/node/storage => parts/storage/test/node}/storage.test.ts (99%) delete mode 100644 src/vs/base/parts/tree/browser/CollapseAll.svg delete mode 100644 src/vs/base/parts/tree/browser/CollapseAll_inverse.svg create mode 100644 src/vs/base/parts/tree/browser/collapse-all-dark.svg create mode 100644 src/vs/base/parts/tree/browser/collapse-all-hc.svg create mode 100644 src/vs/base/parts/tree/browser/collapse-all-light.svg delete mode 100644 src/vs/base/parts/tree/browser/collapsed-dark.svg delete mode 100644 src/vs/base/parts/tree/browser/collapsed-hc.svg delete mode 100644 src/vs/base/parts/tree/browser/collapsed.svg delete mode 100644 src/vs/base/parts/tree/browser/expanded-dark.svg delete mode 100644 src/vs/base/parts/tree/browser/expanded-hc.svg delete mode 100644 src/vs/base/parts/tree/browser/expanded.svg create mode 100644 src/vs/base/parts/tree/browser/tree-collapsed-dark.svg create mode 100644 src/vs/base/parts/tree/browser/tree-collapsed-hc.svg create mode 100644 src/vs/base/parts/tree/browser/tree-collapsed-light.svg create mode 100644 src/vs/base/parts/tree/browser/tree-expanded-dark.svg create mode 100644 src/vs/base/parts/tree/browser/tree-expanded-hc.svg create mode 100644 src/vs/base/parts/tree/browser/tree-expanded-light.svg delete mode 100644 src/vs/base/test/browser/hash.test.ts rename src/vs/base/test/{common => node}/buffer.test.ts (90%) delete mode 100644 src/vs/base/test/node/stream/fixtures/file.css delete mode 100644 src/vs/base/test/node/stream/stream.test.ts delete mode 100644 src/vs/code/electron-main/keyboard.ts delete mode 100644 src/vs/code/electron-main/logUploader.ts delete mode 100644 src/vs/code/electron-main/theme.ts create mode 100644 src/vs/code/test/electron-main/nativeHelpers.test.ts rename src/vs/{workbench/contrib/debug/browser/media/reverse-continue-inverse.svg => editor/browser/widget/media/addition-dark.svg} (61%) delete mode 100644 src/vs/editor/browser/widget/media/addition-inverse.svg rename src/vs/{workbench/contrib/debug/browser/media/reverse-continue.svg => editor/browser/widget/media/addition-light.svg} (61%) delete mode 100644 src/vs/editor/browser/widget/media/addition.svg create mode 100644 src/vs/editor/browser/widget/media/close-dark.svg delete mode 100644 src/vs/editor/browser/widget/media/close-inverse.svg create mode 100644 src/vs/editor/browser/widget/media/close-light.svg create mode 100644 src/vs/editor/browser/widget/media/deletion-dark.svg delete mode 100644 src/vs/editor/browser/widget/media/deletion-inverse.svg create mode 100644 src/vs/editor/browser/widget/media/deletion-light.svg delete mode 100644 src/vs/editor/browser/widget/media/deletion.svg create mode 100644 src/vs/editor/common/model/tokensStore.ts create mode 100644 src/vs/editor/contrib/codeAction/codeActionUi.ts create mode 100644 src/vs/editor/contrib/codeAction/lightbulb-autofix-light.svg delete mode 100644 src/vs/editor/contrib/codeAction/lightbulb-autofix.svg create mode 100644 src/vs/editor/contrib/codeAction/lightbulb-light.svg delete mode 100644 src/vs/editor/contrib/codeAction/lightbulb.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/BooleanData_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/BooleanData_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Class_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Constant_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Constant_16x_inverse.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Document_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Document_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/EnumItem_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/EnumItem_inverse_16x.svg delete mode 100755 src/vs/editor/contrib/documentSymbols/media/Enumerator_16x.svg delete mode 100755 src/vs/editor/contrib/documentSymbols/media/Enumerator_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Field_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Indexer_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Indexer_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Interface_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Method_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Method_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Namespace_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Namespace_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Numeric_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Numeric_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Property_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Property_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Snippet_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Snippet_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/String_16x.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/String_16x_darkp.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/boolean-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/boolean-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/class-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/class-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/constant-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/constant-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/enumerator-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/enumerator-item-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/enumerator-item-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/enumerator-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/event-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/event-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/field-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/field-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/file-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/file-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/indexer-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/indexer-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/interface-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/interface-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/keyword-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/keyword-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/method-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/method-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/namespace-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/namespace-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/numeric-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/numeric-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/operator-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/operator-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/property-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/property-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/snippet-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/snippet-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/string-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/string-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/structure-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/structure-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/template-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/template-light.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/variable-dark.svg create mode 100644 src/vs/editor/contrib/documentSymbols/media/variable-light.svg delete mode 100644 src/vs/editor/contrib/find/images/cancelSelectionFind-inverse.svg delete mode 100644 src/vs/editor/contrib/find/images/cancelSelectionFind.svg create mode 100644 src/vs/editor/contrib/find/images/chevron-next-dark.svg create mode 100644 src/vs/editor/contrib/find/images/chevron-next-light.svg create mode 100644 src/vs/editor/contrib/find/images/chevron-previous-dark.svg create mode 100644 src/vs/editor/contrib/find/images/chevron-previous-light.svg create mode 100644 src/vs/editor/contrib/find/images/close-light.svg delete mode 100644 src/vs/editor/contrib/find/images/close.svg delete mode 100644 src/vs/editor/contrib/find/images/expando-collapsed-dark.svg delete mode 100644 src/vs/editor/contrib/find/images/expando-collapsed.svg delete mode 100644 src/vs/editor/contrib/find/images/expando-expanded-dark.svg delete mode 100644 src/vs/editor/contrib/find/images/expando-expanded.svg create mode 100644 src/vs/editor/contrib/find/images/find-selection-dark.svg create mode 100644 src/vs/editor/contrib/find/images/find-selection-light.svg delete mode 100644 src/vs/editor/contrib/find/images/next-inverse.svg delete mode 100644 src/vs/editor/contrib/find/images/next.svg delete mode 100644 src/vs/editor/contrib/find/images/previous-inverse.svg delete mode 100644 src/vs/editor/contrib/find/images/previous.svg create mode 100644 src/vs/editor/contrib/find/images/replace-all-dark.svg delete mode 100644 src/vs/editor/contrib/find/images/replace-all-inverse.svg create mode 100644 src/vs/editor/contrib/find/images/replace-all-light.svg delete mode 100644 src/vs/editor/contrib/find/images/replace-all.svg create mode 100644 src/vs/editor/contrib/find/images/replace-dark.svg delete mode 100644 src/vs/editor/contrib/find/images/replace-inverse.svg create mode 100644 src/vs/editor/contrib/find/images/replace-light.svg delete mode 100644 src/vs/editor/contrib/find/images/replace.svg create mode 100644 src/vs/editor/contrib/find/images/tree-collapsed-dark.svg create mode 100644 src/vs/editor/contrib/find/images/tree-collapsed-light.svg create mode 100644 src/vs/editor/contrib/find/images/tree-expanded-dark.svg create mode 100644 src/vs/editor/contrib/find/images/tree-expanded-light.svg delete mode 100644 src/vs/editor/contrib/folding/arrow-collapse-dark.svg delete mode 100644 src/vs/editor/contrib/folding/arrow-collapse.svg delete mode 100644 src/vs/editor/contrib/folding/arrow-expand-dark.svg delete mode 100644 src/vs/editor/contrib/folding/arrow-expand.svg create mode 100644 src/vs/editor/contrib/folding/tree-collapsed-dark.svg create mode 100644 src/vs/editor/contrib/folding/tree-collapsed-hc.svg create mode 100644 src/vs/editor/contrib/folding/tree-collapsed-light.svg create mode 100644 src/vs/editor/contrib/folding/tree-expanded-dark.svg create mode 100644 src/vs/editor/contrib/folding/tree-expanded-hc.svg create mode 100644 src/vs/editor/contrib/folding/tree-expanded-light.svg delete mode 100644 src/vs/editor/contrib/gotoError/media/status-error-inverse.svg delete mode 100644 src/vs/editor/contrib/gotoError/media/status-error.svg delete mode 100644 src/vs/editor/contrib/gotoError/media/status-info-inverse.svg delete mode 100644 src/vs/editor/contrib/gotoError/media/status-info.svg delete mode 100644 src/vs/editor/contrib/gotoError/media/status-warning-inverse.svg delete mode 100644 src/vs/editor/contrib/gotoError/media/status-warning.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-down-hc.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-down-inverse.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-down.svg create mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-next-dark.svg create mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-next-light.svg create mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-previous-dark.svg create mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-previous-light.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-up-hc.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse-hc.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/chevron-up.svg create mode 100644 src/vs/editor/contrib/referenceSearch/media/close-dark.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/close-inverse.svg create mode 100644 src/vs/editor/contrib/referenceSearch/media/close-light.svg delete mode 100644 src/vs/editor/contrib/referenceSearch/media/close.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Class_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Class_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/ColorPalette_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/ColorPalette_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Constant_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Constant_16x_inverse.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Document_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Document_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/EnumItem_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/EnumItem_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Enumerator_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Enumerator_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Event_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Event_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Field_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Field_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Folder_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Folder_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Interface_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Interface_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Method_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Method_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Misc_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Misc_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Namespace_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Namespace_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Operator_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Operator_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Property_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Property_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Ruler_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Ruler_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Snippet_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Snippet_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/String_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/String_inverse_16x.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Structure_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Structure_16x_vscode_inverse.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Template_16x_vscode.svg delete mode 100644 src/vs/editor/contrib/suggest/media/Template_16x_vscode_inverse.svg create mode 100644 src/vs/editor/contrib/suggest/media/class-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/class-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/close-light.svg delete mode 100644 src/vs/editor/contrib/suggest/media/close.svg create mode 100644 src/vs/editor/contrib/suggest/media/color-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/color-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/constant-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/constant-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/enumerator-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/enumerator-item-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/enumerator-item-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/enumerator-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/event-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/event-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/field-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/field-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/file-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/file-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/folder-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/folder-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/info-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/info-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/interface-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/interface-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/keyword-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/keyword-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/method-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/method-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/namespace-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/namespace-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/operator-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/operator-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/property-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/property-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/reference-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/reference-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/ruler-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/ruler-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/snippet-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/snippet-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/string-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/string-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/structure-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/structure-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/template-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/template-light.svg create mode 100644 src/vs/editor/contrib/suggest/media/variable-dark.svg create mode 100644 src/vs/editor/contrib/suggest/media/variable-light.svg create mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-dark.svg delete mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg create mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-light.svg delete mode 100644 src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg create mode 100644 src/vs/platform/clipboard/browser/clipboardService.ts delete mode 100644 src/vs/platform/configuration/node/configuration.ts create mode 100644 src/vs/platform/credentials/common/credentials.ts create mode 100644 src/vs/platform/credentials/node/credentialsService.ts delete mode 100644 src/vs/platform/diagnostics/electron-main/diagnosticsService.ts create mode 100644 src/vs/platform/diagnostics/node/diagnosticsIpc.ts rename src/vs/platform/extensionManagement/{node => common}/extensionGalleryService.ts (89%) rename src/vs/platform/extensions/{node => common}/extensionValidator.ts (98%) rename src/vs/{workbench/services => platform}/files/common/fileService.ts (86%) rename src/vs/{workbench/services => platform}/files/electron-browser/diskFileSystemProvider.ts (88%) rename src/vs/{workbench/services => platform}/files/node/diskFileSystemProvider.ts (74%) rename src/vs/{workbench/services => platform}/files/node/watcher/nodejs/watcherService.ts (80%) rename src/vs/{workbench/services => platform}/files/node/watcher/nsfw/nsfwWatcherService.ts (81%) rename src/vs/{workbench/services => platform}/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts (92%) rename src/vs/{workbench/services => platform}/files/node/watcher/nsfw/watcher.ts (74%) rename src/vs/{workbench/services => platform}/files/node/watcher/nsfw/watcherApp.ts (69%) rename src/vs/{workbench/services => platform}/files/node/watcher/nsfw/watcherIpc.ts (80%) rename src/vs/{workbench/services => platform}/files/node/watcher/nsfw/watcherService.ts (61%) rename src/vs/{workbench/services => platform}/files/node/watcher/unix/chokidarWatcherService.ts (81%) rename src/vs/{workbench/services => platform}/files/node/watcher/unix/test/chockidarWatcherService.test.ts (97%) rename src/vs/{workbench/services => platform}/files/node/watcher/unix/watcher.ts (74%) rename src/vs/{workbench/services => platform}/files/node/watcher/unix/watcherApp.ts (68%) rename src/vs/{workbench/services => platform}/files/node/watcher/unix/watcherIpc.ts (80%) rename src/vs/{workbench/services => platform}/files/node/watcher/unix/watcherService.ts (60%) rename src/vs/{workbench/services => platform}/files/node/watcher/watcher.ts (97%) rename src/vs/{workbench/services => platform}/files/node/watcher/win32/CodeHelper.exe (100%) rename src/vs/{workbench/services => platform}/files/node/watcher/win32/CodeHelper.md (100%) rename src/vs/{workbench/services => platform}/files/node/watcher/win32/csharpWatcherService.ts (78%) rename src/vs/{workbench/services => platform}/files/node/watcher/win32/watcherService.ts (60%) rename src/vs/{workbench/services => platform}/files/test/browser/fileService.test.ts (95%) create mode 100644 src/vs/platform/files/test/common/nullFileSystemProvider.ts rename src/vs/{workbench/services => platform}/files/test/node/diskFileService.test.ts (81%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/examples/company.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/examples/conway.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/examples/employee.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/examples/small.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/index.html (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/other/deep/company.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/other/deep/conway.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/other/deep/employee.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/other/deep/small.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/resolver/site.css (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/binary.txt (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/deep/company.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/deep/conway.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/deep/employee.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/deep/small.js (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/index.html (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/lorem.txt (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/small.txt (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/small_umlaut.txt (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/some_utf16le.css (100%) rename src/vs/{workbench/services => platform}/files/test/node/fixtures/service/some_utf8_bom.txt (100%) rename src/vs/{workbench/services => platform}/files/test/node/normalizer.test.ts (99%) rename src/vs/platform/history/{electron-main => common}/historyStorage.ts (76%) create mode 100644 src/vs/platform/launch/common/launchService.ts create mode 100644 src/vs/platform/lifecycle/browser/lifecycleService.ts rename src/vs/platform/log/{node => common}/logIpc.ts (100%) create mode 100644 src/vs/platform/product/browser/productService.ts create mode 100644 src/vs/platform/request/browser/requestService.ts rename src/vs/platform/request/{node => common}/request.ts (66%) create mode 100644 src/vs/platform/request/common/requestIpc.ts delete mode 100644 src/vs/platform/request/electron-browser/requestService.ts rename src/vs/{base => platform/request}/node/proxy.ts (97%) create mode 100644 src/vs/platform/severityIcon/common/severityIcon.ts create mode 100644 src/vs/platform/sign/browser/signService.ts create mode 100644 src/vs/platform/sign/common/sign.ts create mode 100644 src/vs/platform/sign/node/signService.ts create mode 100644 src/vs/platform/storage/browser/storageService.ts create mode 100644 src/vs/platform/storage/test/node/storage.test.ts create mode 100644 src/vs/platform/telemetry/common/gdprTypings.ts create mode 100644 src/vs/platform/telemetry/node/telemetry.ts create mode 100644 src/vs/platform/theme/electron-main/themeMainService.ts rename src/vs/workbench/api/{electron-browser => browser}/extensionHost.contribution.ts (51%) create mode 100644 src/vs/workbench/api/browser/mainThreadCodeInsets.ts delete mode 100644 src/vs/workbench/api/browser/mainThreadHeapService.ts create mode 100644 src/vs/workbench/api/browser/mainThreadLabelService.ts rename src/vs/workbench/api/{electron-browser => browser}/mainThreadWebview.ts (67%) create mode 100644 src/vs/workbench/api/common/extHostCodeInsets.ts delete mode 100644 src/vs/workbench/api/common/extHostHeapService.ts create mode 100644 src/vs/workbench/api/common/extHostLabelService.ts create mode 100644 src/vs/workbench/api/common/shared/webview.ts create mode 100644 src/vs/workbench/browser/actions/developerActions.ts delete mode 100644 src/vs/workbench/browser/actions/media/editor-layout-inverse.svg delete mode 100644 src/vs/workbench/browser/actions/media/editor-layout.svg create mode 100644 src/vs/workbench/browser/actions/media/layout-dark.svg create mode 100644 src/vs/workbench/browser/actions/media/layout-hc.svg create mode 100644 src/vs/workbench/browser/actions/media/layout-light.svg create mode 100644 src/vs/workbench/browser/actions/media/remove-dark.svg create mode 100644 src/vs/workbench/browser/actions/media/remove-light.svg create mode 100644 src/vs/workbench/browser/actions/windowActions.ts create mode 100644 src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg delete mode 100644 src/vs/workbench/browser/parts/activitybar/media/ellipsis-global.svg create mode 100644 src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-all-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-all-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-big-alt.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-big-inverse-alt.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-big-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-big.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dark-alt.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-dark-alt.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-light-alt.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-dirty-light.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-hc.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-light-alt.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/close-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-statusview-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close-statusview.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/close.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/closeall-editors-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/closeall-editors.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/next-diff.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg delete mode 100755 src/vs/workbench/browser/parts/editor/media/paragraph-disabled-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg delete mode 100755 src/vs/workbench/browser/parts/editor/media/paragraph-disabled.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/paragraph.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff-dark.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/previous-diff.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-hc.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-horizontal.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-dark.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-hc.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-inverse.svg create mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical-light.svg delete mode 100644 src/vs/workbench/browser/parts/editor/media/split-editor-vertical.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/close-all-dark.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/close-all-light.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/close-dark.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/close-inverse.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/close-light.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/close.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/closeall-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/closeall.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/configure-dark.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/configure-inverse.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/configure-light.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/configure.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/down-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/down.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/error-dark.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/error-inverse.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/error-light.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/error.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/info-dark.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/info-inverse.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/info-light.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/info.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/tree-collapsed-dark.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/tree-collapsed-light.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/tree-expanded-dark.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/tree-expanded-light.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/up-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/up.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/warning-dark.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/warning-inverse.svg create mode 100644 src/vs/workbench/browser/parts/notifications/media/warning-light.svg delete mode 100644 src/vs/workbench/browser/parts/notifications/media/warning.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-left-dark.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-left-hc.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-left-light.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-right-dark.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-right-hc.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-right-light.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/close-dark.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/close-hc.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/close-inverse.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/close-light.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/close.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/down-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/down.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/ellipsis-dark.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/ellipsis-hc.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/ellipsis-inverse.svg create mode 100644 src/vs/workbench/browser/parts/panel/media/ellipsis-light.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/ellipsis.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/left-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/left.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/right-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/right.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/up-inverse.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/up.svg rename src/vs/workbench/browser/parts/quickinput/media/{dark/arrow-left.svg => arrow-left-dark.svg} (100%) rename src/vs/workbench/browser/parts/quickinput/media/{light/arrow-left.svg => arrow-left-light.svg} (100%) rename src/vs/workbench/browser/parts/quickinput/{ => media}/quickInput.css (100%) create mode 100644 src/vs/workbench/browser/parts/quickopen/media/dirty-dark.svg delete mode 100644 src/vs/workbench/browser/parts/quickopen/media/dirty-inverse.svg create mode 100644 src/vs/workbench/browser/parts/quickopen/media/dirty-light.svg delete mode 100644 src/vs/workbench/browser/parts/quickopen/media/dirty.svg delete mode 100644 src/vs/workbench/browser/parts/statusbar/statusbar.ts delete mode 100644 src/vs/workbench/browser/parts/views/media/collapsed-dark.svg delete mode 100644 src/vs/workbench/browser/parts/views/media/collapsed-hc.svg delete mode 100644 src/vs/workbench/browser/parts/views/media/collapsed.svg delete mode 100644 src/vs/workbench/browser/parts/views/media/expanded-dark.svg delete mode 100644 src/vs/workbench/browser/parts/views/media/expanded-hc.svg delete mode 100644 src/vs/workbench/browser/parts/views/media/expanded.svg create mode 100644 src/vs/workbench/browser/parts/views/media/tree-collapsed-dark.svg create mode 100644 src/vs/workbench/browser/parts/views/media/tree-collapsed-hc.svg create mode 100644 src/vs/workbench/browser/parts/views/media/tree-collapsed-light.svg create mode 100644 src/vs/workbench/browser/parts/views/media/tree-expanded-dark.svg create mode 100644 src/vs/workbench/browser/parts/views/media/tree-expanded-hc.svg create mode 100644 src/vs/workbench/browser/parts/views/media/tree-expanded-light.svg delete mode 100644 src/vs/workbench/contrib/codeinset/common/codeInset.ts delete mode 100644 src/vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution.ts delete mode 100644 src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.ts create mode 100644 src/vs/workbench/contrib/comments/browser/media/close-dark.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/close-hc.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/close-light.svg delete mode 100644 src/vs/workbench/contrib/comments/browser/media/comment.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/delete-dark.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/delete-hc.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/delete-light.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/edit-dark.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/edit-hc.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/edit-light.svg create mode 100644 src/vs/workbench/contrib/comments/browser/media/reaction-light.svg delete mode 100644 src/vs/workbench/contrib/comments/browser/media/reaction.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/debug.contribution.ts (94%) rename src/vs/workbench/contrib/debug/{electron-browser => browser}/debugConfigurationManager.ts (95%) create mode 100644 src/vs/workbench/contrib/debug/browser/debugHelperService.ts rename src/vs/workbench/contrib/debug/{electron-browser => browser}/debugService.ts (94%) rename src/vs/workbench/contrib/debug/{electron-browser => browser}/debugSession.ts (91%) create mode 100644 src/vs/workbench/contrib/debug/browser/media/add-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/add-focus.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/add-hc.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/add-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/add-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/add.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/breakpoints-activate-inverse.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/breakpoints-activate.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-hc.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-repl-inverse.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-repl.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/close-all-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/close-all-hc.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/close-all-light.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/configure-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/configure-hc.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/configure-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/configure-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/configure.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/console-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/console-hc.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/console-light.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/continue-dark.svg delete mode 100755 src/vs/workbench/contrib/debug/browser/media/continue-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/continue-light.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/continue-tb.png (100%) rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/continue-without-debugging-tb.png (100%) delete mode 100755 src/vs/workbench/contrib/debug/browser/media/continue.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/debug-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/debug-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/disconnect-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/disconnect-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/disconnect-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/disconnect.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/pause-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/pause-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/pause-light.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/pause-tb.png (100%) delete mode 100644 src/vs/workbench/contrib/debug/browser/media/pause.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/remove-all-inverse.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/remove-all.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/repl-inverse.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/repl.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/restart-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/restart-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/restart-light.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/restart-tb.png (100%) delete mode 100644 src/vs/workbench/contrib/debug/browser/media/restart.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/reverse-continue-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/reverse-continue-light.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/start-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/start-hc.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/start-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/start-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/start.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-back-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-back-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-back-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-back.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-into-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-into-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-into-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-into.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-out-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-out-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-out-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-out.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-over-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-over-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/step-over-light.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/step-over.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/stepinto-tb.png (100%) rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/stepout-tb.png (100%) rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/stepover-tb.png (100%) create mode 100644 src/vs/workbench/contrib/debug/browser/media/stop-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/stop-inverse.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/stop-light.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/media/stop-tb.png (100%) delete mode 100644 src/vs/workbench/contrib/debug/browser/media/stop.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/toggle-breakpoints-dark.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/toggle-breakpoints-hc.svg create mode 100644 src/vs/workbench/contrib/debug/browser/media/toggle-breakpoints-light.svg rename src/vs/workbench/contrib/debug/{electron-browser => browser}/rawDebugSession.ts (71%) rename src/vs/workbench/contrib/debug/{node => common}/debugger.ts (92%) delete mode 100644 src/vs/workbench/contrib/debug/electron-browser/terminalSupport.ts create mode 100644 src/vs/workbench/contrib/debug/node/debugHelperService.ts rename src/vs/workbench/contrib/debug/test/{electron-browser => browser}/debugModel.test.ts (97%) create mode 100644 src/vs/workbench/contrib/experiments/common/experimentService.ts rename src/vs/workbench/contrib/experiments/{node => electron-browser}/experimentService.ts (88%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionEditor.ts (92%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsActions.ts (95%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsActivationProgress.ts (90%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsDependencyChecker.ts (100%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsList.ts (95%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsViewlet.ts (94%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsViews.ts (89%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionsWidgets.ts (93%) create mode 100644 src/vs/workbench/contrib/extensions/browser/media/clear-dark.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/clear-hc.svg rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/clear-inverse.svg (100%) create mode 100644 src/vs/workbench/contrib/extensions/browser/media/clear-light.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/configure-dark.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/configure-hc.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/configure-light.svg rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/defaultIcon.png (100%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/extensionActions.css (85%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/extensionEditor.css (99%) create mode 100644 src/vs/workbench/contrib/extensions/browser/media/extensions-activity-bar.svg rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/extensions.css (88%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/extensionsViewlet.css (97%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/extensionsWidgets.css (84%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/language-icon.svg (100%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/loading.svg (100%) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/markdown.css (100%) create mode 100644 src/vs/workbench/contrib/extensions/browser/media/profile-start-dark.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/profile-start-light.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/profile-stop-dark.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/profile-stop-light.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/save-dark.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/save-light.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/star-empty.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/star-full.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/star-half.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/star-small.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/start-dark.svg create mode 100644 src/vs/workbench/contrib/extensions/browser/media/start-light.svg rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/media/theme-icon.png (100%) delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/EmptyStar.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/FullStarLight.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/HalfStarLight.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/clear.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/extensions-dark.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/manage-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/manage.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/profile-start-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/profile-start.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/profile-stop-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/profile-stop.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/save-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/save.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/start-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/start.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/status-info-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/status-info.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/status-warning-inverse.svg delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/media/status-warning.svg rename src/vs/workbench/contrib/externalTerminal/{electron-browser => browser}/externalTerminal.contribution.ts (55%) delete mode 100644 src/vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.ts rename src/vs/workbench/contrib/externalTerminal/{electron-browser => node}/TerminalHelper.scpt (100%) rename src/vs/workbench/contrib/externalTerminal/{electron-browser => node}/externalTerminalService.ts (51%) rename src/vs/workbench/contrib/externalTerminal/{electron-browser => node}/iTermHelper.scpt (100%) create mode 100644 src/vs/workbench/contrib/feedback/browser/feedback.contribution.ts rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/feedback.ts (86%) create mode 100644 src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/media/close-dark.svg (100%) rename src/vs/{editor/browser/widget => workbench/contrib/feedback/browser}/media/close.svg (100%) rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/media/feedback.css (94%) rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/media/happy.svg (100%) rename src/vs/{editor/contrib/suggest => workbench/contrib/feedback/browser}/media/info.svg (100%) rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/media/sad.svg (100%) rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/media/smiley.svg (100%) rename src/vs/workbench/contrib/feedback/{electron-browser => browser}/media/twitter.svg (100%) delete mode 100644 src/vs/workbench/contrib/feedback/electron-browser/feedback.contribution.ts delete mode 100644 src/vs/workbench/contrib/feedback/electron-browser/feedbackStatusbarItem.ts delete mode 100644 src/vs/workbench/contrib/feedback/electron-browser/media/close.svg delete mode 100644 src/vs/workbench/contrib/feedback/electron-browser/media/info.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/AddFile.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/AddFile_inverse.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/AddFolder.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/AddFolder_inverse.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/CollapseAll.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/CollapseAll_inverse.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/Preview.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/Preview_inverse.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/Refresh.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/Refresh_inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/action-close-light.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/add-file-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/add-file-hc.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/add-file-light.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/add-folder-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/add-folder-hc.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/add-folder-light.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/check-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/check-inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/check-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/check.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/close-all-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/close-all-hc.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/close-all-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/closeall.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/closeall_inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/files-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/files-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/preview-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/preview-light.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/refresh-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/refresh-hc.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/refresh-light.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/save-all-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/save-all-hc.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/save-all-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/saveall_inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-horizontal.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/split-editor-vertical.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/undo-dark.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/undo-inverse.svg create mode 100644 src/vs/workbench/contrib/files/browser/media/undo-light.svg delete mode 100644 src/vs/workbench/contrib/files/browser/media/undo.svg create mode 100644 src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts create mode 100644 src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg create mode 100644 src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg create mode 100644 src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg delete mode 100755 src/vs/workbench/contrib/markers/browser/media/excludeSettings-dark.svg delete mode 100755 src/vs/workbench/contrib/markers/browser/media/excludeSettings.svg create mode 100644 src/vs/workbench/contrib/markers/browser/media/lightbulb-autofix-hc.svg create mode 100644 src/vs/workbench/contrib/markers/browser/media/lightbulb-autofix-light.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/lightbulb-autofix.svg create mode 100644 src/vs/workbench/contrib/markers/browser/media/lightbulb-hc.svg create mode 100644 src/vs/workbench/contrib/markers/browser/media/lightbulb-light.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/lightbulb.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/status-error-inverse.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/status-error.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/status-info-inverse.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/status-info.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/status-warning-inverse.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/status-warning.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/clear-dark.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/clear-hc.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/clear_output.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/clear_output_inverse.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/locked-dark.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/locked-hc.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/locked-light.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/open-file-dark.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/open-file-hc.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/open-file-light.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/open_log_file.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/open_log_file_inverse.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/output_lock.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/output_lock_inverse.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/output_unlock.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/output_unlock_inverse.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg create mode 100644 src/vs/workbench/contrib/output/browser/media/unlocked-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/action-remove-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/action-remove.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/add-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/add-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/check-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/check-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/clear-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/clear-inverse.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/clear.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/collapseAll.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/collapseAll_inverse.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/configure-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/configure-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/edit-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/edit-json-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/edit-json-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/edit-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/info.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/preferences-editor-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/preferences-editor-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/record-keys-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/record-keys-inverse.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/record-keys-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/record-keys.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/remove-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/remove-light.svg rename src/vs/workbench/contrib/preferences/{electron-browser => browser}/media/settingsEditor2.css (98%) create mode 100644 src/vs/workbench/contrib/preferences/browser/media/sort-precedence-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/sort-precedence-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/sort_precedence.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/sort_precedence_inverse.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/status-error.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/tree-collapsed-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/tree-collapsed-light.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/tree-expanded-dark.svg create mode 100644 src/vs/workbench/contrib/preferences/browser/media/tree-expanded-light.svg rename src/vs/workbench/contrib/preferences/{electron-browser => browser}/preferences.contribution.ts (88%) rename src/vs/workbench/contrib/preferences/{electron-browser => browser}/preferencesSearch.ts (96%) rename src/vs/workbench/contrib/preferences/{electron-browser => browser}/settingsEditor2.ts (98%) delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/check-inverse.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/check.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/configure-inverse.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/configure.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/edit-json-inverse.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/edit-json.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg delete mode 100644 src/vs/workbench/contrib/preferences/electron-browser/media/preferences-editor.svg create mode 100644 src/vs/workbench/contrib/remote/common/remote.contribution.ts create mode 100644 src/vs/workbench/contrib/resources/browser/resourceServiceWorker.ts create mode 100644 src/vs/workbench/contrib/resources/browser/resourceServiceWorkerClient.ts create mode 100644 src/vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.ts delete mode 100644 src/vs/workbench/contrib/scm/browser/media/icon-dark.svg delete mode 100644 src/vs/workbench/contrib/scm/browser/media/icon-light.svg create mode 100644 src/vs/workbench/contrib/scm/browser/media/scm-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/CollapseAll.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/CollapseAll_inverse.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/Refresh.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/Refresh_inverse.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/action-remove-dark.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/action-remove.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/clear-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/clear-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/clear-search-results-dark.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/clear-search-results.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/collapse-all-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/collapse-all-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/collapse-all-light.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/ellipsis-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/ellipsis-hc.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/ellipsis-inverse.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/ellipsis-light.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/ellipsis.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/exclude-settings-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/exclude-settings-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/exclude-settings-light.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/excludeSettings-dark.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/excludeSettings.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/expando-collapsed-dark.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/expando-collapsed.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/expando-expanded-dark.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/expando-expanded.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/refresh-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/refresh-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/refresh-light.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/remove-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/remove-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/remove-light.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/replace-all-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/replace-all-hc.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/replace-all-inverse.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/replace-all-light.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/replace-all.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/replace-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/replace-hc.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/replace-inverse.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/replace-light.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/replace.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/search-activity-bar.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/search-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/stop-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/stop-hc.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/stop-inverse.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/stop-light.svg delete mode 100644 src/vs/workbench/contrib/search/browser/media/stop.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/tree-collapsed-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/tree-collapsed-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/tree-collapsed-light.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/tree-expanded-dark.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/tree-expanded-hc.svg create mode 100644 src/vs/workbench/contrib/search/browser/media/tree-expanded-light.svg rename src/vs/workbench/contrib/stats/{node => electron-browser}/stats.contribution.ts (87%) rename src/vs/workbench/contrib/stats/{node => electron-browser}/workspaceStats.ts (98%) rename src/vs/workbench/contrib/tasks/{electron-browser/task.contribution.ts => browser/abstractTaskService.ts} (75%) rename src/vs/workbench/contrib/tasks/{electron-browser => browser}/runAutomaticTasks.ts (100%) create mode 100644 src/vs/workbench/contrib/tasks/browser/task.contribution.ts create mode 100644 src/vs/workbench/contrib/tasks/browser/taskService.ts rename src/vs/workbench/contrib/tasks/{electron-browser => browser}/terminalTaskSystem.ts (78%) create mode 100644 src/vs/workbench/contrib/tasks/common/media/configure-dark.svg delete mode 100644 src/vs/workbench/contrib/tasks/common/media/configure-inverse.svg create mode 100644 src/vs/workbench/contrib/tasks/common/media/configure-light.svg delete mode 100644 src/vs/workbench/contrib/tasks/common/media/configure.svg delete mode 100644 src/vs/workbench/contrib/tasks/common/media/status-error.svg delete mode 100644 src/vs/workbench/contrib/tasks/common/media/status-info.svg delete mode 100644 src/vs/workbench/contrib/tasks/common/media/status-warning.svg delete mode 100644 src/vs/workbench/contrib/tasks/common/media/task.svg create mode 100644 src/vs/workbench/contrib/tasks/electron-browser/taskService.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/media/configure-dark.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/configure-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/configure-inverse.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/configure-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/configure.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-inverse.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-dark.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-inverse.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/new.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-horizontal-inverse.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-horizontal.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-inverse.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split.svg create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts create mode 100644 src/vs/workbench/contrib/terminal/common/terminalShellConfig.ts create mode 100644 src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts delete mode 100644 src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts create mode 100644 src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts create mode 100644 src/vs/workbench/contrib/terminal/node/terminalRemote.ts rename src/{sql/workbench/api/electron-browser/sqlExtensionHost.contribution.ts => vs/workbench/contrib/update/common/update.ts} (81%) delete mode 100644 src/vs/workbench/contrib/update/electron-browser/media/update.svg create mode 100644 src/vs/workbench/contrib/webview/browser/pre/fake.html create mode 100644 src/vs/workbench/contrib/webview/browser/pre/host.js create mode 100644 src/vs/workbench/contrib/webview/browser/pre/index.html create mode 100644 src/vs/workbench/contrib/webview/browser/pre/service-worker.js create mode 100644 src/vs/workbench/contrib/webview/browser/webviewElement.ts create mode 100644 src/vs/workbench/contrib/webview/browser/webviewService.ts create mode 100644 src/vs/workbench/contrib/webview/common/mimeTypes.ts create mode 100644 src/vs/workbench/contrib/webview/common/portMapping.ts create mode 100644 src/vs/workbench/contrib/webview/common/resourceLoader.ts create mode 100644 src/vs/workbench/contrib/webview/common/themeing.ts create mode 100644 src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts delete mode 100644 src/vs/workbench/electron-browser/actions/media/remove-dark.svg delete mode 100644 src/vs/workbench/electron-browser/actions/media/remove.svg create mode 100644 src/vs/workbench/services/backup/common/backupFileService.ts delete mode 100644 src/vs/workbench/services/broadcast/common/broadcast.ts delete mode 100644 src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts create mode 100644 src/vs/workbench/services/configuration/browser/configurationCache.ts delete mode 100644 src/vs/workbench/services/configuration/node/configurationFileService.ts create mode 100644 src/vs/workbench/services/environment/browser/environmentService.ts create mode 100644 src/vs/workbench/services/extensions/browser/extensionService.ts create mode 100644 src/vs/workbench/services/extensions/common/abstractExtensionService.ts create mode 100644 src/vs/workbench/services/extensions/common/extensionPoints.ts rename src/vs/workbench/services/extensions/{node => common}/extensionsUtil.ts (86%) rename src/vs/workbench/services/extensions/{electron-browser => common}/remoteExtensionHostClient.ts (84%) create mode 100644 src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts delete mode 100644 src/vs/workbench/services/heap/common/heap.ts delete mode 100644 src/vs/workbench/services/heap/node/heap.ts rename src/vs/workbench/services/keybinding/{electron-browser => browser}/keybindingService.ts (68%) create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts create mode 100644 src/vs/workbench/services/keybinding/browser/keymapService.ts create mode 100644 src/vs/workbench/services/keybinding/common/dispatchConfig.ts create mode 100644 src/vs/workbench/services/keybinding/common/keymapInfo.ts rename src/vs/workbench/{electron-browser/actions/media/actions.css => services/keybinding/common/navigatorKeyboard.ts} (57%) create mode 100644 src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts create mode 100644 src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts create mode 100644 src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts create mode 100644 src/vs/workbench/services/progress/browser/editorProgressService.ts rename src/vs/workbench/services/progress/browser/media/{progressService2.css => progressService.css} (85%) create mode 100644 src/vs/workbench/services/progress/browser/progressIndicator.ts delete mode 100644 src/vs/workbench/services/progress/browser/progressService2.ts rename src/vs/workbench/services/progress/test/{progressService.test.ts => progressIndicator.test.ts} (67%) create mode 100644 src/vs/workbench/services/request/browser/requestService.ts create mode 100644 src/vs/workbench/services/search/common/searchService.ts create mode 100644 src/vs/workbench/services/textMate/browser/abstractTextMateService.ts create mode 100644 src/vs/workbench/services/textMate/browser/textMateService.ts create mode 100644 src/vs/workbench/services/textMate/common/TMGrammarFactory.ts create mode 100644 src/vs/workbench/services/textMate/common/TMScopeRegistry.ts rename src/vs/workbench/services/textMate/{electron-browser => common}/cgmanifest.json (100%) create mode 100644 src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts rename src/vs/workbench/services/textfile/{node => common}/textResourcePropertiesService.ts (100%) rename src/vs/workbench/services/themes/{common => browser}/fileIconThemeData.ts (97%) rename src/vs/workbench/services/themes/{common => browser}/fileIconThemeStore.ts (94%) create mode 100644 src/vs/workbench/services/userData/common/fileUserDataProvider.ts create mode 100644 src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts create mode 100644 src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts create mode 100644 src/vs/workbench/workbench.web.api.ts create mode 100644 test/splitview/package.json create mode 100644 test/splitview/public/index.html create mode 100644 test/splitview/server.js create mode 100644 test/splitview/yarn.lock diff --git a/.gitignore b/.gitignore index 3ffb11ed18..160c42ed74 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,14 @@ out-vscode-min/ out-vscode-reh/ out-vscode-reh-min/ out-vscode-reh-pkg/ -**/node_modules +out-vscode-reh-web/ +out-vscode-reh-web-min/ +out-vscode-reh-web-pkg/ +out-vscode-web/ +out-vscode-web-min/ +src/vs/server +resources/server +build/node_modules coverage/ test_data/ test-results/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 55cfea092a..b4336e7d12 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "recommendations": [ "ms-vscode.vscode-typescript-tslint-plugin", "dbaeumer.vscode-eslint", + "EditorConfig.EditorConfig", "msjsdiag.debugger-for-chrome" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 4ae9375942..bc148b2186 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -218,4 +218,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 4acae6fe8e..666092355a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -58,5 +58,8 @@ "url": "./.vscode/cglicenses.schema.json" } ], - "git.ignoreLimitWarning": true -} \ No newline at end of file + "git.ignoreLimitWarning": true, + "remote.extensionKind": { + "msjsdiag.debugger-for-chrome": "workspace" + } +} diff --git a/.yarnrc b/.yarnrc index beab9f70b9..441b5a2e69 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "3.1.8" +target "4.2.5" runtime "electron" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 65c8a42b8d..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1 +0,0 @@ -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. diff --git a/azure-pipelines-linux-mac.yml b/azure-pipelines-linux-mac.yml index e7e10a8560..2354f5857d 100644 --- a/azure-pipelines-linux-mac.yml +++ b/azure-pipelines-linux-mac.yml @@ -35,9 +35,9 @@ steps: yarn tslint displayName: 'Run TSLint' -- script: | - yarn strict-null-check - displayName: 'Run Strict Null Check' +# - script: | +# yarn strict-null-check +# displayName: 'Run Strict Null Check' - script: | yarn compile diff --git a/azure-pipelines-windows.yml b/azure-pipelines-windows.yml index 74bed5ab3a..ab9beec255 100644 --- a/azure-pipelines-windows.yml +++ b/azure-pipelines-windows.yml @@ -20,9 +20,9 @@ steps: yarn tslint displayName: 'Run TSLint' -- script: | - yarn strict-null-check - displayName: 'Run Strict Null Check' +# - script: | +# yarn strict-null-check +# displayName: 'Run Strict Null Check' - script: | yarn compile diff --git a/build/.cachesalt b/build/.cachesalt new file mode 100644 index 0000000000..105ce86ae3 --- /dev/null +++ b/build/.cachesalt @@ -0,0 +1 @@ +2019-07-11T05:47:05.444Z diff --git a/build/.nativeignore b/build/.nativeignore index a04b4dbc23..ca83451456 100644 --- a/build/.nativeignore +++ b/build/.nativeignore @@ -1,5 +1,8 @@ # cleanup rules for native node modules, .gitignore style +nan/** +*/node_modules/nan/** + fsevents/binding.gyp fsevents/fsevents.cc fsevents/build/** @@ -64,14 +67,6 @@ windows-process-tree/build/** windows-process-tree/src/** !windows-process-tree/**/*.node -gc-signals/binding.gyp -gc-signals/build/** -gc-signals/src/** -gc-signals/deps/** - -!gc-signals/build/Release/*.node -!gc-signals/src/index.js - keytar/binding.gyp keytar/build/** keytar/src/** @@ -83,6 +78,7 @@ node-pty/binding.gyp node-pty/build/** node-pty/src/** node-pty/tools/** +node-pty/deps/** !node-pty/build/Release/*.exe !node-pty/build/Release/*.dll !node-pty/build/Release/*.node @@ -102,25 +98,25 @@ core-js/**/** slickgrid/node_modules/** slickgrid/examples/** -vscode-nsfw/binding.gyp -vscode-nsfw/build/** -vscode-nsfw/src/** -vscode-nsfw/openpa/** -vscode-nsfw/includes/** -!vscode-nsfw/build/Release/*.node -!vscode-nsfw/**/*.a +nsfw/binding.gyp +nsfw/build/** +nsfw/src/** +nsfw/openpa/** +nsfw/includes/** +!nsfw/build/Release/*.node +!nsfw/**/*.a +vsda/build/** +vsda/ci/** +vsda/src/** +vsda/.gitignore vsda/binding.gyp vsda/README.md -vsda/build/** -vsda/*.bat -vsda/*.sh -vsda/*.cpp -vsda/*.h +vsda/targets !vsda/build/Release/vsda.node vscode-windows-ca-certs/**/* !vscode-windows-ca-certs/package.json !vscode-windows-ca-certs/**/*.node -node-addon-api/**/* \ No newline at end of file +node-addon-api/**/* diff --git a/build/azure-pipelines/common/extract-telemetry.sh b/build/azure-pipelines/common/extract-telemetry.sh new file mode 100755 index 0000000000..916c87c82c --- /dev/null +++ b/build/azure-pipelines/common/extract-telemetry.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -e + +cd $BUILD_STAGINGDIRECTORY +git clone https://github.com/microsoft/vscode-telemetry-extractor.git +cd vscode-telemetry-extractor +git checkout 4e64f3de30f8fccb58ebdc0d85c4861a135d46cf +npm i +npm run compile +cd src +mkdir telemetry-sources +cd telemetry-sources +git clone --depth 1 https://github.com/Microsoft/vscode-extension-telemetry.git +git clone --depth 1 https://github.com/Microsoft/vscode-chrome-debug-core.git +git clone --depth 1 https://github.com/Microsoft/vscode-chrome-debug.git +git clone --depth 1 https://github.com/Microsoft/vscode-node-debug2.git +git clone --depth 1 https://github.com/Microsoft/vscode-node-debug.git +git clone --depth 1 https://github.com/Microsoft/vscode-docker.git +git clone --depth 1 https://github.com/Microsoft/vscode-go.git +git clone --depth 1 https://github.com/Microsoft/vscode-azure-account.git +git clone --depth 1 https://github.com/Microsoft/vscode-html-languageservice.git +git clone --depth 1 https://github.com/Microsoft/vscode-json-languageservice.git +git clone --depth 1 https://github.com/Microsoft/vscode-mono-debug.git +git clone --depth 1 https://github.com/Microsoft/TypeScript.git +cd ../../ +node ./out/cli-extract.js --sourceDir $BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement +node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement +mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry +mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json +mv declarations-extensions-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json \ No newline at end of file diff --git a/build/azure-pipelines/common/installDistro.ts b/build/azure-pipelines/common/installDistro.ts deleted file mode 100644 index ca3795285b..0000000000 --- a/build/azure-pipelines/common/installDistro.ts +++ /dev/null @@ -1,20 +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 cp from 'child_process'; -import * as path from 'path'; - -function yarnInstall(packageName: string): void { - cp.execSync(`yarn add --no-lockfile ${packageName}`); - cp.execSync(`yarn add --no-lockfile ${packageName}`, { cwd: path.join( process.cwd(), 'remote') }); -} - -const product = require('../../../product.json'); -const dependencies = product.dependencies || {} as { [name: string]: string; }; - -Object.keys(dependencies).forEach(name => { - const url = dependencies[name]; - yarnInstall(url); -}); \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index c17615a8e6..7657332d69 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -152,13 +152,6 @@ async function publish(commit: string, quality: string, platform: string, type: const queuedBy = process.env['BUILD_QUEUEDBY']!; const sourceBranch = process.env['BUILD_SOURCEBRANCH']!; - const isReleased = ( - // Insiders: nightly build from master - (quality === 'insider' && /^master$|^refs\/heads\/master$/.test(sourceBranch) && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy)) || - - // Exploration: any build from electron-4.0.x branch - (quality === 'exploration' && /^electron-4.0.x$|^refs\/heads\/electron-4.0.x$/.test(sourceBranch)) - ); console.log('Publishing...'); console.log('Quality:', quality); @@ -168,7 +161,6 @@ async function publish(commit: string, quality: string, platform: string, type: console.log('Version:', version); console.log('Commit:', commit); console.log('Is Update:', isUpdate); - console.log('Is Released:', isReleased); console.log('File:', file); const stat = await new Promise((c, e) => fs.stat(file, (err, stat) => err ? e(err) : c(stat))); @@ -227,7 +219,7 @@ async function publish(commit: string, quality: string, platform: string, type: id: commit, timestamp: (new Date()).getTime(), version, - isReleased: config.frozen ? false : isReleased, + isReleased: false, sourceBranch, queuedBy, assets: [] as Array, @@ -246,11 +238,6 @@ async function publish(commit: string, quality: string, platform: string, type: } function main(): void { - if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) { - console.warn('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH'); - return; - } - const commit = process.env['BUILD_SOURCEVERSION']; if (!commit) { diff --git a/build/azure-pipelines/common/release.ts b/build/azure-pipelines/common/release.ts new file mode 100644 index 0000000000..134148f820 --- /dev/null +++ b/build/azure-pipelines/common/release.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { DocumentClient } from 'documentdb'; + +interface Config { + id: string; + frozen: boolean; +} + +function createDefaultConfig(quality: string): Config { + return { + id: quality, + frozen: false + }; +} + +function getConfig(quality: string): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/config'; + const query = { + query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`, + parameters: [ + { name: '@quality', value: quality } + ] + }; + + return new Promise((c, e) => { + client.queryDocuments(collection, query).toArray((err, results) => { + if (err && err.code !== 409) { return e(err); } + + c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0] as any as Config); + }); + }); +} + +function doRelease(commit: string, quality: string): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/' + quality; + const query = { + query: 'SELECT TOP 1 * FROM c WHERE c.id = @id', + parameters: [{ name: '@id', value: commit }] + }; + + let updateTries = 0; + + function update(): Promise { + updateTries++; + + return new Promise((c, e) => { + client.queryDocuments(collection, query).toArray((err, results) => { + if (err) { return e(err); } + if (results.length !== 1) { return e(new Error('No documents')); } + + const release = results[0]; + release.isReleased = true; + + client.replaceDocument(release._self, release, err => { + if (err && err.code === 409 && updateTries < 5) { return c(update()); } + if (err) { return e(err); } + + console.log('Build successfully updated.'); + c(); + }); + }); + }); + } + + return update(); +} + +async function release(commit: string, quality: string): Promise { + const config = await getConfig(quality); + + console.log('Quality config:', config); + + if (config.frozen) { + console.log(`Skipping release because quality ${quality} is frozen.`); + return; + } + + await doRelease(commit, quality); +} + +function env(name: string): string { + const result = process.env[name]; + + if (!result) { + throw new Error(`Skipping release due to missing env: ${name}`); + } + + return result; +} + +async function main(): Promise { + const commit = env('BUILD_SOURCEVERSION'); + const quality = env('VSCODE_QUALITY'); + + await release(commit, quality); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/azure-pipelines/common/symbols.ts b/build/azure-pipelines/common/symbols.ts index a334ee1195..079695f733 100644 --- a/build/azure-pipelines/common/symbols.ts +++ b/build/azure-pipelines/common/symbols.ts @@ -36,7 +36,6 @@ export interface IVersionAccessor extends IApplicationAccessor { enum Platform { WIN_32 = 'win32-ia32', WIN_64 = 'win32-x64', - LINUX_32 = 'linux-ia32', LINUX_64 = 'linux-x64', MAC_OS = 'darwin-x64' } @@ -147,6 +146,10 @@ async function ensureVersionAndSymbols(options: IOptions) { // Check version does not exist console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); + if (!Array.isArray(versions.app_versions)) { + throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); + } + if (versions.app_versions.some(v => v.version === options.versions.code)) { console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); return; @@ -185,13 +188,17 @@ const hockeyAppToken = process.argv[3]; const is64 = process.argv[4] === 'x64'; const hockeyAppId = process.argv[5]; +if (process.argv.length !== 6) { + throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); +} + let platform: Platform; if (process.platform === 'darwin') { platform = Platform.MAC_OS; } else if (process.platform === 'win32') { platform = is64 ? Platform.WIN_64 : Platform.WIN_32; } else { - platform = is64 ? Platform.LINUX_64 : Platform.LINUX_32; + platform = Platform.LINUX_64; } // Create version and upload symbols in HockeyApp @@ -212,7 +219,9 @@ if (repository && codeVersion && electronVersion && (product.quality === 'stable }).then(() => { console.log('HockeyApp: done'); }).catch(error => { - console.error(`HockeyApp: error (${error})`); + console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); + + return process.exit(1); }); } else { console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); diff --git a/build/azure-pipelines/common/sync-mooncake.ts b/build/azure-pipelines/common/sync-mooncake.ts index a7a38d3211..a7dea2bcfe 100644 --- a/build/azure-pipelines/common/sync-mooncake.ts +++ b/build/azure-pipelines/common/sync-mooncake.ts @@ -153,11 +153,6 @@ async function sync(commit: string, quality: string): Promise { } function main(): void { - if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) { - error('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH'); - return; - } - const commit = process.env['BUILD_SOURCEVERSION']; if (!commit) { diff --git a/build/azure-pipelines/darwin/build.sh b/build/azure-pipelines/darwin/build.sh deleted file mode 100755 index a47546249f..0000000000 --- a/build/azure-pipelines/darwin/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -e -yarn gulp vscode-darwin-min -yarn gulp vscode-reh-darwin-min -yarn gulp upload-vscode-sourcemaps \ No newline at end of file diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 1d370b0781..c964214ca3 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -2,6 +2,11 @@ steps: - task: NodeTool@0 inputs: versionSpec: "10.15.1" +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" @@ -12,15 +17,15 @@ steps: # vstsFeed: '$(ArtifactFeed)' # condition: eq(variables['System.PullRequest.PullRequestId'], '') - script: | - yarn + yarn --frozen-lockfile displayName: Install Dependencies -# condition: or(ne(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) -# - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 -# inputs: -# keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: '$(ArtifactFeed)' -# condition: and(succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - script: | yarn gulp electron-x64 displayName: Download Electron diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 5d6ec8c2cf..b712e07284 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -1,4 +1,23 @@ steps: +- script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - task: NodeTool@0 inputs: versionSpec: "10.15.1" @@ -17,8 +36,6 @@ steps: set -e cat << EOF > ~/.netrc - machine monacotools.visualstudio.com - password $(devops-pat) machine github.com login vscode password $(github-distro-mixin-password) @@ -26,23 +43,53 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" + displayName: Prepare tooling + +- script: | + set -e git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" git fetch distro git merge $(node -p "require('./package.json').distro") + displayName: Merge distro - yarn - yarn gulp mixin - yarn gulp hygiene - yarn monaco-compile-check - node build/azure-pipelines/common/installDistro.js - node build/lib/builtInExtensions.js - displayName: Prepare build +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + +- script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ - ./build/azure-pipelines/darwin/build.sh + yarn gulp vscode-darwin-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-darwin-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-darwin-min-ci displayName: Build - script: | @@ -51,11 +98,13 @@ steps: # APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`" # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME" displayName: Run unit tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e ./scripts/test-integration.sh --build --tfs "Integration Tests" displayName: Run integration tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh index 6bc80ce04e..fa453bcaff 100755 --- a/build/azure-pipelines/darwin/publish.sh +++ b/build/azure-pipelines/darwin/publish.sh @@ -30,7 +30,7 @@ node build/azure-pipelines/common/publish.js \ ../vscode-server-darwin.zip # publish hockeyapp symbols -node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_MACOS" +node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" x64 "$VSCODE_HOCKEYAPP_ID_MACOS" # upload configuration yarn gulp upload-vscode-configuration diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 639456ad4c..4057894f02 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -30,7 +30,13 @@ steps: git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" git fetch distro - git push distro origin/master:refs/heads/master + + # Push master branch into master and oss/master + git push distro origin/master:refs/heads/master origin/master:refs/heads/oss/master + + # Push every release branch into oss/release + git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro + git merge $(node -p "require('./package.json').distro") displayName: Sync & Merge Distro \ No newline at end of file diff --git a/build/azure-pipelines/linux/build.sh b/build/azure-pipelines/linux/build.sh deleted file mode 100755 index 1c79d76486..0000000000 --- a/build/azure-pipelines/linux/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -e -yarn gulp "vscode-linux-$VSCODE_ARCH-min" - -if [[ "$VSCODE_ARCH" != "ia32" ]]; then - yarn gulp vscode-reh-linux-$VSCODE_ARCH-min -fi \ No newline at end of file diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index db7d7d7402..b861eb697a 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -10,6 +10,11 @@ steps: - task: NodeTool@0 inputs: versionSpec: "10.15.1" +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" @@ -20,15 +25,15 @@ steps: # vstsFeed: '$(ArtifactFeed)' # condition: eq(variables['System.PullRequest.PullRequestId'], '') - script: | - yarn + yarn --frozen-lockfile displayName: Install Dependencies - # condition: or(ne(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) -# - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 -# inputs: -# keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: '$(ArtifactFeed)' -# condition: and(succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - script: | yarn gulp electron-x64 displayName: Download Electron diff --git a/build/azure-pipelines/linux/multiarch/alpine/build.sh b/build/azure-pipelines/linux/multiarch/alpine/build.sh new file mode 100755 index 0000000000..4f01bc9a7e --- /dev/null +++ b/build/azure-pipelines/linux/multiarch/alpine/build.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh b/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh new file mode 100755 index 0000000000..4f01bc9a7e --- /dev/null +++ b/build/azure-pipelines/linux/multiarch/alpine/prebuild.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/alpine/publish.sh b/build/azure-pipelines/linux/multiarch/alpine/publish.sh new file mode 100755 index 0000000000..4f01bc9a7e --- /dev/null +++ b/build/azure-pipelines/linux/multiarch/alpine/publish.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/build.sh b/build/azure-pipelines/linux/multiarch/armhf/build.sh new file mode 100755 index 0000000000..4f01bc9a7e --- /dev/null +++ b/build/azure-pipelines/linux/multiarch/armhf/build.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh b/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh new file mode 100755 index 0000000000..4f01bc9a7e --- /dev/null +++ b/build/azure-pipelines/linux/multiarch/armhf/prebuild.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/multiarch/armhf/publish.sh b/build/azure-pipelines/linux/multiarch/armhf/publish.sh new file mode 100755 index 0000000000..4f01bc9a7e --- /dev/null +++ b/build/azure-pipelines/linux/multiarch/armhf/publish.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-linux-multiarch.yml b/build/azure-pipelines/linux/product-build-linux-multiarch.yml new file mode 100644 index 0000000000..dff5ae2cea --- /dev/null +++ b/build/azure-pipelines/linux/product-build-linux-multiarch.yml @@ -0,0 +1,115 @@ +steps: +- script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- task: Docker@1 + displayName: 'Pull image' + inputs: + azureSubscriptionEndpoint: 'vscode-builds-subscription' + azureContainerRegistry: vscodehub.azurecr.io + command: 'Run an image' + imageName: 'vscode-linux-build-agent:$(VSCODE_ARCH)' + containerCommand: uname + +- 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 remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + +- script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + +- script: | + set -e + CHILD_CONCURRENCY=1 ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/prebuild.sh + displayName: Prebuild + +- script: | + set -e + ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/build.sh + displayName: Build + +- script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + ./build/azure-pipelines/linux/multiarch/$(VSCODE_ARCH)/publish.sh + displayName: Publish + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + continueOnError: true \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index f727e30d25..e97f5cfdef 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -1,4 +1,23 @@ steps: +- script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - task: NodeTool@0 inputs: versionSpec: "10.15.1" @@ -15,14 +34,7 @@ steps: - script: | set -e - export npm_config_arch="$(VSCODE_ARCH)" - if [[ "$(VSCODE_ARCH)" == "ia32" ]]; then - export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" - fi - cat << EOF > ~/.netrc - machine monacotools.visualstudio.com - password $(devops-pat) machine github.com login vscode password $(github-distro-mixin-password) @@ -30,34 +42,66 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" + displayName: Prepare tooling + +- script: | + set -e git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" git fetch distro git merge $(node -p "require('./package.json').distro") + displayName: Merge distro - CHILD_CONCURRENCY=1 yarn - yarn gulp mixin - yarn gulp hygiene - yarn monaco-compile-check - node build/azure-pipelines/common/installDistro.js - node build/lib/builtInExtensions.js - displayName: Prepare build +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + +- script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - ./build/azure-pipelines/linux/build.sh + yarn gulp vscode-linux-x64-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-linux-x64-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-linux-x64-min-ci displayName: Build - script: | set -e - yarn gulp "electron-$(VSCODE_ARCH)" + yarn gulp "electron-x64" # xvfb seems to be crashing often, let's make sure it's always up service xvfb start DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" - # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)" + # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-x64" displayName: Run unit tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e @@ -68,12 +112,12 @@ steps: ./build/azure-pipelines/linux/publish.sh displayName: Publish -- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Detection' - continueOnError: true - - task: PublishPipelineArtifact@0 displayName: 'Publish Pipeline Artifact' inputs: - artifactName: snap-$(VSCODE_ARCH) + artifactName: snap-x64 targetPath: .build/linux/snap-tarball + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + continueOnError: true diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh index 0f864600bd..5fc86d02d6 100755 --- a/build/azure-pipelines/linux/publish.sh +++ b/build/azure-pipelines/linux/publish.sh @@ -4,9 +4,7 @@ REPO="$(pwd)" ROOT="$REPO/.." # Publish tarball -PLATFORM_LINUX="linux-$VSCODE_ARCH" -[[ "$VSCODE_ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" -[[ "$VSCODE_ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" +PLATFORM_LINUX="linux-x64" BUILDNAME="VSCode-$PLATFORM_LINUX" BUILD="$ROOT/$BUILDNAME" BUILD_VERSION="$(date +%s)" @@ -21,44 +19,42 @@ rm -rf $ROOT/code-*.tar.* node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH" # Publish Remote Extension Host -if [[ "$VSCODE_ARCH" != "ia32" ]]; then - LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" - SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" - SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" - SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" +LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" +SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" +SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" +SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - rm -rf $ROOT/vscode-server-*.tar.* - (cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) +rm -rf $ROOT/vscode-server-*.tar.* +(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$VERSION" true "$SERVER_TARBALL_PATH" -fi +node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$VERSION" true "$SERVER_TARBALL_PATH" # Publish hockeyapp symbols -node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_LINUX64" +node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "x64" "$VSCODE_HOCKEYAPP_ID_LINUX64" # Publish DEB -yarn gulp "vscode-linux-$VSCODE_ARCH-build-deb" -PLATFORM_DEB="linux-deb-$VSCODE_ARCH" -[[ "$VSCODE_ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" +yarn gulp "vscode-linux-x64-build-deb" +PLATFORM_DEB="linux-deb-x64" +DEB_ARCH="amd64" DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH" # Publish RPM -yarn gulp "vscode-linux-$VSCODE_ARCH-build-rpm" -PLATFORM_RPM="linux-rpm-$VSCODE_ARCH" -[[ "$VSCODE_ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" +yarn gulp "vscode-linux-x64-build-rpm" +PLATFORM_RPM="linux-rpm-x64" +RPM_ARCH="x86_64" RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH" # Publish Snap -yarn gulp "vscode-linux-$VSCODE_ARCH-prepare-snap" +yarn gulp "vscode-linux-x64-prepare-snap" # Pack snap tarball artifact, in order to preserve file perms mkdir -p $REPO/.build/linux/snap-tarball -SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" +SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-x64.tar.gz" rm -rf $SNAP_TARBALL_PATH (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 9d98e9fa47..4c3f602e8c 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -16,7 +16,7 @@ steps: - task: DownloadPipelineArtifact@0 displayName: 'Download Pipeline Artifact' inputs: - artifactName: snap-$(VSCODE_ARCH) + artifactName: snap-x64 targetPath: .build/linux/snap-tarball - script: | @@ -31,14 +31,13 @@ steps: # Define variables REPO="$(pwd)" - ARCH="$(VSCODE_ARCH)" - SNAP_ROOT="$REPO/.build/linux/snap/$ARCH" + SNAP_ROOT="$REPO/.build/linux/snap/x64" # Install build dependencies (cd build && yarn) # Unpack snap tarball artifact, in order to preserve file perms - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$ARCH.tar.gz" + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-x64.tar.gz" (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) # Create snap package @@ -47,9 +46,9 @@ steps: PACKAGEJSON="$(ls $SNAP_ROOT/code*/usr/share/code*/resources/app/package.json)" VERSION=$(node -p "require(\"$PACKAGEJSON\").version") SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" - (cd $SNAP_ROOT/code-* && sudo snapcraft snap --output "$SNAP_PATH") + (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap --output "$SNAP_PATH") # Publish snap package AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ - node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file + node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-x64" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file diff --git a/build/azure-pipelines/mixin.js b/build/azure-pipelines/mixin.js new file mode 100644 index 0000000000..c9d2ed1d87 --- /dev/null +++ b/build/azure-pipelines/mixin.js @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const json = require('gulp-json-editor'); +const buffer = require('gulp-buffer'); +const filter = require('gulp-filter'); +const es = require('event-stream'); +const vfs = require('vinyl-fs'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); + +function main() { + const quality = process.env['VSCODE_QUALITY']; + + if (!quality) { + console.log('Missing VSCODE_QUALITY, skipping mixin'); + return; + } + + const productJsonFilter = filter('product.json', { restore: true }); + + fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); + return vfs + .src(`quality/${quality}/**`, { base: `quality/${quality}` }) + .pipe(filter(f => !f.isDirectory())) + .pipe(productJsonFilter) + .pipe(buffer()) + .pipe(json(o => Object.assign({}, require('../product.json'), o))) + .pipe(productJsonFilter.restore) + .pipe(es.mapSync(function (f) { + fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); + return f; + })) + .pipe(vfs.dest('.')); +} + +main(); \ No newline at end of file diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index c8bedfbffc..469494c7d5 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,81 +1,135 @@ resources: containers: - container: vscode-x64 - endpoint: VSCodeHub image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - - container: vscode-ia32 endpoint: VSCodeHub - image: vscodehub.azurecr.io/vscode-linux-build-agent:ia32 - container: snapcraft - image: snapcore/snapcraft + image: snapcore/snapcraft:stable jobs: +- job: Compile + pool: + vmImage: 'Ubuntu-16.04' + container: vscode-x64 + steps: + - template: product-compile.yml + - job: Windows - condition: eq(variables['VSCODE_BUILD_WIN32'], 'true') + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32'], 'true')) pool: vmImage: VS2017-Win2016 variables: VSCODE_ARCH: x64 + dependsOn: + - Compile steps: - template: win32/product-build-win32.yml - job: Windows32 - condition: eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true') + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) pool: vmImage: VS2017-Win2016 variables: VSCODE_ARCH: ia32 + dependsOn: + - Compile steps: - template: win32/product-build-win32.yml - job: Linux - condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) pool: vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: x64 container: vscode-x64 + dependsOn: + - Compile steps: - template: linux/product-build-linux.yml - job: LinuxSnap - condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX'], 'true')) pool: vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: x64 container: snapcraft dependsOn: Linux steps: - template: linux/snap-build-linux.yml -- job: Linux32 - condition: eq(variables['VSCODE_BUILD_LINUX_32BIT'], 'true') +- job: LinuxArmhf + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true'), ne(variables['VSCODE_QUALITY'], 'stable')) pool: vmImage: 'Ubuntu-16.04' variables: - VSCODE_ARCH: ia32 - container: vscode-ia32 + VSCODE_ARCH: armhf + dependsOn: + - Compile steps: - - template: linux/product-build-linux.yml + - template: linux/product-build-linux-multiarch.yml + +- job: LinuxAlpine + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true'), ne(variables['VSCODE_QUALITY'], 'stable')) + pool: + vmImage: 'Ubuntu-16.04' + variables: + VSCODE_ARCH: alpine + dependsOn: + - Compile + steps: + - template: linux/product-build-linux-multiarch.yml + +- job: LinuxWeb + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_WEB'], 'true')) + pool: + vmImage: 'Ubuntu-16.04' + variables: + VSCODE_ARCH: x64 + dependsOn: + - Compile + steps: + - template: web/product-build-web.yml - job: macOS - condition: eq(variables['VSCODE_BUILD_MACOS'], 'true') + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) pool: vmImage: macOS 10.13 + dependsOn: + - Compile steps: - template: darwin/product-build-darwin.yml -- job: Mooncake +- job: Release + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), or(eq(variables['VSCODE_RELEASE'], 'true'), and(or(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['VSCODE_QUALITY'], 'exploration')), eq(variables['Build.Reason'], 'Schedule')))) pool: vmImage: 'Ubuntu-16.04' - condition: true dependsOn: - Windows - Windows32 - Linux - LinuxSnap - - Linux32 + - LinuxArmhf + - LinuxAlpine - macOS steps: - - template: sync-mooncake.yml \ No newline at end of file + - template: release.yml + +- job: Mooncake + pool: + vmImage: 'Ubuntu-16.04' + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + dependsOn: + - Windows + - Windows32 + - Linux + - LinuxSnap + - LinuxArmhf + - LinuxAlpine + - macOS + steps: + - template: sync-mooncake.yml + +schedules: +- cron: "0 5 * * Mon-Fri" + displayName: Mon-Fri at 7:00 + branches: + include: + - master \ No newline at end of file diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml new file mode 100644 index 0000000000..e5105326e7 --- /dev/null +++ b/build/azure-pipelines/product-compile.yml @@ -0,0 +1,124 @@ +steps: +- script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- 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 + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + +- script: | + set -e + yarn postinstall + displayName: Run postinstall scripts + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + +# 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 + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + displayName: Run hygiene checks + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set - + ./build/azure-pipelines/common/extract-telemetry.sh + displayName: Extract Telemetry + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- script: | + set -e + yarn gulp compile-build + yarn gulp compile-extensions-build + yarn gulp minify-vscode + yarn gulp minify-vscode-reh + yarn gulp minify-vscode-reh-web + displayName: Compile + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- script: | + set -e + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + node build/azure-pipelines/upload-sourcemaps + displayName: Upload sourcemaps + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) \ No newline at end of file diff --git a/build/azure-pipelines/release.yml b/build/azure-pipelines/release.yml new file mode 100644 index 0000000000..eee54a1d8b --- /dev/null +++ b/build/azure-pipelines/release.yml @@ -0,0 +1,22 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.x" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- script: | + set -e + + (cd build ; yarn) + + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + node build/azure-pipelines/common/release.js diff --git a/build/azure-pipelines/upload-sourcemaps.js b/build/azure-pipelines/upload-sourcemaps.js new file mode 100644 index 0000000000..6d52a0a28b --- /dev/null +++ b/build/azure-pipelines/upload-sourcemaps.js @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const path = require('path'); +const es = require('event-stream'); +const azure = require('gulp-azure-storage'); +const vfs = require('vinyl-fs'); +const util = require('../lib/util'); +const root = path.dirname(path.dirname(__dirname)); +const commit = util.getVersion(root); + +function main() { + const vs = vfs.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) // client source-maps only + .pipe(es.mapSync(f => { + f.path = `${f.base}/core/${f.relative}`; + return f; + })); + + const extensionsOut = vfs.src(['.build/extensions/**/*.js.map', '!**/node_modules/**'], { base: '.build' }); + + return es.merge(vs, extensionsOut) + .pipe(es.through(function (data) { + // debug + console.log('Uploading Sourcemap', data.relative); + this.emit('data', data); + })) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + key: process.env.AZURE_STORAGE_ACCESS_KEY, + container: 'sourcemaps', + prefix: commit + '/' + })); +} + +main(); \ No newline at end of file diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml new file mode 100644 index 0000000000..7c65dc982f --- /dev/null +++ b/build/azure-pipelines/web/product-build-web.yml @@ -0,0 +1,96 @@ +steps: +- script: | + mkdir -p .build + echo -n $BUILD_SOURCEVERSION > .build/commit + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- script: | + set -e + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- 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 remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + +# - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 +# inputs: +# keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' +# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' +# vstsFeed: 'npm-vscode' + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + # condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +# - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 +# inputs: +# keyfile: 'build/.cachesalt, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' +# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' +# vstsFeed: 'npm-vscode' +# condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +# - script: | +# set -e +# yarn postinstall +# displayName: Run postinstall scripts +# condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + +- 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-web-min-ci + displayName: Build + +- script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + ./build/azure-pipelines/web/publish.sh + displayName: Publish diff --git a/build/azure-pipelines/web/publish.sh b/build/azure-pipelines/web/publish.sh new file mode 100755 index 0000000000..9d662fcfe8 --- /dev/null +++ b/build/azure-pipelines/web/publish.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e +REPO="$(pwd)" +ROOT="$REPO/.." + +# Publish Web Client +WEB_BUILD_NAME="vscode-web" +WEB_TARBALL_FILENAME="vscode-web.tar.gz" +WEB_TARBALL_PATH="$ROOT/$WEB_TARBALL_FILENAME" + +rm -rf $ROOT/vscode-web.tar.* + +(cd $ROOT && tar --owner=0 --group=0 -czf $WEB_TARBALL_PATH $WEB_BUILD_NAME) + +node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "web-standalone" archive-unsigned "$WEB_TARBALL_FILENAME" "$VERSION" true "$WEB_TARBALL_PATH" diff --git a/build/azure-pipelines/win32/build.ps1 b/build/azure-pipelines/win32/build.ps1 deleted file mode 100644 index 8334a87e02..0000000000 --- a/build/azure-pipelines/win32/build.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -. build/azure-pipelines/win32/exec.ps1 -$ErrorActionPreference = "Stop" -exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min" } -exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min" } -exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } \ No newline at end of file diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 4508501f68..9b6d61ee97 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -9,22 +9,21 @@ steps: inputs: versionSpec: '2.x' addToPath: true -# - task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 -# inputs: -# keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: '$(ArtifactFeed)' -# condition: eq(variables['System.PullRequest.PullRequestId'], '') +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' - powershell: | - yarn + yarn --frozen-lockfile displayName: Install Dependencies - # condition: or(ne(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) -# - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 -# inputs: -# keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' -# targetfolder: '**/node_modules, !**/node_modules/**/node_modules' -# vstsFeed: '$(ArtifactFeed)' -# condition: and(succeeded(), eq(variables['System.PullRequest.PullRequestId'], ''), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '.yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - powershell: | yarn gulp electron displayName: Download Electron diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 8a6a6abb65..66583616c6 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,4 +1,23 @@ steps: +- powershell: | + mkdir .build -ea 0 + "$env:BUILD_SOURCEVERSION" | Out-File -Encoding ascii -NoNewLine .build\commit + displayName: Prepare cache flag + +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/commit' + targetfolder: '.build, out-build, out-vscode-min, out-vscode-reh-min, out-vscode-reh-web-min' + vstsFeed: 'npm-vscode' + platformIndependent: true + alias: 'Compilation' + +- powershell: | + $ErrorActionPreference = "Stop" + exit 1 + displayName: Check RestoreCache + condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + - task: NodeTool@0 inputs: versionSpec: "10.15.1" @@ -21,29 +40,66 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - "machine monacotools.visualstudio.com`npassword $(devops-pat)`nmachine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" + "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" } + + mkdir .build -ea 0 + "$(VSCODE_ARCH)" | Out-File -Encoding ascii -NoNewLine .build\arch + displayName: Prepare tooling + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } exec { git fetch distro } exec { git merge $(node -p "require('./package.json').distro") } + displayName: Merge distro - exec { yarn } - exec { yarn gulp mixin } - exec { yarn gulp hygiene } - exec { yarn monaco-compile-check } - exec { node build/azure-pipelines/common/installDistro.js } - exec { node build/lib/builtInExtensions.js } - displayName: Prepare build +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:npm_config_arch="$(VSCODE_ARCH)" + $env:CHILD_CONCURRENCY="1" + exec { yarn --frozen-lockfile } + displayName: Install dependencies + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: 'build/.cachesalt, .build/arch, .yarnrc, remote/.yarnrc, **/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: 'npm-vscode' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn postinstall } + displayName: Run postinstall scripts + condition: and(succeeded(), eq(variables['CacheRestored'], 'true')) + +- 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)" - .\build\azure-pipelines\win32\build.ps1 + exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min-ci" } + exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min-ci" } + exec { yarn gulp "vscode-reh-web-win32-$env:VSCODE_ARCH-min-ci" } + exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } displayName: Build - powershell: | @@ -51,8 +107,8 @@ steps: $ErrorActionPreference = "Stop" exec { yarn gulp "electron-$(VSCODE_ARCH)" } exec { .\scripts\test.bat --build --tfs "Unit Tests" } - # yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" displayName: Run unit tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -60,6 +116,7 @@ steps: exec { yarn gulp "electron-$(VSCODE_ARCH)" } exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } displayName: Run integration tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 inputs: @@ -141,6 +198,7 @@ steps: $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" $env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" .\build\azure-pipelines\win32\publish.ps1 displayName: Publish diff --git a/build/builtin/main.js b/build/builtin/main.js index 52223b3310..fdee2f55cd 100644 --- a/build/builtin/main.js +++ b/build/builtin/main.js @@ -10,7 +10,7 @@ const path = require('path'); let window = null; app.once('ready', () => { - window = new BrowserWindow({ width: 800, height: 600 }); + window = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true } }); window.setMenuBarVisibility(false); window.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true })); // window.webContents.openDevTools(); diff --git a/build/download/download.js b/build/download/download.js deleted file mode 100644 index ce27c8d285..0000000000 --- a/build/download/download.js +++ /dev/null @@ -1,91 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const https = require("https"); -const fs = require("fs"); -const path = require("path"); -const cp = require("child_process"); -function ensureDir(filepath) { - if (!fs.existsSync(filepath)) { - ensureDir(path.dirname(filepath)); - fs.mkdirSync(filepath); - } -} -function download(options, destination) { - ensureDir(path.dirname(destination)); - return new Promise((c, e) => { - const fd = fs.openSync(destination, 'w'); - const req = https.get(options, (res) => { - res.on('data', (chunk) => { - fs.writeSync(fd, chunk); - }); - res.on('end', () => { - fs.closeSync(fd); - c(); - }); - }); - req.on('error', (reqErr) => { - console.error(`request to ${options.host}${options.path} failed.`); - console.error(reqErr); - e(reqErr); - }); - }); -} -const MARKER_ARGUMENT = `_download_fork_`; -function base64encode(str) { - return Buffer.from(str, 'utf8').toString('base64'); -} -function base64decode(str) { - return Buffer.from(str, 'base64').toString('utf8'); -} -function downloadInExternalProcess(options) { - const url = `https://${options.requestOptions.host}${options.requestOptions.path}`; - console.log(`Downloading ${url}...`); - return new Promise((c, e) => { - const child = cp.fork(__filename, [MARKER_ARGUMENT, base64encode(JSON.stringify(options))], { - stdio: ['pipe', 'pipe', 'pipe', 'ipc'] - }); - let stderr = []; - child.stderr.on('data', (chunk) => { - stderr.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); - }); - child.on('exit', (code) => { - if (code === 0) { - // normal termination - console.log(`Finished downloading ${url}.`); - c(); - } - else { - // abnormal termination - console.error(Buffer.concat(stderr).toString()); - e(new Error(`Download of ${url} failed.`)); - } - }); - }); -} -exports.downloadInExternalProcess = downloadInExternalProcess; -function _downloadInExternalProcess() { - let options; - try { - options = JSON.parse(base64decode(process.argv[3])); - } - catch (err) { - console.error(`Cannot read arguments`); - console.error(err); - process.exit(-1); - return; - } - download(options.requestOptions, options.destinationPath).then(() => { - process.exit(0); - }, (err) => { - console.error(err); - process.exit(-2); - }); -} -if (process.argv.length >= 4 && process.argv[2] === MARKER_ARGUMENT) { - // running as forked download script - _downloadInExternalProcess(); -} diff --git a/build/download/download.ts b/build/download/download.ts deleted file mode 100644 index 9559c72f58..0000000000 --- a/build/download/download.ts +++ /dev/null @@ -1,111 +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 https from 'https'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as cp from 'child_process'; - -function ensureDir(filepath: string) { - if (!fs.existsSync(filepath)) { - ensureDir(path.dirname(filepath)); - fs.mkdirSync(filepath); - } -} - -function download(options: https.RequestOptions, destination: string): Promise { - ensureDir(path.dirname(destination)); - - return new Promise((c, e) => { - const fd = fs.openSync(destination, 'w'); - const req = https.get(options, (res) => { - res.on('data', (chunk) => { - fs.writeSync(fd, chunk); - }); - res.on('end', () => { - fs.closeSync(fd); - c(); - }); - }); - req.on('error', (reqErr) => { - console.error(`request to ${options.host}${options.path} failed.`); - console.error(reqErr); - e(reqErr); - }); - }); -} - -const MARKER_ARGUMENT = `_download_fork_`; - -function base64encode(str: string): string { - return Buffer.from(str, 'utf8').toString('base64'); -} - -function base64decode(str: string): string { - return Buffer.from(str, 'base64').toString('utf8'); -} - -export interface IDownloadRequestOptions { - host: string; - path: string; -} - -export interface IDownloadOptions { - requestOptions: IDownloadRequestOptions; - destinationPath: string; -} - -export function downloadInExternalProcess(options: IDownloadOptions): Promise { - const url = `https://${options.requestOptions.host}${options.requestOptions.path}`; - console.log(`Downloading ${url}...`); - return new Promise((c, e) => { - const child = cp.fork( - __filename, - [MARKER_ARGUMENT, base64encode(JSON.stringify(options))], - { - stdio: ['pipe', 'pipe', 'pipe', 'ipc'] - } - ); - let stderr: Buffer[] = []; - child.stderr.on('data', (chunk) => { - stderr.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); - }); - child.on('exit', (code) => { - if (code === 0) { - // normal termination - console.log(`Finished downloading ${url}.`); - c(); - } else { - // abnormal termination - console.error(Buffer.concat(stderr).toString()); - e(new Error(`Download of ${url} failed.`)); - } - }); - }); -} - -function _downloadInExternalProcess() { - let options: IDownloadOptions; - try { - options = JSON.parse(base64decode(process.argv[3])); - } catch (err) { - console.error(`Cannot read arguments`); - console.error(err); - process.exit(-1); - return; - } - - download(options.requestOptions, options.destinationPath).then(() => { - process.exit(0); - }, (err) => { - console.error(err); - process.exit(-2); - }); -} - -if (process.argv.length >= 4 && process.argv[2] === MARKER_ARGUMENT) { - // running as forked download script - _downloadInExternalProcess(); -} diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index 9c57ad6a8c..6bd412b8e4 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -5,14 +5,12 @@ 'use strict'; +const gulp = require('gulp'); const util = require('./lib/util'); const task = require('./lib/task'); const compilation = require('./lib/compilation'); -const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); // Full compile, including nls and inline sources in sourcemaps, for build -const compileClientBuildTask = task.define('compile-client-build', task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true))); - -// All Build -const compileBuildTask = task.define('compile-build', task.parallel(compileClientBuildTask, compileExtensionsBuildTask)); +const compileBuildTask = task.define('compile-build', task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true))); +gulp.task(compileBuildTask); exports.compileBuildTask = compileBuildTask; diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 0440e35a2e..b31ce45550 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -22,6 +22,7 @@ const root = path.dirname(__dirname); const commit = util.getVersion(root); const plumber = require('gulp-plumber'); const _ = require('underscore'); +const ext = require('./lib/extensions'); const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); @@ -135,11 +136,7 @@ const tasks = compilations.map(function (tsconfigFile) { gulp.task(compileTask); gulp.task(watchTask); - return { - compileTask: compileTask, - watchTask: watchTask, - compileBuildTask: compileBuildTask - }; + return { compileTask, watchTask, compileBuildTask }; }); const compileExtensionsTask = task.define('compile-extensions', task.parallel(...tasks.map(t => t.compileTask))); @@ -150,5 +147,17 @@ const watchExtensionsTask = task.define('watch-extensions', task.parallel(...tas gulp.task(watchExtensionsTask); exports.watchExtensionsTask = watchExtensionsTask; -const compileExtensionsBuildTask = task.define('compile-extensions-build', task.parallel(...tasks.map(t => t.compileBuildTask))); -exports.compileExtensionsBuildTask = compileExtensionsBuildTask; +const compileExtensionsBuildLegacyTask = task.define('compile-extensions-build-legacy', task.parallel(...tasks.map(t => t.compileBuildTask))); +gulp.task(compileExtensionsBuildLegacyTask); + +// Azure Pipelines + +const cleanExtensionsBuildTask = task.define('clean-extensions-build', util.rimraf('.build/extensions')); +const compileExtensionsBuildTask = task.define('compile-extensions-build', task.series( + cleanExtensionsBuildTask, + task.define('bundle-extensions-build', () => ext.packageLocalExtensionsStream().pipe(gulp.dest('.build'))), + task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream().pipe(gulp.dest('.build'))), +)); + +gulp.task(compileExtensionsBuildTask); +exports.compileExtensionsBuildTask = compileExtensionsBuildTask; \ No newline at end of file diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index a40ae911c6..38d774baf8 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -220,6 +220,17 @@ gulp.task('tslint', () => { function hygiene(some) { let errorCount = 0; + const productJson = es.through(function (file) { + const product = JSON.parse(file.contents.toString('utf8')); + + if (product.extensionsGallery) { + console.error('product.json: Contains "extensionsGallery"'); + errorCount++; + } + + this.emit('data', file); + }); + const indentation = es.through(function (file) { const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/); file.__lines = lines; @@ -320,8 +331,13 @@ function hygiene(some) { input = some; } + const productJsonFilter = filter('product.json', { restore: true }); + const result = input .pipe(filter(f => !f.stat.isDirectory())) + .pipe(productJsonFilter) + .pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson) + .pipe(productJsonFilter.restore) .pipe(filter(indentationFilter)) .pipe(indentation) .pipe(filter(copyrightFilter)) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index fe5cbcfc56..4739923fda 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -7,10 +7,137 @@ const gulp = require('gulp'); +const path = require('path'); +const es = require('event-stream'); +const util = require('./lib/util'); +const task = require('./lib/task'); +const vfs = require('vinyl-fs'); +const flatmap = require('gulp-flatmap'); +const gunzip = require('gulp-gunzip'); +const untar = require('gulp-untar'); +const File = require('vinyl'); +const fs = require('fs'); +const remote = require('gulp-remote-src'); +const rename = require('gulp-rename'); +const filter = require('gulp-filter'); +const cp = require('child_process'); + +const REPO_ROOT = path.dirname(__dirname); + +const BUILD_TARGETS = [ + { platform: 'win32', arch: 'ia32', pkgTarget: 'node8-win-x86' }, + { platform: 'win32', arch: 'x64', pkgTarget: 'node8-win-x64' }, + { platform: 'darwin', arch: null, pkgTarget: 'node8-macos-x64' }, + { platform: 'linux', arch: 'ia32', pkgTarget: 'node8-linux-x86' }, + { platform: 'linux', arch: 'x64', pkgTarget: 'node8-linux-x64' }, + { platform: 'linux', arch: 'armhf', pkgTarget: 'node8-linux-armv7' }, + { platform: 'linux', arch: 'alpine', pkgTarget: 'node8-linux-alpine' }, +]; + const noop = () => { return Promise.resolve(); }; gulp.task('vscode-reh-win32-ia32-min', noop); gulp.task('vscode-reh-win32-x64-min', noop); gulp.task('vscode-reh-darwin-min', noop); gulp.task('vscode-reh-linux-x64-min', noop); -gulp.task('vscode-reh-linux-arm-min', noop); +gulp.task('vscode-reh-linux-armhf-min', noop); +gulp.task('vscode-reh-linux-alpine-min', noop); + +gulp.task('vscode-reh-web-win32-ia32-min', noop); +gulp.task('vscode-reh-web-win32-x64-min', noop); +gulp.task('vscode-reh-web-darwin-min', noop); +gulp.task('vscode-reh-web-linux-x64-min', noop); +gulp.task('vscode-reh-web-linux-alpine-min', noop); + +function getNodeVersion() { + const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8'); + const target = /^target "(.*)"$/m.exec(yarnrc)[1]; + return target; +} + +const nodeVersion = getNodeVersion(); + +BUILD_TARGETS.forEach(({ platform, arch }) => { + if (platform === 'darwin') { + arch = 'x64'; + } + + gulp.task(task.define(`node-${platform}-${arch}`, () => { + const nodePath = path.join('.build', 'node', `v${nodeVersion}`, `${platform}-${arch}`); + + if (!fs.existsSync(nodePath)) { + util.rimraf(nodePath); + + return nodejs(platform, arch) + .pipe(vfs.dest(nodePath)); + } + + return Promise.resolve(null); + })); +}); + +const defaultNodeTask = gulp.task(`node-${process.platform}-${process.arch}`); + +if (defaultNodeTask) { + gulp.task(task.define('node', defaultNodeTask)); +} + +function nodejs(platform, arch) { + if (arch === 'ia32') { + arch = 'x86'; + } + + if (platform === 'win32') { + return remote(`/dist/v${nodeVersion}/win-${arch}/node.exe`, { base: 'https://nodejs.org' }) + .pipe(rename('node.exe')); + } + + if (arch === 'alpine') { + const contents = cp.execSync(`docker run --rm node:${nodeVersion}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' }); + return es.readArray([new File({ path: 'node', contents, stat: { mode: parseInt('755', 8) } })]); + } + + if (platform === 'darwin') { + arch = 'x64'; + } + + if (arch === 'armhf') { + arch = 'armv7l'; + } + + return remote(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org' }) + .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) + .pipe(filter('**/node')) + .pipe(util.setExecutableBit('**')) + .pipe(rename('node')); +} + +function mixinServer(watch) { + const packageJSONPath = path.join(path.dirname(__dirname), 'package.json'); + function exec(cmdLine) { + console.log(cmdLine); + cp.execSync(cmdLine, { stdio: "inherit" }); + } + function checkout() { + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString()); + exec('git fetch distro'); + exec(`git checkout ${packageJSON['distro']} -- src/vs/server resources/server`); + exec('git reset HEAD src/vs/server resources/server'); + } + checkout(); + if (watch) { + console.log('Enter watch mode (observing package.json)'); + const watcher = fs.watch(packageJSONPath); + watcher.addListener('change', () => { + try { + checkout(); + } catch (e) { + console.log(e); + } + }); + } + return Promise.resolve(); +} + +gulp.task(task.define('mixin-server', () => mixinServer(false))); +gulp.task(task.define('mixin-server-watch', () => mixinServer(true))); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index cf7da2b5f0..2cd367b515 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -21,7 +21,6 @@ const json = require('gulp-json-editor'); const _ = require('underscore'); const util = require('./lib/util'); const task = require('./lib/task'); -const ext = require('./lib/extensions'); const buildfile = require('../src/buildfile'); const common = require('./lib/optimize'); const root = path.dirname(__dirname); @@ -33,11 +32,13 @@ const i18n = require('./lib/i18n'); // {{SQL CARBON EDIT}} const serviceDownloader = require('service-downloader').ServiceDownloadProvider; const platformInfo = require('service-downloader/out/platform').PlatformInformation; +const ext = require('./lib/extensions'); // {{SQL CARBON EDIT}} - End const deps = require('./dependencies'); const getElectronVersion = require('./lib/electron').getElectronVersion; const createAsar = require('./lib/asar').createAsar; const { compileBuildTask } = require('./gulpfile.compile'); +const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); // @ts-ignore @@ -61,6 +62,7 @@ const nodeModules = [ const vscodeEntryPoints = _.flatten([ buildfile.entrypoint('vs/workbench/workbench.main'), buildfile.base, + buildfile.serviceWorker, buildfile.workbench, buildfile.code ]); @@ -74,7 +76,7 @@ const vscodeResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-window.js', 'out-build/paths.js', - 'out-build/vs/**/*.{svg,png,cur,html}', + 'out-build/vs/**/*.{svg,png,html}', '!out-build/vs/code/browser/**/*.html', 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/languagePacks.js', @@ -88,8 +90,8 @@ const vscodeResources = [ 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', 'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md', - 'out-build/vs/workbench/services/files/**/*.exe', - 'out-build/vs/workbench/services/files/**/*.md', + 'out-build/vs/platform/files/**/*.exe', + 'out-build/vs/platform/files/**/*.md', 'out-build/vs/code/electron-browser/workbench/**', 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', 'out-build/vs/code/electron-browser/issue/issueReporter.js', @@ -116,47 +118,32 @@ const vscodeResources = [ '!**/test/**' ]; -const BUNDLED_FILE_HEADER = [ - '/*!--------------------------------------------------------', - ' * Copyright (C) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' -].join('\n'); - const optimizeVSCodeTask = task.define('optimize-vscode', task.series( - task.parallel( - util.rimraf('out-vscode'), - compileBuildTask - ), + util.rimraf('out-vscode'), common.optimizeTask({ src: 'out-build', entryPoints: vscodeEntryPoints, resources: vscodeResources, loaderConfig: common.loaderConfig(nodeModules), - header: BUNDLED_FILE_HEADER, out: 'out-vscode', bundleInfo: undefined }) )); +gulp.task(optimizeVSCodeTask); - -const optimizeIndexJSTask = task.define('optimize-index-js', task.series( +const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +const minifyVSCodeTask = task.define('minify-vscode', task.series( optimizeVSCodeTask, + util.rimraf('out-vscode-min'), () => { const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); const contents = fs.readFileSync(fullpath).toString(); const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); fs.writeFileSync(fullpath, newContents); - } -)); - -const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; -const minifyVSCodeTask = task.define('minify-vscode', task.series( - task.parallel( - util.rimraf('out-vscode-min'), - optimizeIndexJSTask - ), + }, common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) )); +gulp.task(minifyVSCodeTask); // Package @@ -277,20 +264,17 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const src = gulp.src(out + '/**', { base: '.' }) .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + out), 'out'); })) - .pipe(util.setExecutableBit(['**/*.sh'])) - .pipe(filter(['**', '!**/*.js.map'])); - - const root = path.resolve(path.join(__dirname, '..')); + .pipe(util.setExecutableBit(['**/*.sh'])); // {{SQL CARBON EDIT}} ext.packageBuiltInExtensions(); - const sources = es.merge(src, ext.packageExtensionsStream({ - sourceMappingURLBase: sourceMappingURLBase - })); + const extensions = gulp.src('.build/extensions/**', { base: '.build', dot: true }); + + const sources = es.merge(src, extensions) + .pipe(filter(['**', '!**/*.js.map'], { dot: true })); let version = packageJson.version; - // @ts-ignore JSON checking: quality is optional const quality = product.quality; if (quality && quality !== 'stable') { @@ -327,13 +311,12 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const dataApi = gulp.src('src/sql/azdata.d.ts').pipe(rename('out/sql/azdata.d.ts')); const sqlopsAPI = gulp.src('src/sql/sqlops.d.ts').pipe(rename('out/sql/sqlops.d.ts')); - const depsSrc = [ - ..._.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])), - // @ts-ignore JSON checking: dependencies is optional - ..._.flatten(Object.keys(product.dependencies || {}).map(d => [`node_modules/${d}/**`, `!node_modules/${d}/**/{test,tests}/**`])) - ]; + const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true }); - const deps = gulp.src(depsSrc, { base: '.', dot: true }) + const root = path.resolve(path.join(__dirname, '..')); + const dependenciesSrc = _.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])); + + const deps = gulp.src(dependenciesSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json'])) .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar')); @@ -354,7 +337,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op ], { base: '.', dot: true }); let all = es.merge( - packageJsonStream, + packageJsonStream, productJsonStream, license, api, @@ -362,6 +345,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op copiedModules, dataApi, sqlopsAPI, + telemetry, sources, deps ); @@ -385,7 +369,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(util.skipDirectories()) .pipe(util.fixWin32DirectoryPermissions()) .pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true }))) - .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true })); // result = es.merge(result, gulp.src('resources/completions/**', { base: '.' })); @@ -402,6 +386,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) + .pipe(replace('@@DATAFOLDER@@', product.dataFolderName)) .pipe(replace('@@QUALITY@@', quality)) .pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; }))); @@ -448,13 +433,18 @@ BUILD_TARGETS.forEach(buildTarget => { const sourceFolderName = `out-vscode${dashed(minified)}`; const destinationFolderName = `azuredatastudio${dashed(platform)}${dashed(arch)}`; - const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( - task.parallel( - minified ? minifyVSCodeTask : optimizeVSCodeTask, - util.rimraf(path.join(buildRoot, destinationFolderName)) - ), + const vscodeTaskCI = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series( + util.rimraf(path.join(buildRoot, destinationFolderName)), packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) )); + gulp.task(vscodeTaskCI); + + const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( + compileBuildTask, + compileExtensionsBuildTask, + minified ? minifyVSCodeTask : optimizeVSCodeTask, + vscodeTaskCI + )); gulp.task(vscodeTask); }); }); @@ -483,6 +473,8 @@ const apiToken = process.env.TRANSIFEX_API_TOKEN; gulp.task(task.define( 'vscode-translations-push', task.series( + compileBuildTask, + compileExtensionsBuildTask, optimizeVSCodeTask, function () { const pathToMetadata = './out-vscode/nls.metadata.json'; @@ -502,6 +494,8 @@ gulp.task(task.define( gulp.task(task.define( 'vscode-translations-export', task.series( + compileBuildTask, + compileExtensionsBuildTask, optimizeVSCodeTask, function () { const pathToMetadata = './out-vscode/nls.metadata.json'; @@ -538,32 +532,6 @@ gulp.task('vscode-translations-import', function () { // {{SQL CARBON EDIT}} - End }); -// Sourcemaps - -gulp.task('upload-vscode-sourcemaps', () => { - const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) - .pipe(es.mapSync(f => { - f.path = `${f.base}/core/${f.relative}`; - return f; - })); - - const extensionsOut = gulp.src('extensions/**/out/**/*.map', { base: '.' }); - const extensionsDist = gulp.src('extensions/**/dist/**/*.map', { base: '.' }); - - return es.merge(vs, extensionsOut, extensionsDist) - .pipe(es.through(function (data) { - // debug - console.log('Uploading Sourcemap', data.relative); - this.emit('data', data); - })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'sourcemaps', - prefix: commit + '/' - })); -}); - // This task is only run for the MacOS build const generateVSCodeConfigurationTask = task.define('generate-vscode-configuration', () => { return new Promise((resolve, reject) => { diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index a563ed929f..7cd31a1a18 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -23,7 +23,7 @@ const commit = util.getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); function getDebPackageArch(arch) { - return { x64: 'amd64', ia32: 'i386', arm: 'armhf', arm64: "arm64" }[arch]; + return { x64: 'amd64', arm: 'armhf', arm64: "arm64" }[arch]; } function prepareDebPackage(arch) { @@ -115,7 +115,7 @@ function getRpmBuildPath(rpmArch) { } function getRpmPackageArch(arch) { - return { x64: 'x86_64', ia32: 'i386', arm: 'armhf', arm64: "arm64" }[arch]; + return { x64: 'x86_64', arm: 'armhf', arm64: "arm64" }[arch]; } function prepareRpmPackage(arch) { @@ -241,7 +241,6 @@ function buildSnapPackage(arch) { } const BUILD_TARGETS = [ - { arch: 'ia32' }, { arch: 'x64' }, { arch: 'arm' }, { arch: 'arm64' }, diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js new file mode 100644 index 0000000000..cddd86631d --- /dev/null +++ b/build/gulpfile.vscode.web.js @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); +const path = require('path'); +const es = require('event-stream'); +const util = require('./lib/util'); +const task = require('./lib/task'); +const common = require('./lib/optimize'); +const product = require('../product.json'); +const rename = require('gulp-rename'); +const filter = require('gulp-filter'); +const json = require('gulp-json-editor'); +const _ = require('underscore'); +const deps = require('./dependencies'); +const vfs = require('vinyl-fs'); +const packageJson = require('../package.json'); +const { compileBuildTask } = require('./gulpfile.compile'); + +const REPO_ROOT = path.dirname(__dirname); +const commit = util.getVersion(REPO_ROOT); +const BUILD_ROOT = path.dirname(REPO_ROOT); +const WEB_FOLDER = path.join(REPO_ROOT, 'remote', 'web'); + +const productionDependencies = deps.getProductionDependencies(WEB_FOLDER); + +const nodeModules = Object.keys(product.dependencies || {}) + .concat(_.uniq(productionDependencies.map(d => d.name))); + +const vscodeWebResources = [ + + // Workbench + 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,html}', + 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', + 'out-build/vs/**/markdown.css', + + // Webview + 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', + + // Excludes + '!out-build/vs/**/{node,electron-browser,electron-main}/**', + '!out-build/vs/editor/standalone/**', + '!out-build/vs/workbench/**/*-tb.png', + '!**/test/**' +]; + +const buildfile = require('../src/buildfile'); + +const vscodeWebEntryPoints = [ + buildfile.workbenchWeb, + buildfile.serviceWorker, + buildfile.keyboardMaps, + buildfile.base +]; + +const optimizeVSCodeWebTask = task.define('optimize-vscode-web', task.series( + util.rimraf('out-vscode-web'), + common.optimizeTask({ + src: 'out-build', + entryPoints: _.flatten(vscodeWebEntryPoints), + otherSources: [], + resources: vscodeWebResources, + loaderConfig: common.loaderConfig(nodeModules), + out: 'out-vscode-web', + bundleInfo: undefined + }) +)); + +const minifyVSCodeWebTask = task.define('minify-vscode-web', task.series( + optimizeVSCodeWebTask, + util.rimraf('out-vscode-web-min'), + common.minifyTask('out-vscode-web', `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`) +)); +gulp.task(minifyVSCodeWebTask); + +function packageTask(sourceFolderName, destinationFolderName) { + const destination = path.join(BUILD_ROOT, destinationFolderName); + + return () => { + const src = gulp.src(sourceFolderName + '/**', { base: '.' }) + .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); })) + .pipe(filter(['**', '!**/*.js.map'])); + + const sources = es.merge(src); + + let version = packageJson.version; + const quality = product.quality; + + if (quality && quality !== 'stable') { + version += '-' + quality; + } + + const name = product.nameShort; + const packageJsonStream = gulp.src(['remote/web/package.json'], { base: 'remote/web' }) + .pipe(json({ name, version })); + + const date = new Date().toISOString(); + + const productJsonStream = gulp.src(['product.json'], { base: '.' }) + .pipe(json({ commit, date })); + + const license = gulp.src(['remote/LICENSE'], { base: 'remote' }); + + const dependenciesSrc = _.flatten(productionDependencies.map(d => path.relative(REPO_ROOT, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`, `!${d}/.bin/**`])); + + const deps = gulp.src(dependenciesSrc, { base: 'remote/web', dot: true }) + .pipe(filter(['**', '!**/package-lock.json'])) + .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))); + + const favicon = gulp.src('resources/server/favicon.ico', { base: 'resources/server' }); + + let all = es.merge( + packageJsonStream, + productJsonStream, + license, + sources, + deps, + favicon + ); + + let result = all + .pipe(util.skipDirectories()) + .pipe(util.fixWin32DirectoryPermissions()); + + return result.pipe(vfs.dest(destination)); + }; +} + +const dashed = (str) => (str ? `-${str}` : ``); + +['', 'min'].forEach(minified => { + const sourceFolderName = `out-vscode-web${dashed(minified)}`; + const destinationFolderName = `vscode-web`; + + const vscodeWebTaskCI = task.define(`vscode-web${dashed(minified)}-ci`, task.series( + minified ? minifyVSCodeWebTask : optimizeVSCodeWebTask, + util.rimraf(path.join(BUILD_ROOT, destinationFolderName)), + packageTask(sourceFolderName, destinationFolderName) + )); + gulp.task(vscodeWebTaskCI); + + const vscodeWebTask = task.define(`vscode-web${dashed(minified)}`, task.series( + compileBuildTask, + vscodeWebTaskCI + )); + gulp.task(vscodeWebTask); +}); \ No newline at end of file diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 33145c2c4c..a9605cd17c 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -26,7 +26,7 @@ const zipDir = arch => path.join(repoPath, '.build', `win32-${arch}`, 'archive') const zipPath = arch => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`); const setupDir = (arch, target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`); const issPath = path.join(__dirname, 'win32', 'code.iss'); -const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup-compiler'))), 'bin', 'ISCC.exe'); +const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup'))), 'bin', 'ISCC.exe'); // const signPS1 = path.join(repoPath, 'build', 'azure-pipelines', 'win32', 'sign.ps1'); function packageInnoSetup(iss, options, cb) { diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 1c9964bc2c..d0230384fb 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -112,7 +112,6 @@ class MonacoGenerator { this._executeSoonTimer = null; this._isWatch = isWatch; this.stream = es.through(); - this._watchers = []; this._watchedFiles = {}; let onWillReadFile = (moduleId, filePath) => { if (!this._isWatch) { @@ -122,26 +121,10 @@ class MonacoGenerator { return; } this._watchedFiles[filePath] = true; - const watcher = fs.watch(filePath); - watcher.addListener('change', () => { + fs.watchFile(filePath, () => { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); - watcher.addListener('error', (err) => { - console.error(`Encountered error while watching ${filePath}.`); - console.log(err); - delete this._watchedFiles[filePath]; - for (let i = 0; i < this._watchers.length; i++) { - if (this._watchers[i] === watcher) { - this._watchers.splice(i, 1); - break; - } - } - watcher.close(); - this._declarationResolver.invalidateCache(moduleId); - this._executeSoon(); - }); - this._watchers.push(watcher); }; this._fsProvider = new class extends monacodts.FSProvider { readFileSync(moduleId, filePath) { @@ -151,11 +134,9 @@ class MonacoGenerator { }; this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); if (this._isWatch) { - const recipeWatcher = fs.watch(monacodts.RECIPE_PATH); - recipeWatcher.addListener('change', () => { + fs.watchFile(monacodts.RECIPE_PATH, () => { this._executeSoon(); }); - this._watchers.push(recipeWatcher); } } _executeSoon() { @@ -168,9 +149,6 @@ class MonacoGenerator { this.execute(); }, 20); } - dispose() { - this._watchers.forEach(watcher => watcher.close()); - } _run() { let r = monacodts.run3(this._declarationResolver); if (!r && !this._isWatch) { diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 81a42c3cab..cfc145f5c7 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -137,7 +137,6 @@ class MonacoGenerator { private readonly _isWatch: boolean; public readonly stream: NodeJS.ReadWriteStream; - private readonly _watchers: fs.FSWatcher[]; private readonly _watchedFiles: { [filePath: string]: boolean; }; private readonly _fsProvider: monacodts.FSProvider; private readonly _declarationResolver: monacodts.DeclarationResolver; @@ -145,7 +144,6 @@ class MonacoGenerator { constructor(isWatch: boolean) { this._isWatch = isWatch; this.stream = es.through(); - this._watchers = []; this._watchedFiles = {}; let onWillReadFile = (moduleId: string, filePath: string) => { if (!this._isWatch) { @@ -156,26 +154,10 @@ class MonacoGenerator { } this._watchedFiles[filePath] = true; - const watcher = fs.watch(filePath); - watcher.addListener('change', () => { + fs.watchFile(filePath, () => { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); - watcher.addListener('error', (err) => { - console.error(`Encountered error while watching ${filePath}.`); - console.log(err); - delete this._watchedFiles[filePath]; - for (let i = 0; i < this._watchers.length; i++) { - if (this._watchers[i] === watcher) { - this._watchers.splice(i, 1); - break; - } - } - watcher.close(); - this._declarationResolver.invalidateCache(moduleId); - this._executeSoon(); - }); - this._watchers.push(watcher); }; this._fsProvider = new class extends monacodts.FSProvider { public readFileSync(moduleId: string, filePath: string): Buffer { @@ -186,11 +168,9 @@ class MonacoGenerator { this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); if (this._isWatch) { - const recipeWatcher = fs.watch(monacodts.RECIPE_PATH); - recipeWatcher.addListener('change', () => { + fs.watchFile(monacodts.RECIPE_PATH, () => { this._executeSoon(); }); - this._watchers.push(recipeWatcher); } } @@ -206,10 +186,6 @@ class MonacoGenerator { }, 20); } - public dispose(): void { - this._watchers.forEach(watcher => watcher.close()); - } - private _run(): monacodts.IMonacoDeclarationResult | null { let r = monacodts.run3(this._declarationResolver); if (!r && !this._isWatch) { diff --git a/build/lib/electron.js b/build/lib/electron.js index ee737e9d60..2c981fe95c 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -24,7 +24,7 @@ module.exports.getElectronVersion = getElectronVersion; if (require.main === module) { const version = getElectronVersion(); const versionFile = path.join(root, '.build', 'electron', 'version'); - const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `v${version}`; + const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; process.exit(isUpToDate ? 0 : 1); } diff --git a/build/lib/extensions.js b/build/lib/extensions.js index c28ae3f7f0..a97c65b0b4 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -23,85 +23,20 @@ const buffer = require('gulp-buffer'); const json = require("gulp-json-editor"); const webpack = require('webpack'); const webpackGulp = require('webpack-stream'); -const root = path.resolve(path.join(__dirname, '..', '..')); -// {{SQL CARBON EDIT}} -const _ = require("underscore"); -const vfs = require("vinyl-fs"); -const deps = require('../dependencies'); -const extensionsRoot = path.join(root, 'extensions'); -const extensionsProductionDependencies = deps.getProductionDependencies(extensionsRoot); -function packageBuiltInExtensions() { - const sqlBuiltInLocalExtensionDescriptions = 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 }; - }) - .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) - .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) >= 0); - const visxDirectory = path.join(path.dirname(root), 'vsix'); - try { - if (!fs.existsSync(visxDirectory)) { - fs.mkdirSync(visxDirectory); - } - } - catch (err) { - // don't fail the build if the output directory already exists - console.warn(err); - } - sqlBuiltInLocalExtensionDescriptions.forEach(element => { - let pkgJson = JSON.parse(fs.readFileSync(path.join(element.path, 'package.json'), { encoding: 'utf8' })); - const packagePath = path.join(visxDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`); - console.info('Creating vsix for ' + element.path + ' result:' + packagePath); - vsce.createVSIX({ - cwd: element.path, - packagePath: packagePath, - useYarn: true - }); - }); -} -exports.packageBuiltInExtensions = packageBuiltInExtensions; -function packageExtensionTask(extensionName, platform, arch) { - var destination = path.join(path.dirname(root), 'azuredatastudio') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); - if (platform === 'darwin') { - destination = path.join(destination, 'Azure Data Studio.app', 'Contents', 'Resources', 'app', 'extensions', extensionName); - } - else { - destination = path.join(destination, 'resources', 'app', 'extensions', extensionName); - } - platform = platform || process.platform; - return () => { - const root = path.resolve(path.join(__dirname, '../..')); - const localExtensionDescriptions = 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 }; - }) - .filter(({ name }) => extensionName === name); - const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { - return fromLocal(extension.path); - })); - let result = localExtensions - .pipe(util2.skipDirectories()) - .pipe(util2.fixWin32DirectoryPermissions()) - .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); - return result.pipe(vfs.dest(destination)); - }; -} -exports.packageExtensionTask = packageExtensionTask; -// {{SQL CARBON EDIT}} - End -function fromLocal(extensionPath, sourceMappingURLBase) { +const util = require('./util'); +const root = path.dirname(path.dirname(__dirname)); +const commit = util.getVersion(root); +const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +function fromLocal(extensionPath) { const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); if (fs.existsSync(webpackFilename)) { - return fromLocalWebpack(extensionPath, sourceMappingURLBase); + return fromLocalWebpack(extensionPath); } else { return fromLocalNormal(extensionPath); } } -function fromLocalWebpack(extensionPath, sourceMappingURLBase) { +function fromLocalWebpack(extensionPath) { const result = es.through(); const packagedDependencies = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); @@ -146,7 +81,7 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { return data; })) .pipe(packageJsonFilter.restore); - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { + const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { const webpackDone = (err, stats) => { fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); if (err) { @@ -172,22 +107,14 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { // source map handling: // * rewrite sourceMappingURL // * save to disk so that upload-task picks this up - if (sourceMappingURLBase) { - 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'); - if (/\.js\.map$/.test(data.path)) { - if (!fs.existsSync(path.dirname(data.path))) { - fs.mkdirSync(path.dirname(data.path)); - } - fs.writeFileSync(data.path, data.contents); - } - } + 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(sequence(webpackStreams), patchFilesStream) + es.merge(...webpackStreams, patchFilesStream) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -273,30 +200,7 @@ if (process.env['VSCODE_QUALITY'] === 'stable') { sqlBuiltInExtensions.push('resource-deployment'); } const builtInExtensions = require('../builtInExtensions.json'); -/** - * We're doing way too much stuff at once, with webpack et al. So much stuff - * that while downloading extensions from the marketplace, node js doesn't get enough - * stack frames to complete the download in under 2 minutes, at which point the - * marketplace server cuts off the http request. So, we sequentialize the extensino tasks. - */ -function sequence(streamProviders) { - const result = es.through(); - function pop() { - if (streamProviders.length === 0) { - result.emit('end'); - } - else { - const fn = streamProviders.shift(); - fn() - .on('end', function () { setTimeout(pop, 0); }) - .pipe(result, { end: false }); - } - } - pop(); - return result; -} -function packageExtensionsStream(optsIn) { - const opts = optsIn || {}; +function packageLocalExtensionsStream() { const localExtensionDescriptions = glob.sync('extensions/*/package.json') .map(manifestPath => { const extensionPath = path.dirname(path.join(root, manifestPath)); @@ -304,33 +208,86 @@ function packageExtensionsStream(optsIn) { return { name: extensionName, path: extensionPath }; }) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) - // {{SQL CARBON EDIT}} - .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) === -1); - const localExtensions = () => sequence([...localExtensionDescriptions.map(extension => () => { - return fromLocal(extension.path, opts.sourceMappingURLBase) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - })]); - // {{SQL CARBON EDIT}} - const extensionDepsSrc = [ - ..._.flatten(extensionsProductionDependencies.map((d) => path.relative(root, d.path)).map((d) => [`${d}/**`, `!${d}/**/{test,tests}/**`])), - ]; - const localExtensionDependencies = () => gulp.src(extensionDepsSrc, { base: '.', dot: true }) - .pipe(filter(['**', '!**/package-lock.json'])); - // Original code commented out here - // const localExtensionDependencies = () => gulp.src('extensions/node_modules/**', { base: '.' }); - // const marketplaceExtensions = () => es.merge( - // ...builtInExtensions - // .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) - // .map(extension => { - // return fromMarketplace(extension.name, extension.version, extension.metadata) - // .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - // }) - // ); - return sequence([localExtensions, localExtensionDependencies,]) - .pipe(util2.setExecutableBit(['**/*.sh'])) - .pipe(filter(['**', '!**/*.js.map'])); - // {{SQL CARBON EDIT}} - End + .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) === -1); // {{SQL CARBON EDIT}} add aditional filter + const nodeModules = gulp.src('extensions/node_modules/**', { base: '.' }); + const localExtensions = localExtensionDescriptions.map(extension => { + return fromLocal(extension.path) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + }); + return es.merge(nodeModules, ...localExtensions) + .pipe(util2.setExecutableBit(['**/*.sh'])); } -exports.packageExtensionsStream = packageExtensionsStream; +exports.packageLocalExtensionsStream = packageLocalExtensionsStream; +function packageMarketplaceExtensionsStream() { + const extensions = builtInExtensions.map(extension => { + return fromMarketplace(extension.name, extension.version, extension.metadata) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + }); + return es.merge(extensions) + .pipe(util2.setExecutableBit(['**/*.sh'])); +} +exports.packageMarketplaceExtensionsStream = packageMarketplaceExtensionsStream; +const vfs = require("vinyl-fs"); +function packageBuiltInExtensions() { + const sqlBuiltInLocalExtensionDescriptions = 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 }; + }) + .filter(({ name }) => excludedExtensions.indexOf(name) === -1) + .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) + .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) >= 0); + const visxDirectory = path.join(path.dirname(root), 'vsix'); + try { + if (!fs.existsSync(visxDirectory)) { + fs.mkdirSync(visxDirectory); + } + } + catch (err) { + // don't fail the build if the output directory already exists + console.warn(err); + } + sqlBuiltInLocalExtensionDescriptions.forEach(element => { + let pkgJson = JSON.parse(fs.readFileSync(path.join(element.path, 'package.json'), { encoding: 'utf8' })); + const packagePath = path.join(visxDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`); + console.info('Creating vsix for ' + element.path + ' result:' + packagePath); + vsce.createVSIX({ + cwd: element.path, + packagePath: packagePath, + useYarn: true + }); + }); +} +exports.packageBuiltInExtensions = packageBuiltInExtensions; +function packageExtensionTask(extensionName, platform, arch) { + var destination = path.join(path.dirname(root), 'azuredatastudio') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); + if (platform === 'darwin') { + destination = path.join(destination, 'Azure Data Studio.app', 'Contents', 'Resources', 'app', 'extensions', extensionName); + } + else { + destination = path.join(destination, 'resources', 'app', 'extensions', extensionName); + } + platform = platform || process.platform; + return () => { + const root = path.resolve(path.join(__dirname, '../..')); + const localExtensionDescriptions = 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 }; + }) + .filter(({ name }) => extensionName === name); + const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { + return fromLocal(extension.path); + })); + let result = localExtensions + .pipe(util2.skipDirectories()) + .pipe(util2.fixWin32DirectoryPermissions()) + .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + return result.pipe(vfs.dest(destination)); + }; +} +exports.packageExtensionTask = packageExtensionTask; +// {{SQL CARBON EDIT}} - End diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 017c995040..e1c4c77dac 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -23,91 +23,21 @@ const buffer = require('gulp-buffer'); import json = require('gulp-json-editor'); const webpack = require('webpack'); const webpackGulp = require('webpack-stream'); +const util = require('./util'); +const root = path.dirname(path.dirname(__dirname)); +const commit = util.getVersion(root); +const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; -const root = path.resolve(path.join(__dirname, '..', '..')); - -// {{SQL CARBON EDIT}} -import * as _ from 'underscore'; -import * as vfs from 'vinyl-fs'; -const deps = require('../dependencies'); -const extensionsRoot = path.join(root, 'extensions'); -const extensionsProductionDependencies = deps.getProductionDependencies(extensionsRoot); - -export function packageBuiltInExtensions() { - const sqlBuiltInLocalExtensionDescriptions = 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 }; - }) - .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) - .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) >= 0); - const visxDirectory = path.join(path.dirname(root), 'vsix'); - try { - if (!fs.existsSync(visxDirectory)) { - fs.mkdirSync(visxDirectory); - } - } catch (err) { - // don't fail the build if the output directory already exists - console.warn(err); - } - sqlBuiltInLocalExtensionDescriptions.forEach(element => { - let pkgJson = JSON.parse(fs.readFileSync(path.join(element.path, 'package.json'), { encoding: 'utf8' })); - const packagePath = path.join(visxDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`); - console.info('Creating vsix for ' + element.path + ' result:' + packagePath); - vsce.createVSIX({ - cwd: element.path, - packagePath: packagePath, - useYarn: true - }); - }); -} - -export function packageExtensionTask(extensionName: string, platform: string, arch: string) { - var destination = path.join(path.dirname(root), 'azuredatastudio') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); - if (platform === 'darwin') { - destination = path.join(destination, 'Azure Data Studio.app', 'Contents', 'Resources', 'app', 'extensions', extensionName); - } else { - destination = path.join(destination, 'resources', 'app', 'extensions', extensionName); - } - - platform = platform || process.platform; - - return () => { - const root = path.resolve(path.join(__dirname, '../..')); - const localExtensionDescriptions = 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 }; - }) - .filter(({ name }) => extensionName === name); - - const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { - return fromLocal(extension.path); - })); - - let result = localExtensions - .pipe(util2.skipDirectories()) - .pipe(util2.fixWin32DirectoryPermissions()) - .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); - - return result.pipe(vfs.dest(destination)); - }; -} -// {{SQL CARBON EDIT}} - End - -function fromLocal(extensionPath: string, sourceMappingURLBase?: string): Stream { +function fromLocal(extensionPath: string): Stream { const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); if (fs.existsSync(webpackFilename)) { - return fromLocalWebpack(extensionPath, sourceMappingURLBase); + return fromLocalWebpack(extensionPath); } else { return fromLocalNormal(extensionPath); } } -function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | undefined): Stream { +function fromLocalWebpack(extensionPath: string): Stream { const result = es.through(); const packagedDependencies: string[] = []; @@ -163,7 +93,7 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | .pipe(packageJsonFilter.restore); - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { + const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { const webpackDone = (err: any, stats: any) => { fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); @@ -195,24 +125,16 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | // source map handling: // * rewrite sourceMappingURL // * save to disk so that upload-task picks this up - if (sourceMappingURLBase) { - 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'); + 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'); - if (/\.js\.map$/.test(data.path)) { - if (!fs.existsSync(path.dirname(data.path))) { - fs.mkdirSync(path.dirname(data.path)); - } - fs.writeFileSync(data.path, data.contents); - } - } this.emit('data', data); })); }); - es.merge(sequence(webpackStreams), patchFilesStream) + es.merge(...webpackStreams, patchFilesStream) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -282,14 +204,6 @@ export function fromMarketplace(extensionName: string, version: string, metadata .pipe(packageJsonFilter.restore); } -interface IPackageExtensionsOptions { - /** - * Set to undefined to package all of them. - */ - desiredExtensions?: string[]; - sourceMappingURLBase?: string; -} - const excludedExtensions = [ 'vscode-api-tests', 'vscode-colorize-tests', @@ -332,33 +246,7 @@ interface IBuiltInExtension { const builtInExtensions: IBuiltInExtension[] = require('../builtInExtensions.json'); -/** - * We're doing way too much stuff at once, with webpack et al. So much stuff - * that while downloading extensions from the marketplace, node js doesn't get enough - * stack frames to complete the download in under 2 minutes, at which point the - * marketplace server cuts off the http request. So, we sequentialize the extensino tasks. - */ -function sequence(streamProviders: { (): Stream }[]): Stream { - const result = es.through(); - - function pop() { - if (streamProviders.length === 0) { - result.emit('end'); - } else { - const fn = streamProviders.shift()!; - fn() - .on('end', function () { setTimeout(pop, 0); }) - .pipe(result, { end: false }); - } - } - - pop(); - return result; -} - -export function packageExtensionsStream(optsIn?: IPackageExtensionsOptions): NodeJS.ReadWriteStream { - const opts = optsIn || {}; - +export function packageLocalExtensionsStream(): NodeJS.ReadWriteStream { const localExtensionDescriptions = (glob.sync('extensions/*/package.json')) .map(manifestPath => { const extensionPath = path.dirname(path.join(root, manifestPath)); @@ -366,38 +254,94 @@ export function packageExtensionsStream(optsIn?: IPackageExtensionsOptions): Nod return { name: extensionName, path: extensionPath }; }) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) - // {{SQL CARBON EDIT}} - .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) === -1); + .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) === -1); // {{SQL CARBON EDIT}} add aditional filter - const localExtensions = () => sequence([...localExtensionDescriptions.map(extension => () => { - return fromLocal(extension.path, opts.sourceMappingURLBase) + const nodeModules = gulp.src('extensions/node_modules/**', { base: '.' }); + const localExtensions = localExtensionDescriptions.map(extension => { + return fromLocal(extension.path) .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - })]); + }); - // {{SQL CARBON EDIT}} - const extensionDepsSrc = [ - ..._.flatten(extensionsProductionDependencies.map((d: any) => path.relative(root, d.path)).map((d: any) => [`${d}/**`, `!${d}/**/{test,tests}/**`])), - ]; - - const localExtensionDependencies = () => gulp.src(extensionDepsSrc, { base: '.', dot: true }) - .pipe(filter(['**', '!**/package-lock.json'])); - - // Original code commented out here - // const localExtensionDependencies = () => gulp.src('extensions/node_modules/**', { base: '.' }); - - // const marketplaceExtensions = () => es.merge( - // ...builtInExtensions - // .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) - // .map(extension => { - // return fromMarketplace(extension.name, extension.version, extension.metadata) - // .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - // }) - // ); - - return sequence([localExtensions, localExtensionDependencies, /*marketplaceExtensions*/]) - .pipe(util2.setExecutableBit(['**/*.sh'])) - .pipe(filter(['**', '!**/*.js.map'])); - // {{SQL CARBON EDIT}} - End + return es.merge(nodeModules, ...localExtensions) + .pipe(util2.setExecutableBit(['**/*.sh'])); } + +export function packageMarketplaceExtensionsStream(): NodeJS.ReadWriteStream { + const extensions = builtInExtensions.map(extension => { + return fromMarketplace(extension.name, extension.version, extension.metadata) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + }); + + return es.merge(extensions) + .pipe(util2.setExecutableBit(['**/*.sh'])); +} + +// {{SQL CARBON EDIT}} +import * as _ from 'underscore'; +import * as vfs from 'vinyl-fs'; + +export function packageBuiltInExtensions() { + const sqlBuiltInLocalExtensionDescriptions = 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 }; + }) + .filter(({ name }) => excludedExtensions.indexOf(name) === -1) + .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) + .filter(({ name }) => sqlBuiltInExtensions.indexOf(name) >= 0); + const visxDirectory = path.join(path.dirname(root), 'vsix'); + try { + if (!fs.existsSync(visxDirectory)) { + fs.mkdirSync(visxDirectory); + } + } catch (err) { + // don't fail the build if the output directory already exists + console.warn(err); + } + sqlBuiltInLocalExtensionDescriptions.forEach(element => { + let pkgJson = JSON.parse(fs.readFileSync(path.join(element.path, 'package.json'), { encoding: 'utf8' })); + const packagePath = path.join(visxDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`); + console.info('Creating vsix for ' + element.path + ' result:' + packagePath); + vsce.createVSIX({ + cwd: element.path, + packagePath: packagePath, + useYarn: true + }); + }); +} + +export function packageExtensionTask(extensionName: string, platform: string, arch: string) { + var destination = path.join(path.dirname(root), 'azuredatastudio') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); + if (platform === 'darwin') { + destination = path.join(destination, 'Azure Data Studio.app', 'Contents', 'Resources', 'app', 'extensions', extensionName); + } else { + destination = path.join(destination, 'resources', 'app', 'extensions', extensionName); + } + + platform = platform || process.platform; + + return () => { + const root = path.resolve(path.join(__dirname, '../..')); + const localExtensionDescriptions = 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 }; + }) + .filter(({ name }) => extensionName === name); + + const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { + return fromLocal(extension.path); + })); + + let result = localExtensions + .pipe(util2.skipDirectories()) + .pipe(util2.fixWin32DirectoryPermissions()) + .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + + return result.pipe(vfs.dest(destination)); + }; +} +// {{SQL CARBON EDIT}} - End \ No newline at end of file diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 22d3d165c3..32b0e6fb4c 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -38,10 +38,6 @@ "name": "vs/workbench/contrib/codeEditor", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/codeinset", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/callHierarchy", "project": "vscode-workbench" diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 7bb98cbab7..616a672ce4 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -25,7 +25,7 @@ function log(message: any, ...rest: any[]): void { export interface Language { id: string; // language id, e.g. zh-tw, de - translationId?: string; // language id used in translation tools, e.g zh-hant, de (optional, if not set, the id is used) + translationId?: string; // language id used in translation tools, e.g. zh-hant, de (optional, if not set, the id is used) folderName?: string; // language specific folder name, e.g. cht, deu (optional, if not set, the id is used) } diff --git a/build/lib/node.js b/build/lib/node.js new file mode 100644 index 0000000000..4e723015ca --- /dev/null +++ b/build/lib/node.js @@ -0,0 +1,15 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const path = require("path"); +const fs = require("fs"); +const root = path.dirname(path.dirname(__dirname)); +const yarnrcPath = path.join(root, 'remote', '.yarnrc'); +const yarnrc = fs.readFileSync(yarnrcPath, 'utf8'); +const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)[1]; +const node = process.platform === 'win32' ? 'node.exe' : 'node'; +const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node); +console.log(nodePath); diff --git a/build/lib/node.ts b/build/lib/node.ts new file mode 100644 index 0000000000..651fa9eebc --- /dev/null +++ b/build/lib/node.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 * as path from 'path'; +import * as fs from 'fs'; + +const root = path.dirname(path.dirname(__dirname)); +const yarnrcPath = path.join(root, 'remote', '.yarnrc'); +const yarnrc = fs.readFileSync(yarnrcPath, 'utf8'); +const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)![1]; +const node = process.platform === 'win32' ? 'node.exe' : 'node'; +const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node); + +console.log(nodePath); \ No newline at end of file diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 71577f4e63..52435e9051 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -113,12 +113,17 @@ function toBundleStream(src, bundledFileHeader, bundles) { return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); })); } +const DEFAULT_FILE_HEADER = [ + '/*!--------------------------------------------------------', + ' * Copyright (C) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' +].join('\n'); function optimizeTask(opts) { const src = opts.src; const entryPoints = opts.entryPoints; const resources = opts.resources; const loaderConfig = opts.loaderConfig; - const bundledFileHeader = opts.header; + const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); const out = opts.out; return function () { diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 1d67928e87..9c3612e2ce 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -156,7 +156,7 @@ export interface IOptimizeTaskOpts { /** * (basically the Copyright treatment) */ - header: string; + header?: string; /** * (emit bundleInfo.json file) */ @@ -171,12 +171,18 @@ export interface IOptimizeTaskOpts { languages?: Language[]; } +const DEFAULT_FILE_HEADER = [ + '/*!--------------------------------------------------------', + ' * Copyright (C) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' +].join('\n'); + export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { const src = opts.src; const entryPoints = opts.entryPoints; const resources = opts.resources; const loaderConfig = opts.loaderConfig; - const bundledFileHeader = opts.header; + const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader); const out = opts.out; diff --git a/build/lib/test/i18n.test.js b/build/lib/test/i18n.test.js index cebbc25dbc..7dc614c29a 100644 --- a/build/lib/test/i18n.test.js +++ b/build/lib/test/i18n.test.js @@ -27,14 +27,14 @@ suite('XLF Parser Tests', () => { }); test('JSON file source path to Transifex resource match', () => { const editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench'; - const platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; + const platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/textfile', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; assert.deepEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); assert.deepEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); assert.deepEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); assert.deepEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); - assert.deepEqual(i18n.getResource('vs/workbench/services/files/node/fileService'), workbenchServices); + assert.deepEqual(i18n.getResource('vs/workbench/services/textfile/node/testFileService'), workbenchServices); assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); }); }); diff --git a/build/lib/test/i18n.test.ts b/build/lib/test/i18n.test.ts index 979493bd3e..0678bd9451 100644 --- a/build/lib/test/i18n.test.ts +++ b/build/lib/test/i18n.test.ts @@ -39,7 +39,7 @@ suite('XLF Parser Tests', () => { base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, - workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, + workbenchServices = { name: 'vs/workbench/services/textfile', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject}; assert.deepEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); @@ -48,7 +48,7 @@ suite('XLF Parser Tests', () => { assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); assert.deepEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); - assert.deepEqual(i18n.getResource('vs/workbench/services/files/node/fileService'), workbenchServices); + assert.deepEqual(i18n.getResource('vs/workbench/services/textfile/node/testFileService'), workbenchServices); assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); }); }); \ No newline at end of file diff --git a/build/lib/util.js b/build/lib/util.js index 175b6934b2..7d38cb229d 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -13,8 +13,6 @@ const fs = require("fs"); const _rimraf = require("rimraf"); const git = require("./git"); const VinylFile = require("vinyl"); -const download_1 = require("../download/download"); -const REPO_ROOT = path.join(__dirname, '../../'); const NoCancellationToken = { isCancellationRequested: () => false }; function incremental(streamProvider, initial, supportsCancellation) { const input = es.through(); @@ -68,6 +66,9 @@ function fixWin32DirectoryPermissions() { exports.fixWin32DirectoryPermissions = fixWin32DirectoryPermissions; function setExecutableBit(pattern) { const setBit = es.mapSync(f => { + if (!f.stat) { + f.stat = { isFile() { return true; } }; + } f.stat.mode = /* 100755 */ 33261; return f; }); @@ -177,7 +178,7 @@ function rimraf(dir) { return cb(err); }); }; - retry.taskName = `clean-${path.basename(dir)}`; + retry.taskName = `clean-${path.basename(dir).toLowerCase()}`; return retry; } exports.rimraf = rimraf; @@ -218,38 +219,3 @@ function versionStringToNumber(versionStr) { return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); } exports.versionStringToNumber = versionStringToNumber; -function download(requestOptions) { - const result = es.through(); - const filename = path.join(REPO_ROOT, `.build/tmp-${Date.now()}-${path.posix.basename(requestOptions.path)}`); - const opts = { - requestOptions: requestOptions, - destinationPath: filename - }; - download_1.downloadInExternalProcess(opts).then(() => { - fs.stat(filename, (err, stat) => { - if (err) { - result.emit('error', err); - return; - } - fs.readFile(filename, (err, data) => { - if (err) { - result.emit('error', err); - return; - } - fs.unlink(filename, () => { - result.emit('data', new VinylFile({ - path: path.normalize(requestOptions.path), - stat: stat, - base: path.normalize(requestOptions.path), - contents: data - })); - result.emit('end'); - }); - }); - }); - }, (err) => { - result.emit('error', err); - }); - return result; -} -exports.download = download; diff --git a/build/lib/util.ts b/build/lib/util.ts index 594086a5c8..f31d8c0cbc 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -17,9 +17,6 @@ import * as git from './git'; import * as VinylFile from 'vinyl'; import { ThroughStream } from 'through'; import * as sm from 'source-map'; -import { IDownloadOptions, downloadInExternalProcess, IDownloadRequestOptions } from '../download/download'; - -const REPO_ROOT = path.join(__dirname, '../../'); export interface ICancellationToken { isCancellationRequested(): boolean; @@ -96,6 +93,9 @@ export function fixWin32DirectoryPermissions(): NodeJS.ReadWriteStream { export function setExecutableBit(pattern?: string | string[]): NodeJS.ReadWriteStream { const setBit = es.mapSync(f => { + if (!f.stat) { + f.stat = { isFile() { return true; } } as any; + } f.stat.mode = /* 100755 */ 33261; return f; }); @@ -234,7 +234,7 @@ export function rimraf(dir: string): (cb: any) => void { return cb(err); }); }; - retry.taskName = `clean-${path.basename(dir)}`; + retry.taskName = `clean-${path.basename(dir).toLowerCase()}`; return retry; } @@ -281,38 +281,3 @@ export function versionStringToNumber(versionStr: string) { return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); } - -export function download(requestOptions: IDownloadRequestOptions): NodeJS.ReadWriteStream { - const result = es.through(); - const filename = path.join(REPO_ROOT, `.build/tmp-${Date.now()}-${path.posix.basename(requestOptions.path)}`); - const opts: IDownloadOptions = { - requestOptions: requestOptions, - destinationPath: filename - }; - downloadInExternalProcess(opts).then(() => { - fs.stat(filename, (err, stat) => { - if (err) { - result.emit('error', err); - return; - } - fs.readFile(filename, (err, data) => { - if (err) { - result.emit('error', err); - return; - } - fs.unlink(filename, () => { - result.emit('data', new VinylFile({ - path: path.normalize(requestOptions.path), - stat: stat, - base: path.normalize(requestOptions.path), - contents: data - })); - result.emit('end'); - }); - }); - }); - }, (err) => { - result.emit('error', err); - }); - return result; -} diff --git a/build/lib/watch/package.json b/build/lib/watch/package.json index 0d03134015..b26f589ce0 100644 --- a/build/lib/watch/package.json +++ b/build/lib/watch/package.json @@ -4,7 +4,8 @@ "description": "", "author": "Microsoft ", "private": true, + "license": "MIT", "devDependencies": { - "gulp-watch": "^4.3.9" + "gulp-watch": "5.0.1" } } diff --git a/build/lib/watch/yarn.lock b/build/lib/watch/yarn.lock index 0f2ac1e204..f7d5d976b1 100644 --- a/build/lib/watch/yarn.lock +++ b/build/lib/watch/yarn.lock @@ -7,23 +7,29 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= +ansi-colors@1.1.0, 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: - co "^4.6.0" - json-stable-stringify "^1.0.1" + 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-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= +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@^1.3.0: version "1.3.2" @@ -33,6 +39,14 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -53,97 +67,69 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: +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-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= - -array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= +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= array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y= +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -assert-plus@1.0.0, assert-plus@^1.0.0: +assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - integrity sha1-GdOGodntxufByF04iu28xW0zYC0= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8= - -aws4@^1.2.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" - integrity sha1-g+9cqGCysy5KDe7e6MdxudtXRx4= +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - integrity sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40= +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: - tweetnacl "^0.14.3" - -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" binary-extensions@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" integrity sha1-muuabF6IY4qtFx4Wf1kAq+JINdA= -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8= - dependencies: - hoek "2.x.x" - brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -161,64 +147,127 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.0.0, chalk@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" -chokidar@^1.6.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +chokidar@^2.0.0: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" + integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" is-binary-path "^1.0.0" - is-glob "^2.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" path-is-absolute "^1.0.0" - readdirp "^2.0.0" + readdirp "^2.2.1" + upath "^1.1.1" optionalDependencies: - fsevents "^1.0.0" + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" + integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.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@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= +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@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" integrity sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8= -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +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" 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" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" - integrity sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk= +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: - delayed-stream "~1.0.0" + map-visit "^1.0.0" + object-visit "^1.0.0" + +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== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== concat-map@0.0.1: version "0.0.1" @@ -230,46 +279,61 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -core-util-is@1.0.2, core-util-is@~1.0.0: +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +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= -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g= - dependencies: - boom "2.x.x" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" - integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= - -debug@^2.2.0: +debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -deep-extend@~0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" - integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8= +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" -delayed-stream@~1.0.0: +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" delegates@^1.0.0: version "1.0.0" @@ -281,25 +345,6 @@ detect-libc@^1.0.2: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.2.tgz#71ad5d204bf17a6a6ca8f450c61454066ef461e1" integrity sha1-ca1dIEvxempsqPRQxhRUBm70YeE= -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= - dependencies: - readable-stream "~1.1.9" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - integrity sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU= - dependencies: - jsbn "~0.1.0" - -escape-string-regexp@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -307,6 +352,19 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" @@ -314,10 +372,20 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -extend@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, 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" extglob@^0.3.1: version "0.3.2" @@ -326,17 +394,27 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" -extsprintf@1.3.0, extsprintf@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -fancy-log@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" - integrity sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg= +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: - chalk "^1.1.1" + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fancy-log@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" + integrity sha1-9BEl49hPLn2JpD0G2VjI94vha+E= + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" time-stamp "^1.0.0" filename-regex@^2.0.0: @@ -355,6 +433,16 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + 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" @@ -362,7 +450,7 @@ first-chunk-stream@^2.0.0: dependencies: readable-stream "^2.0.2" -for-in@^1.0.1: +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= @@ -374,51 +462,32 @@ for-own@^0.1.4: dependencies: for-in "^1.0.1" -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE= +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" + map-cache "^0.2.2" + +fs-minipass@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + dependencies: + minipass "^2.2.1" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" - integrity sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q== +fsevents@^1.2.7: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.39" - -fstream-ignore@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" - integrity sha1-nDHa40dnAY/h0kmyTa2mfQktoQU= - dependencies: - fstream "^1.0.0" - inherits "2" - minimatch "^3.0.0" - -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" + nan "^2.12.1" + node-pre-gyp "^0.12.0" gauge@~2.7.3: version "2.7.4" @@ -434,12 +503,10 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= glob-base@^0.3.0: version "0.3.0" @@ -456,7 +523,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob-parent@^3.0.1: +glob-parent@^3.0.1, glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= @@ -476,120 +543,83 @@ glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" -glogg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" - integrity sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U= - dependencies: - sparkles "^1.0.0" +graceful-fs@^4.1.11: + version "4.2.0" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" + integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= -gulp-util@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-watch@^4.3.9: - version "4.3.11" - resolved "https://registry.yarnpkg.com/gulp-watch/-/gulp-watch-4.3.11.tgz#162fc563de9fc770e91f9a7ce3955513a9a118c0" - integrity sha1-Fi/FY96fx3DpH5p845VVE6mhGMA= +gulp-watch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/gulp-watch/-/gulp-watch-5.0.1.tgz#83d378752f5bfb46da023e73c17ed1da7066215d" + integrity sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog== dependencies: + ansi-colors "1.1.0" anymatch "^1.3.0" - chokidar "^1.6.1" + chokidar "^2.0.0" + fancy-log "1.3.2" glob-parent "^3.0.1" - gulp-util "^3.0.7" object-assign "^4.1.0" path-is-absolute "^1.0.1" + plugin-error "1.0.1" readable-stream "^2.2.2" slash "^1.0.0" - vinyl "^1.2.0" + vinyl "^2.1.0" vinyl-file "^2.0.0" -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^1.0.0" - -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4= - -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio= - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= -hawk@3.1.3, hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ= +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= - -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8= +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" inflight@^1.0.4: version "1.0.6" @@ -599,16 +629,35 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +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== + ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4= +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -621,6 +670,38 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -633,17 +714,24 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= +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@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= -is-extglob@^2.1.0: +is-extglob@^2.1.0, 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= @@ -669,6 +757,13 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" +is-glob@^4.0.0: + 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@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -683,6 +778,13 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-plain-object@^2.0.3, 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-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -693,20 +795,15 @@ is-primitive@^2.0.0: resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" @@ -720,49 +817,12 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - -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" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -kind-of@^3.0.2: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= @@ -776,104 +836,27 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: - lodash._root "^3.0.0" - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" + object-visit "^1.0.0" micromatch@^2.1.5: version "2.3.11" @@ -894,19 +877,26 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" -mime-db@~1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" - integrity sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE= - -mime-types@^2.1.12, mime-types@~2.1.7: - version "2.1.17" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" - integrity sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo= +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: - mime-db "~1.30.0" + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -918,12 +908,35 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.0, minimist@^1.2.0: +minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -"mkdirp@>=0.5 0", mkdirp@^0.5.1: +minipass@^2.2.1, minipass@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -935,34 +948,57 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: - duplexer2 "0.0.2" + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" -nan@^2.3.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" - integrity sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY= +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" -node-pre-gyp@^0.6.39: - version "0.6.39" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" - integrity sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ== +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== dependencies: detect-libc "^1.0.2" - hawk "3.1.3" mkdirp "^0.5.1" + needle "^2.2.1" nopt "^4.0.1" + npm-packlist "^1.1.6" npmlog "^4.0.2" - rc "^1.1.7" - request "2.81.0" + rc "^1.2.7" rimraf "^2.6.1" semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" + tar "^4" nopt@^4.0.1: version "4.0.1" @@ -972,13 +1008,31 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" -normalize-path@^2.0.0, normalize-path@^2.0.1: +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" +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== + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" + integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -994,21 +1048,27 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -oauth-sign@~0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= - object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -1017,7 +1077,14 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -once@^1.3.0, once@^1.3.3: +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -1052,6 +1119,11 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -1062,11 +1134,6 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -1084,26 +1151,36 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +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" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +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== + process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM= - randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -1112,17 +1189,17 @@ randomatic@^1.1.3: is-number "^3.0.0" kind-of "^4.0.0" -rc@^1.1.7: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" - integrity sha1-2M6ctX6NZNnHut2YdsfDTL48cHc= +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: - deep-extend "~0.4.0" + deep-extend "^0.6.0" ini "~1.3.0" minimist "^1.2.0" strip-json-comments "~2.0.1" -readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" integrity sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ== @@ -1135,25 +1212,27 @@ readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= +readable-stream@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== dependencies: core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" + 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" -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg= +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" + graceful-fs "^4.1.11" + micromatch "^3.1.10" readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" regex-cache@^0.4.2: version "0.4.4" @@ -1162,6 +1241,14 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + 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" @@ -1172,7 +1259,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -1182,46 +1269,55 @@ replace-ext@0.0.1: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= -request@2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA= - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" +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= -rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== dependencies: glob "^7.0.5" -safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + semver@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -1232,10 +1328,15 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" signal-exit@^3.0.0: version "3.0.2" @@ -1247,32 +1348,71 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg= +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: - hoek "2.x.x" + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" -sparkles@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" - integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= - -sshpk@^1.7.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" - integrity sha1-US322mKHFEMW3EwY/hzx2UBzm+M= +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" @@ -1283,11 +1423,6 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" @@ -1295,10 +1430,12 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" -stringstream@~0.0.4: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg= +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-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" @@ -1327,90 +1464,87 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -tar-pack@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" - integrity sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg== +tar@^4: + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== dependencies: - debug "^2.2.0" - fstream "^1.0.10" - fstream-ignore "^1.0.5" - once "^1.3.3" - readable-stream "^2.1.4" - rimraf "^2.5.1" - tar "^2.2.1" - uid-number "^0.0.6" - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -through2@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.5" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" 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= -tough-cookie@~2.3.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" - integrity sha1-C2GKVWW23qkL80JdBNVe3EdadWE= +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: - punycode "^1.4.1" + kind-of "^3.0.2" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: - safe-buffer "^5.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" -uid-number@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== 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= -uuid@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" - integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vinyl-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a" @@ -1423,16 +1557,7 @@ vinyl-file@^2.0.0: strip-bom-stream "^2.0.0" vinyl "^1.1.0" -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^1.1.0, vinyl@^1.2.0: +vinyl@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= @@ -1441,6 +1566,18 @@ vinyl@^1.1.0, vinyl@^1.2.0: clone-stats "^0.0.1" replace-ext "0.0.1" +vinyl@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.0.tgz#d85b07da96e458d25b2ffe19fece9f2caa13ed86" + integrity sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg== + 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" + wide-align@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" @@ -1453,7 +1590,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= +yallist@^3.0.0, yallist@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 0374586a6d..569a111009 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -20,13 +20,10 @@ function yarnInstall(location, opts) { const raw = process.env['npm_config_argv'] || '{}'; const argv = JSON.parse(raw); const original = argv.original || []; - const args = ['install']; + const args = original.filter(arg => arg === '--ignore-optional' || arg === '--frozen-lockfile'); - if (original.indexOf('--ignore-optional') > -1) { - args.push('--ignore-optional'); - } - - console.log('Installing dependencies in \'%s\'.', location); + console.log(`Installing dependencies in ${location}...`); + console.log(`$ yarn ${args.join(' ')}`); const result = cp.spawnSync(yarn, args, opts); if (result.error || result.status !== 0) { @@ -38,6 +35,8 @@ yarnInstall('extensions'); // node modules shared by all extensions yarnInstall('remote'); // node modules used by vscode server +yarnInstall('remote/web'); // node modules used by vscode web + const allExtensionFolders = fs.readdirSync('extensions'); const extensions = allExtensionFolders.filter(e => { try { diff --git a/build/npm/update-distro.js b/build/npm/update-distro.js new file mode 100644 index 0000000000..c384d8d58d --- /dev/null +++ b/build/npm/update-distro.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +const cp = require('child_process'); +const path = require('path'); +const fs = require('fs'); + +const rootPath = path.dirname(path.dirname(path.dirname(__dirname))); +const vscodePath = path.join(rootPath, 'vscode'); +const distroPath = path.join(rootPath, 'vscode-distro'); +const commit = cp.execSync('git rev-parse HEAD', { cwd: distroPath, encoding: 'utf8' }).trim(); +const packageJsonPath = path.join(vscodePath, 'package.json'); +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + +packageJson.distro = commit; +fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); \ No newline at end of file diff --git a/build/npm/update-grammar.js b/build/npm/update-grammar.js index 47598875c7..87b37a92a3 100644 --- a/build/npm/update-grammar.js +++ b/build/npm/update-grammar.js @@ -82,7 +82,7 @@ function getCommitSha(repoId, repoPath) { }); } -exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'master') { +exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'master', packageJsonPathOverride = '') { var contentPath = 'https://raw.githubusercontent.com/' + repoId + `/${version}/` + repoPath; console.log('Reading from ' + contentPath); return download(contentPath).then(function (content) { @@ -128,7 +128,11 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'mas // Add commit sha to cgmanifest. if (currentCommitDate > commitDate) { - let packageJsonPath = 'https://raw.githubusercontent.com/' + repoId + `/${info.commitSha}/package.json`; + let packageJsonPath = 'https://raw.githubusercontent.com/' + repoId + `/${info.commitSha}/`; + if (packageJsonPathOverride) { + packageJsonPath += packageJsonPathOverride; + } + packageJsonPath += '/package.json'; for (let i = 0; i < cgmanifestRead.registrations.length; i++) { if (cgmanifestRead.registrations[i].component.git.repositoryUrl.substr(cgmanifestRead.registrations[i].component.git.repositoryUrl.length - repoId.length, repoId.length) === repoId) { cgmanifestRead.registrations[i].component.git.commitHash = info.commitSha; diff --git a/build/package.json b/build/package.json index 8fe67192a0..ca4bacca00 100644 --- a/build/package.json +++ b/build/package.json @@ -1,6 +1,7 @@ { "name": "azuredatastudio-oss-dev-build", "version": "1.0.0", + "license": "MIT", "devDependencies": { "@types/ansi-colors": "^3.2.0", "@types/azure": "0.9.19", @@ -43,7 +44,7 @@ "request": "^2.85.0", "tslint": "^5.9.1", "service-downloader": "github:anthonydresser/service-downloader#0.1.5", - "typescript": "3.4.5", + "typescript": "3.5.2", "vsce": "1.48.0", "xml2js": "^0.4.17" }, diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index dcc1440b35..3c95fb34cd 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "build_const" version = "0.2.0" @@ -27,7 +29,7 @@ dependencies = [ [[package]] name = "inno_updater" -version = "0.7.1" +version = "0.8.0" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/build/win32/code.iss b/build/win32/code.iss index 65ee07aa6f..d13e4014e5 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -170,7 +170,7 @@ begin AltArch := '32'; end; - if not Result then begin + if not Result and not WizardSilent() then begin MsgBox('Please uninstall the ' + AltArch + '-bit version of {#NameShort} before installing this ' + ThisArch + '-bit version.', mbInformation, MB_OK); end; end; diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl index 4bd6c75ad4..a6aab59b95 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -2,7 +2,7 @@ AddContextMenuFiles=Add "Open with %1" action to Windows Explorer file context menu AddContextMenuFolders=Add "Open with %1" action to Windows Explorer directory context menu AssociateWithFiles=Register %1 as an editor for supported file types -AddToPath=Add to PATH (available after restart) +AddToPath=Add to PATH (requires shell restart) RunAfter=Run %1 after installation Other=Other: SourceFile=%1 Source File \ No newline at end of file diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index 140065f673c7e9ffd238fbf52c445e4417fd2208..baca28361d147d072e1c1144eddd5065877d40ec 100644 GIT binary patch literal 396800 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 ODJ1^*|6l*Z3j7Q3=OaD< literal 386560 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

eFA1Q-#0a%9*pdsXs^H78BATp3U%;gyOZmr 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ӛpOZl6*u%EAJvW@bJoSwZ;`mspdx8eC>QNNRp^7i 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", - " Copyright (C) ", - "", - " This library is free software; you can redistribute it and/or", - " modify it under the terms of the GNU Lesser General Public", - " License as published by the Free Software Foundation; either", - " version 2.1 of the License, or (at your option) any later version.", - "", - " This library is distributed in the hope that it will be useful,", - " but WITHOUT ANY WARRANTY; without even the implied warranty of", - " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", - " Lesser General Public License for more details.", - "", - " You should have received a copy of the GNU Lesser General Public", - " License along with this library; if not, write to the Free Software", - " Foundation, Inc.,", - "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", - "", - "Also add information on how to contact you by electronic and paper mail.", - "", - "You should also get your employer (if you work as a programmer) or your", - "school, if any, to sign a \"copyright disclaimer\" for the library, if", - "necessary. Here is a sample; alter the names:", - "", - " Yoyodyne, Inc., hereby disclaims all copyright interest in the", - " library `Frob' (a library for tweaking knobs) written by James Random Hacker.", - "", - " ,", - "1 April 1990", - " Ty Coon, President of Vice", - "", - "That's all there is to it!" - ] -}, -{ - // Added here because the module `parse5` has a dependency to it. - // The module `parse5` is shipped via the `extension-editing` built-in extension. - // The module `parse5` does not want to remove it https://github.com/inikulin/parse5/issues/225 - "name": "@types/node", - "licenseDetail": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}, -{ - // We override the license that gets discovered at - // https://github.com/Microsoft/TypeScript/blob/master/LICENSE.txt - // because it does not contain a Copyright statement - "name": "typescript", - "licenseDetail": [ - "Copyright (c) Microsoft Corporation. All rights reserved.", - "", - "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" - ] -}, -{ - // This module comes in from https://github.com/Microsoft/vscode-node-debug2/blob/master/package-lock.json - "name": "@types/source-map", - "licenseDetail": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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": "tunnel-agent", - "licenseDetail": [ - "Copyright (c) tunnel-agent authors", - "", - "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" - ] -}, -{ - // Waiting for https://github.com/segmentio/noop-logger/issues/2 - "name": "noop-logger", - "licenseDetail": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -} -] \ No newline at end of file + { + // Reason: The license at https://github.com/aadsm/jschardet/blob/master/LICENSE + // does not include a clear Copyright statement and does not credit authors. + "name": "jschardet", + "licenseDetail": [ + "Chardet was originally ported from C++ by Mark Pilgrim. It is now maintained", + " by Dan Blanchard and Ian Cordasco, and was formerly maintained by Erik Rose.", + " JSChardet was ported from python to JavaScript by António Afonso ", + " (https://github.com/aadsm/jschardet) and transformed into an npm package by ", + "Markus Ast (https://github.com/brainafk)", + "", + "GNU LESSER GENERAL PUBLIC LICENSE", + "\t\t Version 2.1, February 1999", + "", + " Copyright (C) 1991,", + "1999 Free Software Foundation, Inc.", + " 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", + " Everyone is permitted to copy and distribute verbatim copies", + " of this license document, but changing it is not allowed.", + "", + "[This is the first released version of the Lesser GPL. It also counts", + " as the successor of the GNU Library Public License, version 2, hence", + " the version number 2.1.", + "]", + "", + "\t\t\t Preamble", + "", + " The licenses for most software are designed to take away your", + "freedom to share and change it. By contrast, the GNU General Public", + "Licenses are intended to guarantee your freedom to share and change", + "free software--to make sure the software is free for all its users.", + "", + " This license, the Lesser General Public License, applies to some", + "specially designated software packages--typically libraries--of the", + "Free Software Foundation and other authors who decide to use it. You", + "can use it too, but we suggest you first think carefully about whether", + "this license or the ordinary General Public License is the better", + "strategy to use in any particular case, based on the explanations below.", + "", + " When we speak of free software, we are referring to freedom of use,", + "not price. Our General Public Licenses are designed to make sure that", + "you have the freedom to distribute copies of free software (and charge", + "for this service if you wish); that you receive source code or can get", + "it if you want it; that you can change the software and use pieces of", + "it in new free programs; and that you are informed that you can do", + "these things.", + "", + " To protect your rights, we need to make restrictions that forbid", + "distributors to deny you these rights or to ask you to surrender these", + "rights. These restrictions translate to certain responsibilities for", + "you if you distribute copies of the library or if you modify it.", + "", + " For example, if you distribute copies of the library, whether gratis", + "or for a fee, you must give the recipients all the rights that we gave", + "you. You must make sure that they, too, receive or can get the source", + "code. If you link other code with the library, you must provide", + "complete object files to the recipients, so that they can relink them", + "with the library after making changes to the library and recompiling", + "it. And you must show them these terms so they know their rights.", + "", + " We protect your rights with a two-step method: (1) we copyright the", + "library, and (2) we offer you this license, which gives you legal", + "permission to copy, distribute and/or modify the library.", + "", + " To protect each distributor, we want to make it very clear that", + "there is no warranty for the free library. Also, if the library is", + "modified by someone else and passed on, the recipients should know", + "that what they have is not the original version, so that the original", + "author's reputation will not be affected by problems that might be", + "introduced by others.", + "", + " Finally, software patents pose a constant threat to the existence of", + "any free program. We wish to make sure that a company cannot", + "effectively restrict the users of a free program by obtaining a", + "restrictive license from a patent holder. Therefore, we insist that", + "any patent license obtained for a version of the library must be", + "consistent with the full freedom of use specified in this license.", + "", + " Most GNU software, including some libraries, is covered by the", + "ordinary GNU General Public License. This license, the GNU Lesser", + "General Public License, applies to certain designated libraries, and", + "is quite different from the ordinary General Public License. We use", + "this license for certain libraries in order to permit linking those", + "libraries into non-free programs.", + "", + " When a program is linked with a library, whether statically or using", + "a shared library, the combination of the two is legally speaking a", + "combined work, a derivative of the original library. The ordinary", + "General Public License therefore permits such linking only if the", + "entire combination fits its criteria of freedom. The Lesser General", + "Public License permits more lax criteria for linking other code with", + "the library.", + "", + " We call this license the \"Lesser\" General Public License because it", + "does Less to protect the user's freedom than the ordinary General", + "Public License. It also provides other free software developers Less", + "of an advantage over competing non-free programs. These disadvantages", + "are the reason we use the ordinary General Public License for many", + "libraries. However, the Lesser license provides advantages in certain", + "special circumstances.", + "", + " For example, on rare occasions, there may be a special need to", + "encourage the widest possible use of a certain library, so that it becomes", + "a de-facto standard. To achieve this, non-free programs must be", + "allowed to use the library. A more frequent case is that a free", + "library does the same job as widely used non-free libraries. In this", + "case, there is little to gain by limiting the free library to free", + "software only, so we use the Lesser General Public License.", + "", + " In other cases, permission to use a particular library in non-free", + "programs enables a greater number of people to use a large body of", + "free software. For example, permission to use the GNU C Library in", + "non-free programs enables many more people to use the whole GNU", + "operating system, as well as its variant, the GNU/Linux operating", + "system.", + "", + " Although the Lesser General Public License is Less protective of the", + "users' freedom, it does ensure that the user of a program that is", + "linked with the Library has the freedom and the wherewithal to run", + "that program using a modified version of the Library.", + "", + " The precise terms and conditions for copying, distribution and", + "modification follow. Pay close attention to the difference between a", + "\"work based on the library\" and a \"work that uses the library\". The", + "former contains code derived from the library, whereas the latter must", + "be combined with the library in order to run.", + "", + "\t\t GNU LESSER GENERAL PUBLIC LICENSE", + " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", + "", + " 0. This License Agreement applies to any software library or other", + "program which contains a notice placed by the copyright holder or", + "other authorized party saying it may be distributed under the terms of", + "this Lesser General Public License (also called \"this License\").", + "Each licensee is addressed as \"you\".", + "", + " A \"library\" means a collection of software functions and/or data", + "prepared so as to be conveniently linked with application programs", + "(which use some of those functions and data) to form executables.", + "", + " The \"Library\", below, refers to any such software library or work", + "which has been distributed under these terms. A \"work based on the", + "Library\" means either the Library or any derivative work under", + "copyright law: that is to say, a work containing the Library or a", + "portion of it, either verbatim or with modifications and/or translated", + "straightforwardly into another language. (Hereinafter, translation is", + "included without limitation in the term \"modification\".)", + "", + " \"Source code\" for a work means the preferred form of the work for", + "making modifications to it. For a library, complete source code means", + "all the source code for all modules it contains, plus any associated", + "interface definition files, plus the scripts used to control compilation", + "and installation of the library.", + "", + " Activities other than copying, distribution and modification are not", + "covered by this License; they are outside its scope. The act of", + "running a program using the Library is not restricted, and output from", + "such a program is covered only if its contents constitute a work based", + "on the Library (independent of the use of the Library in a tool for", + "writing it). Whether that is true depends on what the Library does", + "and what the program that uses the Library does.", + "", + " 1. You may copy and distribute verbatim copies of the Library's", + "complete source code as you receive it, in any medium, provided that", + "you conspicuously and appropriately publish on each copy an", + "appropriate copyright notice and disclaimer of warranty; keep intact", + "all the notices that refer to this License and to the absence of any", + "warranty; and distribute a copy of this License along with the", + "Library.", + "", + " You may charge a fee for the physical act of transferring a copy,", + "and you may at your option offer warranty protection in exchange for a", + "fee.", + "", + " 2. You may modify your copy or copies of the Library or any portion", + "of it, thus forming a work based on the Library, and copy and", + "distribute such modifications or work under the terms of Section 1", + "above, provided that you also meet all of these conditions:", + "", + " a) The modified work must itself be a software library.", + "", + " b) You must cause the files modified to carry prominent notices", + " stating that you changed the files and the date of any change.", + "", + " c) You must cause the whole of the work to be licensed at no", + " charge to all third parties under the terms of this License.", + "", + " d) If a facility in the modified Library refers to a function or a", + " table of data to be supplied by an application program that uses", + " the facility, other than as an argument passed when the facility", + " is invoked, then you must make a good faith effort to ensure that,", + " in the event an application does not supply such function or", + " table, the facility still operates, and performs whatever part of", + " its purpose remains meaningful.", + "", + " (For example, a function in a library to compute square roots has", + " a purpose that is entirely well-defined independent of the", + " application. Therefore, Subsection 2d requires that any", + " application-supplied function or table used by this function must", + " be optional: if the application does not supply it, the square", + " root function must still compute square roots.)", + "", + "These requirements apply to the modified work as a whole. If", + "identifiable sections of that work are not derived from the Library,", + "and can be reasonably considered independent and separate works in", + "themselves, then this License, and its terms, do not apply to those", + "sections when you distribute them as separate works. But when you", + "distribute the same sections as part of a whole which is a work based", + "on the Library, the distribution of the whole must be on the terms of", + "this License, whose permissions for other licensees extend to the", + "entire whole, and thus to each and every part regardless of who wrote", + "it.", + "", + "Thus, it is not the intent of this section to claim rights or contest", + "your rights to work written entirely by you; rather, the intent is to", + "exercise the right to control the distribution of derivative or", + "collective works based on the Library.", + "", + "In addition, mere aggregation of another work not based on the Library", + "with the Library (or with a work based on the Library) on a volume of", + "a storage or distribution medium does not bring the other work under", + "the scope of this License.", + "", + " 3. You may opt to apply the terms of the ordinary GNU General Public", + "License instead of this License to a given copy of the Library. To do", + "this, you must alter all the notices that refer to this License, so", + "that they refer to the ordinary GNU General Public License, version 2,", + "instead of to this License. (If a newer version than version 2 of the", + "ordinary GNU General Public License has appeared, then you can specify", + "that version instead if you wish.) Do not make any other change in", + "these notices.", + "", + " Once this change is made in a given copy, it is irreversible for", + "that copy, so the ordinary GNU General Public License applies to all", + "subsequent copies and derivative works made from that copy.", + "", + " This option is useful when you wish to copy part of the code of", + "the Library into a program that is not a library.", + "", + " 4. You may copy and distribute the Library (or a portion or", + "derivative of it, under Section 2) in object code or executable form", + "under the terms of Sections 1 and 2 above provided that you accompany", + "it with the complete corresponding machine-readable source code, which", + "must be distributed under the terms of Sections 1 and 2 above on a", + "medium customarily used for software interchange.", + "", + " If distribution of object code is made by offering access to copy", + "from a designated place, then offering equivalent access to copy the", + "source code from the same place satisfies the requirement to", + "distribute the source code, even though third parties are not", + "compelled to copy the source along with the object code.", + "", + " 5. A program that contains no derivative of any portion of the", + "Library, but is designed to work with the Library by being compiled or", + "linked with it, is called a \"work that uses the Library\". Such a", + "work, in isolation, is not a derivative work of the Library, and", + "therefore falls outside the scope of this License.", + "", + " However, linking a \"work that uses the Library\" with the Library", + "creates an executable that is a derivative of the Library (because it", + "contains portions of the Library), rather than a \"work that uses the", + "library\". The executable is therefore covered by this License.", + "Section 6 states terms for distribution of such executables.", + "", + " When a \"work that uses the Library\" uses material from a header file", + "that is part of the Library, the object code for the work may be a", + "derivative work of the Library even though the source code is not.", + "Whether this is true is especially significant if the work can be", + "linked without the Library, or if the work is itself a library. The", + "threshold for this to be true is not precisely defined by law.", + "", + " If such an object file uses only numerical parameters, data", + "structure layouts and accessors, and small macros and small inline", + "functions (ten lines or less in length), then the use of the object", + "file is unrestricted, regardless of whether it is legally a derivative", + "work. (Executables containing this object code plus portions of the", + "Library will still fall under Section 6.)", + "", + " Otherwise, if the work is a derivative of the Library, you may", + "distribute the object code for the work under the terms of Section 6.", + "Any executables containing that work also fall under Section 6,", + "whether or not they are linked directly with the Library itself.", + "", + " 6. As an exception to the Sections above, you may also combine or", + "link a \"work that uses the Library\" with the Library to produce a", + "work containing portions of the Library, and distribute that work", + "under terms of your choice, provided that the terms permit", + "modification of the work for the customer's own use and reverse", + "engineering for debugging such modifications.", + "", + " You must give prominent notice with each copy of the work that the", + "Library is used in it and that the Library and its use are covered by", + "this License. You must supply a copy of this License. If the work", + "during execution displays copyright notices, you must include the", + "copyright notice for the Library among them, as well as a reference", + "directing the user to the copy of this License. Also, you must do one", + "of these things:", + "", + " a) Accompany the work with the complete corresponding", + " machine-readable source code for the Library including whatever", + " changes were used in the work (which must be distributed under", + " Sections 1 and 2 above); and, if the work is an executable linked", + " with the Library, with the complete machine-readable \"work that", + " uses the Library\", as object code and/or source code, so that the", + " user can modify the Library and then relink to produce a modified", + " executable containing the modified Library. (It is understood", + " that the user who changes the contents of definitions files in the", + " Library will not necessarily be able to recompile the application", + " to use the modified definitions.)", + "", + " b) Use a suitable shared library mechanism for linking with the", + " Library. A suitable mechanism is one that (1) uses at run time a", + " copy of the library already present on the user's computer system,", + " rather than copying library functions into the executable, and (2)", + " will operate properly with a modified version of the library, if", + " the user installs one, as long as the modified version is", + " interface-compatible with the version that the work was made with.", + "", + " c) Accompany the work with a written offer, valid for at", + " least three years, to give the same user the materials", + " specified in Subsection 6a, above, for a charge no more", + " than the cost of performing this distribution.", + "", + " d) If distribution of the work is made by offering access to copy", + " from a designated place, offer equivalent access to copy the above", + " specified materials from the same place.", + "", + " e) Verify that the user has already received a copy of these", + " materials or that you have already sent this user a copy.", + "", + " For an executable, the required form of the \"work that uses the", + "Library\" must include any data and utility programs needed for", + "reproducing the executable from it. However, as a special exception,", + "the materials to be distributed need not include anything that is", + "normally distributed (in either source or binary form) with the major", + "components (compiler, kernel, and so on) of the operating system on", + "which the executable runs, unless that component itself accompanies", + "the executable.", + "", + " It may happen that this requirement contradicts the license", + "restrictions of other proprietary libraries that do not normally", + "accompany the operating system. Such a contradiction means you cannot", + "use both them and the Library together in an executable that you", + "distribute.", + "", + " 7. You may place library facilities that are a work based on the", + "Library side-by-side in a single library together with other library", + "facilities not covered by this License, and distribute such a combined", + "library, provided that the separate distribution of the work based on", + "the Library and of the other library facilities is otherwise", + "permitted, and provided that you do these two things:", + "", + " a) Accompany the combined library with a copy of the same work", + " based on the Library, uncombined with any other library", + " facilities. This must be distributed under the terms of the", + " Sections above.", + "", + " b) Give prominent notice with the combined library of the fact", + " that part of it is a work based on the Library, and explaining", + " where to find the accompanying uncombined form of the same work.", + "", + " 8. You may not copy, modify, sublicense, link with, or distribute", + "the Library except as expressly provided under this License. Any", + "attempt otherwise to copy, modify, sublicense, link with, or", + "distribute the Library is void, and will automatically terminate your", + "rights under this License. However, parties who have received copies,", + "or rights, from you under this License will not have their licenses", + "terminated so long as such parties remain in full compliance.", + "", + " 9. You are not required to accept this License, since you have not", + "signed it. However, nothing else grants you permission to modify or", + "distribute the Library or its derivative works. These actions are", + "prohibited by law if you do not accept this License. Therefore, by", + "modifying or distributing the Library (or any work based on the", + "Library), you indicate your acceptance of this License to do so, and", + "all its terms and conditions for copying, distributing or modifying", + "the Library or works based on it.", + "", + " 10. Each time you redistribute the Library (or any work based on the", + "Library), the recipient automatically receives a license from the", + "original licensor to copy, distribute, link with or modify the Library", + "subject to these terms and conditions. You may not impose any further", + "restrictions on the recipients' exercise of the rights granted herein.", + "You are not responsible for enforcing compliance by third parties with", + "this License.", + "", + " 11. If, as a consequence of a court judgment or allegation of patent", + "infringement or for any other reason (not limited to patent issues),", + "conditions are imposed on you (whether by court order, agreement or", + "otherwise) that contradict the conditions of this License, they do not", + "excuse you from the conditions of this License. If you cannot", + "distribute so as to satisfy simultaneously your obligations under this", + "License and any other pertinent obligations, then as a consequence you", + "may not distribute the Library at all. For example, if a patent", + "license would not permit royalty-free redistribution of the Library by", + "all those who receive copies directly or indirectly through you, then", + "the only way you could satisfy both it and this License would be to", + "refrain entirely from distribution of the Library.", + "", + "If any portion of this section is held invalid or unenforceable under any", + "particular circumstance, the balance of the section is intended to apply,", + "and the section as a whole is intended to apply in other circumstances.", + "", + "It is not the purpose of this section to induce you to infringe any", + "patents or other property right claims or to contest validity of any", + "such claims; this section has the sole purpose of protecting the", + "integrity of the free software distribution system which is", + "implemented by public license practices. Many people have made", + "generous contributions to the wide range of software distributed", + "through that system in reliance on consistent application of that", + "system; it is up to the author/donor to decide if he or she is willing", + "to distribute software through any other system and a licensee cannot", + "impose that choice.", + "", + "This section is intended to make thoroughly clear what is believed to", + "be a consequence of the rest of this License.", + "", + " 12. If the distribution and/or use of the Library is restricted in", + "certain countries either by patents or by copyrighted interfaces, the", + "original copyright holder who places the Library under this License may add", + "an explicit geographical distribution limitation excluding those countries,", + "so that distribution is permitted only in or among countries not thus", + "excluded. In such case, this License incorporates the limitation as if", + "written in the body of this License.", + "", + " 13. The Free Software Foundation may publish revised and/or new", + "versions of the Lesser General Public License from time to time.", + "Such new versions will be similar in spirit to the present version,", + "but may differ in detail to address new problems or concerns.", + "", + "Each version is given a distinguishing version number. If the Library", + "specifies a version number of this License which applies to it and", + "\"any later version\", you have the option of following the terms and", + "conditions either of that version or of any later version published by", + "the Free Software Foundation. If the Library does not specify a", + "license version number, you may choose any version ever published by", + "the Free Software Foundation.", + "", + " 14. If you wish to incorporate parts of the Library into other free", + "programs whose distribution conditions are incompatible with these,", + "write to the author to ask for permission. For software which is", + "copyrighted by the Free Software Foundation, write to the Free", + "Software Foundation; we sometimes make exceptions for this. Our", + "decision will be guided by the two goals of preserving the free status", + "of all derivatives of our free software and of promoting the sharing", + "and reuse of software generally.", + "", + "\t\t\t NO WARRANTY", + "", + " 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO", + "WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.", + "EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR", + "OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY", + "KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE", + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", + "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE", + "LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME", + "THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.", + "", + " 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN", + "WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY", + "AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU", + "FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR", + "CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE", + "LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING", + "RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A", + "FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF", + "SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH", + "DAMAGES.", + "", + "\t\t END OF TERMS AND CONDITIONS", + "", + " How to Apply These Terms to Your New Libraries", + "", + " If you develop a new library, and you want it to be of the greatest", + "possible use to the public, we recommend making it free software that", + "everyone can redistribute and change. You can do so by permitting", + "redistribution under these terms (or, alternatively, under the terms of the", + "ordinary General Public License).", + "", + " To apply these terms, attach the following notices to the library. It is", + "safest to attach them to the start of each source file to most effectively", + "convey the exclusion of warranty; and each file should have at least the", + "\"copyright\" line and a pointer to where the full notice is found.", + "", + " ", + " Copyright (C) ", + "", + " This library is free software; you can redistribute it and/or", + " modify it under the terms of the GNU Lesser General Public", + " License as published by the Free Software Foundation; either", + " version 2.1 of the License, or (at your option) any later version.", + "", + " This library is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", + " Lesser General Public License for more details.", + "", + " You should have received a copy of the GNU Lesser General Public", + " License along with this library; if not, write to the Free Software", + " Foundation, Inc.,", + "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", + "", + "Also add information on how to contact you by electronic and paper mail.", + "", + "You should also get your employer (if you work as a programmer) or your", + "school, if any, to sign a \"copyright disclaimer\" for the library, if", + "necessary. Here is a sample; alter the names:", + "", + " Yoyodyne, Inc., hereby disclaims all copyright interest in the", + " library `Frob' (a library for tweaking knobs) written by James Random Hacker.", + "", + " ,", + "1 April 1990", + " Ty Coon, President of Vice", + "", + "That's all there is to it!" + ] + }, + { + // Added here because the module `parse5` has a dependency to it. + // The module `parse5` is shipped via the `extension-editing` built-in extension. + // The module `parse5` does not want to remove it https://github.com/inikulin/parse5/issues/225 + "name": "@types/node", + "licenseDetail": [ + "This project is licensed under the MIT license.", + "Copyrights are respective of each contributor listed at the beginning of each definition file.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ] + }, + { + // We override the license that gets discovered at + // https://github.com/Microsoft/TypeScript/blob/master/LICENSE.txt + // because it does not contain a Copyright statement + "name": "typescript", + "licenseDetail": [ + "Copyright (c) Microsoft Corporation. All rights reserved.", + "", + "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" + ] + }, + { + // This module comes in from https://github.com/Microsoft/vscode-node-debug2/blob/master/package-lock.json + "name": "@types/source-map", + "licenseDetail": [ + "This project is licensed under the MIT license.", + "Copyrights are respective of each contributor listed at the beginning of each definition file.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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": "tunnel-agent", + "licenseDetail": [ + "Copyright (c) tunnel-agent authors", + "", + "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" + ] + }, + { + // Waiting for https://github.com/segmentio/noop-logger/issues/2 + "name": "noop-logger", + "licenseDetail": [ + "This project is licensed under the MIT license.", + "Copyrights are respective of each contributor listed at the beginning of each definition file.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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": "xterm-addon-search", + "licenseDetail": [ + "Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION 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": "xterm-addon-web-links", + "licenseDetail": [ + "Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION 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 01cdd8df5f..b8c4f3bfef 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "164c37e3f235134c88e80fac2a182cfba3f07f00" + "commitHash": "c6a08e5368de4352903e702cde750b33239a50ab" } }, "licenseDetail": [ @@ -40,20 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "66.0.3359.181" - }, - { - "component": { - "type": "git", - "git": { - "name": "libchromiumcontent", - "repositoryUrl": "https://github.com/electron/libchromiumcontent", - "commitHash": "7ea271f92018b1eeb8e70ec6de8c29f9758a0c05" - } - }, - "isOnlyProductionDependency": true, - "license": "MIT", - "version": "66.0.3359.181" + "version": "69.0.3497.128" }, { "component": { @@ -61,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "5cbb905c1af7cea2d709932d59827d7c6d03ef4a" + "commitHash": "8c70b2084ce5f76ea1e3b3c4ccdeee4483fe338b" } }, "isOnlyProductionDependency": true, - "version": "10.2.0" + "version": "10.11.0" }, { "component": { @@ -73,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "e84a6860e35e14b4031b88bb9b49841cdb89a305" + "commitHash": "5d67ec3da5376a5058990e8a9557bc9124ad59a8" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "3.1.8" + "version": "4.2.5" }, { "component": { @@ -111,11 +98,11 @@ "git": { "name": "vscode-octicons-font", "repositoryUrl": "https://github.com/Microsoft/vscode-octicons-font", - "commitHash": "4f69de3a233ed501c2098e33047e116ac2fbbf42" + "commitHash": "415cd5b42ab699b6b46c0bf011ada0a2ae50bfb4" } }, "license": "MIT", - "version": "1.1.0" + "version": "1.3.1" }, { "component": { diff --git a/extensions/admin-tool-ext-win/.vscodeignore b/extensions/admin-tool-ext-win/.vscodeignore index c84cb57447..0715f4fe4d 100644 --- a/extensions/admin-tool-ext-win/.vscodeignore +++ b/extensions/admin-tool-ext-win/.vscodeignore @@ -2,4 +2,6 @@ out/test/** src/** .gitignore tsconfig.json -InstallSsmsMin.bat \ No newline at end of file +InstallSsmsMin.bat +cgmanifest.json +.vscode diff --git a/extensions/bat/package.json b/extensions/bat/package.json index c54d1a4ce5..00bd84e4ae 100644 --- a/extensions/bat/package.json +++ b/extensions/bat/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js mmims/language-batchfile grammars/batchfile.cson ./syntaxes/batchfile.tmLanguage.json" diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index e9f9e255f3..e555d61457 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.0.0" }, @@ -108,6 +109,6 @@ ] }, "devDependencies": { - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" } } diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 38b3c640c5..55950a9021 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -5,6 +5,7 @@ "type": "object", "definitions": { "devContainerCommon": { + "type": "object", "properties": { "name": { "type": "string", @@ -18,11 +19,14 @@ } }, "settings": { - "type": "object", + "$ref": "vscode://schemas/settings/machine", "description": "Machine specific settings that should be copied into the container." }, "postCreateCommand": { - "type": ["string", "array"], + "type": [ + "string", + "array" + ], "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", "items": { "type": "string" @@ -35,12 +39,20 @@ } }, "nonComposeBase": { + "type": "object", "properties": { "appPort": { - "type": ["integer", "string", "array"], + "type": [ + "integer", + "string", + "array" + ], "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", "items": { - "type": ["integer", "string"] + "type": [ + "integer", + "string" + ] } }, "runArgs": { @@ -52,7 +64,10 @@ }, "shutdownAction": { "type": "string", - "enum": ["none", "stopContainer"], + "enum": [ + "none", + "stopContainer" + ], "description": "Action to take when VS Code is shutting down. The default is to stop the container." }, "overrideCommand": { @@ -70,6 +85,7 @@ } }, "dockerFileContainer": { + "type": "object", "properties": { "dockerFile": { "type": "string", @@ -80,21 +96,30 @@ "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." } }, - "required": ["dockerFile"] + "required": [ + "dockerFile" + ] }, "imageContainer": { + "type": "object", "properties": { "image": { "type": "string", "description": "The docker image that will be used to create the container." } }, - "required": ["image"] + "required": [ + "image" + ] }, "composeContainer": { + "type": "object", "properties": { "dockerComposeFile": { - "type": ["string", "array"], + "type": [ + "string", + "array" + ], "description": "The name of the docker-compose file(s) used to start the services.", "items": { "type": "string" @@ -110,11 +135,18 @@ }, "shutdownAction": { "type": "string", - "enum": ["none", "stopCompose"], + "enum": [ + "none", + "stopCompose" + ], "description": "Action to take when VS Code is shutting down. The default is to stop the containers." } }, - "required": ["dockerComposeFile", "service", "workspaceFolder"] + "required": [ + "dockerComposeFile", + "service", + "workspaceFolder" + ] } }, "allOf": [ diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index 49c4716606..ce653f097a 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== jsonc-parser@2.0.2: version "2.0.2" diff --git a/extensions/css-language-features/server/src/test/links.test.ts b/extensions/css-language-features/server/src/test/links.test.ts new file mode 100644 index 0000000000..ff0aae4390 --- /dev/null +++ b/extensions/css-language-features/server/src/test/links.test.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import 'mocha'; +import * as assert from 'assert'; +import Uri from 'vscode-uri'; +import { resolve } from 'path'; +import { TextDocument, DocumentLink } from 'vscode-languageserver-types'; +import { WorkspaceFolder } from 'vscode-languageserver-protocol'; +import { getCSSLanguageService } from 'vscode-css-languageservice'; +import { getDocumentContext } from '../utils/documentContext'; + +export interface ItemDescription { + offset: number; + value: string; + target: string; +} + +suite('Links', () => { + const cssLanguageService = getCSSLanguageService(); + + let assertLink = function (links: DocumentLink[], expected: ItemDescription, document: TextDocument) { + let matches = links.filter(link => { + return document.offsetAt(link.range.start) === expected.offset; + }); + + assert.equal(matches.length, 1, `${expected.offset} should only existing once: Actual: ${links.map(l => document.offsetAt(l.range.start)).join(', ')}`); + let match = matches[0]; + assert.equal(document.getText(match.range), expected.value); + assert.equal(match.target, expected.target); + }; + + function assertLinks(value: string, expected: ItemDescription[], testUri: string, workspaceFolders?: WorkspaceFolder[], lang: string = 'css'): void { + const offset = value.indexOf('|'); + value = value.substr(0, offset) + value.substr(offset + 1); + + const document = TextDocument.create(testUri, lang, 0, value); + + if (!workspaceFolders) { + workspaceFolders = [{ name: 'x', uri: testUri.substr(0, testUri.lastIndexOf('/')) }]; + } + + const context = getDocumentContext(testUri, workspaceFolders); + + const stylesheet = cssLanguageService.parseStylesheet(document); + let links = cssLanguageService.findDocumentLinks(document, stylesheet, context)!; + + assert.equal(links.length, expected.length); + + for (let item of expected) { + assertLink(links, item, document); + } + } + + function getTestResource(path: string) { + return Uri.file(resolve(__dirname, '../../test/linksTestFixtures', path)).toString(); + } + + test('url links', function () { + + let testUri = getTestResource('about.css'); + let folders = [{ name: 'x', uri: getTestResource('') }]; + + assertLinks('html { background-image: url("hello.html|")', + [{ offset: 29, value: '"hello.html"', target: getTestResource('hello.html') }], testUri, folders + ); + }); + + test('node module resolving', function () { + + let testUri = getTestResource('about.css'); + let folders = [{ name: 'x', uri: getTestResource('') }]; + + assertLinks('html { background-image: url("~foo/hello.html|")', + [{ offset: 29, value: '"~foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders + ); + }); +}); \ No newline at end of file diff --git a/extensions/css-language-features/server/test/linksTestFixtures/.gitignore b/extensions/css-language-features/server/test/linksTestFixtures/.gitignore new file mode 100644 index 0000000000..d591cdfd50 --- /dev/null +++ b/extensions/css-language-features/server/test/linksTestFixtures/.gitignore @@ -0,0 +1 @@ +!/node_modules \ No newline at end of file diff --git a/src/vs/base/test/node/stream/fixtures/empty.txt b/extensions/css-language-features/server/test/linksTestFixtures/node_modules/foo/package.json similarity index 100% rename from src/vs/base/test/node/stream/fixtures/empty.txt rename to extensions/css-language-features/server/test/linksTestFixtures/node_modules/foo/package.json diff --git a/extensions/docker/package.json b/extensions/docker/package.json index 41df24723f..f5f2f795fc 100644 --- a/extensions/docker/package.json +++ b/extensions/docker/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js moby/moby contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage ./syntaxes/docker.tmLanguage.json" diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 52159b965b..9c07a2568a 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.4.0" }, @@ -53,6 +54,6 @@ }, "devDependencies": { "@types/markdown-it": "0.0.2", - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" } } diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index 720c84f065..971bf580ba 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" integrity sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== "@types/node@^6.0.46": version "6.0.78" diff --git a/extensions/git-ui/.vscodeignore b/extensions/git-ui/.vscodeignore new file mode 100644 index 0000000000..7462f7448d --- /dev/null +++ b/extensions/git-ui/.vscodeignore @@ -0,0 +1,8 @@ +src/** +test/** +out/** +tsconfig.json +build/** +extension.webpack.config.js +cgmanifest.json +yarn.lock diff --git a/extensions/git-ui/README.md b/extensions/git-ui/README.md new file mode 100644 index 0000000000..d418425acf --- /dev/null +++ b/extensions/git-ui/README.md @@ -0,0 +1,7 @@ +# Git UI integration for Visual Studio Code + +**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. + +## Features + +See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension. diff --git a/extensions/git-ui/cgmanifest.json b/extensions/git-ui/cgmanifest.json new file mode 100644 index 0000000000..f3071eb691 --- /dev/null +++ b/extensions/git-ui/cgmanifest.json @@ -0,0 +1,4 @@ +{ + "registrations": [], + "version": 1 +} \ No newline at end of file diff --git a/src/typings/getmac.d.ts b/extensions/git-ui/extension.webpack.config.js similarity index 66% rename from src/typings/getmac.d.ts rename to extensions/git-ui/extension.webpack.config.js index 98f0458a20..b63c59c65d 100644 --- a/src/typings/getmac.d.ts +++ b/extensions/git-ui/extension.webpack.config.js @@ -3,10 +3,15 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module getmac { - export function getMac(callback: (error: Error, macAddress: string) => void): void; -} +//@ts-check -declare module 'getmac' { - export = getmac; -} \ No newline at end of file +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + main: './src/main.ts' + } +}); diff --git a/extensions/git-ui/package.json b/extensions/git-ui/package.json new file mode 100644 index 0000000000..2f1ab43f89 --- /dev/null +++ b/extensions/git-ui/package.json @@ -0,0 +1,28 @@ +{ + "name": "git-ui", + "displayName": "%displayName%", + "description": "%description%", + "publisher": "vscode", + "version": "1.0.0", + "engines": { + "vscode": "^1.5.0" + }, + "extensionKind": "ui", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "enableProposedApi": true, + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:git.credential" + ], + "main": "./out/main", + "icon": "resources/icons/git.png", + "scripts": { + "compile": "gulp compile-extension:git-ui", + "watch": "gulp watch-extension:git-ui" + }, + "devDependencies": { + "@types/node": "^10.14.8" + } +} \ No newline at end of file diff --git a/extensions/git-ui/package.nls.json b/extensions/git-ui/package.nls.json new file mode 100644 index 0000000000..5303e91f4c --- /dev/null +++ b/extensions/git-ui/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Git UI", + "description": "Git SCM UI Integration" +} \ No newline at end of file diff --git a/extensions/git-ui/resources/icons/git.png b/extensions/git-ui/resources/icons/git.png new file mode 100644 index 0000000000000000000000000000000000000000..51f4ae5404fc79be09e69171bfc9d34d48810297 GIT binary patch literal 2383 zcmb_edr(tX8b3FeJQNAIb;Tf>)JL(q3J4vLirl*vi*Kow20-DLSS9|3^Zwk7&^dy0_BKc76X2>}!()lOlVnV}tr z%7&jSS|mYX4~n%NS=f@&X(b^Q(N+N%0zF~9g{A- zZfSW+-Hr{m9%u8|2k$3dtage|2Sl;$h{sF(-i|c>B<|Qtz%K^PbRz=513=(`{A&g* z;RKU3B@gTnOaDvupQ!DT9hv|fD?ePvQ@0F%CkIDtqH+r6-`Pof?+-KO$>7G7NXXf@Nusk$YC-Nu- z23d{+;0ha8oHXp(O9h@TmW@CbU`rV z;l_K-qf92mZ;_k&oa6Cfm$b0End8ChmVX)f^pq##^aeu^Al9jBsZfODGUaf3a1>BU z#)l8$FxyesvmRxYX%&I`xjrI)@}}{Yz?WIexeH}QYZaSD3*0t5$8`+Ut3z}uuEv_l zb~=$F*(jZi!&FC0jtaAaNV1^#;yj+uX?0ZYbnwg26}mtz%iwT$HXX)ck)u$l7-(c|flTERxKM;g%Wl*nxU4yuBeLE+%mLQk3j#Q<_)GxC!`o6owL0SxvRSsR*zM_= zM;TNgl4km?o7x7!8}u>19+UmMGd!jky3e24KfUA+5jayP8JB}vti|+79AKDU-2xDY zqn+Hu(=dH4_b`}DuK6stpXcU@G9Z(UVjJC3;j2(CH&^;lDwRFv=gGOes*l+Q;vj%R z1CjmXXlAdvD}6z?*_cB>EeMeU3vbyDzxWD8a=-Y{)zbaNRsI}cXmGi}n|<>UAbYG9 z;xUVTfTo<;jjje( z<5@yEyyg^r9lW4kDOmWi7F$h4OvgW4*hR#{_R2TkNomGiT2@BGExcp}eH{5=tvPPr znrdcAhi9x>Cm;X5CI-$%(YABf@VI-Er7q>WS-MM&t|)rN1|93ssE#gv_%ypPosiRO zHauI2pZ{7#4Cak`9Bm+Eea_1Ri=F@QCrUo01C6qy1LOxLv(y%#4Rb1Cchij&G zr#4JZU%q$h=`k1i`eKFEX0D35ZPMpXFU?6-DzEpNF}+yks$ZJ7-sJd`US6|5H&c)= z?g+u?LxEVi_P~Y{L{FuKKRsbd!pg{zU6$E&)tT=G*E)_wv{c$(%`Lu27Pz|UsdXoo zog#W>2g$8C_+Cx>O@=}tkFwdxlOcOdgU!`~l$ABWSbdW+ZsxSDuMA~-A?WTkY=Uc%5xpg0$kgN=(W^ez> zH_0Zh@CX}tY3=ThcqDg~OVlqDK(!IiGN zC}T7iq-;&)B2ASvfD#5%fx7n~mIN!?Q47j>Zp`9ZwCvXL{U~GNpN-6MZ!^s|bb5-u zl{43z!4-)KlMrXIEQx3vDV*3C+$6xH%iJ^_k9EUe041;&y!qhF5f0HAh zFXfj)ECZw`K31%^WqqQaPbqdu$R@JyV3>73;@C#tjq&C3al~h1#r9#_?ZT-$)5Nk^ zmUQOPgk&zS|FArSgk}Pw6l3UjcW!>`-=XfV0hI_S$2b4{BL82 z2k(D``e93+SzOn9`ydHEt@(u*!(K_su{EJ|-OXKk#a^R&C_m@uwUJZEka5TH48ge_ vp%EL>Wi@Nxn8V*2x$L2D+u#MyDSqcb+P#5YHS3sP9I$26&gfeklTQ2-d^Cga literal 0 HcmV?d00001 diff --git a/extensions/git-ui/src/main.ts b/extensions/git-ui/src/main.ts new file mode 100644 index 0000000000..057074fae6 --- /dev/null +++ b/extensions/git-ui/src/main.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtensionContext, commands } from 'vscode'; + +import * as cp from 'child_process'; + +export async function deactivate(): Promise { +} + +export async function activate(context: ExtensionContext): Promise { + context.subscriptions.push(commands.registerCommand('git.credential', async (data: any) => { + try { + const { stdout, stderr } = await exec(`git credential ${data.command}`, { + stdin: data.stdin, + env: Object.assign(process.env, { GIT_TERMINAL_PROMPT: '0' }) + }); + return { stdout, stderr, code: 0 }; + } catch ({ stdout, stderr, error }) { + const code = error.code || 0; + if (stderr.indexOf('terminal prompts disabled') !== -1) { + stderr = ''; + } + return { stdout, stderr, code }; + } + })); +} + +export interface ExecResult { + error: Error | null; + stdout: string; + stderr: string; +} + + +export function exec(command: string, options: cp.ExecOptions & { stdin?: string } = {}) { + return new Promise((resolve, reject) => { + const child = cp.exec(command, options, (error, stdout, stderr) => { + (error ? reject : resolve)({ error, stdout, stderr }); + }); + if (options.stdin) { + child.stdin.write(options.stdin, (err: any) => { + if (err) { + reject(err); + return; + } + child.stdin.end((err: any) => { + if (err) { + reject(err); + } + }); + }); + } + }); +} diff --git a/extensions/git-ui/src/typings/refs.d.ts b/extensions/git-ui/src/typings/refs.d.ts new file mode 100644 index 0000000000..e28bb39716 --- /dev/null +++ b/extensions/git-ui/src/typings/refs.d.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. + *--------------------------------------------------------------------------------------------*/ + +/// +/// +/// \ No newline at end of file diff --git a/extensions/git-ui/tsconfig.json b/extensions/git-ui/tsconfig.json new file mode 100644 index 0000000000..27e9268b39 --- /dev/null +++ b/extensions/git-ui/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + "experimentalDecorators": true, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/git-ui/yarn.lock b/extensions/git-ui/yarn.lock new file mode 100644 index 0000000000..b23b0ac039 --- /dev/null +++ b/extensions/git-ui/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json index d0bdb9ac44..e8081d6472 100644 --- a/extensions/git/cgmanifest.json +++ b/extensions/git/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/git.tmbundle", "repositoryUrl": "https://github.com/textmate/git.tmbundle", - "commitHash": "3f6ad2138200db14b57a090ecb2d2e733275ca3e" + "commitHash": "5870cf3f8abad3a6637bdf69250b5d2ded427dc4" } }, "licenseDetail": [ diff --git a/extensions/git/package.json b/extensions/git/package.json index a6cf112f69..814f53020a 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3,6 +3,7 @@ "displayName": "%displayName%", "description": "%description%", "publisher": "vscode", + "license": "MIT", "version": "1.0.0", "engines": { "vscode": "^1.5.0" @@ -20,7 +21,8 @@ "scripts": { "compile": "gulp compile-extension:git", "watch": "gulp watch-extension:git", - "update-grammar": "node ./build/update-grammars.js" + "update-grammar": "node ./build/update-grammars.js", + "test": "mocha" }, "contributes": { "commands": [ @@ -80,8 +82,8 @@ "title": "%command.openFile%", "category": "Git", "icon": { - "light": "resources/icons/light/open-file-mono.svg", - "dark": "resources/icons/dark/open-file-mono.svg" + "light": "resources/icons/light/open-file.svg", + "dark": "resources/icons/dark/open-file.svg" } }, { @@ -317,12 +319,12 @@ }, { "command": "git.pushWithTags", - "title": "%command.pushWithTags%", + "title": "%command.pushFollowTags%", "category": "Git" }, { "command": "git.pushWithTagsForce", - "title": "%command.pushWithTagsForce%", + "title": "%command.pushFollowTagsForce%", "category": "Git" }, { @@ -1458,13 +1460,14 @@ "jschardet": "^1.6.0", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", + "vscode-uri": "^2.0.0", "which": "^1.3.0" }, "devDependencies": { "@types/byline": "4.2.31", "@types/file-type": "^5.2.1", "@types/mocha": "2.2.43", - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "@types/which": "^1.0.28", "mocha": "^3.2.0" } diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 90f3d82499..adff0134ed 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -47,8 +47,8 @@ "command.pushForce": "Push (Force)", "command.pushTo": "Push to...", "command.pushToForce": "Push to... (Force)", - "command.pushWithTags": "Push With Tags", - "command.pushWithTagsForce": "Push With Tags (Force)", + "command.pushFollowTags": "Push (Follow Tags)", + "command.pushFollowTagsForce": "Push (Follow Tags, Force)", "command.addRemote": "Add Remote", "command.removeRemote": "Remove Remote", "command.sync": "Sync", diff --git a/extensions/git/resources/icons/dark/check.svg b/extensions/git/resources/icons/dark/check.svg index c225b2f597..865cc83c34 100644 --- a/extensions/git/resources/icons/dark/check.svg +++ b/extensions/git/resources/icons/dark/check.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/dark/clean.svg b/extensions/git/resources/icons/dark/clean.svg index 3770d63d5f..de85d6ba67 100644 --- a/extensions/git/resources/icons/dark/clean.svg +++ b/extensions/git/resources/icons/dark/clean.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/dark/git.svg b/extensions/git/resources/icons/dark/git.svg index c08b1c2e40..4d9389336b 100644 --- a/extensions/git/resources/icons/dark/git.svg +++ b/extensions/git/resources/icons/dark/git.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/dark/open-change.svg b/extensions/git/resources/icons/dark/open-change.svg index 6f785c26a5..41ebc85a8c 100644 --- a/extensions/git/resources/icons/dark/open-change.svg +++ b/extensions/git/resources/icons/dark/open-change.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/dark/open-file-mono.svg b/extensions/git/resources/icons/dark/open-file-mono.svg deleted file mode 100644 index 830727e70b..0000000000 --- a/extensions/git/resources/icons/dark/open-file-mono.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extensions/git/resources/icons/dark/open-file.svg b/extensions/git/resources/icons/dark/open-file.svg index f6302185aa..ed302ae139 100644 --- a/extensions/git/resources/icons/dark/open-file.svg +++ b/extensions/git/resources/icons/dark/open-file.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/dark/refresh.svg b/extensions/git/resources/icons/dark/refresh.svg index d79fdaa4e8..e1f05aadee 100644 --- a/extensions/git/resources/icons/dark/refresh.svg +++ b/extensions/git/resources/icons/dark/refresh.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/extensions/git/resources/icons/dark/stage.svg b/extensions/git/resources/icons/dark/stage.svg index 3475c1e196..4d9389336b 100644 --- a/extensions/git/resources/icons/dark/stage.svg +++ b/extensions/git/resources/icons/dark/stage.svg @@ -1 +1,3 @@ -Layer 1 \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/dark/unstage.svg b/extensions/git/resources/icons/dark/unstage.svg index 2de46fcf5b..4c5a9c1e3a 100644 --- a/extensions/git/resources/icons/dark/unstage.svg +++ b/extensions/git/resources/icons/dark/unstage.svg @@ -1 +1,3 @@ -Layer 1 \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/check.svg b/extensions/git/resources/icons/light/check.svg index 3f365c4800..e1a546660e 100644 --- a/extensions/git/resources/icons/light/check.svg +++ b/extensions/git/resources/icons/light/check.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/clean.svg b/extensions/git/resources/icons/light/clean.svg index f86ec7d627..b70626957d 100644 --- a/extensions/git/resources/icons/light/clean.svg +++ b/extensions/git/resources/icons/light/clean.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/git.svg b/extensions/git/resources/icons/light/git.svg index d1049a44d0..01a9de7d5a 100644 --- a/extensions/git/resources/icons/light/git.svg +++ b/extensions/git/resources/icons/light/git.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/open-change.svg b/extensions/git/resources/icons/light/open-change.svg index 873b93d810..772c3c198f 100644 --- a/extensions/git/resources/icons/light/open-change.svg +++ b/extensions/git/resources/icons/light/open-change.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/open-file-mono.svg b/extensions/git/resources/icons/light/open-file-mono.svg deleted file mode 100644 index fa3f245b75..0000000000 --- a/extensions/git/resources/icons/light/open-file-mono.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extensions/git/resources/icons/light/open-file.svg b/extensions/git/resources/icons/light/open-file.svg index d23a23c6b5..392a840c5e 100644 --- a/extensions/git/resources/icons/light/open-file.svg +++ b/extensions/git/resources/icons/light/open-file.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/refresh.svg b/extensions/git/resources/icons/light/refresh.svg index e034574819..9b1d910840 100644 --- a/extensions/git/resources/icons/light/refresh.svg +++ b/extensions/git/resources/icons/light/refresh.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/extensions/git/resources/icons/light/stage.svg b/extensions/git/resources/icons/light/stage.svg index bdecdb0e45..01a9de7d5a 100644 --- a/extensions/git/resources/icons/light/stage.svg +++ b/extensions/git/resources/icons/light/stage.svg @@ -1 +1,3 @@ -Layer 1 \ No newline at end of file + + + diff --git a/extensions/git/resources/icons/light/unstage.svg b/extensions/git/resources/icons/light/unstage.svg index f5d128b2df..d12a8ee313 100644 --- a/extensions/git/resources/icons/light/unstage.svg +++ b/extensions/git/resources/icons/light/unstage.svg @@ -1 +1,3 @@ -Layer 1 \ No newline at end of file + + + diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 767aacd3c9..9e82d769f1 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -235,4 +235,4 @@ export const enum GitErrorCodes { CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', PatchDoesNotApply = 'PatchDoesNotApply', NoPathFound = 'NoPathFound' -} \ No newline at end of file +} diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e034c98593..5efd4ccede 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -56,7 +56,13 @@ class CheckoutRemoteHeadItem extends CheckoutItem { return; } - await repository.checkoutTracking(this.ref.name); + const branches = await repository.findTrackingBranches(this.ref.name); + + if (branches.length > 0) { + await repository.checkout(branches[0].name!); + } else { + await repository.checkoutTracking(this.ref.name); + } } } @@ -196,7 +202,7 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { enum PushType { Push, PushTo, - PushTags, + PushFollowTags, } interface PushOptions { @@ -481,10 +487,10 @@ export class CommandCenter { (_, token) => this.git.clone(url!, parentPath, token) ); - const choices = []; let message = localize('proposeopen', "Would you like to open the cloned repository?"); - const open = localize('openrepo', "Open Repository"); - choices.push(open); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; const addToWorkspace = localize('add', "Add to Workspace"); if (workspace.workspaceFolders) { @@ -509,6 +515,8 @@ export class CommandCenter { commands.executeCommand('vscode.openFolder', uri); } else if (result === addToWorkspace) { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); + } else if (result === openNewWindow) { + commands.executeCommand('vscode.openFolder', uri, true); } } catch (err) { if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { @@ -593,10 +601,10 @@ export class CommandCenter { await this.git.init(repositoryPath); - const choices = []; let message = localize('proposeopen init', "Would you like to open the initialized repository?"); - const open = localize('openrepo', "Open Repository"); - choices.push(open); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; if (!askToOpen) { return; @@ -615,6 +623,8 @@ export class CommandCenter { commands.executeCommand('vscode.openFolder', uri); } else if (result === addToWorkspace) { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); + } else if (result === openNewWindow) { + commands.executeCommand('vscode.openFolder', uri, true); } else { await this.model.openRepository(repositoryPath); } @@ -663,7 +673,6 @@ export class CommandCenter { if (!(resource instanceof Resource)) { // can happen when called from a keybinding - console.log('WHAT'); resource = this.getSCMResource(); } @@ -728,6 +737,8 @@ export class CommandCenter { } const HEAD = await this.getLeftResource(resource); + const basename = path.basename(resource.resourceUri.fsPath); + const title = `${basename} (HEAD)`; if (!HEAD) { window.showWarningMessage(localize('HEAD not available', "HEAD version of '{0}' is not available.", path.basename(resource.resourceUri.fsPath))); @@ -738,7 +749,7 @@ export class CommandCenter { preview }; - return await commands.executeCommand('vscode.open', HEAD, opts); + return await commands.executeCommand('vscode.open', HEAD, opts, title); } @command('git.openChange') @@ -1560,8 +1571,11 @@ export class CommandCenter { @command('git.renameBranch', { repository: true }) async renameBranch(repository: Repository): Promise { - const placeHolder = localize('provide branch name', "Please provide a branch name"); - const name = await window.showInputBox({ placeHolder }); + const name = await window.showInputBox({ + placeHolder: localize('branch name', "Branch name"), + prompt: localize('provide branch name', "Please provide a branch name"), + value: repository.HEAD && repository.HEAD.name + }); if (!name || name.trim().length === 0) { return; @@ -1753,10 +1767,8 @@ export class CommandCenter { } } - if (pushOptions.pushType === PushType.PushTags) { - await repository.pushTags(undefined, forcePushMode); - - window.showInformationMessage(localize('push with tags success', "Successfully pushed with tags.")); + if (pushOptions.pushType === PushType.PushFollowTags) { + await repository.pushFollowTags(undefined, forcePushMode); return; } @@ -1813,13 +1825,13 @@ export class CommandCenter { } @command('git.pushWithTags', { repository: true }) - async pushWithTags(repository: Repository): Promise { - await this._push(repository, { pushType: PushType.PushTags }); + async pushFollowTags(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushFollowTags }); } @command('git.pushWithTagsForce', { repository: true }) - async pushWithTagsForce(repository: Repository): Promise { - await this._push(repository, { pushType: PushType.PushTags, forcePush: true }); + async pushFollowTagsForce(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true }); } @command('git.pushTo', { repository: true }) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 3684b30c5a..15f0f16062 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,7 +12,8 @@ import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; -import { CancellationToken, Uri, workspace } from 'vscode'; +import { CancellationToken } from 'vscode'; +import { URI } from 'vscode-uri'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; @@ -328,8 +329,8 @@ export class Git { this.env = options.env || {}; } - open(repository: string): Repository { - return new Repository(this, repository); + open(repository: string, dotGit: string): Repository { + return new Repository(this, repository, dotGit); } async init(repository: string): Promise { @@ -338,7 +339,7 @@ export class Git { } async clone(url: string, parentPath: string, cancellationToken?: CancellationToken): Promise { - let baseFolderName = decodeURI(url).replace(/^.*\//, '').replace(/\.git$/, '') || 'repository'; + let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*\//, '').replace(/\.git$/, '') || 'repository'; let folderName = baseFolderName; let folderPath = path.join(parentPath, folderName); let count = 1; @@ -369,6 +370,17 @@ export class Git { return path.normalize(result.stdout.trim()); } + async getRepositoryDotGit(repositoryPath: string): Promise { + const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir']); + let dotGitPath = result.stdout.trim(); + + if (!path.isAbsolute(dotGitPath)) { + dotGitPath = path.join(repositoryPath, dotGitPath); + } + + return path.normalize(dotGitPath); + } + async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise> { options = assign({ cwd }, options || {}); return await this._exec(args, options); @@ -557,7 +569,7 @@ export function parseGitmodules(raw: string): Submodule[] { return; } - const propertyMatch = /^\s*(\w+) = (.*)$/.exec(line); + const propertyMatch = /^\s*(\w+)\s+=\s+(.*)$/.exec(line); if (!propertyMatch) { return; @@ -636,6 +648,7 @@ export interface CommitOptions { export interface PullOptions { unshallow?: boolean; + tags?: boolean; } export enum ForcePushMode { @@ -647,7 +660,8 @@ export class Repository { constructor( private _git: Git, - private repositoryRoot: string + private repositoryRoot: string, + readonly dotGit: string ) { } get git(): Git { @@ -995,7 +1009,7 @@ export class Repository { break; } - const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); + const originalUri = URI.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); let status: Status = Status.UNTRACKED; // Copy or Rename status comes with a number, e.g. 'R100'. We don't need the number, so we use only first character of the status. @@ -1023,7 +1037,7 @@ export class Repository { break; } - const uri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); + const uri = URI.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); result.push({ uri, renameUri: uri, @@ -1363,9 +1377,8 @@ export class Repository { async pull(rebase?: boolean, remote?: string, branch?: string, options: PullOptions = {}): Promise { const args = ['pull']; - const config = workspace.getConfiguration('git', Uri.file(this.root)); - if (config.get('pullTags')) { + if (options.tags) { args.push('--tags'); } @@ -1418,7 +1431,7 @@ export class Repository { } if (tags) { - args.push('--tags'); + args.push('--follow-tags'); } if (remote) { @@ -1578,6 +1591,14 @@ export class Repository { } } + async findTrackingBranches(upstreamBranch: string): Promise { + const result = await this.run(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']); + return result.stdout.trim().split('\n') + .map(line => line.trim().split('\0')) + .filter(([_, upstream]) => upstream === upstreamBranch) + .map(([ref]) => ({ name: ref, type: RefType.Head } as Branch)); + } + async getRefs(): Promise { const result = await this.run(['for-each-ref', '--format', '%(refname) %(objectname)', '--sort', '-committerdate']); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index ca4ed63ebb..94cf12a025 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -78,7 +78,7 @@ export class Model { this.disposables.push(fsWatcher); const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete); - const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git\//.test(uri.path)); + const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git/.test(uri.path)); const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri)); onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables); @@ -232,7 +232,8 @@ export class Model { return; } - const repository = new Repository(this.git.open(repositoryRoot), this.globalState); + const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel); this.open(repository); } catch (err) { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 26888a11a2..4afe4fd8af 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -3,9 +3,9 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode'; +import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env } from 'vscode'; import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git'; -import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent } from './util'; +import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable, watch, IFileWatcher } from './util'; import { memoize, throttle, debounce } from './decorators'; import { toGitUri } from './uri'; import { AutoFetcher } from './autofetch'; @@ -299,6 +299,7 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', + FindTrackingBranches = 'GetTracking', Apply = 'Apply', Blame = 'Blame', Log = 'Log', @@ -446,6 +447,91 @@ class ProgressManager { } } +class FileEventLogger { + + private eventDisposable: IDisposable = EmptyDisposable; + private logLevelDisposable: IDisposable = EmptyDisposable; + + constructor( + private onWorkspaceWorkingTreeFileChange: Event, + private onDotGitFileChange: Event, + private outputChannel: OutputChannel + ) { + this.logLevelDisposable = env.onDidChangeLogLevel(this.onDidChangeLogLevel, this); + this.onDidChangeLogLevel(env.logLevel); + } + + private onDidChangeLogLevel(level: LogLevel): void { + this.eventDisposable.dispose(); + + if (level > LogLevel.Debug) { + return; + } + + this.eventDisposable = combinedDisposable([ + this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`[debug] [wt] Change: ${uri.fsPath}`)), + this.onDotGitFileChange(uri => this.outputChannel.appendLine(`[debug] [.git] Change: ${uri.fsPath}`)) + ]); + } + + dispose(): void { + this.eventDisposable.dispose(); + this.logLevelDisposable.dispose(); + } +} + +class DotGitWatcher implements IFileWatcher { + + readonly event: Event; + + private emitter = new EventEmitter(); + private transientDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private repository: Repository, + private outputChannel: OutputChannel + ) { + const rootWatcher = watch(repository.dotGit); + this.disposables.push(rootWatcher); + + const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path)); + this.event = anyEvent(filteredRootWatcher, this.emitter.event); + + repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables); + this.updateTransientWatchers(); + } + + private updateTransientWatchers() { + this.transientDisposables = dispose(this.transientDisposables); + + if (!this.repository.HEAD || !this.repository.HEAD.upstream) { + return; + } + + this.transientDisposables = dispose(this.transientDisposables); + + const { name, remote } = this.repository.HEAD.upstream; + const upstreamPath = path.join(this.repository.dotGit, 'refs', 'remotes', remote, name); + + try { + const upstreamWatcher = watch(upstreamPath); + this.transientDisposables.push(upstreamWatcher); + upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); + } catch (err) { + if (env.logLevel <= LogLevel.Info) { + this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`); + } + } + } + + dispose() { + this.emitter.dispose(); + this.transientDisposables = dispose(this.transientDisposables); + this.disposables = dispose(this.disposables); + } +} + export class Repository implements Disposable { private _onDidChangeRepository = new EventEmitter(); @@ -543,36 +629,41 @@ export class Repository implements Disposable { return this.repository.root; } + get dotGit(): string { + return this.repository.dotGit; + } + private isRepositoryHuge = false; private didWarnAboutLimit = false; private isFreshRepository: boolean | undefined = undefined; + private disposables: Disposable[] = []; constructor( private readonly repository: BaseRepository, - globalState: Memento + globalState: Memento, + outputChannel: OutputChannel ) { - const fsWatcher = workspace.createFileSystemWatcher('**'); - this.disposables.push(fsWatcher); + const workspaceWatcher = workspace.createFileSystemWatcher('**'); + this.disposables.push(workspaceWatcher); - const workspaceFilter = (uri: Uri) => isDescendant(repository.root, uri.fsPath); - const onWorkspaceDelete = filterEvent(fsWatcher.onDidDelete, workspaceFilter); - const onWorkspaceChange = filterEvent(anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate), workspaceFilter); - const onRepositoryDotGitDelete = filterEvent(onWorkspaceDelete, uri => /\/\.git$/.test(uri.path)); - const onRepositoryChange = anyEvent(onWorkspaceDelete, onWorkspaceChange); + const onWorkspaceFileChange = anyEvent(workspaceWatcher.onDidChange, workspaceWatcher.onDidCreate, workspaceWatcher.onDidDelete); + const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath)); + const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path)); - // relevant repository changes are: - // - DELETE .git folder - // - ANY CHANGE within .git folder except .git itself and .git/index.lock - const onRelevantRepositoryChange = anyEvent( - onRepositoryDotGitDelete, - filterEvent(onRepositoryChange, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path)) - ); + const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); + this.disposables.push(dotGitFileWatcher); - onRelevantRepositoryChange(this.onFSChange, this, this.disposables); + // FS changes should trigger `git status`: + // - any change inside the repository working tree + // - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock` + const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event); + onFileChange(this.onFileChange, this, this.disposables); - const onRelevantGitChange = filterEvent(onRelevantRepositoryChange, uri => /\/\.git\//.test(uri.path)); - onRelevantGitChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); + // Relevate repository changes should trigger virtual document change events + dotGitFileWatcher.event(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); + + this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event, outputChannel)); const root = Uri.file(repository.root); this._sourceControl = scm.createSourceControl('git', 'Git', root); @@ -582,9 +673,9 @@ export class Repository implements Disposable { this._sourceControl.inputBox.validateInput = this.validateInput.bind(this); this.disposables.push(this._sourceControl); - this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "Merge Changes")); - this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "Staged Changes")); - this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "Changes")); + this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES")); + this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES")); + this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES")); const updateIndexGroupVisibility = () => { const config = workspace.getConfiguration('git', root); @@ -909,6 +1000,10 @@ export class Repository implements Disposable { await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true })); } + async findTrackingBranches(upstreamRef: string): Promise { + return await this.run(Operation.FindTrackingBranches, () => this.repository.findTrackingBranches(upstreamRef)); + } + async getCommit(ref: string): Promise { return await this.repository.getCommit(ref); } @@ -979,11 +1074,12 @@ export class Repository implements Disposable { await this.maybeAutoStash(async () => { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); + const tags = config.get('pullTags'); if (fetchOnPull) { - await this.repository.pull(rebase, undefined, undefined, { unshallow }); + await this.repository.pull(rebase, undefined, undefined, { unshallow, tags }); } else { - await this.repository.pull(rebase, remote, branch, { unshallow }); + await this.repository.pull(rebase, remote, branch, { unshallow, tags }); } }); }); @@ -1006,7 +1102,7 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream, undefined, forcePushMode)); } - async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise { + async pushFollowTags(remote?: string, forcePushMode?: ForcePushMode): Promise { await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode)); } @@ -1039,11 +1135,12 @@ export class Repository implements Disposable { await this.maybeAutoStash(async () => { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); + const tags = config.get('pullTags'); if (fetchOnPull) { - await this.repository.pull(rebase); + await this.repository.pull(rebase, undefined, undefined, { tags }); } else { - await this.repository.pull(rebase, remoteName, pullBranch); + await this.repository.pull(rebase, remoteName, pullBranch, { tags }); } const remote = this.remotes.find(r => r.name === remoteName); @@ -1156,7 +1253,7 @@ export class Repository implements Disposable { } // https://git-scm.com/docs/git-check-ignore#git-check-ignore--z - const child = this.repository.stream(['check-ignore', '-z', '--stdin'], { stdio: [null, null, null] }); + const child = this.repository.stream(['check-ignore', '-v', '-z', '--stdin'], { stdio: [null, null, null] }); child.stdin.end(filePaths.join('\0'), 'utf8'); const onExit = (exitCode: number) => { @@ -1164,8 +1261,7 @@ export class Repository implements Disposable { // nothing ignored resolve(new Set()); } else if (exitCode === 0) { - // paths are separated by the null-character - resolve(new Set(data.split('\0'))); + resolve(new Set(this.parseIgnoreCheck(data))); } else { if (/ is in submodule /.test(stderr)) { reject(new GitError({ stdout: data, stderr, exitCode, gitErrorCode: GitErrorCodes.IsInSubmodule })); @@ -1193,6 +1289,23 @@ export class Repository implements Disposable { }); } + // Parses output of `git check-ignore -v -z` and returns only those paths + // that are actually ignored by git. + // Matches to a negative pattern (starting with '!') are filtered out. + // See also https://git-scm.com/docs/git-check-ignore#_output. + private parseIgnoreCheck(raw: string): string[] { + const ignored = []; + const elements = raw.split('\0'); + for (let i = 0; i < elements.length; i += 4) { + const pattern = elements[i + 2]; + const path = elements[i + 3]; + if (pattern && !pattern.startsWith('!')) { + ignored.push(path); + } + } + return ignored; + } + private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise { if (this.state !== RepositoryState.Idle) { throw new Error('Repository not initialized'); @@ -1430,7 +1543,7 @@ export class Repository implements Disposable { return result; } - private onFSChange(_uri: Uri): void { + private onFileChange(_uri: Uri): void { const config = workspace.getConfiguration('git'); const autorefresh = config.get('autorefresh'); diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index 8e27a374fa..facabe3550 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -172,6 +172,17 @@ suite('git', () => { { name: 'deps/spdlog4', path: 'deps/spdlog4', url: 'https://github.com/gabime/spdlog4.git' } ]); }); + + test('whitespace #74844', () => { + const sample = `[submodule "deps/spdlog"] + path = deps/spdlog + url = https://github.com/gabime/spdlog.git +`; + + assert.deepEqual(parseGitmodules(sample), [ + { name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' } + ]); + }); }); suite('parseGitCommit', () => { diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 70a1456380..7dc9927a3c 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vscode'; -import { dirname, sep } from 'path'; +import { Event, EventEmitter, Uri } from 'vscode'; +import { dirname, sep, join } from 'path'; import { Readable } from 'stream'; import * as fs from 'fs'; import * as byline from 'byline'; @@ -343,4 +343,20 @@ export function pathEquals(a: string, b: string): boolean { } return a === b; -} \ No newline at end of file +} + +export interface IFileWatcher extends IDisposable { + readonly event: Event; +} + +export function watch(location: string): IFileWatcher { + const dotGitWatcher = fs.watch(location); + const onDotGitFileChangeEmitter = new EventEmitter(); + dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string)))); + dotGitWatcher.on('error', err => console.error(err)); + + return new class implements IFileWatcher { + event = onDotGitFileChangeEmitter.event; + dispose() { dotGitWatcher.close(); } + }; +} diff --git a/extensions/git/syntaxes/git-rebase.tmLanguage.json b/extensions/git/syntaxes/git-rebase.tmLanguage.json index a2c116bd09..0238d8a6f2 100644 --- a/extensions/git/syntaxes/git-rebase.tmLanguage.json +++ b/extensions/git/syntaxes/git-rebase.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/textmate/git.tmbundle/commit/3f6ad2138200db14b57a090ecb2d2e733275ca3e", + "version": "https://github.com/textmate/git.tmbundle/commit/5870cf3f8abad3a6637bdf69250b5d2ded427dc4", "name": "Git Rebase Message", "scopeName": "text.git-rebase", "patterns": [ @@ -47,6 +47,15 @@ }, "match": "^\\s*(exec|x)\\s+(.*)$", "name": "meta.commit-command.git-rebase" + }, + { + "captures": { + "1": { + "name": "support.function.git-rebase" + } + }, + "match": "^\\s*(break|b)\\s*$", + "name": "meta.commit-command.git-rebase" } ] } \ No newline at end of file diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index f13ef93c34..1f2ea8aed4 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -26,10 +26,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== "@types/which@^1.0.28": version "1.0.28" @@ -325,6 +325,11 @@ vscode-nls@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-uri@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" + integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw== + which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 0a6d602886..7514379bc5 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -11,7 +11,7 @@ import { xhr, XHRResponse, getErrorStatusDescription } from 'request-light'; const localize = nls.loadMessageBundle(); import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, Position, SelectionRange } from 'vscode'; -import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature } from 'vscode-languageclient'; +import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; import { hash } from './utils/hash'; @@ -83,7 +83,12 @@ export function activate(context: ExtensionContext) { let documentSelector = ['json', 'jsonc']; - let schemaResolutionErrorStatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 0); + let schemaResolutionErrorStatusBarItem = window.createStatusBarItem({ + id: 'status.json.resolveError', + name: localize('json.resolveError', "JSON: Schema Resolution Error"), + alignment: StatusBarAlignment.Right, + priority: 0 + }); schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema.') + ' ' + localize('json.clickToRetry', 'Click to retry.'); schemaResolutionErrorStatusBarItem.text = '$(alert)'; @@ -154,7 +159,7 @@ export function activate(context: ExtensionContext) { return xhr({ url: uriPath, followRedirects: 5, headers }).then(response => { return response.responseText; }, (error: XHRResponse) => { - return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + return Promise.reject(new ResponseError(error.status, error.responseText || getErrorStatusDescription(error.status) || error.toString())); }); } }); diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 9dc4f87d9f..167583302c 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { "vscode": "0.10.x" @@ -101,12 +102,12 @@ } }, "dependencies": { + "request-light": "^0.2.4", "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^5.2.1", - "vscode-nls": "^4.0.0", - "request-light": "^0.2.4" + "vscode-languageclient": "^5.3.0-next.6", + "vscode-nls": "^4.1.1" }, "devDependencies": { - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" } } diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index 27be5ab1bb..9b02dc24ef 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -154,7 +154,8 @@ To connect to the server from NodeJS, see Remy Suen's great write-up on [how to ## Participate -The source code of the JSON language server can be found [VSCode repository](https://github.com/Microsoft/vscode) at [extensions/json-language-features/server](https://github.com/Microsoft/vscode/tree/master/extensions/json-language-features/server). +The source code of the JSON language server can be found in the [VSCode repository](https://github.com/Microsoft/vscode) at [extensions/json-language-features/server](https://github.com/Microsoft/vscode/tree/master/extensions/json-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: @@ -164,10 +165,12 @@ Most of the functionality of the server is located in libraries: Help on any of these projects is very welcome. -Please see also our [Code of Conduct](CODE_OF_CONDUCT.md). +## 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](LICENSE.txt) License. +Licensed under the [MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt) License. diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 1f73cea229..68b639c2aa 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,16 +12,16 @@ }, "main": "./out/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.0.3", + "jsonc-parser": "^2.1.0", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.3.0-next.7", - "vscode-languageserver": "^5.3.0-next.2", - "vscode-nls": "^4.0.0", - "vscode-uri": "^1.0.6" + "vscode-json-languageservice": "^3.3.0", + "vscode-languageserver": "^5.3.0-next.8", + "vscode-nls": "^4.1.1", + "vscode-uri": "^2.0.1" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" }, "scripts": { "prepublishOnly": "npm run clean && npm run test", diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 31be048d24..b5e74af2a9 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -11,7 +11,7 @@ import { import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; import * as fs from 'fs'; -import URI from 'vscode-uri'; +import { URI } from 'vscode-uri'; import * as URL from 'url'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice'; @@ -105,9 +105,9 @@ let languageService = getLanguageService({ contributions: [], }); -// Create a simple text document manager. The text document manager -// supports full document sync only +// Create a text document manager. const documents: TextDocuments = new TextDocuments(); + // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection); diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 3f0383a8c4..1bb198a521 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== agent-base@4, agent-base@^4.1.0: version "4.1.2" @@ -54,10 +54,10 @@ https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" -jsonc-parser@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.3.tgz#6d4199ccab7f21ff5d2a4225050c54e981fb21a2" - integrity sha512-WJi9y9ABL01C8CxTKxRRQkkSpY/x2bo4Gy0WuiZGrInxQqgxQpvkBCLNcDYcHOSdhx4ODgbFcgAvfL49C+PHgQ== +jsonc-parser@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.0.tgz#eb0d0c7a3c33048524ce3574c57c7278fb2f1bf3" + integrity sha512-n9GrT8rrr2fhvBbANa1g+xFmgGK5X91KFeDwlKQ3+SJfmH5+tKv/M/kahx/TXOMflfWHKGKqKyfHQaLKTNzJ6w== ms@2.0.0: version "2.0.0" @@ -73,40 +73,41 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.3.0-next.7: - version "3.3.0-next.7" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0-next.7.tgz#3ad4bf31f37fa110676b2c0db69b5f4810bdd4b9" - integrity sha512-uKXnzoZrqNOPRa+FmUdoCpNU5KCLhy7yDGCAzzfn0mocsEllPcspjHcBDu9HJCfT165UkhulZ2gl5vVX5NzBVA== +vscode-json-languageservice@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0.tgz#f80ec21c19fb8648c815220a2857f9f0b22e1094" + integrity sha512-upq1PhwDItazdtRJ/R7uU0Fgrf9iaYa1xLK4WFLExR0DgbPojd0YgMpfyknVyXGlxsg3fJQ0H7J++QeByXHh9w== dependencies: - jsonc-parser "^2.0.3" - vscode-languageserver-types "^3.14.0" - vscode-nls "^4.0.0" - vscode-uri "^1.0.6" + jsonc-parser "^2.1.0" + vscode-languageserver-types "^3.15.0-next.2" + vscode-nls "^4.1.1" + vscode-uri "^2.0.1" -vscode-jsonrpc@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" - integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== +vscode-jsonrpc@^4.1.0-next.2: + version "4.1.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" + integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== -vscode-languageserver-protocol@3.15.0-next.1: - version "3.15.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" - integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== +vscode-languageserver-protocol@^3.15.0-next.6: + version "3.15.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" + integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" + vscode-jsonrpc "^4.1.0-next.2" + vscode-languageserver-types "^3.15.0-next.2" -vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== +vscode-languageserver-types@^3.15.0-next.2: + version "3.15.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" + integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== -vscode-languageserver@^5.3.0-next.2: - version "5.3.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" - integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== +vscode-languageserver@^5.3.0-next.8: + version "5.3.0-next.8" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.8.tgz#12a4adf60374dbb93e153e08bdca5525f9b2029f" + integrity sha512-6vUb96wsRfrFqndril3gct/FBCSc24OxFZ2iz7kuEuXvLaIcEVOcSZIqQK8oFN7PdbAIaa9nnIpKSy4Yd15cIw== dependencies: - vscode-languageserver-protocol "3.15.0-next.1" + vscode-languageserver-protocol "^3.15.0-next.6" + vscode-textbuffer "^1.0.0" vscode-uri "^1.0.6" vscode-nls@^4.0.0: @@ -114,7 +115,22 @@ vscode-nls@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + +vscode-textbuffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086" + integrity sha512-zPaHo4urgpwsm+PrJWfNakolRpryNja18SUip/qIIsfhuEqEIPEXMxHOlFPjvDC4JgTaimkncNW7UMXRJTY6ow== + vscode-uri@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== + +vscode-uri@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.1.tgz#5448e4f77d21d93ffa34b96f84c6c5e09e3f5a9b" + integrity sha512-s/k0zsYr6y+tsocFyxT/+G5aq8mEdpDZuph3LZ+UmCs7LNhx/xomiCy5kyP+jOAKC7RMCUvb6JbPD1/TgAvq0g== diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 758f96fd51..fdc89dd7cf 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== agent-base@4, agent-base@^4.1.0: version "4.2.1" @@ -113,37 +113,42 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" - integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== +vscode-jsonrpc@^4.1.0-next.2: + version "4.1.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" + integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== -vscode-languageclient@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" - integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== +vscode-languageclient@^5.3.0-next.6: + version "5.3.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.3.0-next.6.tgz#35e74882781158e8b111911c0953869d3df08777" + integrity sha512-DxT8+gkenjCjJV6ArcP75/AQfx6HP6m6kHIbacPCpffMeoE1YMLKj6ZixA9J87yr0fMtBmqumLmDeGe7MIF2bw== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.14.1" + vscode-languageserver-protocol "^3.15.0-next.6" -vscode-languageserver-protocol@3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" - integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== +vscode-languageserver-protocol@^3.15.0-next.6: + version "3.15.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" + integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" + vscode-jsonrpc "^4.1.0-next.2" + vscode-languageserver-types "^3.15.0-next.2" -vscode-languageserver-types@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== +vscode-languageserver-types@^3.15.0-next.2: + version "3.15.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" + integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== vscode-nls@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + zone.js@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/json/package.json b/extensions/json/package.json index fd0dd54ff2..1606f6a3d6 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "0.10.x" }, @@ -23,7 +24,6 @@ ".bowerrc", ".jshintrc", ".jscsrc", - ".eslintrc", ".swcrc", ".webmanifest", ".js.map", @@ -48,7 +48,9 @@ "extensions": [ ".hintrc", ".babelrc", - ".jsonc" + ".jsonc", + ".eslintrc", + ".eslintrc.json" ], "configuration": "./language-configuration.json" } diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index 8d1ef2828c..26a3599cf5 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.20.0" }, diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index f8fd1e8b49..b94e9eba4a 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -716,7 +716,7 @@ { "begin": "(^|\\G)(\\s*)(.*)", "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", - "contentName": "meta.embedded.block.cpp", + "contentName": "meta.embedded.block.cpp source.cpp", "patterns": [ { "include": "source.cpp" diff --git a/extensions/markdown-language-features/cgmanifest.json b/extensions/markdown-language-features/cgmanifest.json deleted file mode 100644 index 71df78ef41..0000000000 --- a/extensions/markdown-language-features/cgmanifest.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "textmate/markdown.tmbundle", - "repositoryUrl": "https://github.com/textmate/markdown.tmbundle", - "commitHash": "11cf764606cb2cde54badb5d0e5a0758a8871c4b" - } - }, - "licenseDetail": [ - "Copyright (c) markdown.tmbundle authors", - "", - "If not otherwise specified (see below), files in this repository fall 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.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ], - "license": "TextMate Bundle License", - "version": "0.0.0" - } - ], - "version": 1 -} \ No newline at end of file diff --git a/extensions/markdown-language-features/media/Preview.svg b/extensions/markdown-language-features/media/Preview.svg deleted file mode 100644 index 860ba09394..0000000000 --- a/extensions/markdown-language-features/media/Preview.svg +++ /dev/null @@ -1 +0,0 @@ -SwitchToPreview_16x \ No newline at end of file diff --git a/extensions/markdown-language-features/media/PreviewOnRightPane_16x.svg b/extensions/markdown-language-features/media/PreviewOnRightPane_16x.svg deleted file mode 100644 index 5433796f50..0000000000 --- a/extensions/markdown-language-features/media/PreviewOnRightPane_16x.svg +++ /dev/null @@ -1 +0,0 @@ -PreviewInRightPanel_16x diff --git a/extensions/markdown-language-features/media/PreviewOnRightPane_16x_dark.svg b/extensions/markdown-language-features/media/PreviewOnRightPane_16x_dark.svg deleted file mode 100644 index 390b2658bb..0000000000 --- a/extensions/markdown-language-features/media/PreviewOnRightPane_16x_dark.svg +++ /dev/null @@ -1 +0,0 @@ -PreviewInRightPanel_16x \ No newline at end of file diff --git a/extensions/markdown-language-features/media/Preview_inverse.svg b/extensions/markdown-language-features/media/Preview_inverse.svg deleted file mode 100644 index e61e16690a..0000000000 --- a/extensions/markdown-language-features/media/Preview_inverse.svg +++ /dev/null @@ -1 +0,0 @@ -SwitchToPreview_16x diff --git a/extensions/markdown-language-features/media/ViewSource.svg b/extensions/markdown-language-features/media/ViewSource.svg deleted file mode 100644 index fccdf83d46..0000000000 --- a/extensions/markdown-language-features/media/ViewSource.svg +++ /dev/null @@ -1,3 +0,0 @@ - -]> \ No newline at end of file diff --git a/extensions/markdown-language-features/media/ViewSource_inverse.svg b/extensions/markdown-language-features/media/ViewSource_inverse.svg deleted file mode 100644 index f6302185aa..0000000000 --- a/extensions/markdown-language-features/media/ViewSource_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 50ff09c4dd..62d64e1539 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -741,8 +741,8 @@ document.addEventListener('click', event => { if (node.getAttribute('href').startsWith('#')) { break; } - if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:')) { - const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').split('#'); + if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:') || node.href.startsWith(settings.webviewResourceRoot)) { + const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').replace(new RegExp(`^${escapeRegExp(settings.webviewResourceRoot)}`)).split('#'); messaging.postMessage('clickLink', { path, fragment }); event.preventDefault(); event.stopPropagation(); @@ -768,6 +768,9 @@ if (settings.scrollEditorWithPreview) { } }, 50)); } +function escapeRegExp(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); +} /***/ }), @@ -983,4 +986,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNB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Ozs7Ozs7Ozs7Ozs7QUN2SUQ7OztnR0FHZ0c7O0FBWWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFDLEdBQVc7SUFDbEMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHRcdHN0YXRlLmxpbmUgPSBsaW5lO1xuXHRcdFx0XHR2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSwgNTApKTtcbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuZXhwb3J0IGludGVyZmFjZSBNZXNzYWdlUG9zdGVyIHtcblx0LyoqXG5cdCAqIFBvc3QgYSBtZXNzYWdlIHRvIHRoZSBtYXJrZG93biBleHRlbnNpb25cblx0ICovXG5cdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZDtcbn1cblxuZXhwb3J0IGNvbnN0IGNyZWF0ZVBvc3RlckZvclZzQ29kZSA9ICh2c2NvZGU6IGFueSkgPT4ge1xuXHRyZXR1cm4gbmV3IGNsYXNzIGltcGxlbWVudHMgTWVzc2FnZVBvc3RlciB7XG5cdFx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkIHtcblx0XHRcdHZzY29kZS5wb3N0TWVzc2FnZSh7XG5cdFx0XHRcdHR5cGUsXG5cdFx0XHRcdHNvdXJjZTogZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG5cdFx0XHRcdGJvZHlcblx0XHRcdH0pO1xuXHRcdH1cblx0fTtcbn07XG5cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5cbmZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCBtYXg6IG51bWJlciwgdmFsdWU6IG51bWJlcikge1xuXHRyZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lOiBudW1iZXIpIHtcblx0cmV0dXJuIGNsYW1wKDAsIGdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5cblxuZXhwb3J0IGludGVyZmFjZSBDb2RlTGluZUVsZW1lbnQge1xuXHRlbGVtZW50OiBIVE1MRWxlbWVudDtcblx0bGluZTogbnVtYmVyO1xufVxuXG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcblx0bGV0IGVsZW1lbnRzOiBDb2RlTGluZUVsZW1lbnRbXTtcblx0cmV0dXJuICgpID0+IHtcblx0XHRpZiAoIWVsZW1lbnRzKSB7XG5cdFx0XHRlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG5cdFx0XHRmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJykpIHtcblx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJykhO1xuXHRcdFx0XHRpZiAoIWlzTmFOKGxpbmUpKSB7XG5cdFx0XHRcdFx0ZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQgYXMgSFRNTEVsZW1lbnQsIGxpbmUgfSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGVsZW1lbnRzO1xuXHR9O1xufSkoKTtcblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZTogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG5cdFx0aWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRpZiAoIWdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAobGluZSA8PSAwKSB7XG5cdFx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRpZiAoIXByZXZpb3VzKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGxldCBzY3JvbGxUbyA9IDA7XG5cdGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcblx0XHQvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuXHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0Y29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcblx0fSBlbHNlIHtcblx0XHRjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuXHRcdHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG5cdH1cblx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0OiBudW1iZXIpIHtcblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmIChwcmV2aW91cykge1xuXHRcdGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgaW50ZXJmYWNlIFByZXZpZXdTZXR0aW5ncyB7XG5cdHNvdXJjZTogc3RyaW5nO1xuXHRsaW5lOiBudW1iZXI7XG5cdGxpbmVDb3VudDogbnVtYmVyO1xuXHRzY3JvbGxQcmV2aWV3V2l0aEVkaXRvcj86IGJvb2xlYW47XG5cdHNjcm9sbEVkaXRvcldpdGhQcmV2aWV3OiBib29sZWFuO1xuXHRkaXNhYmxlU2VjdXJpdHlXYXJuaW5nczogYm9vbGVhbjtcblx0ZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yOiBib29sZWFuO1xufVxuXG5sZXQgY2FjaGVkU2V0dGluZ3M6IFByZXZpZXdTZXR0aW5ncyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGEoa2V5OiBzdHJpbmcpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcblx0aWYgKGVsZW1lbnQpIHtcblx0XHRjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgbG9hZCBkYXRhIGZvciAke2tleX1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNldHRpbmdzKCk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHNldHRpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNB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SUFBSSxDQUFDLENBQUM7U0FDdkI7YUFDSTtZQUNKLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDM0UsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztZQUNuRCxPQUFPLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN2QjtLQUNEO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDYixDQUFDO0FBakJELDRFQWlCQzs7Ozs7Ozs7Ozs7Ozs7QUN2SUQ7OztnR0FHZ0c7O0FBYWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFTLEdBQVc7SUFDMUMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YTx7IGxpbmU6IG51bWJlciB9PignZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aChzZXR0aW5ncy53ZWJ2aWV3UmVzb3VyY2VSb290KSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnJlcGxhY2UobmV3IFJlZ0V4cChgXiR7ZXNjYXBlUmVnRXhwKHNldHRpbmdzLndlYnZpZXdSZXNvdXJjZVJvb3QpfWApKS5zcGxpdCgnIycpO1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NsaWNrTGluaycsIHsgcGF0aCwgZnJhZ21lbnQgfSk7XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHRcdGJyZWFrO1xuXHRcdH1cblx0XHRub2RlID0gbm9kZS5wYXJlbnROb2RlO1xuXHR9XG59LCB0cnVlKTtcblxuaWYgKHNldHRpbmdzLnNjcm9sbEVkaXRvcldpdGhQcmV2aWV3KSB7XG5cdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aHJvdHRsZSgoKSA9PiB7XG5cdFx0aWYgKHNjcm9sbERpc2FibGVkKSB7XG5cdFx0XHRzY3JvbGxEaXNhYmxlZCA9IGZhbHNlO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjb25zdCBsaW5lID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQod2luZG93LnNjcm9sbFkpO1xuXHRcdFx0aWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcblx0XHRcdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdyZXZlYWxMaW5lJywgeyBsaW5lIH0pO1xuXHRcdFx0XHRzdGF0ZS5saW5lID0gbGluZTtcblx0XHRcdFx0dnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sIDUwKSk7XG59XG5cbmZ1bmN0aW9uIGVzY2FwZVJlZ0V4cCh0ZXh0OiBzdHJpbmcpIHtcblx0cmV0dXJuIHRleHQucmVwbGFjZSgvWy1bXFxde30oKSorPy4sXFxcXF4kfCNcXHNdL2csICdcXFxcJCYnKTtcbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuZXhwb3J0IGludGVyZmFjZSBNZXNzYWdlUG9zdGVyIHtcblx0LyoqXG5cdCAqIFBvc3QgYSBtZXNzYWdlIHRvIHRoZSBtYXJrZG93biBleHRlbnNpb25cblx0ICovXG5cdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZDtcbn1cblxuZXhwb3J0IGNvbnN0IGNyZWF0ZVBvc3RlckZvclZzQ29kZSA9ICh2c2NvZGU6IGFueSkgPT4ge1xuXHRyZXR1cm4gbmV3IGNsYXNzIGltcGxlbWVudHMgTWVzc2FnZVBvc3RlciB7XG5cdFx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkIHtcblx0XHRcdHZzY29kZS5wb3N0TWVzc2FnZSh7XG5cdFx0XHRcdHR5cGUsXG5cdFx0XHRcdHNvdXJjZTogZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG5cdFx0XHRcdGJvZHlcblx0XHRcdH0pO1xuXHRcdH1cblx0fTtcbn07XG5cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5cbmZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCBtYXg6IG51bWJlciwgdmFsdWU6IG51bWJlcikge1xuXHRyZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lOiBudW1iZXIpIHtcblx0cmV0dXJuIGNsYW1wKDAsIGdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5cblxuZXhwb3J0IGludGVyZmFjZSBDb2RlTGluZUVsZW1lbnQge1xuXHRlbGVtZW50OiBIVE1MRWxlbWVudDtcblx0bGluZTogbnVtYmVyO1xufVxuXG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcblx0bGV0IGVsZW1lbnRzOiBDb2RlTGluZUVsZW1lbnRbXTtcblx0cmV0dXJuICgpID0+IHtcblx0XHRpZiAoIWVsZW1lbnRzKSB7XG5cdFx0XHRlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG5cdFx0XHRmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJykpIHtcblx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJykhO1xuXHRcdFx0XHRpZiAoIWlzTmFOKGxpbmUpKSB7XG5cdFx0XHRcdFx0ZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQgYXMgSFRNTEVsZW1lbnQsIGxpbmUgfSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGVsZW1lbnRzO1xuXHR9O1xufSkoKTtcblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZTogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG5cdFx0aWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRpZiAoIWdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAobGluZSA8PSAwKSB7XG5cdFx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRpZiAoIXByZXZpb3VzKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGxldCBzY3JvbGxUbyA9IDA7XG5cdGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcblx0XHQvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuXHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0Y29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcblx0fSBlbHNlIHtcblx0XHRjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuXHRcdHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG5cdH1cblx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0OiBudW1iZXIpIHtcblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmIChwcmV2aW91cykge1xuXHRcdGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgaW50ZXJmYWNlIFByZXZpZXdTZXR0aW5ncyB7XG5cdHJlYWRvbmx5IHNvdXJjZTogc3RyaW5nO1xuXHRyZWFkb25seSBsaW5lOiBudW1iZXI7XG5cdHJlYWRvbmx5IGxpbmVDb3VudDogbnVtYmVyO1xuXHRyZWFkb25seSBzY3JvbGxQcmV2aWV3V2l0aEVkaXRvcj86IGJvb2xlYW47XG5cdHJlYWRvbmx5IHNjcm9sbEVkaXRvcldpdGhQcmV2aWV3OiBib29sZWFuO1xuXHRyZWFkb25seSBkaXNhYmxlU2VjdXJpdHlXYXJuaW5nczogYm9vbGVhbjtcblx0cmVhZG9ubHkgZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yOiBib29sZWFuO1xuXHRyZWFkb25seSB3ZWJ2aWV3UmVzb3VyY2VSb290OiBzdHJpbmc7XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YTxUID0ge30+KGtleTogc3RyaW5nKTogVCB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 1be3a3fa03..d4f6b427b6 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ html, body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; - font-size: 14px; + font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif); + font-size: var(--vscode-markdown-font-size, 14px); padding: 0 26px; - line-height: 22px; + line-height: var(--vscode-markdown-line-height, 22px); word-wrap: break-word; } @@ -135,7 +135,6 @@ h3 code, h4 code, h5 code, h6 code { - font-size: inherit; line-height: auto; } @@ -168,8 +167,8 @@ blockquote { code { font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; - font-size: 1rem; - line-height: 1.357rem; + font-size: 1em; + line-height: 1.357em; } body.wordWrap pre { diff --git a/extensions/markdown-language-features/media/pre.js b/extensions/markdown-language-features/media/pre.js index 0342311b95..5ec502df26 100644 --- a/extensions/markdown-language-features/media/pre.js +++ b/extensions/markdown-language-features/media/pre.js @@ -277,4 +277,4 @@ exports.getStrings = getStrings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvY3NwLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3N0cmluZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0EseURBQWlELGNBQWM7QUFDL0Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsbUNBQTJCLDBCQUEwQixFQUFFO0FBQ3ZELHlDQUFpQyxlQUFlO0FBQ2hEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDhEQUFzRCwrREFBK0Q7O0FBRXJIO0FBQ0E7OztBQUdBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDbkVBOzs7Z0dBR2dHOztBQUdoRyxzRkFBeUM7QUFDekMsbUZBQXVDO0FBRXZDOztHQUVHO0FBQ0gsTUFBYSxVQUFVO0lBTXRCO1FBTFEsWUFBTyxHQUFHLEtBQUssQ0FBQztRQUNoQixzQkFBaUIsR0FBRyxLQUFLLENBQUM7UUFLakMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLHlCQUF5QixFQUFFLEdBQUcsRUFBRTtZQUN6RCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDckIsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUMsS0FBSyxF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O0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0M7Ozs7Ozs7Ozs7Ozs7O0FDeENEOzs7Z0dBR2dHOztBQUVoRyxTQUFnQixVQUFVO0lBQ3pCLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN0RSxJQUFJLEtBQUssRUFBRTtRQUNWLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEQsSUFBSSxJQUFJLEVBQUU7WUFDVCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDeEI7S0FDRDtJQUNELE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBVEQsZ0NBU0MiLCJmaWxlIjoicHJlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7XG4gXHRcdFx0XHRjb25maWd1cmFibGU6IGZhbHNlLFxuIFx0XHRcdFx0ZW51bWVyYWJsZTogdHJ1ZSxcbiBcdFx0XHRcdGdldDogZ2V0dGVyXG4gXHRcdFx0fSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiXCI7XG5cblxuIFx0Ly8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXyhfX3dlYnBhY2tfcmVxdWlyZV9fLnMgPSBcIi4vcHJldmlldy1zcmMvcHJlLnRzXCIpO1xuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IE1lc3NhZ2VQb3N0ZXIgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHsgZ2V0U3RyaW5ncyB9IGZyb20gJy4vc3RyaW5ncyc7XG5cbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIENzcEFsZXJ0ZXIge1xuXHRwcml2YXRlIGRpZFNob3cgPSBmYWxzZTtcblx0cHJpdmF0ZSBkaWRIYXZlQ3NwV2FybmluZyA9IGZhbHNlO1xuXG5cdHByaXZhdGUgbWVzc2FnaW5nPzogTWVzc2FnZVBvc3RlcjtcblxuXHRjb25zdHJ1Y3RvcigpIHtcblx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdzZWN1cml0eXBvbGljeXZpb2xhdGlvbicsICgpID0+IHtcblx0XHRcdHRoaXMub25Dc3BXYXJuaW5nKCk7XG5cdFx0fSk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIChldmVudCkgPT4ge1xuXHRcdFx0aWYgKGV2ZW50ICYmIGV2ZW50LmRhdGEgJiYgZXZlbnQuZGF0YS5uYW1lID09PSAndnNjb2RlLWRpZC1ibG9jay1zdmcnKSB7XG5cdFx0XHRcdHRoaXMub25Dc3BXYXJuaW5nKCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cblxuXHRwdWJsaWMgc2V0UG9zdGVyKHBvc3RlcjogTWVzc2FnZVBvc3Rlcikge1xuXHRcdHRoaXMubWVzc2FnaW5nID0gcG9zdGVyO1xuXHRcdGlmICh0aGlzLmRpZEhhdmVDc3BXYXJuaW5nKSB7XG5cdFx0XHR0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBvbkNzcFdhcm5pbmcoKSB7XG5cdFx0dGhpcy5kaWRIYXZlQ3NwV2FybmluZyA9IHRydWU7XG5cdFx0dGhpcy5zaG93Q3NwV2FybmluZygpO1xuXHR9XG5cblx0cHJpdmF0ZSBzaG93Q3NwV2FybmluZygpIHtcblx0XHRjb25zdCBzdHJpbmdzID0gZ2V0U3RyaW5ncygpO1xuXHRcdGNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuXHRcdGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuZGlkU2hvdyA9IHRydWU7XG5cblx0XHRjb25zdCBub3RpZmljYXRpb24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG5cdFx0bm90aWZpY2F0aW9uLmlubmVyVGV4dCA9IHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGV4dDtcblx0XHRub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgndGl0bGUnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZVRpdGxlKTtcblxuXHRcdG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsIHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlTGFiZWwpO1xuXHRcdG5vdGlmaWNhdGlvbi5vbmNsaWNrID0gKCkgPT4ge1xuXHRcdFx0dGhpcy5tZXNzYWdpbmchLnBvc3RNZXNzYWdlKCdzaG93UHJldmlld1NlY3VyaXR5U2VsZWN0b3InLCB7IHNvdXJjZTogc2V0dGluZ3Muc291cmNlIH0pO1xuXHRcdH07XG5cdFx0ZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuXHR9XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IE1lc3NhZ2VQb3N0ZXIgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5cbmV4cG9ydCBjbGFzcyBTdHlsZUxvYWRpbmdNb25pdG9yIHtcblx0cHJpdmF0ZSB1bmxvYWRlZFN0eWxlczogc3RyaW5nW10gPSBbXTtcblx0cHJpdmF0ZSBmaW5pc2hlZExvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcblxuXHRwcml2YXRlIHBvc3Rlcj86IE1lc3NhZ2VQb3N0ZXI7XG5cblx0Y29uc3RydWN0b3IoKSB7XG5cdFx0Y29uc3Qgb25TdHlsZUxvYWRFcnJvciA9IChldmVudDogYW55KSA9PiB7XG5cdFx0XHRjb25zdCBzb3VyY2UgPSBldmVudC50YXJnZXQuZGF0YXNldC5zb3VyY2U7XG5cdFx0XHR0aGlzLnVubG9hZGVkU3R5bGVzLnB1c2goc291cmNlKTtcblx0XHR9O1xuXG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG5cdFx0XHRmb3IgKGNvbnN0IGxpbmsgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS11c2VyLXN0eWxlJykgYXMgSFRNTENvbGxlY3Rpb25PZjxIVE1MRWxlbWVudD4pIHtcblx0XHRcdFx0aWYgKGxpbmsuZGF0YXNldC5zb3VyY2UpIHtcblx0XHRcdFx0XHRsaW5rLm9uZXJyb3IgPSBvblN0eWxlTG9hZEVycm9yO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0+IHtcblx0XHRcdGlmICghdGhpcy51bmxvYWRlZFN0eWxlcy5sZW5ndGgpIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5maW5pc2hlZExvYWRpbmcgPSB0cnVlO1xuXHRcdFx0aWYgKHRoaXMucG9zdGVyKSB7XG5cdFx0XHRcdHRoaXMucG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9XG5cblx0cHVibGljIHNldFBvc3Rlcihwb3N0ZXI6IE1lc3NhZ2VQb3N0ZXIpOiB2b2lkIHtcblx0XHR0aGlzLnBvc3RlciA9IHBvc3Rlcjtcblx0XHRpZiAodGhpcy5maW5pc2hlZExvYWRpbmcpIHtcblx0XHRcdHBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcblx0XHR9XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQ3NwQWxlcnRlciB9IGZyb20gJy4vY3NwJztcbmltcG9ydCB7IFN0eWxlTG9hZGluZ01vbml0b3IgfSBmcm9tICcuL2xvYWRpbmcnO1xuXG5kZWNsYXJlIGdsb2JhbCB7XG5cdGludGVyZmFjZSBXaW5kb3cge1xuXHRcdGNzcEFsZXJ0ZXI6IENzcEFsZXJ0ZXI7XG5cdFx0c3R5bGVMb2FkaW5nTW9uaXRvcjogU3R5bGVMb2FkaW5nTW9uaXRvcjtcblx0fVxufVxuXG53aW5kb3cuY3NwQWxlcnRlciA9IG5ldyBDc3BBbGVydGVyKCk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvciA9IG5ldyBTdHlsZUxvYWRpbmdNb25pdG9yKCk7IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJldmlld1NldHRpbmdzIHtcblx0c291cmNlOiBzdHJpbmc7XG5cdGxpbmU6IG51bWJlcjtcblx0bGluZUNvdW50OiBudW1iZXI7XG5cdHNjcm9sbFByZXZpZXdXaXRoRWRpdG9yPzogYm9vbGVhbjtcblx0c2Nyb2xsRWRpdG9yV2l0aFByZXZpZXc6IGJvb2xlYW47XG5cdGRpc2FibGVTZWN1cml0eVdhcm5pbmdzOiBib29sZWFuO1xuXHRkb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3I6IGJvb2xlYW47XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YShrZXk6IHN0cmluZyk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U3RyaW5ncygpOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9IHtcblx0Y29uc3Qgc3RvcmUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoc3RvcmUpIHtcblx0XHRjb25zdCBkYXRhID0gc3RvcmUuZ2V0QXR0cmlidXRlKCdkYXRhLXN0cmluZ3MnKTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc3RyaW5ncycpO1xufVxuIl0sInNvdXJjZVJvb3QiOiIifQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvY3NwLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3N0cmluZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0EseURBQWlELGNBQWM7QUFDL0Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsbUNBQTJCLDBCQUEwQixFQUFFO0FBQ3ZELHlDQUFpQyxlQUFlO0FBQ2hEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDhEQUFzRCwrREFBK0Q7O0FBRXJIO0FBQ0E7OztBQUdBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDbkVBOzs7Z0dBR2dHOztBQUdoRyxzRkFBeUM7QUFDekMsbUZBQXVDO0FBRXZDOztHQUVHO0FBQ0gsTUFBYSxVQUFVO0lBTXRCO1FBTFEsWUFBTyxHQUFHLEtBQUssQ0FBQztRQUNoQixzQkFBaUIsR0FBRyxLQUFLLENBQUM7UUFLakMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLHlCQUF5QixFQUFFLEdBQUcsRUFBRTtZQUN6RCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDckIsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUMsS0FBSyxF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O0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0M7Ozs7Ozs7Ozs7Ozs7O0FDekNEOzs7Z0dBR2dHOztBQUVoRyxTQUFnQixVQUFVO0lBQ3pCLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN0RSxJQUFJLEtBQUssRUFBRTtRQUNWLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEQsSUFBSSxJQUFJLEVBQUU7WUFDVCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDeEI7S0FDRDtJQUNELE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBVEQsZ0NBU0MiLCJmaWxlIjoicHJlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7XG4gXHRcdFx0XHRjb25maWd1cmFibGU6IGZhbHNlLFxuIFx0XHRcdFx0ZW51bWVyYWJsZTogdHJ1ZSxcbiBcdFx0XHRcdGdldDogZ2V0dGVyXG4gXHRcdFx0fSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiXCI7XG5cblxuIFx0Ly8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXyhfX3dlYnBhY2tfcmVxdWlyZV9fLnMgPSBcIi4vcHJldmlldy1zcmMvcHJlLnRzXCIpO1xuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IE1lc3NhZ2VQb3N0ZXIgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHsgZ2V0U3RyaW5ncyB9IGZyb20gJy4vc3RyaW5ncyc7XG5cbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIENzcEFsZXJ0ZXIge1xuXHRwcml2YXRlIGRpZFNob3cgPSBmYWxzZTtcblx0cHJpdmF0ZSBkaWRIYXZlQ3NwV2FybmluZyA9IGZhbHNlO1xuXG5cdHByaXZhdGUgbWVzc2FnaW5nPzogTWVzc2FnZVBvc3RlcjtcblxuXHRjb25zdHJ1Y3RvcigpIHtcblx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdzZWN1cml0eXBvbGljeXZpb2xhdGlvbicsICgpID0+IHtcblx0XHRcdHRoaXMub25Dc3BXYXJuaW5nKCk7XG5cdFx0fSk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIChldmVudCkgPT4ge1xuXHRcdFx0aWYgKGV2ZW50ICYmIGV2ZW50LmRhdGEgJiYgZXZlbnQuZGF0YS5uYW1lID09PSAndnNjb2RlLWRpZC1ibG9jay1zdmcnKSB7XG5cdFx0XHRcdHRoaXMub25Dc3BXYXJuaW5nKCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cblxuXHRwdWJsaWMgc2V0UG9zdGVyKHBvc3RlcjogTWVzc2FnZVBvc3Rlcikge1xuXHRcdHRoaXMubWVzc2FnaW5nID0gcG9zdGVyO1xuXHRcdGlmICh0aGlzLmRpZEhhdmVDc3BXYXJuaW5nKSB7XG5cdFx0XHR0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBvbkNzcFdhcm5pbmcoKSB7XG5cdFx0dGhpcy5kaWRIYXZlQ3NwV2FybmluZyA9IHRydWU7XG5cdFx0dGhpcy5zaG93Q3NwV2FybmluZygpO1xuXHR9XG5cblx0cHJpdmF0ZSBzaG93Q3NwV2FybmluZygpIHtcblx0XHRjb25zdCBzdHJpbmdzID0gZ2V0U3RyaW5ncygpO1xuXHRcdGNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuXHRcdGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuZGlkU2hvdyA9IHRydWU7XG5cblx0XHRjb25zdCBub3RpZmljYXRpb24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG5cdFx0bm90aWZpY2F0aW9uLmlubmVyVGV4dCA9IHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGV4dDtcblx0XHRub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgndGl0bGUnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZVRpdGxlKTtcblxuXHRcdG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsIHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlTGFiZWwpO1xuXHRcdG5vdGlmaWNhdGlvbi5vbmNsaWNrID0gKCkgPT4ge1xuXHRcdFx0dGhpcy5tZXNzYWdpbmchLnBvc3RNZXNzYWdlKCdzaG93UHJldmlld1NlY3VyaXR5U2VsZWN0b3InLCB7IHNvdXJjZTogc2V0dGluZ3Muc291cmNlIH0pO1xuXHRcdH07XG5cdFx0ZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuXHR9XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IE1lc3NhZ2VQb3N0ZXIgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5cbmV4cG9ydCBjbGFzcyBTdHlsZUxvYWRpbmdNb25pdG9yIHtcblx0cHJpdmF0ZSB1bmxvYWRlZFN0eWxlczogc3RyaW5nW10gPSBbXTtcblx0cHJpdmF0ZSBmaW5pc2hlZExvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcblxuXHRwcml2YXRlIHBvc3Rlcj86IE1lc3NhZ2VQb3N0ZXI7XG5cblx0Y29uc3RydWN0b3IoKSB7XG5cdFx0Y29uc3Qgb25TdHlsZUxvYWRFcnJvciA9IChldmVudDogYW55KSA9PiB7XG5cdFx0XHRjb25zdCBzb3VyY2UgPSBldmVudC50YXJnZXQuZGF0YXNldC5zb3VyY2U7XG5cdFx0XHR0aGlzLnVubG9hZGVkU3R5bGVzLnB1c2goc291cmNlKTtcblx0XHR9O1xuXG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG5cdFx0XHRmb3IgKGNvbnN0IGxpbmsgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS11c2VyLXN0eWxlJykgYXMgSFRNTENvbGxlY3Rpb25PZjxIVE1MRWxlbWVudD4pIHtcblx0XHRcdFx0aWYgKGxpbmsuZGF0YXNldC5zb3VyY2UpIHtcblx0XHRcdFx0XHRsaW5rLm9uZXJyb3IgPSBvblN0eWxlTG9hZEVycm9yO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0+IHtcblx0XHRcdGlmICghdGhpcy51bmxvYWRlZFN0eWxlcy5sZW5ndGgpIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5maW5pc2hlZExvYWRpbmcgPSB0cnVlO1xuXHRcdFx0aWYgKHRoaXMucG9zdGVyKSB7XG5cdFx0XHRcdHRoaXMucG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9XG5cblx0cHVibGljIHNldFBvc3Rlcihwb3N0ZXI6IE1lc3NhZ2VQb3N0ZXIpOiB2b2lkIHtcblx0XHR0aGlzLnBvc3RlciA9IHBvc3Rlcjtcblx0XHRpZiAodGhpcy5maW5pc2hlZExvYWRpbmcpIHtcblx0XHRcdHBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcblx0XHR9XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQ3NwQWxlcnRlciB9IGZyb20gJy4vY3NwJztcbmltcG9ydCB7IFN0eWxlTG9hZGluZ01vbml0b3IgfSBmcm9tICcuL2xvYWRpbmcnO1xuXG5kZWNsYXJlIGdsb2JhbCB7XG5cdGludGVyZmFjZSBXaW5kb3cge1xuXHRcdGNzcEFsZXJ0ZXI6IENzcEFsZXJ0ZXI7XG5cdFx0c3R5bGVMb2FkaW5nTW9uaXRvcjogU3R5bGVMb2FkaW5nTW9uaXRvcjtcblx0fVxufVxuXG53aW5kb3cuY3NwQWxlcnRlciA9IG5ldyBDc3BBbGVydGVyKCk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvciA9IG5ldyBTdHlsZUxvYWRpbmdNb25pdG9yKCk7IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJldmlld1NldHRpbmdzIHtcblx0cmVhZG9ubHkgc291cmNlOiBzdHJpbmc7XG5cdHJlYWRvbmx5IGxpbmU6IG51bWJlcjtcblx0cmVhZG9ubHkgbGluZUNvdW50OiBudW1iZXI7XG5cdHJlYWRvbmx5IHNjcm9sbFByZXZpZXdXaXRoRWRpdG9yPzogYm9vbGVhbjtcblx0cmVhZG9ubHkgc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXc6IGJvb2xlYW47XG5cdHJlYWRvbmx5IGRpc2FibGVTZWN1cml0eVdhcm5pbmdzOiBib29sZWFuO1xuXHRyZWFkb25seSBkb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3I6IGJvb2xlYW47XG5cdHJlYWRvbmx5IHdlYnZpZXdSZXNvdXJjZVJvb3Q6IHN0cmluZztcbn1cblxubGV0IGNhY2hlZFNldHRpbmdzOiBQcmV2aWV3U2V0dGluZ3MgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREYXRhPFQgPSB7fT4oa2V5OiBzdHJpbmcpOiBUIHtcblx0Y29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG5cdGlmIChlbGVtZW50KSB7XG5cdFx0Y29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG5cdFx0aWYgKGRhdGEpIHtcblx0XHRcdHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuXHRcdH1cblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXR0aW5ncygpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHRjYWNoZWRTZXR0aW5ncyA9IGdldERhdGEoJ2RhdGEtc2V0dGluZ3MnKTtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTdHJpbmdzKCk6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0ge1xuXHRjb25zdCBzdG9yZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG5cdGlmIChzdG9yZSkge1xuXHRcdGNvbnN0IGRhdGEgPSBzdG9yZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RyaW5ncycpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblx0dGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzdHJpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 diff --git a/extensions/markdown-language-features/media/preview-dark.svg b/extensions/markdown-language-features/media/preview-dark.svg new file mode 100644 index 0000000000..ec71ea8114 --- /dev/null +++ b/extensions/markdown-language-features/media/preview-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/markdown-language-features/media/preview-light.svg b/extensions/markdown-language-features/media/preview-light.svg new file mode 100644 index 0000000000..4a6b85b583 --- /dev/null +++ b/extensions/markdown-language-features/media/preview-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/markdown-language-features/media/preview-right-dark.svg b/extensions/markdown-language-features/media/preview-right-dark.svg new file mode 100644 index 0000000000..1d59877196 --- /dev/null +++ b/extensions/markdown-language-features/media/preview-right-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/markdown-language-features/media/preview-right-light.svg b/extensions/markdown-language-features/media/preview-right-light.svg new file mode 100644 index 0000000000..3f1152fc3c --- /dev/null +++ b/extensions/markdown-language-features/media/preview-right-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/markdown-language-features/media/view-source-dark.svg b/extensions/markdown-language-features/media/view-source-dark.svg new file mode 100644 index 0000000000..ed302ae139 --- /dev/null +++ b/extensions/markdown-language-features/media/view-source-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/markdown-language-features/media/view-source-light.svg b/extensions/markdown-language-features/media/view-source-light.svg new file mode 100644 index 0000000000..392a840c5e --- /dev/null +++ b/extensions/markdown-language-features/media/view-source-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index e18099fe67..84fdbd24b4 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -4,7 +4,9 @@ "description": "%description%", "version": "1.0.0", "icon": "icon.png", - "publisher": "vscode", + "publisher": "vscode", + "enableProposedApi": true, + "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { "vscode": "^1.20.0" @@ -32,8 +34,8 @@ "title": "%markdown.preview.title%", "category": "Markdown", "icon": { - "light": "./media/Preview.svg", - "dark": "./media/Preview_inverse.svg" + "light": "./media/preview-light.svg", + "dark": "./media/preview-dark.svg" } }, { @@ -41,8 +43,8 @@ "title": "%markdown.previewSide.title%", "category": "Markdown", "icon": { - "light": "./media/PreviewOnRightPane_16x.svg", - "dark": "./media/PreviewOnRightPane_16x_dark.svg" + "light": "./media/preview-right-light.svg", + "dark": "./media/preview-right-dark.svg" } }, { @@ -50,8 +52,8 @@ "title": "%markdown.showLockedPreviewToSide.title%", "category": "Markdown", "icon": { - "light": "./media/PreviewOnRightPane_16x.svg", - "dark": "./media/PreviewOnRightPane_16x_dark.svg" + "light": "./media/preview-right-light.svg", + "dark": "./media/preview-right-dark.svg" } }, { @@ -59,8 +61,8 @@ "title": "%markdown.showSource.title%", "category": "Markdown", "icon": { - "light": "./media/ViewSource.svg", - "dark": "./media/ViewSource_inverse.svg" + "light": "./media/view-source-light.svg", + "dark": "./media/view-source-dark.svg" } }, { @@ -313,7 +315,7 @@ "build-preview": "webpack --mode development" }, "dependencies": { - "highlight.js": "9.13.1", + "highlight.js": "9.15.8", "markdown-it": "^8.4.2", "markdown-it-front-matter": "^0.1.2", "vscode-extension-telemetry": "0.1.1", @@ -323,7 +325,7 @@ "@types/highlight.js": "9.12.3", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "0.0.2", - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "lodash.throttle": "^4.1.1", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index cae89bb56c..6efaa4e310 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -19,7 +19,7 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); // Set VS Code state -let state = getData('data-state'); +let state = getData<{ line: number }>('data-state'); vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -131,8 +131,8 @@ document.addEventListener('click', event => { if (node.getAttribute('href').startsWith('#')) { break; } - if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:')) { - const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').split('#'); + if (node.href.startsWith('file://') || node.href.startsWith('vscode-resource:') || node.href.startsWith(settings.webviewResourceRoot)) { + const [path, fragment] = node.href.replace(/^(file:\/\/|vscode-resource:)/i, '').replace(new RegExp(`^${escapeRegExp(settings.webviewResourceRoot)}`)).split('#'); messaging.postMessage('clickLink', { path, fragment }); event.preventDefault(); event.stopPropagation(); @@ -157,4 +157,8 @@ if (settings.scrollEditorWithPreview) { } } }, 50)); +} + +function escapeRegExp(text: string) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); } \ No newline at end of file diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index 8cd8d7fd45..fcb35a0791 100644 --- a/extensions/markdown-language-features/preview-src/settings.ts +++ b/extensions/markdown-language-features/preview-src/settings.ts @@ -4,18 +4,19 @@ *--------------------------------------------------------------------------------------------*/ export interface PreviewSettings { - source: string; - line: number; - lineCount: number; - scrollPreviewWithEditor?: boolean; - scrollEditorWithPreview: boolean; - disableSecurityWarnings: boolean; - doubleClickToSwitchToEditor: boolean; + readonly source: string; + readonly line: number; + readonly lineCount: number; + readonly scrollPreviewWithEditor?: boolean; + readonly scrollEditorWithPreview: boolean; + readonly disableSecurityWarnings: boolean; + readonly doubleClickToSwitchToEditor: boolean; + readonly webviewResourceRoot: string; } let cachedSettings: PreviewSettings | undefined = undefined; -export function getData(key: string): PreviewSettings { +export function getData(key: string): T { const element = document.getElementById('vscode-markdown-preview-data'); if (element) { const data = element.getAttribute(key); diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index a41e4adc7f..c36cfc03c2 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -5,17 +5,20 @@ import * as path from 'path'; import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; import { OpenDocumentLinkCommand } from '../commands/openDocumentLink'; import { getUriForLinkWithKnownExternalScheme } from '../util/links'; -function normalizeLink( +const localize = nls.loadMessageBundle(); + +function parseLink( document: vscode.TextDocument, link: string, base: string -): vscode.Uri { +): { uri: vscode.Uri, tooltip?: string } { const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link); if (externalSchemeUri) { - return externalSchemeUri; + return { uri: externalSchemeUri }; } // Assume it must be an relative or absolute file path @@ -34,7 +37,10 @@ function normalizeLink( resourcePath = base ? path.join(base, tempUri.path) : tempUri.path; } - return OpenDocumentLinkCommand.createCommandUri(resourcePath, tempUri.fragment); + return { + uri: OpenDocumentLinkCommand.createCommandUri(resourcePath, tempUri.fragment), + tooltip: localize('documentLink.tooltip', 'Follow link') + }; } function matchAll( @@ -61,18 +67,21 @@ function extractDocumentLink( const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); try { - return new vscode.DocumentLink( + const { uri, tooltip } = parseLink(document, link, base); + const documentLink = new vscode.DocumentLink( new vscode.Range(linkStart, linkEnd), - normalizeLink(document, link, base)); + uri); + documentLink.tooltip = tooltip; + return documentLink; } catch (e) { return undefined; } } export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; - private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; - private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; + private readonly referenceLinkPattern = /(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]/g; + private readonly definitionPattern = /^([\t ]*\[((?:\\\]|[^\]])+)\]:\s*)(\S+)/gm; public provideDocumentLinks( document: vscode.TextDocument, @@ -144,11 +153,10 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } } - for (const definition of Array.from(definitions.values())) { + for (const definition of definitions.values()) { try { - results.push(new vscode.DocumentLink( - definition.linkRange, - normalizeLink(document, definition.link, base))); + const { uri } = parseLink(document, definition.link, base); + results.push(new vscode.DocumentLink(definition.linkRange, uri)); } catch (e) { // noop } diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index e8cd0730ce..748686ef2a 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -16,6 +16,7 @@ import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { isMarkdownFile } from '../util/file'; import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; +import { WebviewResourceProvider, normalizeResource } from '../util/resources'; const localize = nls.loadMessageBundle(); interface WebviewMessage { @@ -345,8 +346,8 @@ export class MarkdownPreview extends Disposable { private get iconPath() { const root = path.join(this._contributionProvider.extensionPath, 'media'); return { - light: vscode.Uri.file(path.join(root, 'Preview.svg')), - dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg')) + light: vscode.Uri.file(path.join(root, 'preview-light.svg')), + dark: vscode.Uri.file(path.join(root, 'preview-dark.svg')) }; } @@ -388,31 +389,46 @@ export class MarkdownPreview extends Disposable { } private async doUpdate(): Promise { - const resource = this._resource; + if (this._disposed) { + return; + } + + const markdownResource = this._resource; clearTimeout(this.throttleTimer); this.throttleTimer = undefined; let document: vscode.TextDocument; try { - document = await vscode.workspace.openTextDocument(resource); + document = await vscode.workspace.openTextDocument(markdownResource); } catch { await this.showFileNotFoundError(); return; } - const pendingVersion = new PreviewDocumentVersion(resource, document.version); + if (this._disposed) { + return; + } + + const pendingVersion = new PreviewDocumentVersion(markdownResource, document.version); if (!this.forceUpdate && this.currentVersion && this.currentVersion.equals(pendingVersion)) { if (this.line) { - this.updateForView(resource, this.line); + this.updateForView(markdownResource, this.line); } return; } this.forceUpdate = false; this.currentVersion = pendingVersion; - if (this._resource === resource) { - const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state); + if (this._resource === markdownResource) { + const self = this; + const resourceProvider: WebviewResourceProvider = { + toWebviewResource: (resource) => { + return this.editor.webview.toWebviewResource(normalizeResource(markdownResource, resource)); + }, + get cspSource() { return self.editor.webview.cspSource; } + }; + const content = await this._contentProvider.provideTextDocumentContent(document, resourceProvider, this._previewConfigurations, this.line, this.state); // Another call to `doUpdate` may have happened. // Make sure we are still updating for the correct document if (this.currentVersion && this.currentVersion.equals(pendingVersion)) { @@ -432,21 +448,19 @@ export class MarkdownPreview extends Disposable { } private static getLocalResourceRoots( - resource: vscode.Uri, + base: vscode.Uri, contributions: MarkdownContributions ): ReadonlyArray { - const baseRoots = contributions.previewResourceRoots; + const baseRoots = Array.from(contributions.previewResourceRoots); - const folder = vscode.workspace.getWorkspaceFolder(resource); + const folder = vscode.workspace.getWorkspaceFolder(base); if (folder) { - return baseRoots.concat(folder.uri); + baseRoots.push(folder.uri); + } else if (!base.scheme || base.scheme === 'file') { + baseRoots.push(vscode.Uri.file(path.dirname(base.fsPath))); } - if (!resource.scheme || resource.scheme === 'file') { - return baseRoots.concat(vscode.Uri.file(path.dirname(resource.fsPath))); - } - - return baseRoots; + return baseRoots.map(root => normalizeResource(base, root)); } private onDidScrollPreview(line: number) { diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 85bbba5a2e..b13915a14f 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -3,17 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; import * as path from 'path'; -import { MarkdownEngine } from '../markdownEngine'; - +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - import { Logger } from '../logger'; -import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security'; -import { MarkdownPreviewConfigurationManager, MarkdownPreviewConfiguration } from './previewConfig'; +import { MarkdownEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; +import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security'; +import { WebviewResourceProvider } from '../util/resources'; +import { MarkdownPreviewConfiguration, MarkdownPreviewConfigurationManager } from './previewConfig'; + +const localize = nls.loadMessageBundle(); /** * Strings used inside the markdown preview. @@ -35,8 +35,8 @@ const previewStrings = { 'Content Disabled Security Warning') }; -function escapeAttribute(value: string): string { - return value.replace(/"/g, '"'); +function escapeAttribute(value: string | vscode.Uri): string { + return value.toString().replace(/"/g, '"'); } export class MarkdownContentProvider { @@ -50,6 +50,7 @@ export class MarkdownContentProvider { public async provideTextDocumentContent( markdownDocument: vscode.TextDocument, + resourceProvider: WebviewResourceProvider, previewConfigurations: MarkdownPreviewConfigurationManager, initialLine: number | undefined = undefined, state?: any @@ -63,18 +64,19 @@ export class MarkdownContentProvider { scrollPreviewWithEditor: config.scrollPreviewWithEditor, scrollEditorWithPreview: config.scrollEditorWithPreview, doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor, - disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings() + disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(), + webviewResourceRoot: resourceProvider.toWebviewResource(markdownDocument.uri).toString(), }; this.logger.log('provideTextDocumentContent', initialData); // Content Security Policy const nonce = new Date().getTime() + '' + new Date().getMilliseconds(); - const csp = this.getCspForResource(sourceUri, nonce); + const csp = this.getCsp(resourceProvider, sourceUri, nonce); const body = await this.engine.render(markdownDocument); return ` - + ${csp} @@ -82,14 +84,14 @@ export class MarkdownContentProvider { data-settings="${escapeAttribute(JSON.stringify(initialData))}" data-strings="${escapeAttribute(JSON.stringify(previewStrings))}" data-state="${escapeAttribute(JSON.stringify(state || {}))}"> - - ${this.getStyles(sourceUri, nonce, config, state)} - + + ${this.getStyles(resourceProvider, sourceUri, config, state)} + ${body}

- ${this.getScripts(nonce)} + ${this.getScripts(resourceProvider, nonce)} `; } @@ -107,13 +109,13 @@ export class MarkdownContentProvider { `; } - private extensionResourcePath(mediaFile: string): string { - return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))) - .with({ scheme: 'vscode-resource' }) - .toString(); + private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string { + const webviewResource = resourceProvider.toWebviewResource( + vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))); + return webviewResource.toString(); } - private fixHref(resource: vscode.Uri, href: string): string { + private fixHref(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, href: string): string { if (!href) { return href; } @@ -124,42 +126,36 @@ export class MarkdownContentProvider { // Assume it must be a local file if (path.isAbsolute(href)) { - return vscode.Uri.file(href) - .with({ scheme: 'vscode-resource' }) - .toString(); + return resourceProvider.toWebviewResource(vscode.Uri.file(href)).toString(); } // Use a workspace relative path if there is a workspace const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { - return vscode.Uri.file(path.join(root.uri.fsPath, href)) - .with({ scheme: 'vscode-resource' }) - .toString(); + return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); } // Otherwise look relative to the markdown file - return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)) - .with({ scheme: 'vscode-resource' }) - .toString(); + return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString(); } - private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { - if (Array.isArray(config.styles)) { - return config.styles.map(style => { - return ``; - }).join('\n'); + private computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { + if (!Array.isArray(config.styles)) { + return ''; } - return ''; + const out: string[] = []; + for (const style of config.styles) { + out.push(``); + } + return out.join('\n'); } - private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfiguration): string { - return ``; + private getSettingsOverrideStyles(config: MarkdownPreviewConfiguration): string { + return [ + config.fontFamily ? `--vscode-markdown-font-family: ${config.fontFamily};` : '', + isNaN(config.fontSize) ? '' : `--vscode-markdown-font-size: ${config.fontSize}px;`, + isNaN(config.lineHeight) ? '' : `--vscode-markdown-line-height: ${config.lineHeight};`, + ].join(' '); } private getImageStabilizerStyles(state?: any) { @@ -177,37 +173,47 @@ export class MarkdownContentProvider { return ret; } - private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string { - const baseStyles = this.contributionProvider.contributions.previewStyles - .map(resource => ``) - .join('\n'); + private getStyles(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration, state?: any): string { + const baseStyles: string[] = []; + for (const resource of this.contributionProvider.contributions.previewStyles) { + baseStyles.push(``); + } - return `${baseStyles} - ${this.getSettingsOverrideStyles(nonce, config)} - ${this.computeCustomStyleSheetIncludes(resource, config)} + return `${baseStyles.join('\n')} + ${this.computeCustomStyleSheetIncludes(resourceProvider, resource, config)} ${this.getImageStabilizerStyles(state)}`; } - private getScripts(nonce: string): string { - return this.contributionProvider.contributions.previewScripts - .map(resource => ``) - .join('\n'); + private getScripts(resourceProvider: WebviewResourceProvider, nonce: string): string { + const out: string[] = []; + for (const resource of this.contributionProvider.contributions.previewScripts) { + out.push(``); + } + return out.join('\n'); } - private getCspForResource(resource: vscode.Uri, nonce: string): string { + private getCsp( + provider: WebviewResourceProvider, + resource: vscode.Uri, + nonce: string + ): string { + const rule = provider.cspSource; switch (this.cspArbiter.getSecurityLevelForResource(resource)) { case MarkdownPreviewSecurityLevel.AllowInsecureContent: - return ``; + return ``; case MarkdownPreviewSecurityLevel.AllowInsecureLocalContent: - return ``; + return ``; case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent: return ''; case MarkdownPreviewSecurityLevel.Strict: default: - return ``; + return ``; } } } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 16f2d5350e..0a7a1249cd 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -234,7 +234,6 @@ export class MarkdownEngine { return normalizeLink(externalSchemeUri.toString(true)); } - // Assume it must be an relative or absolute file path // Use a fake scheme to avoid parse warnings let uri = vscode.Uri.parse(`vscode-resource:${link}`); @@ -271,7 +270,7 @@ export class MarkdownEngine { const validateLink = md.validateLink; md.validateLink = (link: string) => { // support file:// links - return validateLink(link) || link.indexOf('file:') === 0; + return validateLink(link) || link.startsWith('file:') || /^data:image\/.*?;/.test(link); }; } diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index bc78d859d4..8189e4ee81 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -9,8 +9,7 @@ import { Disposable } from './util/dispose'; import * as arrays from './util/arrays'; const resolveExtensionResource = (extension: vscode.Extension, resourcePath: string): vscode.Uri => { - return vscode.Uri.file(path.join(extension.extensionPath, resourcePath)) - .with({ scheme: 'vscode-resource' }); + return vscode.Uri.file(path.join(extension.extensionPath, resourcePath)); }; const resolveExtensionResources = (extension: vscode.Extension, resourcePaths: unknown): vscode.Uri[] => { diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 5c0e985b63..d1dec19917 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -72,6 +72,14 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link.range, new vscode.Range(0, 6, 0, 25)); }); + // #35245 + test('Should handle links with escaped characters in name', () => { + const links = 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', () => { { diff --git a/extensions/markdown-language-features/src/util/resources.ts b/extensions/markdown-language-features/src/util/resources.ts new file mode 100644 index 0000000000..77d2779261 --- /dev/null +++ b/extensions/markdown-language-features/src/util/resources.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * 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 interface WebviewResourceProvider { + toWebviewResource(resource: vscode.Uri): vscode.Uri; + + readonly cspSource: string; +} + +export function normalizeResource( + base: vscode.Uri, + resource: vscode.Uri +): vscode.Uri { + // If we have a windows path and are loading a workspace with an authority, + // make sure we use a unc path with an explicit localhost authority. + // + // Otherwise, the `` rule will insert the authority into the resolved resource + // URI incorrectly. + if (base.authority && !resource.authority) { + const driveMatch = resource.path.match(/^\/(\w):\//); + if (driveMatch) { + return vscode.Uri.file(`\\\\localhost\\${driveMatch[1]}$\\${resource.fsPath.replace(/^\w:\\/, '')}`).with({ + fragment: resource.fragment, + query: resource.query + }); + } + } + return resource; +} \ No newline at end of file diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index e85ae950b3..a1b876e7dc 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -29,10 +29,10 @@ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" integrity sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== abbrev@1: version "1.1.1" @@ -2933,10 +2933,10 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -highlight.js@9.13.1: - version "9.13.1" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" - integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== +highlight.js@9.15.8: + version "9.15.8" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.8.tgz#f344fda123f36f1a65490e932cf90569e4999971" + integrity sha512-RrapkKQWwE+wKdF73VsOa2RQdIoO3mxwJ4P8mhbI6KYJUraUHRKM5w5zQQKXNk0xNL4UVRdulV9SBJcmzJNzVA== hmac-drbg@^1.0.0: version "1.0.1" diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index efc0ff0cf7..2828669b04 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -5,6 +5,7 @@ "description": "%description%", "icon": "resources/icons/merge-conflict.png", "version": "1.0.0", + "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { "vscode": "^1.5.0" @@ -114,6 +115,21 @@ "type": "boolean", "description": "%config.autoNavigateNextConflictEnabled%", "default": false + }, + "merge-conflict.diffViewPosition": { + "type": "string", + "enum": [ + "Current", + "Beside", + "Below" + ], + "description": "%config.diffViewPosition%", + "enumDescriptions": [ + "%config.diffViewPosition.current%", + "%config.diffViewPosition.beside%", + "%config.diffViewPosition.below%" + ], + "default": "Current" } } } @@ -122,6 +138,6 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "8.0.33" + "@types/node": "^10.14.8" } } diff --git a/extensions/merge-conflict/package.nls.json b/extensions/merge-conflict/package.nls.json index 94599bf281..3310dac7e2 100644 --- a/extensions/merge-conflict/package.nls.json +++ b/extensions/merge-conflict/package.nls.json @@ -15,5 +15,9 @@ "config.title": "Merge Conflict", "config.autoNavigateNextConflictEnabled": "Whether to automatically navigate to the next merge conflict after resolving a merge conflict.", "config.codeLensEnabled": "Create a Code Lens for merge conflict blocks within editor.", - "config.decoratorsEnabled": "Create decorators for merge conflict blocks within editor." + "config.decoratorsEnabled": "Create decorators for merge conflict blocks within editor.", + "config.diffViewPosition": "Controls where the diff view should be opened when comparing changes in merge conflicts.", + "config.diffViewPosition.current": "Open the diff view in the current editor group.", + "config.diffViewPosition.beside": "Open the diff view next to the current editor group.", + "config.diffViewPosition.below": "Open the diff view below the current editor group." } \ No newline at end of file diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index 5807223e4e..5f29c9b5c8 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -34,8 +34,8 @@ export default class CommandHandler implements vscode.Disposable { this.registerTextEditorCommand('merge-conflict.accept.incoming', this.acceptIncoming), this.registerTextEditorCommand('merge-conflict.accept.selection', this.acceptSelection), this.registerTextEditorCommand('merge-conflict.accept.both', this.acceptBoth), - this.registerTextEditorCommand('merge-conflict.accept.all-current', this.acceptAllCurrent), - this.registerTextEditorCommand('merge-conflict.accept.all-incoming', this.acceptAllIncoming), + this.registerTextEditorCommand('merge-conflict.accept.all-current', this.acceptAllCurrent, this.acceptAllCurrentResources), + this.registerTextEditorCommand('merge-conflict.accept.all-incoming', this.acceptAllIncoming, this.acceptAllIncomingResources), this.registerTextEditorCommand('merge-conflict.accept.all-both', this.acceptAllBoth), this.registerTextEditorCommand('merge-conflict.next', this.navigateNext), this.registerTextEditorCommand('merge-conflict.previous', this.navigatePrevious), @@ -43,8 +43,11 @@ export default class CommandHandler implements vscode.Disposable { ); } - private registerTextEditorCommand(command: string, cb: (editor: vscode.TextEditor, ...args: any[]) => Promise) { + private registerTextEditorCommand(command: string, cb: (editor: vscode.TextEditor, ...args: any[]) => Promise, resourceCB?: (uris: vscode.Uri[]) => Promise) { return vscode.commands.registerCommand(command, (...args) => { + if (resourceCB && args.length && args.every(arg => arg && arg.resourceUri)) { + return resourceCB.call(this, args.map(arg => arg.resourceUri)); + } const editor = vscode.window.activeTextEditor; return editor && cb.call(this, editor, ...args); }); @@ -70,6 +73,14 @@ export default class CommandHandler implements vscode.Disposable { return this.acceptAll(interfaces.CommitType.Incoming, editor); } + acceptAllCurrentResources(resources: vscode.Uri[]): Promise { + return this.acceptAllResources(interfaces.CommitType.Current, resources); + } + + acceptAllIncomingResources(resources: vscode.Uri[]): Promise { + return this.acceptAllResources(interfaces.CommitType.Incoming, resources); + } + acceptAllBoth(editor: vscode.TextEditor): Promise { return this.acceptAll(interfaces.CommitType.Both, editor); } @@ -88,18 +99,54 @@ export default class CommandHandler implements vscode.Disposable { } } + const conflicts = await this.tracker.getConflicts(editor.document); + + // Still failed to find conflict, warn the user and exit + if (!conflicts) { + vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict')); + return; + } + 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 leftUri = editor.document.uri.with({ scheme: ContentProvider.scheme, - query: JSON.stringify({ scheme, range }) + query: JSON.stringify({ scheme, range: range, ranges: leftRanges }) }); + range = conflict.incoming.content; - const rightUri = leftUri.with({ query: JSON.stringify({ scheme, range }) }); + const rightUri = leftUri.with({ query: JSON.stringify({ scheme, ranges: rightRanges }) }); + + let mergeConflictLineOffsets = 0; + for (let nextconflict of conflicts) { + if (nextconflict.range.isEqual(conflict.range)) { + break; + } else { + mergeConflictLineOffsets += (nextconflict.range.end.line - nextconflict.range.start.line) - (nextconflict.incoming.content.end.line - nextconflict.incoming.content.start.line); + } + } + const selection = new vscode.Range( + conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character, + conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character + ); const title = localize('compareChangesTitle', '{0}: Current Changes ⟷ Incoming Changes', fileName); - vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, title); + const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict'); + const openToTheSide = mergeConflictConfig.get('diffViewPosition'); + const opts: vscode.TextDocumentShowOptions = { + viewColumn: openToTheSide === 'Beside' ? vscode.ViewColumn.Beside : vscode.ViewColumn.Active, + selection + }; + + if (openToTheSide === 'Below') { + await vscode.commands.executeCommand('workbench.action.newGroupBelow'); + } + + await vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, title, opts); } navigateNext(editor: vscode.TextEditor): Promise { @@ -223,10 +270,31 @@ export default class CommandHandler implements vscode.Disposable { // Apply all changes as one edit await editor.edit((edit) => conflicts.forEach(conflict => { - conflict.applyEdit(type, editor, edit); + conflict.applyEdit(type, editor.document, edit); })); } + private async acceptAllResources(type: interfaces.CommitType, resources: vscode.Uri[]): Promise { + const documents = await Promise.all(resources.map(resource => vscode.workspace.openTextDocument(resource))); + const edit = new vscode.WorkspaceEdit(); + for (const document of documents) { + const conflicts = await this.tracker.getConflicts(document); + + if (!conflicts || conflicts.length === 0) { + continue; + } + + // For get the current state of the document, as we know we are doing to do a large edit + this.tracker.forget(document); + + // Apply all changes as one edit + conflicts.forEach(conflict => { + conflict.applyEdit(type, document, { replace: (range, newText) => edit.replace(document.uri, range, newText) }); + }); + } + vscode.workspace.applyEdit(edit); + } + private async findConflictContainingSelection(editor: vscode.TextEditor, conflicts?: interfaces.IDocumentMergeConflict[]): Promise { if (!conflicts) { diff --git a/extensions/merge-conflict/src/contentProvider.ts b/extensions/merge-conflict/src/contentProvider.ts index 542a9ccc02..49bbf0aa94 100644 --- a/extensions/merge-conflict/src/contentProvider.ts +++ b/extensions/merge-conflict/src/contentProvider.ts @@ -23,11 +23,27 @@ export default class MergeConflictContentProvider implements vscode.TextDocument async provideTextDocumentContent(uri: vscode.Uri): Promise { try { - const { scheme, range } = JSON.parse(uri.query) as { scheme: string; range: { line: number, character: number }[] }; - const [start, end] = range; + const { scheme, ranges } = JSON.parse(uri.query) as { scheme: string, ranges: [{ line: number, character: number }[], { line: number, character: number }[]][] }; + // complete diff const document = await vscode.workspace.openTextDocument(uri.with({ scheme, query: '' })); - const text = document.getText(new vscode.Range(start.line, start.character, end.line, end.character)); + + let text = ''; + let lastPosition = new vscode.Position(0, 0); + + ranges.forEach(rangeObj => { + let [conflictRange, fullRange] = rangeObj; + const [start, end] = conflictRange; + const [fullStart, fullEnd] = fullRange; + + text += document.getText(new vscode.Range(lastPosition.line, lastPosition.character, fullStart.line, fullStart.character)); + text += document.getText(new vscode.Range(start.line, start.character, end.line, end.character)); + lastPosition = new vscode.Position(fullEnd.line, fullEnd.character); + }); + + let documentEnd = document.lineAt(document.lineCount - 1).range.end; + text += document.getText(new vscode.Range(lastPosition.line, lastPosition.character, documentEnd.line, documentEnd.character)); + return text; } catch (ex) { diff --git a/extensions/merge-conflict/src/documentMergeConflict.ts b/extensions/merge-conflict/src/documentMergeConflict.ts index 1f28b1d7e2..006260d86b 100644 --- a/extensions/merge-conflict/src/documentMergeConflict.ts +++ b/extensions/merge-conflict/src/documentMergeConflict.ts @@ -25,14 +25,14 @@ export class DocumentMergeConflict implements interfaces.IDocumentMergeConflict if (edit) { - this.applyEdit(type, editor, edit); + this.applyEdit(type, editor.document, edit); return Promise.resolve(true); } - return editor.edit((edit) => this.applyEdit(type, editor, edit)); + return editor.edit((edit) => this.applyEdit(type, editor.document, edit)); } - public applyEdit(type: interfaces.CommitType, editor: vscode.TextEditor, edit: vscode.TextEditorEdit): void { + public applyEdit(type: interfaces.CommitType, document: vscode.TextDocument, edit: { replace(range: vscode.Range, newText: string): void; }): void { // Each conflict is a set of ranges as follows, note placements or newlines // which may not in in spans @@ -45,24 +45,24 @@ export class DocumentMergeConflict implements interfaces.IDocumentMergeConflict // ] if (type === interfaces.CommitType.Current) { // Replace [ Conflict Range ] with [ Current Content ] - let content = editor.document.getText(this.current.content); + let content = document.getText(this.current.content); this.replaceRangeWithContent(content, edit); } else if (type === interfaces.CommitType.Incoming) { - let content = editor.document.getText(this.incoming.content); + let content = document.getText(this.incoming.content); this.replaceRangeWithContent(content, edit); } else if (type === interfaces.CommitType.Both) { // Replace [ Conflict Range ] with [ Current Content ] + \n + [ Incoming Content ] - const currentContent = editor.document.getText(this.current.content); - const incomingContent = editor.document.getText(this.incoming.content); + const currentContent = document.getText(this.current.content); + const incomingContent = document.getText(this.incoming.content); edit.replace(this.range, currentContent.concat(incomingContent)); } } - private replaceRangeWithContent(content: string, edit: vscode.TextEditorEdit) { + private replaceRangeWithContent(content: string, edit: { replace(range: vscode.Range, newText: string): void; }) { if (this.isNewlineOnly(content)) { edit.replace(this.range, ''); return; diff --git a/extensions/merge-conflict/src/interfaces.ts b/extensions/merge-conflict/src/interfaces.ts index db13a8cd32..b8f48cdcd8 100644 --- a/extensions/merge-conflict/src/interfaces.ts +++ b/extensions/merge-conflict/src/interfaces.ts @@ -25,7 +25,7 @@ export interface IExtensionConfiguration { export interface IDocumentMergeConflict extends IDocumentMergeConflictDescriptor { commitEdit(type: CommitType, editor: vscode.TextEditor, edit?: vscode.TextEditorEdit): Thenable; - applyEdit(type: CommitType, editor: vscode.TextEditor, edit: vscode.TextEditorEdit): void; + applyEdit(type: CommitType, document: vscode.TextDocument, edit: { replace(range: vscode.Range, newText: string): void; }): void; } export interface IDocumentMergeConflictDescriptor { diff --git a/extensions/merge-conflict/yarn.lock b/extensions/merge-conflict/yarn.lock index 6767cb8d8c..e6247e2925 100644 --- a/extensions/merge-conflict/yarn.lock +++ b/extensions/merge-conflict/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@8.0.33": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" - integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/mssql/.vscodeignore b/extensions/mssql/.vscodeignore index acc1856e97..f3d973bca1 100644 --- a/extensions/mssql/.vscodeignore +++ b/extensions/mssql/.vscodeignore @@ -3,3 +3,4 @@ out/** tsconfig.json extension.webpack.config.js yarn.lock +.vscode diff --git a/extensions/objective-c/build/update-grammars.js b/extensions/objective-c/build/update-grammars.js new file mode 100644 index 0000000000..4bdac2a6cf --- /dev/null +++ b/extensions/objective-c/build/update-grammars.js @@ -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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +var updateGrammar = require('../../../build/npm/update-grammar'); + +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/objc.tmLanguage.json', './syntaxes/objective-c.tmLanguage.json', undefined, 'master', 'source/languages/cpp'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/objcpp.tmLanguage.json', './syntaxes/objective-c++.tmLanguage.json', undefined, 'master', 'source/languages/cpp'); + diff --git a/extensions/objective-c/test/colorize-fixtures/test.mm b/extensions/objective-c/test/colorize-fixtures/test.mm new file mode 100644 index 0000000000..d5d31433ae --- /dev/null +++ b/extensions/objective-c/test/colorize-fixtures/test.mm @@ -0,0 +1,52 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#import "UseQuotes.h" +#import + +/* + Multi + Line + Comments +*/ +@implementation Test + +- (void) applicationWillFinishLaunching:(NSNotification *)notification +{ +} + +- (IBAction)onSelectInput:(id)sender +{ + NSString* defaultDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0]; + + NSOpenPanel* panel = [NSOpenPanel openPanel]; + [panel setAllowedFileTypes:[[NSArray alloc] initWithObjects:@"ipa", @"xcarchive", @"app", nil]]; + + [panel beginWithCompletionHandler:^(NSInteger result) + { + if (result == NSFileHandlingPanelOKButton) + [self.inputTextField setStringValue:[panel.URL path]]; + }]; + return YES; + + int hex = 0xFEF1F0F; + float ing = 3.14; + ing = 3.14e0; + ing = 31.4e-2; +} + +-(id) initWithParams:(id) aHandler withDeviceStateManager:(id) deviceStateManager +{ + // add a tap gesture recognizer + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; + NSMutableArray *gestureRecognizers = [NSMutableArray array]; + [gestureRecognizers addObject:tapGesture]; + [gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers]; + scnView.gestureRecognizers = gestureRecognizers; + + return tapGesture; + return nil; +} + +@end diff --git a/extensions/objective-c/test/colorize-results/test_mm.json b/extensions/objective-c/test/colorize-results/test_mm.json new file mode 100644 index 0000000000..2af2751bce --- /dev/null +++ b/extensions/objective-c/test/colorize-results/test_mm.json @@ -0,0 +1,3093 @@ +[ + { + "c": "//", + "t": "source.objcpp comment.line.double-slash.objcpp punctuation.definition.comment.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "//", + "t": "source.objcpp comment.line.double-slash.objcpp punctuation.definition.comment.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Copyright (c) Microsoft Corporation. All rights reserved.", + "t": "source.objcpp comment.line.double-slash.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "//", + "t": "source.objcpp comment.line.double-slash.objcpp punctuation.definition.comment.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.objcpp meta.preprocessor.include.objcpp keyword.control.directive.import.objcpp punctuation.definition.directive.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "import", + "t": "source.objcpp meta.preprocessor.include.objcpp keyword.control.directive.import.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.preprocessor.include.objcpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, + { + "c": "\"", + "t": "source.objcpp meta.preprocessor.include.objcpp string.quoted.double.include.objcpp punctuation.definition.string.begin.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "UseQuotes.h", + "t": "source.objcpp meta.preprocessor.include.objcpp string.quoted.double.include.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.objcpp meta.preprocessor.include.objcpp string.quoted.double.include.objcpp punctuation.definition.string.end.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "#", + "t": "source.objcpp meta.preprocessor.include.objcpp keyword.control.directive.import.objcpp punctuation.definition.directive.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "import", + "t": "source.objcpp meta.preprocessor.include.objcpp keyword.control.directive.import.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.preprocessor.include.objcpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, + { + "c": "<", + "t": "source.objcpp meta.preprocessor.include.objcpp string.quoted.other.lt-gt.include.objcpp punctuation.definition.string.begin.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "Use/GTLT.h", + "t": "source.objcpp meta.preprocessor.include.objcpp string.quoted.other.lt-gt.include.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "source.objcpp meta.preprocessor.include.objcpp string.quoted.other.lt-gt.include.objcpp punctuation.definition.string.end.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "/*", + "t": "source.objcpp comment.block.objcpp punctuation.definition.comment.begin.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\tMulti", + "t": "source.objcpp comment.block.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\tLine", + "t": "source.objcpp comment.block.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\tComments", + "t": "source.objcpp comment.block.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "*/", + "t": "source.objcpp comment.block.objcpp punctuation.definition.comment.end.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.objcpp meta.implementation.objcpp storage.type.objcpp punctuation.definition.storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "implementation", + "t": "source.objcpp meta.implementation.objcpp storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Test", + "t": "source.objcpp meta.implementation.objcpp entity.name.type.objcpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "- ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": "void", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp storage.type.built-in.primitive.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": "applicationWillFinishLaunching", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp entity.name.function.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": ":", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp entity.name.function.name-of-parameter.objcpp punctuation.separator.arguments.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSNotification", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp keyword.operator.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "notification", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp variable.parameter.function.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "{", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.section.block.begin.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.section.block.end.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "- ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": "IBAction", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": "onSelectInput", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp entity.name.function.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": ":", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp entity.name.function.name-of-parameter.objcpp punctuation.separator.arguments.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "id", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp storage.type.id.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "sender", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp variable.parameter.function.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "{", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.section.block.begin.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSString", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": "*", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " defaultDir ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.whitespace.support.function.leading.cocoa.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSSearchPathForDirectoriesInDomains", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp support.function.cocoa.objcpp", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp punctuation.section.parens.begin.bracket.round.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSDocumentDirectory", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp support.constant.cocoa.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp punctuation.separator.delimiter.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSUserDomainMask", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp support.constant.cocoa.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ",", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp punctuation.separator.delimiter.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "true", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp constant.language.objcpp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.parens.block.objcpp punctuation.section.parens.end.bracket.round.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSOpenPanel", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": "*", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " panel ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSOpenPanel", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": " openPanel", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "panel setAllowedFileTypes:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSArray", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": " alloc", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " initWithObjects:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@\"", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp punctuation.definition.string.begin.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ipa", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp punctuation.definition.string.end.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.separator.delimiter.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@\"", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp punctuation.definition.string.begin.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "xcarchive", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp punctuation.definition.string.end.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.separator.delimiter.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@\"", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp punctuation.definition.string.begin.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "app", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp string.quoted.double.objcpp punctuation.definition.string.end.objcpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ",", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.separator.delimiter.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "nil", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp constant.language.objcpp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "panel beginWithCompletionHandler:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "^", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp keyword.operator.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.section.parens.begin.bracket.round.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSInteger", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp support.type.cocoa.leopard.objcpp", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0" + } + }, + { + "c": " result", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.section.parens.end.bracket.round.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "{", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.section.block.begin.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "if", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp keyword.control.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.parens.block.objcpp punctuation.section.parens.begin.bracket.round.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "result ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.parens.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "==", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.parens.block.objcpp keyword.operator.comparison.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.parens.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSFileHandlingPanelOKButton", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.parens.block.objcpp support.constant.cocoa.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.parens.block.objcpp punctuation.section.parens.end.bracket.round.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "self", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp variable.other.object.access.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.separator.dot-access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "inputTextField", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp variable.other.member.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " setStringValue:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "panel", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp variable.other.object.access.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.separator.dot-access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "URL", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp variable.other.member.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " path", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.section.block.end.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.control.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "YES", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.language.objcpp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "int", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp storage.type.built-in.primitive.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " hex ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "0x", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.other.unit.hexadecimal.objcpp", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "FEF1F0F", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.hexadecimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "float", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp storage.type.built-in.primitive.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ing ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "3", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.point.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "14", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t ing ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "3", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.point.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "14", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "e", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.other.unit.exponent.decimal.objcpp", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "0", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.exponent.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t ing ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "31", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.point.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "4", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "e", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.other.unit.exponent.decimal.objcpp", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "-", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.minus.exponent.decimal.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "2", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.exponent.decimal.objcpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.section.block.end.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": "id", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp storage.type.id.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp", + "r": { + "dark_plus": "meta.return-type: #4EC9B0", + "light_plus": "meta.return-type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.return-type: #4EC9B0" + } + }, + { + "c": "initWithParams", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.return-type.objcpp entity.name.function.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": ":", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp entity.name.function.name-of-parameter.objcpp punctuation.separator.arguments.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "id", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "<", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp meta.protocol-list.objcpp punctuation.section.scope.begin.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "anObject", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp meta.protocol-list.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp meta.protocol-list.objcpp punctuation.section.scope.end.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "aHandler", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp variable.parameter.function.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "withDeviceStateManager", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp entity.name.function.name-of-parameter.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": ":", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp entity.name.function.name-of-parameter.objcpp punctuation.separator.arguments.objcpp", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.begin.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "id", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "<", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp meta.protocol-list.objcpp punctuation.section.scope.begin.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "anotherObject", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp meta.protocol-list.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ">", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp meta.id-with-protocol.objcpp meta.protocol-list.objcpp punctuation.section.scope.end.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp punctuation.definition.type.end.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "deviceStateManager", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.function.objcpp meta.argument-type.objcpp variable.parameter.function.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "{", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.section.block.begin.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.whitespace.comment.leading.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "//", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp comment.line.double-slash.objcpp punctuation.definition.comment.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " add a tap gesture recognizer", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp comment.line.double-slash.objcpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " UITapGestureRecognizer ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "tapGesture ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "UITapGestureRecognizer alloc", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " initWithTarget:self action:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.selector.objcpp storage.type.objcpp punctuation.definition.storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "selector", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.selector.objcpp storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "(", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.selector.objcpp punctuation.definition.storage.type.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "handleTap:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.selector.objcpp meta.selector.method-name.objcpp support.function.any-method.name-of-parameter.objcpp", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": ")", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp meta.selector.objcpp punctuation.definition.storage.type.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSMutableArray", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "*", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "gestureRecognizers ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "NSMutableArray", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp support.class.cocoa.objcpp", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0" + } + }, + { + "c": " array", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "gestureRecognizers addObject:tapGesture", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.begin.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "gestureRecognizers addObjectsFromArray:", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "scnView", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp variable.other.object.access.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.separator.dot-access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "gestureRecognizers", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp variable.other.member.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "]", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp punctuation.definition.end.bracket.square.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "scnView", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp variable.other.object.access.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.separator.dot-access.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "gestureRecognizers", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp variable.other.member.objcpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.assignment.objcpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " gestureRecognizers", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.control.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " tapGesture", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "return", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.control.objcpp", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " ", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "nil", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.language.objcpp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": ";", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.terminator.statement.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp punctuation.section.block.end.bracket.curly.objcpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.objcpp meta.implementation.objcpp storage.type.objcpp punctuation.definition.storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "end", + "t": "source.objcpp meta.implementation.objcpp storage.type.objcpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + } +] \ No newline at end of file diff --git a/extensions/package.json b/extensions/package.json index 2615d1efa9..52ece5e205 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.5.0-dev.20190522" + "typescript": "3.5.2" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/powershell/package.json b/extensions/powershell/package.json index 399f6fc560..232acb1a7d 100644 --- a/extensions/powershell/package.json +++ b/extensions/powershell/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "languages": [{ diff --git a/extensions/python/.vscodeignore b/extensions/python/.vscodeignore index 89fb2149dc..4d5a14fc91 100644 --- a/extensions/python/.vscodeignore +++ b/extensions/python/.vscodeignore @@ -1,4 +1,6 @@ test/** src/** tsconfig.json +extension.webpack.config.js cgmanifest.json +.vscode \ No newline at end of file diff --git a/extensions/python/extension.webpack.config.js b/extensions/python/extension.webpack.config.js new file mode 100644 index 0000000000..183f7f9a22 --- /dev/null +++ b/extensions/python/extension.webpack.config.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * 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'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + pythonMain: './src/pythonMain.ts' + } +}); diff --git a/extensions/python/package.json b/extensions/python/package.json index 53ca25bd88..645cba2bae 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -4,14 +4,16 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "activationEvents": ["onLanguage:python"], "main": "./out/pythonMain", "contributes": { "languages": [{ "id": "python", - "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk", ".pyi", ".ipy"], + "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".pyi", ".ipy"], "aliases": [ "Python", "py" ], + "filenames": [ "Snakefile" ], "firstLine": "^#!\\s*/.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" }], diff --git a/extensions/r/package.json b/extensions/r/package.json index c9b682918b..63ff6a6dcb 100644 --- a/extensions/r/package.json +++ b/extensions/r/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js Ikuyadeu/vscode-R syntax/r.json ./syntaxes/r.tmLanguage.json" diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index b5d1e7998c..80e1517b3d 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -9,11 +9,17 @@ 'use strict'; const path = require('path'); +const fs = require('fs'); const merge = require('merge-options'); const CopyWebpackPlugin = require('copy-webpack-plugin'); - +const { NLSBundlePlugin } = require('vscode-nls-dev/lib/webpack-bundler'); module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { + // Need to find the top-most `package.json` file + const folderName = path.relative(__dirname, extConfig.context).split(/[\\\/]/)[0]; + const pkgPath = path.join(__dirname, folderName, 'package.json'); + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + const id = `${pkg.publisher}.${pkg.name}`; /** @type WebpackConfig */ let defaultConfig = { @@ -64,9 +70,11 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { // yes, really source maps devtool: 'source-map', plugins: [ + // @ts-ignore new CopyWebpackPlugin([ - { from: './out/**/*', to: '.', ignore: ['*.js', '*.js.map'], flatten: true } - ]) + { from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] } + ]), + new NLSBundlePlugin(id) ], }; diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index 4cdccf12d9..7abcea36e5 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "Microsoft/vscode-mssql", "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "cd754662e5607c62ecdc51d2a2dc844546a0bbb6" + "commitHash": "a79741f76fd33bd137a8c28172c9750b978309b6" } }, "license": "MIT", - "version": "1.4.0" + "version": "1.6.0" } ], "version": 1 diff --git a/extensions/sql/package.json b/extensions/sql/package.json index f0256026d4..8c283e9724 100644 --- a/extensions/sql/package.json +++ b/extensions/sql/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js Microsoft/vscode-mssql syntaxes/SQL.plist ./syntaxes/sql.tmLanguage.json" diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index edb1de2f17..b10b1a6605 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/vscode-mssql/commit/cd754662e5607c62ecdc51d2a2dc844546a0bbb6", + "version": "https://github.com/Microsoft/vscode-mssql/commit/a79741f76fd33bd137a8c28172c9750b978309b6", "name": "SQL", "scopeName": "source.sql", "patterns": [ diff --git a/extensions/theme-abyss/package.json b/extensions/theme-abyss/package.json index 2d6e6962c2..bdeb6a2a64 100644 --- a/extensions/theme-abyss/package.json +++ b/extensions/theme-abyss/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 1f7d48782d..43a3ae648d 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -86,7 +86,9 @@ "name": "Class name", "scope": [ "entity.name.class", - "entity.name.type" + "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution" ], "settings": { "fontStyle": "underline", diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json index fbfc50afa3..39c00d37b7 100644 --- a/extensions/theme-defaults/package.json +++ b/extensions/theme-defaults/package.json @@ -5,6 +5,7 @@ "categories": [ "Themes" ], "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index e73cc3a09a..1898153ba8 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -21,6 +21,8 @@ "support.class", "support.type", "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution", "entity.name.class", "storage.type.numeric.go", "storage.type.byte.go", @@ -67,10 +69,10 @@ } }, { - "name": "Control flow keywords", + "name": "Control flow / Special keywords", "scope": [ "keyword.control", - "keyword.operator.new.cpp", + "source.cpp keyword.operator.new", "keyword.operator.delete", "keyword.other.using", "keyword.other.operator" diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index 870681495d..8119256d5f 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -26,6 +26,8 @@ "support.class", "support.type", "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution", "entity.name.class", "storage.type.cs", "storage.type.generic.cs", @@ -65,11 +67,11 @@ } }, { - "name": "Control flow keywords", + "name": "Control flow / Special keywords", "scope": [ "keyword.control", - "keyword.operator.new.cpp", - "keyword.operator.delete.cpp", + "source.cpp keyword.operator.new", + "source.cpp keyword.operator.delete", "keyword.other.using", "keyword.other.operator" ], diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index ce23ed901d..7138f045d5 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -21,6 +21,8 @@ "support.class", "support.type", "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution", "entity.name.class", "storage.type.numeric.go", "storage.type.byte.go", @@ -67,11 +69,11 @@ } }, { - "name": "Control flow keywords", + "name": "Control flow / Special keywords", "scope": [ "keyword.control", - "keyword.operator.new.cpp", - "keyword.operator.delete.cpp", + "source.cpp keyword.operator.new", + "source.cpp keyword.operator.delete", "keyword.other.using", "keyword.other.operator" ], diff --git a/extensions/theme-kimbie-dark/package.json b/extensions/theme-kimbie-dark/package.json index 06682901e0..1031c34d2e 100644 --- a/extensions/theme-kimbie-dark/package.json +++ b/extensions/theme-kimbie-dark/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index fac06fd00d..4b0eb5bdfc 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -147,7 +147,9 @@ "scope": [ "support.class", "entity.name.class", - "entity.name.type" + "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution" ], "settings": { "foreground": "#f06431" diff --git a/extensions/theme-monokai-dimmed/package.json b/extensions/theme-monokai-dimmed/package.json index f64721a898..66c4711d9a 100644 --- a/extensions/theme-monokai-dimmed/package.json +++ b/extensions/theme-monokai-dimmed/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index ed325071b6..5f5e5cff91 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -143,7 +143,7 @@ }, { "name": "Class name", - "scope": "entity.name.class, entity.name.type", + "scope": "entity.name.class, entity.name.type, entity.name.namespace, entity.name.scope-resolution", "settings": { "fontStyle": "", "foreground": "#9B0000", @@ -255,7 +255,7 @@ } }, { - "name": "Keyword Control", + "name": "Keyword Control / Special", "scope": [ "keyword.control", "keyword.operator.new.cpp", diff --git a/extensions/theme-monokai/package.json b/extensions/theme-monokai/package.json index 550f22933d..13b2db10d0 100644 --- a/extensions/theme-monokai/package.json +++ b/extensions/theme-monokai/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 14d616f615..c16fa3c557 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -205,7 +205,7 @@ }, { "name": "Class name", - "scope": "entity.name.type, entity.name.class", + "scope": "entity.name.type, entity.name.class, entity.name.namespace, entity.name.scope-resolution", "settings": { "fontStyle": "underline", "foreground": "#A6E22E" diff --git a/extensions/theme-quietlight/package.json b/extensions/theme-quietlight/package.json index 0620f73088..0263925eee 100644 --- a/extensions/theme-quietlight/package.json +++ b/extensions/theme-quietlight/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 67f7caa4da..38681612b4 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -125,6 +125,8 @@ "name": "Classes", "scope": [ "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution", "entity.other.inherited-class", "support.class" ], diff --git a/extensions/theme-red/package.json b/extensions/theme-red/package.json index 4b4f294fc5..ba751a33e4 100644 --- a/extensions/theme-red/package.json +++ b/extensions/theme-red/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 6b8fa8f54b..193c22fe61 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -248,11 +248,11 @@ }, "_db_light": { "fontCharacter": "\\E01D", - "fontColor": "#bfc2c1" + "fontColor": "#dd4b78" }, "_db": { "fontCharacter": "\\E01D", - "fontColor": "#d4d7d6" + "fontColor": "#f55385" }, "_default_light": { "fontCharacter": "\\E01E", @@ -968,11 +968,11 @@ }, "_shell_light": { "fontCharacter": "\\E075", - "fontColor": "#bfc2c1" + "fontColor": "#455155" }, "_shell": { "fontCharacter": "\\E075", - "fontColor": "#d4d7d6" + "fontColor": "#4d5a5e" }, "_slim_light": { "fontCharacter": "\\E076", diff --git a/extensions/theme-seti/package.json b/extensions/theme-seti/package.json index 554119b52b..a9721611a6 100644 --- a/extensions/theme-seti/package.json +++ b/extensions/theme-seti/package.json @@ -5,6 +5,7 @@ "displayName": "%displayName%", "description": "%description%", "publisher": "vscode", + "license": "MIT", "icon": "icons/seti-circular-128x128.png", "scripts": { "update": "node ./build/update-icon-theme.js" diff --git a/extensions/theme-solarized-dark/package.json b/extensions/theme-solarized-dark/package.json index bb43708ca1..427b50fe48 100644 --- a/extensions/theme-solarized-dark/package.json +++ b/extensions/theme-solarized-dark/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 5605aba721..f2ee483bea 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -72,7 +72,9 @@ "name": "Class name", "scope": [ "entity.name.class", - "entity.name.type" + "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution" ], "settings": { "fontStyle": "", diff --git a/extensions/theme-solarized-light/package.json b/extensions/theme-solarized-light/package.json index 1925afaa39..3afb2b7ed9 100644 --- a/extensions/theme-solarized-light/package.json +++ b/extensions/theme-solarized-light/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index b94aeb71de..8cc7e74a77 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -72,7 +72,9 @@ "name": "Class name", "scope": [ "entity.name.class", - "entity.name.type" + "entity.name.type", + "entity.name.namespace", + "entity.name.scope-resolution" ], "settings": { "foreground": "#268BD2" @@ -219,16 +221,14 @@ "meta.diff.header" ], "settings": { - "background": "#b58900", "fontStyle": "italic", - "foreground": "#E0EDDD" + "foreground": "#268bd2" } }, { "name": "diff: deleted", "scope": "markup.deleted", "settings": { - "background": "#eee8d5", "fontStyle": "", "foreground": "#dc322f" } @@ -237,7 +237,6 @@ "name": "diff: changed", "scope": "markup.changed", "settings": { - "background": "#eee8d5", "fontStyle": "", "foreground": "#cb4b16" } @@ -246,7 +245,6 @@ "name": "diff: inserted", "scope": "markup.inserted", "settings": { - "background": "#eee8d5", "foreground": "#219186" } }, @@ -417,7 +415,7 @@ "tab.activeBackground": "#FDF6E3", "tab.inactiveForeground": "#586E75", "tab.inactiveBackground": "#D3CBB7", - "tab.modifiedBorder": "#cb4b16", + "tab.activeModifiedBorder": "#cb4b16", // "tab.activeBackground": "", // "tab.activeForeground": "", // "tab.inactiveForeground": "", diff --git a/extensions/theme-tomorrow-night-blue/package.json b/extensions/theme-tomorrow-night-blue/package.json index bed66ce548..1266ac58ca 100644 --- a/extensions/theme-tomorrow-night-blue/package.json +++ b/extensions/theme-tomorrow-night-blue/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "themes": [ diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json index 2ca39bf565..6c985d1ea5 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -101,7 +101,7 @@ }, { "name": "Class, Support", - "scope": "entity.name.class, entity.name.type, support.type, support.class", + "scope": "entity.name.class, entity.name.type, entity.name.namespace, entity.name.scope-resolution, support.type, support.class", "settings": { "fontStyle": "", "foreground": "#FFEEAD" diff --git a/extensions/typescript-language-features/src/tsServer/serverError.ts b/extensions/typescript-language-features/src/tsServer/serverError.ts new file mode 100644 index 0000000000..794972995a --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/serverError.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Proto from '../protocol'; +import { escapeRegExp } from '../utils/regexp'; +import { TypeScriptVersion } from '../utils/versionProvider'; + +export class TypeScriptServerError extends Error { + public static create( + serverId: string, + version: TypeScriptVersion, + response: Proto.Response + ): TypeScriptServerError { + const parsedResult = TypeScriptServerError.parseErrorText(version, response); + return new TypeScriptServerError(serverId, version, response, parsedResult ? parsedResult.message : undefined, parsedResult ? parsedResult.stack : undefined); + } + + private constructor( + serverId: string, + version: TypeScriptVersion, + private readonly response: Proto.Response, + public readonly serverMessage: string | undefined, + public readonly serverStack: string | undefined + ) { + super(`<${serverId}> TypeScript Server Error (${version.versionString})\n${serverMessage}\n${serverStack}`); + } + + public get serverErrorText() { return this.response.message; } + + public get serverCommand() { return this.response.command; } + + /** + * Given a `errorText` from a tsserver request indicating failure in handling a request, + * prepares a payload for telemetry-logging. + */ + private static parseErrorText(version: TypeScriptVersion, response: Proto.Response) { + const errorText = response.message; + if (errorText) { + const errorPrefix = 'Error processing request. '; + if (errorText.startsWith(errorPrefix)) { + const prefixFreeErrorText = errorText.substr(errorPrefix.length); + const newlineIndex = prefixFreeErrorText.indexOf('\n'); + if (newlineIndex >= 0) { + // Newline expected between message and stack. + return { + message: prefixFreeErrorText.substring(0, newlineIndex), + stack: TypeScriptServerError.normalizeMessageStack(version, prefixFreeErrorText.substring(newlineIndex + 1)) + }; + } + } + } + return undefined; + } + + /** + * Try to replace full TS Server paths with 'tsserver.js' so that we don't have to post process the data as much + */ + private static normalizeMessageStack(version: TypeScriptVersion, message: string | undefined) { + if (!message) { + return ''; + } + return message.replace(new RegExp(`${escapeRegExp(version.path)}[/\\\\]tsserver.js:`, 'gi'), 'tsserver.js:'); + } +} diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts new file mode 100644 index 0000000000..64b6a011af --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as child_process from 'child_process'; +import * as path from 'path'; +import * as stream from 'stream'; +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import API from '../utils/api'; +import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; +import * as electron from '../utils/electron'; +import LogDirectoryProvider from '../utils/logDirectoryProvider'; +import Logger from '../utils/logger'; +import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; +import { PluginManager } from '../utils/plugins'; +import TelemetryReporter from '../utils/telemetry'; +import Tracer from '../utils/tracer'; +import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; +import { ITypeScriptServer, PipeRequestCanceller, ProcessBasedTsServer, SyntaxRoutingTsServer, TsServerProcess } from './server'; + +type ServerKind = 'main' | 'syntax' | 'semantic'; + +export class TypeScriptServerSpawner { + public constructor( + private readonly _versionProvider: TypeScriptVersionProvider, + private readonly _logDirectoryProvider: LogDirectoryProvider, + private readonly _pluginPathsProvider: TypeScriptPluginPathsProvider, + private readonly _logger: Logger, + private readonly _telemetryReporter: TelemetryReporter, + private readonly _tracer: Tracer, + ) { } + + public spawn( + version: TypeScriptVersion, + configuration: TypeScriptServiceConfiguration, + pluginManager: PluginManager + ): ITypeScriptServer { + if (this.shouldUseSeparateSyntaxServer(version, configuration)) { + const syntaxServer = this.spawnTsServer('syntax', version, configuration, pluginManager); + const semanticServer = this.spawnTsServer('semantic', version, configuration, pluginManager); + return new SyntaxRoutingTsServer(syntaxServer, semanticServer); + } + + return this.spawnTsServer('main', version, configuration, pluginManager); + } + + private shouldUseSeparateSyntaxServer( + version: TypeScriptVersion, + configuration: TypeScriptServiceConfiguration, + ): boolean { + return configuration.useSeparateSyntaxServer && !!version.apiVersion && version.apiVersion.gte(API.v340); + } + + private spawnTsServer( + kind: ServerKind, + version: TypeScriptVersion, + configuration: TypeScriptServiceConfiguration, + pluginManager: PluginManager, + ): ITypeScriptServer { + const apiVersion = version.apiVersion || API.defaultVersion; + + const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(kind, configuration, version, apiVersion, pluginManager); + + if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { + if (tsServerLogFile) { + this._logger.info(`<${kind}> Log file: ${tsServerLogFile}`); + } else { + this._logger.error(`<${kind}> Could not create log directory`); + } + } + + this._logger.info(`<${kind}> Forking...`); + const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions(kind)); + this._logger.info(`<${kind}> Starting...`); + + return new ProcessBasedTsServer( + kind, + new ChildServerProcess(childProcess), + tsServerLogFile, + new PipeRequestCanceller(kind, cancellationPipeName, this._tracer), + version, + this._telemetryReporter, + this._tracer); + } + + private getForkOptions(kind: ServerKind) { + const debugPort = TypeScriptServerSpawner.getDebugPort(kind); + const tsServerForkOptions: electron.ForkOptions = { + execArgv: debugPort ? [`--inspect=${debugPort}`] : [], + }; + return tsServerForkOptions; + } + + private getTsServerArgs( + kind: ServerKind, + configuration: TypeScriptServiceConfiguration, + currentVersion: TypeScriptVersion, + apiVersion: API, + pluginManager: PluginManager, + ): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } { + const args: string[] = []; + let cancellationPipeName: string | undefined; + let tsServerLogFile: string | undefined; + + if (kind === 'syntax') { + args.push('--syntaxOnly'); + } + + if (apiVersion.gte(API.v206)) { + if (apiVersion.gte(API.v250)) { + args.push('--useInferredProjectPerProjectRoot'); + } else { + args.push('--useSingleInferredProject'); + } + + if (configuration.disableAutomaticTypeAcquisition || kind === 'syntax') { + args.push('--disableAutomaticTypingAcquisition'); + } + } + + if (apiVersion.gte(API.v208) && kind !== 'syntax') { + args.push('--enableTelemetry'); + } + + if (apiVersion.gte(API.v222)) { + cancellationPipeName = electron.getTempFile('tscancellation'); + args.push('--cancellationPipeName', cancellationPipeName + '*'); + } + + if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { + const logDir = this._logDirectoryProvider.getNewLogDirectory(); + if (logDir) { + tsServerLogFile = path.join(logDir, `tsserver.log`); + args.push('--logVerbosity', TsServerLogLevel.toString(configuration.tsServerLogLevel)); + args.push('--logFile', tsServerLogFile); + } + } + + if (apiVersion.gte(API.v230)) { + const pluginPaths = this._pluginPathsProvider.getPluginPaths(); + + if (pluginManager.plugins.length) { + args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); + + const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; + for (const plugin of pluginManager.plugins) { + if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { + pluginPaths.push(plugin.path); + } + } + } + + if (pluginPaths.length !== 0) { + args.push('--pluginProbeLocations', pluginPaths.join(',')); + } + } + + if (apiVersion.gte(API.v234)) { + if (configuration.npmLocation) { + args.push('--npmLocation', `"${configuration.npmLocation}"`); + } + } + + if (apiVersion.gte(API.v260)) { + args.push('--locale', TypeScriptServerSpawner.getTsLocale(configuration)); + } + + if (apiVersion.gte(API.v291)) { + args.push('--noGetErrOnBackgroundUpdate'); + } + + if (apiVersion.gte(API.v345)) { + args.push('--validateDefaultNpmLocation'); + } + + return { args, cancellationPipeName, tsServerLogFile }; + } + + private static getDebugPort(kind: ServerKind): number | undefined { + if (kind === 'syntax') { + // We typically only want to debug the main semantic server + return undefined; + } + const value = process.env['TSS_DEBUG']; + if (value) { + const port = parseInt(value); + if (!isNaN(port)) { + return port; + } + } + return undefined; + } + + private static isLoggingEnabled(apiVersion: API, configuration: TypeScriptServiceConfiguration) { + return apiVersion.gte(API.v222) && + configuration.tsServerLogLevel !== TsServerLogLevel.Off; + } + + private static getTsLocale(configuration: TypeScriptServiceConfiguration): string { + return configuration.locale + ? configuration.locale + : vscode.env.language; + } +} + +class ChildServerProcess implements TsServerProcess { + + public constructor( + private readonly _process: child_process.ChildProcess, + ) { } + + get stdout(): stream.Readable { return this._process.stdout!; } + + write(serverRequest: Proto.Request): void { + this._process.stdin!.write(JSON.stringify(serverRequest) + '\r\n', 'utf8'); + } + + on(name: 'exit', handler: (code: number | null) => void): void; + on(name: 'error', handler: (error: Error) => void): void; + on(name: any, handler: any) { + this._process.on(name, handler); + } + + kill(): void { + this._process.kill(); + } +} \ No newline at end of file diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts new file mode 100644 index 0000000000..01155ce6b0 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -0,0 +1,343 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { window, Terminal, TerminalVirtualProcess, EventEmitter, TerminalDimensions, workspace, ConfigurationTarget } from 'vscode'; +import { doesNotThrow, equal, ok } from 'assert'; + +suite('window namespace tests', () => { + suiteSetup(async () => { + // Disable conpty in integration tests because of https://github.com/microsoft/vscode/issues/76548 + await workspace.getConfiguration('terminal.integrated').update('windowsEnableConpty', false, ConfigurationTarget.Global); + }); + suite('Terminal', () => { + test('sendText immediately after createTerminal should not throw', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + terminal.dispose(); + reg1.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + }); + const terminal = window.createTerminal(); + doesNotThrow(terminal.sendText.bind(terminal, 'echo "foo"')); + }); + + test('onDidCloseTerminal event fires when terminal is disposed', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + terminal.dispose(); + reg1.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + }); + const terminal = window.createTerminal(); + }); + + test('processId immediately after createTerminal should fetch the pid', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + reg1.dispose(); + terminal.processId.then(id => { + ok(id > 0); + terminal.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + }); + }); + const terminal = window.createTerminal(); + }); + + test('name in constructor should set terminal.name', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + terminal.dispose(); + reg1.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + }); + const terminal = window.createTerminal('a'); + equal(terminal.name, 'a'); + }); + + test('onDidOpenTerminal should fire when a terminal is created', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(term.name, 'b'); + reg1.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + terminal.dispose(); + }); + const terminal = window.createTerminal('b'); + }); + + test('Terminal.sendText should fire Terminal.onInput', (done) => { + const reg1 = window.onDidOpenTerminal(terminal => { + reg1.dispose(); + const reg2 = renderer.onDidAcceptInput(data => { + equal(data, 'bar'); + reg2.dispose(); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + }); + terminal.sendText('bar', false); + }); + const renderer = window.createTerminalRenderer('foo'); + }); + + // test('onDidChangeActiveTerminal should fire when new terminals are created', (done) => { + // const reg1 = window.onDidChangeActiveTerminal((active: Terminal | undefined) => { + // equal(active, terminal); + // equal(active, window.activeTerminal); + // reg1.dispose(); + // const reg2 = window.onDidChangeActiveTerminal((active: Terminal | undefined) => { + // equal(active, undefined); + // equal(active, window.activeTerminal); + // reg2.dispose(); + // done(); + // }); + // terminal.dispose(); + // }); + // const terminal = window.createTerminal(); + // terminal.show(); + // }); + + // test('onDidChangeTerminalDimensions should fire when new terminals are created', (done) => { + // const reg1 = window.onDidChangeTerminalDimensions(async (event: TerminalDimensionsChangeEvent) => { + // equal(event.terminal, terminal1); + // equal(typeof event.dimensions.columns, 'number'); + // equal(typeof event.dimensions.rows, 'number'); + // ok(event.dimensions.columns > 0); + // ok(event.dimensions.rows > 0); + // reg1.dispose(); + // let terminal2: Terminal; + // const reg2 = window.onDidOpenTerminal((newTerminal) => { + // // This is guarantees to fire before dimensions change event + // if (newTerminal !== terminal1) { + // terminal2 = newTerminal; + // reg2.dispose(); + // } + // }); + // let firstCalled = false; + // let secondCalled = false; + // const reg3 = window.onDidChangeTerminalDimensions((event: TerminalDimensionsChangeEvent) => { + // if (event.terminal === terminal1) { + // // The original terminal should fire dimension change after a split + // firstCalled = true; + // } else if (event.terminal !== terminal1) { + // // The new split terminal should fire dimension change + // secondCalled = true; + // } + // if (firstCalled && secondCalled) { + // let firstDisposed = false; + // let secondDisposed = false; + // const reg4 = window.onDidCloseTerminal(term => { + // if (term === terminal1) { + // firstDisposed = true; + // } + // if (term === terminal2) { + // secondDisposed = true; + // } + // if (firstDisposed && secondDisposed) { + // reg4.dispose(); + // done(); + // } + // }); + // terminal1.dispose(); + // terminal2.dispose(); + // reg3.dispose(); + // } + // }); + // await timeout(500); + // commands.executeCommand('workbench.action.terminal.split'); + // }); + // const terminal1 = window.createTerminal({ name: 'test' }); + // terminal1.show(); + // }); + + suite('hideFromUser', () => { + // test('should fire onDidWriteData correctly', done => { + // const terminal = window.createTerminal({ name: 'bg', hideFromUser: true }); + // let data = ''; + // terminal.onDidWriteData(e => { + // data += e; + // if (data.indexOf('foo') !== -1) { + // const reg3 = window.onDidCloseTerminal(() => { + // reg3.dispose(); + // done(); + // }); + // terminal.dispose(); + // } + // }); + // terminal.sendText('foo'); + // }); + + test('should be available to terminals API', done => { + const terminal = window.createTerminal({ name: 'bg', hideFromUser: true }); + window.onDidOpenTerminal(t => { + equal(t, terminal); + equal(t.name, 'bg'); + ok(window.terminals.indexOf(terminal) !== -1); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + }); + }); + }); + + suite('Terminal renderers (deprecated)', () => { + test('should fire onDidOpenTerminal and onDidCloseTerminal from createTerminalRenderer terminal', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(term.name, 'c'); + reg1.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + term.dispose(); + }); + window.createTerminalRenderer('c'); + }); + + test('should get maximum dimensions set when shown', (done) => { + let terminal: Terminal; + const reg1 = window.onDidOpenTerminal(term => { + reg1.dispose(); + term.show(); + terminal = term; + }); + const renderer = window.createTerminalRenderer('foo'); + const reg2 = renderer.onDidChangeMaximumDimensions(dimensions => { + ok(dimensions.columns > 0); + ok(dimensions.rows > 0); + reg2.dispose(); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + }); + }); + + test('should fire Terminal.onData on write', (done) => { + const reg1 = window.onDidOpenTerminal(terminal => { + reg1.dispose(); + const reg2 = terminal.onDidWriteData(data => { + equal(data, 'bar'); + reg2.dispose(); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + }); + renderer.write('bar'); + }); + const renderer = window.createTerminalRenderer('foo'); + }); + }); + + suite('Virtual process terminals', () => { + test('should fire onDidOpenTerminal and onDidCloseTerminal', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(term.name, 'c'); + reg1.dispose(); + const reg2 = window.onDidCloseTerminal(() => { + reg2.dispose(); + done(); + }); + term.dispose(); + }); + const virtualProcess: TerminalVirtualProcess = { + onDidWrite: new EventEmitter().event + }; + window.createTerminal({ name: 'c', virtualProcess }); + }); + + test('should fire Terminal.onData on write', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + reg1.dispose(); + const reg2 = terminal.onDidWriteData(data => { + equal(data, 'bar'); + reg2.dispose(); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + }); + writeEmitter.fire('bar'); + }); + const writeEmitter = new EventEmitter(); + const virtualProcess: TerminalVirtualProcess = { + onDidWrite: writeEmitter.event + }; + const terminal = window.createTerminal({ name: 'foo', virtualProcess }); + }); + + test('should fire provide dimensions on start as the terminal has been shown', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + reg1.dispose(); + }); + const virtualProcess: TerminalVirtualProcess = { + onDidWrite: new EventEmitter().event, + start: (dimensions) => { + ok(dimensions!.columns > 0); + ok(dimensions!.rows > 0); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + } + }; + const terminal = window.createTerminal({ name: 'foo', virtualProcess }); + }); + + test('should respect dimension overrides', (done) => { + const reg1 = window.onDidOpenTerminal(term => { + equal(terminal, term); + reg1.dispose(); + term.show(); + const reg2 = window.onDidChangeTerminalDimensions(e => { + equal(e.dimensions.columns, 10); + equal(e.dimensions.rows, 5); + equal(e.terminal, terminal); + reg2.dispose(); + const reg3 = window.onDidCloseTerminal(() => { + reg3.dispose(); + done(); + }); + terminal.dispose(); + }); + overrideDimensionsEmitter.fire({ columns: 10, rows: 5 }); + }); + const writeEmitter = new EventEmitter(); + const overrideDimensionsEmitter = new EventEmitter(); + const virtualProcess: TerminalVirtualProcess = { + onDidWrite: writeEmitter.event, + onDidOverrideDimensions: overrideDimensionsEmitter.event + }; + const terminal = window.createTerminal({ name: 'foo', virtualProcess }); + }); + }); + }); +}); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts new file mode 100644 index 0000000000..03eb6c8d55 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.fs.test.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { posix } from 'path'; + +suite('workspace-fs', () => { + + let root: vscode.Uri; + + suiteSetup(function () { + root = vscode.workspace.workspaceFolders![0]!.uri; + }); + + test('fs.stat', async function () { + const stat = await vscode.workspace.fs.stat(root); + assert.equal(stat.type, vscode.FileType.Directory); + + assert.equal(typeof stat.size, 'number'); + assert.equal(typeof stat.mtime, 'number'); + assert.equal(typeof stat.ctime, 'number'); + + + const entries = await vscode.workspace.fs.readDirectory(root); + assert.ok(entries.length > 0); + + // find far.js + const tuple = entries.find(tuple => tuple[0] === 'far.js')!; + assert.ok(tuple); + assert.equal(tuple[0], 'far.js'); + assert.equal(tuple[1], vscode.FileType.File); + }); + + test('fs.stat - bad scheme', async function () { + try { + await vscode.workspace.fs.stat(vscode.Uri.parse('foo:/bar/baz/test.txt')); + assert.ok(false); + } catch { + assert.ok(true); + } + }); + + test('fs.stat - missing file', async function () { + try { + await vscode.workspace.fs.stat(root.with({ path: root.path + '.bad' })); + assert.ok(false); + } catch (e) { + assert.ok(true); + } + }); + + test('fs.write/stat/delete', async function () { + + const uri = root.with({ path: posix.join(root.path, 'new.file') }); + await vscode.workspace.fs.writeFile(uri, Buffer.from('HELLO')); + + const stat = await vscode.workspace.fs.stat(uri); + assert.equal(stat.type, vscode.FileType.File); + + await vscode.workspace.fs.delete(uri); + + try { + await vscode.workspace.fs.stat(uri); + assert.ok(false); + } catch { + assert.ok(true); + } + }); + + test('fs.delete folder', async function () { + + const folder = root.with({ path: posix.join(root.path, 'folder') }); + const file = root.with({ path: posix.join(root.path, 'folder/file') }); + + await vscode.workspace.fs.createDirectory(folder); + await vscode.workspace.fs.writeFile(file, Buffer.from('FOO')); + + await vscode.workspace.fs.stat(folder); + await vscode.workspace.fs.stat(file); + + // ensure non empty folder cannot be deleted + try { + await vscode.workspace.fs.delete(folder, { recursive: false, useTrash: false }); + assert.ok(false); + } catch { + await vscode.workspace.fs.stat(folder); + await vscode.workspace.fs.stat(file); + } + + // ensure non empty folder cannot be deleted is DEFAULT + try { + await vscode.workspace.fs.delete(folder); // recursive: false as default + assert.ok(false); + } catch { + await vscode.workspace.fs.stat(folder); + await vscode.workspace.fs.stat(file); + } + + // delete non empty folder with recursive-flag + await vscode.workspace.fs.delete(folder, { recursive: true, useTrash: false }); + + // esnure folder/file are gone + try { + await vscode.workspace.fs.stat(folder); + assert.ok(false); + } catch { + assert.ok(true); + } + try { + await vscode.workspace.fs.stat(file); + assert.ok(false); + } catch { + assert.ok(true); + } + }); + + test('throws FileSystemError', async function () { + + try { + await vscode.workspace.fs.stat(vscode.Uri.file(`/c468bf16-acfd-4591-825e-2bcebba508a3/71b1f274-91cb-4c19-af00-8495eaab4b73/4b60cb48-a6f2-40ea-9085-0936f4a8f59a.tx6`)); + assert.ok(false); + } catch (e) { + assert.ok(e instanceof vscode.FileSystemError); + assert.equal(e.name, vscode.FileSystemError.FileNotFound().name); + } + }); + + test('throws FileSystemError', async function () { + + try { + await vscode.workspace.fs.stat(vscode.Uri.parse('foo:/bar')); + assert.ok(false); + } catch (e) { + assert.ok(e instanceof vscode.FileSystemError); + assert.equal(e.name, vscode.FileSystemError.Unavailable().name); + } + }); +}); diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index 517429bb6d..702e36e66c 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -3,6 +3,7 @@ "description": "Colorize tests for VS Code", "version": "0.0.1", "publisher": "vscode", + "license": "MIT", "private": true, "engines": { "vscode": "*" @@ -11,7 +12,7 @@ "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" }, "devDependencies": { - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "vscode": "1.1.5" diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index 46684d06b5..368b81f2f9 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== ajv@^5.1.0: version "5.3.0" diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 6377065d87..167df92db2 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -21,7 +21,7 @@ ], "main": "./out/extension", "devDependencies": { - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "vscode": "1.1.5" }, "contributes": { @@ -43,11 +43,6 @@ "category": "Remote-TestResolver", "command": "vscode-testresolver.newWindow" }, - { - "title": "New Window with Error", - "category": "Remote-TestResolver", - "command": "vscode-testresolver.newWindowWithError" - }, { "title": "Show Log", "category": "Remote-TestResolver", @@ -61,11 +56,6 @@ "when": "!remoteAuthority", "group": "9_local_testresolver@2" }, - { - "command": "vscode-testresolver.newWindowWithError", - "when": "!remoteAuthority", - "group": "9_local_testresolver@3" - }, { "command": "vscode-testresolver.showLog", "when": "remoteAuthority =~ /^test\\+.*$/", @@ -75,13 +65,27 @@ "command": "vscode-testresolver.newWindow", "when": "remoteAuthority =~ /^test\\+.*$/", "group": "1_remote_testresolver_open@1" - }, - { - "command": "vscode-testresolver.newWindowWithError", - "when": "remoteAuthority =~ /^test\\+.*$/", - "group": "1_remote_testresolver_open@2" } ] + }, + "configuration": { + "properties": { + "testresolver.startupDelay": { + "description": "If set, the resolver will delay for the given amount of seconds. Use ths setting for testing a slow resolver", + "type": "number", + "default": 0 + }, + "testresolver.startupError": { + "description": "If set, the resolver will fail. Use ths setting for testing the failure of a resolver.", + "type": "boolean", + "default": false + }, + "testresolver.pause": { + "description": "If set, connection is paused", + "type": "boolean", + "default": false + } + } } } diff --git a/extensions/vscode-test-resolver/scripts/terminateProcess.sh b/extensions/vscode-test-resolver/scripts/terminateProcess.sh new file mode 100755 index 0000000000..908a68287f --- /dev/null +++ b/extensions/vscode-test-resolver/scripts/terminateProcess.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +terminateTree() { + for cpid in $(/usr/bin/pgrep -P $1); do + terminateTree $cpid + done + kill -9 $1 > /dev/null 2>&1 +} + +for pid in $*; do + terminateTree $pid +done \ No newline at end of file diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 6d2d786823..2716770fba 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -8,9 +8,10 @@ import * as cp from 'child_process'; import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; +import * as net from 'net'; import { downloadAndUnzipVSCodeServer } from './download'; +import { terminateProcess } from './util/processes'; -let startPromise: Thenable | undefined = void 0; let extHostProcess: cp.ChildProcess | undefined; const enum CharCode { Backspace = 8, @@ -22,14 +23,15 @@ let outputChannel: vscode.OutputChannel; export function activate(context: vscode.ExtensionContext) { function doResolve(_authority: string, progress: vscode.Progress<{ message?: string; increment?: number }>): Promise { - return new Promise(async (res, rej) => { + const serverPromise = new Promise(async (res, rej) => { progress.report({ message: 'Starting Test Resolver' }); outputChannel = vscode.window.createOutputChannel('TestResolver'); - let isStarted = false; + let isResolved = false; async function processError(message: string) { outputChannel.appendLine(message); - if (!isStarted) { + if (!isResolved) { + isResolved = true; outputChannel.show(); const result = await vscode.window.showErrorMessage(message, { modal: true }, ...getActions()); @@ -48,7 +50,7 @@ export function activate(context: vscode.ExtensionContext) { if (chr === CharCode.LineFeed) { const match = lastProgressLine.match(/Extension host agent listening on (\d+)/); if (match) { - isStarted = true; + isResolved = true; res(new vscode.ResolvedAuthority('localhost', parseInt(match[1], 10))); // success! } lastProgressLine = ''; @@ -59,66 +61,153 @@ export function activate(context: vscode.ExtensionContext) { } } } + const delay = getConfiguration('startupDelay'); + if (typeof delay === 'number') { + let remaining = Math.ceil(delay); + outputChannel.append(`Delaying startup by ${remaining} seconds (configured by "testresolver.startupDelay").`); + while (remaining > 0) { + progress.report({ message: `Delayed resolving: Remaining ${remaining}s` }); + await (sleep(1000)); + remaining--; + } + } - if (_authority === 'test+error' || vscode.workspace.getConfiguration('testresolver').get('error') === true) { - processError('Unable to start the Test Resolver.'); + if (getConfiguration('startupError') === true) { + processError('Test Resolver failed for testing purposes (configured by "testresolver.startupError").'); return; } - const { updateUrl, commit, quality } = getProductConfiguration(); + const { updateUrl, commit, quality, serverDataFolderName, dataFolderName } = getProductConfiguration(); + const serverCommand = process.platform === 'win32' ? 'server.bat' : 'server.sh'; + const commandArgs = ['--port=0', '--disable-telemetry']; + const env = getNewEnv(); + const remoteDataDir = process.env['TESTRESOLVER_DATA_FOLDER'] || path.join(os.homedir(), serverDataFolderName || `${dataFolderName}-testresolver`); + env['VSCODE_AGENT_FOLDER'] = remoteDataDir; + outputChannel.appendLine(`Using data folder at ${remoteDataDir}`); + if (!commit) { // dev mode const vscodePath = path.resolve(path.join(context.extensionPath, '..', '..')); - const nodeExec = process.platform === 'win32' ? 'node.exe' : 'node'; - const nodePath = path.join(vscodePath, '.build', 'node-remote', nodeExec); - - if (!fs.existsSync(nodePath)) { - try { - progress.report({ message: 'Installing node' }); - outputChannel.appendLine(`Installing node at ${nodePath}`); - cp.execSync(`node ${path.join(vscodePath, 'node_modules/gulp/bin/gulp.js')} node-remote`); - } catch (e) { - processError(`Problem downloading node: ${e.message}`); - - } - } - outputChannel.appendLine(`Using node at ${nodePath}`); - - const env = getNewEnv(); - env['PATH'] = path.join(vscodePath, 'resources', 'server', 'bin') + path.delimiter + env['PATH']; // allow calling code-dev.sh - - outputChannel.appendLine(env['PATH'] || ''); - - extHostProcess = cp.spawn(nodePath, [path.join('out', 'remoteExtensionHostAgent'), '--port=0'], { cwd: vscodePath, env }); + const serverCommandPath = path.join(vscodePath, 'resources', 'server', 'bin-dev', serverCommand); + extHostProcess = cp.spawn(serverCommandPath, commandArgs, { env, cwd: vscodePath }); } else { - const serverBin = path.resolve(os.homedir(), '.vscode-remote', 'bin'); + const serverBin = path.join(remoteDataDir, 'bin'); progress.report({ message: 'Installing VSCode Server' }); const serverLocation = await downloadAndUnzipVSCodeServer(updateUrl, commit, quality, serverBin); outputChannel.appendLine(`Using server build at ${serverLocation}`); - const commandArgs = ['--port=0', '--disable-telemetry']; - - const env = getNewEnv(); - env['PATH'] = path.join(serverLocation, 'bin') + path.delimiter + env['PATH']; // code command for the terminal - - extHostProcess = cp.spawn(path.join(serverLocation, 'server.sh'), commandArgs, { env, cwd: serverLocation }); + extHostProcess = cp.spawn(path.join(serverLocation, serverCommand), commandArgs, { env, cwd: serverLocation }); } extHostProcess.stdout.on('data', (data: Buffer) => processOutput(data.toString())); extHostProcess.stderr.on('data', (data: Buffer) => processOutput(data.toString())); - extHostProcess.on('error', (error: Error) => processError(`remoteExtensionHostAgent failed with error:\n${error.message}`)); - extHostProcess.on('close', (code: number) => processError(`remoteExtensionHostAgent closed unexpectedly.\nError code: ${code}`)); + extHostProcess.on('error', (error: Error) => { + processError(`server failed with error:\n${error.message}`); + extHostProcess = undefined; + }); + extHostProcess.on('close', (code: number) => { + processError(`server closed unexpectedly.\nError code: ${code}`); + extHostProcess = undefined; + }); + context.subscriptions.push({ + dispose: () => { + if (extHostProcess) { + terminateProcess(extHostProcess, context.extensionPath); + } + } + }); + }); + return serverPromise.then(serverAddr => { + return new Promise(async (res, _rej) => { + const proxyServer = net.createServer(proxySocket => { + outputChannel.appendLine(`Proxy connection accepted`); + let remoteReady = true, localReady = true; + const remoteSocket = net.createConnection({ port: serverAddr.port }); + + let isDisconnected = getConfiguration('pause') === true; + vscode.workspace.onDidChangeConfiguration(_ => { + let newIsDisconnected = getConfiguration('pause') === true; + if (isDisconnected !== newIsDisconnected) { + outputChannel.appendLine(`Connection state: ${newIsDisconnected ? 'open' : 'paused'}`); + isDisconnected = newIsDisconnected; + if (!isDisconnected) { + outputChannel.appendLine(`Resume remote and proxy sockets.`); + if (remoteSocket.isPaused() && localReady) { + remoteSocket.resume(); + } + if (proxySocket.isPaused() && remoteReady) { + proxySocket.resume(); + } + } else { + outputChannel.appendLine(`Pausing remote and proxy sockets.`); + if (!remoteSocket.isPaused()) { + remoteSocket.pause(); + } + if (!proxySocket.isPaused()) { + proxySocket.pause(); + } + } + } + }); + + proxySocket.on('data', (data) => { + remoteReady = remoteSocket.write(data); + if (!remoteReady) { + proxySocket.pause(); + } + }); + remoteSocket.on('data', (data) => { + localReady = proxySocket.write(data); + if (!localReady) { + remoteSocket.pause(); + } + }); + proxySocket.on('drain', () => { + localReady = true; + if (!isDisconnected) { + remoteSocket.resume(); + } + }); + remoteSocket.on('drain', () => { + remoteReady = true; + if (!isDisconnected) { + proxySocket.resume(); + } + }); + proxySocket.on('close', () => { + outputChannel.appendLine(`Proxy socket closed, closing remote socket.`); + remoteSocket.end(); + }); + remoteSocket.on('close', () => { + outputChannel.appendLine(`Remote socket closed, closing proxy socket.`); + proxySocket.end(); + }); + context.subscriptions.push({ + dispose: () => { + proxySocket.end(); + remoteSocket.end(); + } + }); + }); + proxyServer.listen(0, () => { + const port = (proxyServer.address()).port; + outputChannel.appendLine(`Going through proxy at port ${port}`); + res({ host: '127.0.0.1', port }); + }); + context.subscriptions.push({ + dispose: () => { + proxyServer.close(); + } + }); + }); }); } vscode.workspace.registerRemoteAuthorityResolver('test', { resolve(_authority: string): Thenable { - if (!startPromise) { - startPromise = vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: 'Open TestResolver Remote ([details](command:remote-testresolver.showLog))', - cancellable: false - }, (progress) => doResolve(_authority, progress)); - } - return startPromise; + return vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: 'Open TestResolver Remote ([details](command:remote-testresolver.showLog))', + cancellable: false + }, (progress) => doResolve(_authority, progress)); } }); @@ -133,7 +222,6 @@ export function activate(context: vscode.ExtensionContext) { outputChannel.show(); } }); - } type ActionItem = (vscode.MessageItem & { execute: () => void; }); @@ -170,6 +258,8 @@ export interface IProductConfiguration { updateUrl: string; commit: string; quality: string; + dataFolderName: string; + serverDataFolderName?: string; } function getProductConfiguration(): IProductConfiguration { @@ -183,8 +273,12 @@ function getNewEnv(): { [x: string]: string | undefined } { return env; } -export function deactivate() { - if (extHostProcess) { - extHostProcess.kill(); - } +function sleep(ms: number): Promise { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + +function getConfiguration(id: string): T | undefined { + return vscode.workspace.getConfiguration('testresolver').get(id); } diff --git a/extensions/vscode-test-resolver/src/util/processes.ts b/extensions/vscode-test-resolver/src/util/processes.ts new file mode 100644 index 0000000000..517c33d087 --- /dev/null +++ b/extensions/vscode-test-resolver/src/util/processes.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as cp from 'child_process'; +import * as path from 'path'; + +export interface TerminateResponse { + success: boolean; + error?: any; +} + +export function terminateProcess(p: cp.ChildProcess, extensionPath: string): TerminateResponse { + if (process.platform === 'win32') { + try { + const options: any = { + stdio: ['pipe', 'pipe', 'ignore'] + }; + cp.execFileSync('taskkill', ['/T', '/F', '/PID', p.pid.toString()], options); + } catch (err) { + return { success: false, error: err }; + } + } else if (process.platform === 'darwin' || process.platform === 'linux') { + try { + const cmd = path.join(extensionPath, 'scripts', 'terminateProcess.sh'); + const result = cp.spawnSync(cmd, [process.pid.toString()]); + if (result.error) { + return { success: false, error: result.error }; + } + } catch (err) { + return { success: false, error: err }; + } + } else { + p.kill('SIGKILL'); + } + return { success: true }; +} \ No newline at end of file diff --git a/extensions/vscode-test-resolver/yarn.lock b/extensions/vscode-test-resolver/yarn.lock index 4cb3781773..925cef2c14 100644 --- a/extensions/vscode-test-resolver/yarn.lock +++ b/extensions/vscode-test-resolver/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.30" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.30.tgz#4c2b4f0015f214f8158a347350481322b3b29b2f" - integrity sha512-nsqTN6zUcm9xtdJiM9OvOJ5EF0kOI8f1Zuug27O/rgtxCRJHGqncSWfCMZUP852dCKPsDsYXGvBhxfRjDBkF5Q== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== ajv@^6.5.5: version "6.10.0" diff --git a/extensions/xml-language-features/.vscodeignore b/extensions/xml-language-features/.vscodeignore index 30d948fbc6..469e86abac 100644 --- a/extensions/xml-language-features/.vscodeignore +++ b/extensions/xml-language-features/.vscodeignore @@ -8,3 +8,4 @@ cgmanifest.json yarn.lock preview-src/** webpack.config.js +.vscode diff --git a/extensions/xml/package.json b/extensions/xml/package.json index 985266edf9..2e6cc49fd6 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "contributes": { "languages": [{ diff --git a/extensions/yaml/.gitignore b/extensions/yaml/.gitignore deleted file mode 100644 index 3c3629e647..0000000000 --- a/extensions/yaml/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/extensions/yaml/package.json b/extensions/yaml/package.json index 79e2eecc31..5b12e0fbc5 100644 --- a/extensions/yaml/package.json +++ b/extensions/yaml/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 659f98aa61..8e552194c1 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.5.0-dev.20190522: - version "3.5.0-dev.20190522" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190522.tgz#ade4702c6e599a7e8905d7acaaf416f7e32ba0fc" - integrity sha512-V+QsNMtXl8lGwov4O04D+E6vtiL0TWEpz6iDxI2tAZizEn7y8Hh9SXzz3JRWepr7dfdqqxEv9CT3J18zpaZKJQ== +typescript@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" + integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== diff --git a/gulpfile.js b/gulpfile.js index 19c7be05c2..1dd50f784f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -40,4 +40,4 @@ process.on('unhandledRejection', (reason, p) => { // Load all the gulpfiles only if running tasks other than the editor tasks const build = path.join(__dirname, 'build'); require('glob').sync('gulpfile.*.js', { cwd: build }) - .forEach(f => require(`./build/${f}`)); + .forEach(f => require(`./build/${f}`)); \ No newline at end of file diff --git a/package.json b/package.json index ee6e7f28a8..65b7d98ef3 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "author": { "name": "Microsoft Corporation" }, + "license": "MIT", "main": "./out/main", "private": true, "scripts": { @@ -16,7 +17,7 @@ "watch-client": "gulp watch-client --max_old_space_size=4095", "monaco-editor-test": "mocha --only-monaco-editor", "precommit": "node build/gulpfile.hygiene.js", - "gulp": "gulp --max_old_space_size=4095", + "gulp": "gulp --max_old_space_size=8192", "7z": "7z", "update-grammars": "node build/npm/update-all-grammars.js", "update-localization-extension": "node build/npm/update-localization-extension.js", @@ -26,7 +27,7 @@ "strict-null-check": "tsc -p src/tsconfig.strictNullChecks.json", "strict-null-check-watch": "tsc -p src/tsconfig.strictNullChecks.json --watch", "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", - "web": "node scripts/code-web.js" + "update-distro": "node build/npm/update-distro.js" }, "dependencies": { "@angular/animations": "~4.1.3", @@ -45,40 +46,40 @@ "ansi_up": "^3.0.0", "applicationinsights": "1.0.8", "chart.js": "^2.6.0", - "gc-signals": "^0.0.2", - "getmac": "1.4.1", "graceful-fs": "4.1.11", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.1", - "iconv-lite": "0.4.23", + "iconv-lite": "0.5.0", "jquery": "3.4.0", "jschardet": "1.6.0", - "keytar": "4.2.1", + "keytar": "^4.11.0", "minimist": "1.2.0", "native-is-elevated": "^0.2.1", - "native-keymap": "1.2.5", + "native-keymap": "2.0.0", "native-watchdog": "1.0.0", "ng2-charts": "^1.6.0", - "node-pty": "0.9.0-beta9", + "node-pty": "0.9.0-beta19", + "nsfw": "1.2.5", + "onigasm-umd": "^2.2.2", "plotly.js-dist": "^1.48.3", "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", "sanitize-html": "^1.19.1", "semver": "^5.5.0", "slickgrid": "github:anthonydresser/SlickGrid#2.3.29", - "spdlog": "0.8.1", - "sudo-prompt": "8.2.0", + "spdlog": "^0.9.0", + "sudo-prompt": "9.0.0", "v8-inspect-profiler": "^0.0.20", - "vscode-chokidar": "1.6.5", - "vscode-debugprotocol": "1.34.0", - "vscode-nsfw": "1.1.1", + "vscode-chokidar": "2.1.7", "vscode-proxy-agent": "0.4.0", - "vscode-ripgrep": "^1.2.5", - "vscode-sqlite3": "4.0.7", - "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.14.0-beta3", - "yauzl": "^2.9.1", + "vscode-ripgrep": "^1.3.1", + "vscode-sqlite3": "4.0.8", + "vscode-textmate": "^4.2.2", + "xterm": "3.15.0-beta71", + "xterm-addon-search": "0.2.0-beta2", + "xterm-addon-web-links": "0.1.0-beta10", + "yauzl": "^2.9.2", "yazl": "^2.4.3", "zone.js": "^0.8.4" }, @@ -86,7 +87,7 @@ "7zip": "0.0.6", "@types/chart.js": "^2.7.31", "@types/htmlparser2": "^3.7.31", - "@types/keytar": "^4.0.1", + "@types/keytar": "^4.4.0", "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", "@types/node": "^10.12.12", @@ -105,7 +106,7 @@ "debounce": "^1.0.0", "documentdb": "^1.5.1", "electron-mksnapshot": "~2.0.0", - "eslint": ">=4.18.2", + "eslint": "^4.18.2", "event-stream": "3.3.4", "express": "^4.13.1", "fancy-log": "^1.3.3", @@ -134,7 +135,7 @@ "gulp-vinyl-zip": "^2.1.2", "http-server": "^0.11.1", "husky": "^0.13.1", - "innosetup-compiler": "^5.5.60", + "innosetup": "5.6.1", "is": "^3.1.0", "istanbul": "^0.3.17", "jsdom-no-contextify": "^3.1.0", @@ -161,14 +162,15 @@ "tslint": "^5.16.0", "tslint-microsoft-contrib": "^6.0.0", "typemoq": "^0.3.2", - "typescript": "3.4.5", + "typescript": "3.5.2", "typescript-formatter": "7.1.0", "uglify-es": "^3.0.18", "underscore": "^1.8.2", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-nls-dev": "3.2.5", + "vscode-debugprotocol": "1.35.0", + "vscode-nls-dev": "^3.3.1", "webpack": "^4.16.5", "webpack-cli": "^3.1.0", "webpack-stream": "^5.1.1" @@ -185,12 +187,6 @@ "vscode-windows-registry": "1.0.1", "windows-foreground-love": "0.1.0", "windows-mutex": "0.2.1", - "windows-process-tree": "0.2.3" - }, - "resolutions": { - "rc": "1.2.8", - "event-stream": "3.3.4", - "natives": "1.1.6", - "@types/node": "10.12.12" + "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file diff --git a/remote/.yarnrc b/remote/.yarnrc index 29c22bc4be..b28191e6ba 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,3 +1,3 @@ disturl "http://nodejs.org/dist" -target "10.2.1" +target "10.11.0" runtime "node" diff --git a/remote/installDevModules.sh b/remote/installDevModules.sh deleted file mode 100755 index 3b8941d002..0000000000 --- a/remote/installDevModules.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -ex - -# Install Node and Yarn -export NODE_VERSION=10.2.1 -export YARN_VERSION=1.10.1 -curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" -curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" -tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C "$HOME" --no-same-owner -tar -xzf "yarn-v$YARN_VERSION.tar.gz" -C "$HOME" -mkdir -p "$HOME/bin" -ln -s "$HOME/node-v$NODE_VERSION-linux-x64/bin/node" "$HOME/bin/node" -ln -s "$HOME/yarn-v$YARN_VERSION/bin/yarn" "$HOME/bin/yarn" -ln -s "$HOME/yarn-v$YARN_VERSION/bin/yarnpkg" "$HOME/bin/yarnpkg" -rm "node-v$NODE_VERSION-linux-x64.tar.xz" "yarn-v$YARN_VERSION.tar.gz" - -# Compile native /remote node_modules -PATH="$HOME/bin:$PATH" \ -PYTHON=/usr/bin/python2.7 \ -yarn --ignore-optional diff --git a/remote/installDevPackagesAsRoot.sh b/remote/installDevPackagesAsRoot.sh deleted file mode 100755 index 1df37767c9..0000000000 --- a/remote/installDevPackagesAsRoot.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -ex - -# Install libraries and tools -apt-get update -apt-get install -y \ - curl \ - make \ - gcc \ - g++ \ - python2.7 \ - libx11-dev \ - libxkbfile-dev \ - libsecret-1-dev \ - xz-utils -rm -rf /var/lib/apt/lists/* diff --git a/remote/launchDevMode.sh b/remote/launchDevMode.sh deleted file mode 100755 index cd7ab12976..0000000000 --- a/remote/launchDevMode.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e - -export NODE_ENV=development -export VSCODE_DEV=1 -export VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH="$HOME/.vscode-remote/bin/dev-remote/node_modules" - -cd $VSCODE_REPO - -if [ -z "$extensions" ] ; then - echo No extensions to install. - mkdir -p /root/.vscode-remote -else - (PATH="$HOME/bin:$PATH" node out/remoteExtensionHostAgent.js ${VSCODE_TELEMETRY_ARG} ${extensions} || true) -fi - -PATH="$HOME/bin:$PATH" node out/remoteExtensionHostAgent.js ${VSCODE_TELEMETRY_ARG} --port $PORT diff --git a/remote/package.json b/remote/package.json index cd8b8c6673..6fc6471a2c 100644 --- a/remote/package.json +++ b/remote/package.json @@ -3,26 +3,31 @@ "version": "0.0.0", "dependencies": { "applicationinsights": "1.0.8", - "getmac": "^1.4.6", + "getmac": "1.4.1", "graceful-fs": "4.1.11", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.1", - "iconv-lite": "0.4.23", + "iconv-lite": "0.5.0", "jschardet": "1.6.0", - "keytar": "4.2.1", "minimist": "1.2.0", "native-watchdog": "1.0.0", - "node-pty": "0.8.1", + "node-pty": "0.9.0-beta19", + "nsfw": "1.2.5", + "onigasm-umd": "^2.2.2", "semver": "^5.5.0", - "spdlog": "0.8.1", - "vscode-chokidar": "1.6.5", - "vscode-nsfw": "1.1.1", + "spdlog": "^0.9.0", + "vscode-chokidar": "2.1.7", "vscode-proxy-agent": "0.4.0", - "vscode-ripgrep": "^1.2.5", - "yauzl": "^2.9.1", + "vscode-ripgrep": "^1.3.1", + "vscode-textmate": "^4.2.2", + "xterm": "3.15.0-beta71", + "xterm-addon-search": "0.2.0-beta2", + "xterm-addon-web-links": "0.1.0-beta10", + "yauzl": "^2.9.2", "yazl": "^2.4.3" }, "optionalDependencies": { - "vscode-windows-ca-certs": "0.1.0" + "vscode-windows-ca-certs": "0.1.0", + "vscode-windows-registry": "1.0.1" } } diff --git a/remote/web/.yarnrc b/remote/web/.yarnrc new file mode 100644 index 0000000000..b28191e6ba --- /dev/null +++ b/remote/web/.yarnrc @@ -0,0 +1,3 @@ +disturl "http://nodejs.org/dist" +target "10.11.0" +runtime "node" diff --git a/remote/web/package.json b/remote/web/package.json new file mode 100644 index 0000000000..38f730e7da --- /dev/null +++ b/remote/web/package.json @@ -0,0 +1,11 @@ +{ + "name": "vscode-web", + "version": "0.0.0", + "dependencies": { + "onigasm-umd": "^2.2.2", + "vscode-textmate": "^4.1.1", + "xterm": "3.15.0-beta67", + "xterm-addon-search": "0.2.0-beta2", + "xterm-addon-web-links": "0.1.0-beta10" + } + } \ No newline at end of file diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock new file mode 100644 index 0000000000..3f790e1f28 --- /dev/null +++ b/remote/web/yarn.lock @@ -0,0 +1,42 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +onigasm-umd@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" + integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw== + +oniguruma@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.2.0.tgz#c9a59c1ea7b9fe67e237a02e02139b638856f3af" + integrity sha512-bh+ZLdykY1sdIx8jBp2zpLbVFDBc3XmKH4Ceo2lijNaN1WhEqtnpqFlmtCbRuDB17nJ58RAUStVwfW8e8uEbnA== + dependencies: + nan "^2.14.0" + +vscode-textmate@^4.1.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.2.2.tgz#0b4dabc69a6fba79a065cb6b615f66eac07c8f4c" + integrity sha512-1U4ih0E/KP1zNK/EbpUqyYtI7PY+Ccd2nDGTtiMR/UalLFnmaYkwoWhN1oI7B91ptBN8NdVwWuvyUnvJAulCUw== + dependencies: + oniguruma "^7.2.0" + +xterm-addon-search@0.2.0-beta2: + version "0.2.0-beta2" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta2.tgz#c3173f0a6f207ee9f1848849174ee5d6b6ce8262" + integrity sha512-XEcwi2TeFGk2MuIFjiI/OpVXSNO5dGQBvHH3o+9KzqG3ooVqhhDqzwxs092QGNcNCGh8hGn/PWZiczaBBnKm/g== + +xterm-addon-web-links@0.1.0-beta10: + version "0.1.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" + integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== + +xterm@3.15.0-beta67: + version "3.15.0-beta67" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta67.tgz#71973e174bdc08df620945eecd3f87912f1ac550" + integrity sha512-qLfo9GHVlu/IxgDI3vRGObWZM7UL4eLhMfjZhprx2aXNMpzmrOW6l3JDRsCjUWm93EoVavbULtnDhGSiTlKitQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index 2df672960f..7d522e2901 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -16,16 +16,6 @@ agent-base@~4.2.0: dependencies: es6-promisify "^5.0.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -35,123 +25,129 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +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= -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= - dependencies: - arr-flatten "^1.0.1" +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= -arr-flatten@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" - integrity sha1-5f/lTUXhnzLyFukeuZyM6JK7YEs= +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= - -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - integrity sha1-GdOGodntxufByF04iu28xW0zYC0= - -balanced-match@^1.0.0: +assign-symbols@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" binary-extensions@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" integrity sha1-muuabF6IY4qtFx4Wf1kAq+JINdA= -bindings@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" - integrity sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw== - -bl@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" - integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" + file-uri-to-path "1.0.0" -brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - integrity sha1-wHshHHyVLsH479Uad+8NHTmQopI= +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" repeat-element "^1.1.2" - -buffer-alloc-unsafe@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-0.1.1.tgz#ffe1f67551dd055737de253337bfe853dfab1a6a" - integrity sha1-/+H2dVHdBVc33iUzN7/oU9+rGmo= - -buffer-alloc@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.1.0.tgz#05514d33bf1656d3540c684f65b1202e90eca303" - integrity sha1-BVFNM78WVtNUDGhPZbEgLpDsowM= - dependencies: - buffer-alloc-unsafe "^0.1.0" - buffer-fill "^0.1.0" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-fill@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-0.1.1.tgz#76d825c4d6e50e06b7a31eb520c04d08cc235071" - integrity sha512-YgBMBzdRLEfgxJIGu2wrvI2E03tMCFU1p7d1KhB4BOoMN0VxmTFjSyN5JtKt9z8Z9JajMHruI6SE25W96wNv7Q== - -chownr@^1.0.1: +cache-base@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" - integrity sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE= + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" -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" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.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 sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" -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" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-util-is@~1.0.0: version "1.0.2" @@ -165,27 +161,39 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= +debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - mimic-response "^1.0.0" + ms "2.0.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -delegates@^1.0.0: +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" diagnostic-channel-publishers@0.2.1: version "0.2.1" @@ -207,12 +215,12 @@ eachr@^3.2.0: editions "^1.1.1" typechecker "^4.3.0" -editions@^1.1.1: +editions@^1.1.1, editions@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg== -editions@^2.0.2, editions@^2.1.0, editions@^2.1.2: +editions@^2.1.0, editions@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/editions/-/editions-2.1.3.tgz#727ccf3ec2c7b12dcc652c71000f16c4824d6f7d" integrity sha512-xDZyVm0A4nLgMNWVVLJvcwMjI80ShiH/27RyLiCnW1L273TcJIA25C4pwJ33AWV01OX6UriP35Xu+lH4S7HWQw== @@ -220,13 +228,6 @@ editions@^2.0.2, editions@^2.1.0, editions@^2.1.2: errlop "^1.1.1" semver "^5.6.0" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" - integrity sha1-epDYM+/abPpurA9JSduw+tOmMgY= - dependencies: - once "^1.4.0" - errlop@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/errlop/-/errlop-1.1.1.tgz#d9ae4c76c3e64956c5d79e6e035d6343bfd62250" @@ -246,31 +247,47 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: - is-posix-bracket "^0.1.0" + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: - fill-range "^2.1.0" + is-extendable "^0.1.0" -expand-template@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.1.tgz#981f188c0c3a87d2e28f559bc541426ff94f21dd" - integrity sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg== - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= +extend-shallow@^3.0.0, 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: - is-extglob "^1.0.0" + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" extract-opts@^3.2.0: version "3.3.1" @@ -281,45 +298,39 @@ extract-opts@^3.2.0: editions "^1.1.1" typechecker "^4.3.0" -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= dependencies: pend "~1.2.0" -filename-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" - integrity sha1-mW4+gEebmLmJfxWopYs9CE6SZ3U= - -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - integrity sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM= - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -for-in@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.5.tgz#007374e2b6d5c67420a1479bdb75a04872b738c4" - integrity sha1-AHN04rbVxnQgoUeb23WgSHK3OMQ= - -for-own@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" - integrity sha1-AUm0GjkIjHUV9R6+HBOG1F+TUHI= - dependencies: - for-in "^0.1.5" - -fs-constants@^1.0.0: +file-uri-to-path@1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" fs-extra@^7.0.0: version "7.0.1" @@ -330,57 +341,67 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - 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" +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -getmac@^1.4.6: - version "1.4.6" - resolved "https://registry.yarnpkg.com/getmac/-/getmac-1.4.6.tgz#ffe7db07900e222916939d44e4c7274adbecc662" - integrity sha512-3JPwiIr4P6Sgr6y6SVXX0+l2mrB6pyf4Cdyua7rvEV7SveWQkAp11vrkNym8wvRxzLrBenKRcwe93asdghuwWg== +getmac@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/getmac/-/getmac-1.4.1.tgz#cfefcb3ee7d7a73cba5292129cb100c19afbe17a" + integrity sha512-mQp+8D+grQX0gG8EJn6VfH0PxE56ZKNsTguOMxPShAiVk9lvH8Ey36eXepG705Ac1HCsvaSrQ/6bPHZ0++F/Mg== dependencies: - editions "^2.0.2" + editions "^1.3.4" extract-opts "^3.2.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" - integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" + is-glob "^3.1.0" + path-dirname "^1.0.0" -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - -graceful-fs@4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@4.1.11, graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= +graceful-fs@^4.1.11, graceful-fs@^4.1.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" + integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" http-proxy-agent@2.1.0, http-proxy-agent@^2.1.0: version "2.1.0" @@ -398,28 +419,42 @@ https-proxy-agent@2.2.1, https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" -iconv-lite@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== +iconv-lite@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" + integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== dependencies: safer-buffer ">= 2.1.2 < 3" -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== + +inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@~1.3.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4= - ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -432,63 +467,92 @@ is-buffer@^1.0.2: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" integrity sha1-z8hszV3FpS+oBIkRHGkgxFfi2Ys= -is-dotfile@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" - integrity sha1-LBMjg/ORmfjtwmjKAbmwB9IFzE0= +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: - is-primitive "^2.0.0" + kind-of "^3.0.2" -is-extendable@^0.1.1: +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= - -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" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= +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: - number-is-nan "^1.0.0" + is-plain-object "^2.0.4" -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-extglob@^2.1.0, 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@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= dependencies: - is-extglob "^1.0.0" + is-extglob "^2.1.0" -is-number@^2.0.2, is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= +is-glob@^4.0.0: + 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@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= +is-plain-object@^2.0.3, 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-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" @@ -502,6 +566,11 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + jschardet@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.6.0.tgz#c7d1a71edcff2839db2f9ec30fc5d5ebd3c1a678" @@ -514,14 +583,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -keytar@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/keytar/-/keytar-4.2.1.tgz#8a06a6577fdf6373e0aa6b112277e63dec77fd12" - integrity sha1-igamV3/fY3PgqmsRInfmPex3/RI= - dependencies: - nan "2.8.0" - prebuild-install "^2.4.1" - kind-of@^3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.0.4.tgz#7b8ecf18a4e17f8269d73b501c9f232c96887a74" @@ -529,6 +590,30 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.0.2" +kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" @@ -539,47 +624,55 @@ lodash.isundefined@^3.0.1: resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" integrity sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g= -micromatch@^2.1.5: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -mimic-response@^1.0.0: +map-visit@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" - integrity sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4= - -minimatch@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: - brace-expansion "^1.1.7" + object-visit "^1.0.0" + +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0, minimist@^1.2.0: +minimist@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -592,111 +685,104 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -nan@2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" - integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== +nan@^2.0.0, nan@^2.12.1, nan@^2.13.2, nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -nan@2.8.0, nan@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" - integrity sha1-7XFfP+neArV6XmJS2QqWZ14fCFo= - -nan@^2.10.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" - integrity sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw== +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" native-watchdog@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.0.0.tgz#97344e83cd6815a8c8e6c44a52e7be05832e65ca" integrity sha512-HKQATz5KLUMPyQQ/QaalzgTXaGz2plYPBxjyalaR4ECIu/UznXY8YJD+a9SLkkcvtxnJ8/zHLY3xik06vUZ7uA== -node-abi@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.1.tgz#7628c4d4ec4e9cd3764ceb3652f36b2e7f8d4923" - integrity sha512-pUlswqpHQ7zGPI9lGjZ4XDNIEUDbHxsltfIRb7dTnYdhgHWHOcB0MLZKLoCz6UMcGzSPG5wGl1HODZVQAUsH6w== - dependencies: - semver "^5.4.1" - node-addon-api@1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== -node-pty@0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f" - integrity sha512-j+/g0Q5dR+vkELclpJpz32HcS3O/3EdPSGPvDXJZVJQLCvgG0toEbfmymxAEyQyZEpaoKHAcoL+PvKM+4N9nlw== +node-pty@0.9.0-beta19: + version "0.9.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0-beta19.tgz#0fd381b2006f4665c4c2ee0509219e591842371a" + integrity sha512-MkKEvBnauGnzgXNr/oaoWQLVXm1gheIKZs4YQp8883ZiETmbEnpSvD0FU3bELcPXG5VFPRqIGsQJ4KUMBLzkPA== dependencies: - nan "2.12.1" + nan "^2.13.2" -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= +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== -normalize-path@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= +nsfw@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/nsfw/-/nsfw-1.2.5.tgz#febe581af616f7b042f89df133abe62416c4c803" + integrity sha512-m3mwZUKXiCR69PDMLfAmKmiNzy0Oe9LhFE0DYZC5cc1htNj5Hyb1sAgglXhuaDkibFy22AVvPC5cCFB3A6mYIw== dependencies: - remove-trailing-separator "^1.0.1" + fs-extra "^7.0.0" + lodash.isinteger "^4.0.4" + lodash.isundefined "^3.0.1" + nan "^2.0.0" -normalize-path@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" - integrity sha1-R4hqwWYnYNQmG32XnSQXCdPOP3o= - -npmlog@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" -number-is-nan@^1.0.0: +object-visit@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object.omit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.0.tgz#868597333d54e60662940bb458605dd6ae12fe94" - integrity sha1-hoWXMz1U5gZilAu0WGBd1q4S/pQ= + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: - for-own "^0.1.3" - is-extendable "^0.1.1" + isobject "^3.0.0" -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: - wrappy "1" + isobject "^3.0.1" -os-homedir@^1.0.1: +onigasm-umd@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" + integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw== + +oniguruma@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.2.0.tgz#c9a59c1ea7b9fe67e237a02e02139b638856f3af" + integrity sha512-bh+ZLdykY1sdIx8jBp2zpLbVFDBc3XmKH4Ceo2lijNaN1WhEqtnpqFlmtCbRuDB17nJ58RAUStVwfW8e8uEbnA== + dependencies: + nan "^2.14.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= path-is-absolute@^1.0.0: version "1.0.1" @@ -708,76 +794,21 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -prebuild-install@^2.4.1: - version "2.5.3" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" - integrity sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g== - dependencies: - detect-libc "^1.0.3" - expand-template "^1.0.2" - github-from-package "0.0.0" - minimist "^1.2.0" - mkdirp "^0.5.1" - node-abi "^2.2.0" - noop-logger "^0.1.1" - npmlog "^4.0.1" - os-homedir "^1.0.1" - pump "^2.0.1" - rc "^1.1.6" - simple-get "^2.7.0" - tar-fs "^1.13.0" - tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" +picomatch@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" + integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -pump@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" - integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -randomatic@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.5.tgz#5e9ef5f2d573c67bd2b8124ae90b5156e457840b" - integrity sha1-Xp718tVzxnvSuBJK6QtRVuRXhAs= - dependencies: - is-number "^2.0.2" - kind-of "^3.0.2" - -rc@^1.1.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - readable-stream@^2.0.2: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" @@ -791,57 +822,55 @@ readable-stream@^2.0.2: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== 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" - -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg= - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" + graceful-fs "^4.1.11" + micromatch "^3.1.10" readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" -regex-cache@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" - integrity sha1-mxpsNdTQ3871cRrmUejp09cRQUU= +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: - is-equal-shallow "^0.1.3" - is-primitive "^2.0.0" - -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= + extend-shallow "^3.0.2" + safe-regex "^1.1.0" repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= -repeat-string@^1.5.2: - version "1.5.4" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.5.4.tgz#64ec0c91e0f4b475f90d5b643651e3e6e5b6c2d5" - integrity sha1-ZOwMkeD0tHX5DVtkNlHj5uW2wtU= +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -852,7 +881,7 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.4.1, semver@^5.5.0: +semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== @@ -862,40 +891,51 @@ semver@^5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -simple-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" - integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= - -simple-get@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" - integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== dependencies: - decompress-response "^3.3.0" - once "^1.3.1" - simple-concat "^1.0.0" + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" smart-buffer@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3" integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg== +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + socks-proxy-agent@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" @@ -912,31 +952,50 @@ socks@~2.2.0: ip "^1.1.5" smart-buffer "^4.0.1" -spdlog@0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.8.1.tgz#dfb3f3422ab3efe32be79e4769b95440ed72699f" - integrity sha512-W0s8IOXpn86md+8PJ4mJeB/22thykzH5YaNc3Rgnql4x4/zFIhvNiEx6/a1arnqvmJF0HtRO0Ehlswg0WcwTLQ== +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== dependencies: - bindings "^1.3.0" + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +spdlog@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.9.0.tgz#c85dd9d0b9cd385f6f3f5b92dc9d2e1691092b5c" + integrity sha512-AeLWPCYjGi4w5DfpXFKb9pCdgMe4gFBMroGfgwXiNfzwmcNYGoFQkIuD1MChZBR1Iwrx0nGhsTSHFslt/qfTAQ== + dependencies: + bindings "^1.5.0" mkdirp "^0.5.1" - nan "^2.8.0" + nan "^2.14.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" + extend-shallow "^3.0.0" -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" + define-property "^0.2.5" + object-copy "^0.1.0" string_decoder@~1.0.3: version "1.0.3" @@ -945,66 +1004,30 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.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== +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: - safe-buffer "~5.1.0" + kind-of "^3.0.2" -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" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: - ansi-regex "^2.0.0" + is-number "^3.0.0" + repeat-string "^1.6.1" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: - ansi-regex "^3.0.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" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -tar-fs@^1.13.0: - version "1.16.2" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.2.tgz#17e5239747e399f7e77344f5f53365f04af53577" - integrity sha512-LdknWjPEiZC1nOBwhv0JBzfJBGPJar08dZg2rwZe0ZTLQoRGEzgrl7vF3qUEkCHpI/wN9e7RyCuDhMsJUCLPPQ== - dependencies: - chownr "^1.0.1" - mkdirp "^0.5.1" - pump "^1.0.0" - tar-stream "^1.1.2" - -tar-stream@^1.1.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.1.tgz#f84ef1696269d6223ca48f6e1eeede3f7e81f395" - integrity sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA== - dependencies: - bl "^1.0.0" - buffer-alloc "^1.1.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.0" - xtend "^4.0.0" - -to-buffer@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" typechecker@^4.3.0: version "4.7.0" @@ -1013,56 +1036,82 @@ typechecker@^4.3.0: dependencies: editions "^2.1.0" +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + 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= -vscode-anymatch@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-1.3.3.tgz#0613d31a949c8025473bbdad848d219f47a44f86" - integrity sha512-LQ4vF4BWb9gwAvbMtN+3HC4HKDxLd+ZyWmAjACOdD05O/ZMcgvvnjO24GseEIQ6cWn8gW+Ft08gHFihnQy1eSw== +vscode-anymatch@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-3.0.3.tgz#5a79101e6df7e659a1f070367bc42f190eb4ae76" + integrity sha512-qQgfbzJJ5nNShh4jjC3BBekY4d8emcxHFgnqcXwsB/PUKvJPCg7AZYXM7hqS7EDnKrX9tsIFwFMihZ7yut92Qg== dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" + normalize-path "^3.0.0" + picomatch "^2.0.4" -vscode-chokidar@1.6.5: - version "1.6.5" - resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-1.6.5.tgz#f38a1f909fa364a5429a4d4d70ecb40a15b54d0b" - integrity sha512-bc5dNF8tW2R+oesYT/Au//qumyd/0HTwSRqVXcg8ADQW1MsWKFwv+IxfSIuCHckaEy4I81GpSDaRWVGQqtsELw== +vscode-chokidar@2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-2.1.7.tgz#c5b31eb87402f4779bb4170915245bdcb6f7854b" + integrity sha512-uSNEQetPjAlgIAHmcF9E6M+KCw0f842rsEnJ64aamUAV6TO7gkXNCvLSzb4MuLsPU7ZQyCa++DrLQFjvciK5dg== dependencies: - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" is-binary-path "^1.0.0" - is-glob "^2.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" path-is-absolute "^1.0.0" - readdirp "^2.0.0" - vscode-anymatch "1.3.3" + readdirp "^2.2.1" + upath "^1.1.1" + vscode-anymatch "3.0.3" optionalDependencies: - vscode-fsevents "0.3.10" + vscode-fsevents "1.2.12" -vscode-fsevents@0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-0.3.10.tgz#65a586c3c6df3080bea20482146963a0f3a2c58d" - integrity sha512-iNlCKNgEB9A2JZkLf4h4sJlOS1u0lbe4QjM0Dr0PHaTmsttkJEfOaQeci2Ja1wUA7hUUAF6sNbei/Qp2DacFLw== +vscode-fsevents@1.2.12: + version "1.2.12" + resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-1.2.12.tgz#01a71a01f90ee95ca822c34427aba437a17c03a7" + integrity sha512-bH/jRdDpSesGpqiVLjp6gHLSKUOh7oNvppzZ17JIrdbRYCcDmV7dIWR5gQc27DFy0RD9JDT+t+ixMid94MkM1A== dependencies: - nan "^2.10.0" - -vscode-nsfw@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.1.1.tgz#7c3febe153677c5850b197a0b64a197cd11e95c7" - integrity sha512-Wg3vzN1U3T6P1uE13LdVVRIhdy7XWnWkwmAXhkLsIkH2QY0E/pvNDRLrwAMMW6GC1Fvvbxm3hzdIrCmr7Hq3FA== - dependencies: - fs-extra "^7.0.0" - lodash.isinteger "^4.0.4" - lodash.isundefined "^3.0.1" - nan "^2.10.0" + nan "^2.14.0" vscode-proxy-agent@0.4.0: version "0.4.0" @@ -1074,10 +1123,17 @@ vscode-proxy-agent@0.4.0: https-proxy-agent "2.2.1" socks-proxy-agent "4.0.1" -vscode-ripgrep@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.5.tgz#2093c8f36d52bd2dab9eb45b003dd02533c5499c" - integrity sha512-n5XBm9od5hahpljw9T8wbkuMnAY7LlAG1OyEEtcCZEX9aCHFuBKSP0IcvciGRTbtWRovNuT83A2iRjt6PL3bLg== +vscode-ripgrep@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.3.1.tgz#51fb93debcd0c18a8b90dbc37f84f94333d0c486" + integrity sha512-4WLB/n4ZeWNi5AEzPTkfYrqbKtXlv0SlgmxbRVdulwZzGx/lfWeWPu9Shy32orM27IofQAQDuirbRBOYNJVzBA== + +vscode-textmate@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.2.2.tgz#0b4dabc69a6fba79a065cb6b615f66eac07c8f4c" + integrity sha512-1U4ih0E/KP1zNK/EbpUqyYtI7PY+Ccd2nDGTtiMR/UalLFnmaYkwoWhN1oI7B91ptBN8NdVwWuvyUnvJAulCUw== + dependencies: + oniguruma "^7.2.0" vscode-windows-ca-certs@0.1.0: version "0.1.0" @@ -1086,35 +1142,35 @@ vscode-windows-ca-certs@0.1.0: dependencies: node-addon-api "1.6.2" -which-pm-runs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" - integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== +vscode-windows-registry@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.1.tgz#bc9f765563eb6dc1c9ad9a41f9eaacc84dfadc7c" + integrity sha512-q0aKXi9Py1OBdmXIJJFeJBzpPJMMUxMJNBU9FysWIXEwJyMQGEVevKzM2J3Qz/cHSc5LVqibmoUWzZ7g+97qRg== dependencies: - string-width "^1.0.2 || 2" + nan "^2.12.1" -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +xterm-addon-search@0.2.0-beta2: + version "0.2.0-beta2" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta2.tgz#c3173f0a6f207ee9f1848849174ee5d6b6ce8262" + integrity sha512-XEcwi2TeFGk2MuIFjiI/OpVXSNO5dGQBvHH3o+9KzqG3ooVqhhDqzwxs092QGNcNCGh8hGn/PWZiczaBBnKm/g== -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= +xterm-addon-web-links@0.1.0-beta10: + version "0.1.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" + integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== -yauzl@^2.9.1: - version "2.9.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f" - integrity sha1-qBmB6nCleUYTOIPwKcWCGok1mn8= +xterm@3.15.0-beta71: + version "3.15.0-beta71" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta71.tgz#2728c9800ca3b08423e835e9504bd1f4b5de6253" + integrity sha512-8M/cLaxZ+iDopRxLPdPfKuDGaNNyYTdCeytdxjMSH0N7dZzbx6fbaEygQdCrV5pO9cGnT92MefSjVPGRXRiBLA== + +yauzl@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= dependencies: buffer-crc32 "~0.2.3" - fd-slicer "~1.0.1" + fd-slicer "~1.1.0" yazl@^2.4.3: version "2.4.3" diff --git a/resources/completions/bash/code b/resources/completions/bash/code index e377c5d24e..f40963db05 100644 --- a/resources/completions/bash/code +++ b/resources/completions/bash/code @@ -50,7 +50,7 @@ _code() --uninstall-extension --enable-proposed-api --verbose --log -s --status -p --performance --prof-startup --disable-extensions --disable-extension --inspect-extensions - --inspect-brk-extensions --disable-gpu --upload-logs + --inspect-brk-extensions --disable-gpu --max-memory=' -- "$cur") ) [[ $COMPREPLY == *= ]] && compopt -o nospace return diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code index 9579cffb2f..054ab351e9 100644 --- a/resources/completions/zsh/_code +++ b/resources/completions/zsh/_code @@ -14,6 +14,7 @@ arguments=( '--user-data-dir[specify the directory that user data is kept in]:directory:_directories' '(- *)'{-v,--version}'[print version]' '(- *)'{-h,--help}'[print usage]' + '(- *)'{--telemetry}'[Shows all telemetry events which VS code collects.]' '--extensions-dir[set the root path for extensions]:root path:_directories' '--list-extensions[list the installed extensions]' '--show-versions[show versions of installed extensions, when using --list-extension]' @@ -30,7 +31,6 @@ arguments=( '--inspect-extensions[allow debugging and profiling of extensions]' '--inspect-brk-extensions[allow debugging and profiling of extensions with the extension host being paused after start]' '--disable-gpu[disable GPU hardware acceleration]' - '--upload-logs[upload logs from current session to a secure endpoint]:confirm:(iConfirmLogsUpload)' '--max-memory=[max memory size for a window (in Mbytes)]:size (Mbytes)' '*:file or directory:_files' ) diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index d0b64c4fa6..34f127e1ae 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -63,83 +63,5 @@ "libxcb.so.1()(64bit)", "libxkbfile.so.1()(64bit)", "libsecret-1.so.0()(64bit)" - ], - "i386": [ - "ld-linux.so.2", - "ld-linux.so.2(GLIBC_2.1)", - "libX11-xcb.so.1", - "libX11.so.6", - "libXcomposite.so.1", - "libXcursor.so.1", - "libXdamage.so.1", - "libXext.so.6", - "libXfixes.so.3", - "libXi.so.6", - "libXrandr.so.2", - "libXrender.so.1", - "libXss.so.1", - "libXtst.so.6", - "libasound.so.2", - "libatk-1.0.so.0", - "libc.so.6", - "libc.so.6(GLIBC_2.0)", - "libc.so.6(GLIBC_2.1)", - "libc.so.6(GLIBC_2.1.3)", - "libc.so.6(GLIBC_2.11)", - "libc.so.6(GLIBC_2.2)", - "libc.so.6(GLIBC_2.2.3)", - "libc.so.6(GLIBC_2.3)", - "libc.so.6(GLIBC_2.3.2)", - "libc.so.6(GLIBC_2.3.4)", - "libc.so.6(GLIBC_2.4)", - "libc.so.6(GLIBC_2.6)", - "libc.so.6(GLIBC_2.7)", - "libcairo.so.2", - "libcups.so.2", - "libdbus-1.so.3", - "libdl.so.2", - "libdl.so.2(GLIBC_2.0)", - "libdl.so.2(GLIBC_2.1)", - "libexpat.so.1", - "libfontconfig.so.1", - "libfreetype.so.6", - "libgcc_s.so.1", - "libgcc_s.so.1(GCC_4.0.0)", - "libgcc_s.so.1(GLIBC_2.0)", - "libgdk-x11-2.0.so.0", - "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", - "libm.so.6", - "libm.so.6(GLIBC_2.0)", - "libm.so.6(GLIBC_2.1)", - "libnspr4.so", - "libnss3.so", - "libnssutil3.so", - "libpango-1.0.so.0", - "libpangocairo-1.0.so.0", - "libpthread.so.0", - "libpthread.so.0(GLIBC_2.0)", - "libpthread.so.0(GLIBC_2.1)", - "libpthread.so.0(GLIBC_2.2)", - "libpthread.so.0(GLIBC_2.2.3)", - "libpthread.so.0(GLIBC_2.3.2)", - "libpthread.so.0(GLIBC_2.3.3)", - "librt.so.1", - "librt.so.1(GLIBC_2.2)", - "libsmime3.so", - "libstdc++.so.6", - "libstdc++.so.6(GLIBCXX_3.4)", - "libstdc++.so.6(GLIBCXX_3.4.10)", - "libstdc++.so.6(GLIBCXX_3.4.11)", - "libstdc++.so.6(GLIBCXX_3.4.14)", - "libstdc++.so.6(GLIBCXX_3.4.15)", - "libstdc++.so.6(GLIBCXX_3.4.9)", - "libxcb.so.1", - "libxkbfile.so.1", - "libsecret-1.so.0" ] } \ No newline at end of file diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index f11f3b7591..d8616e4ae7 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -51,6 +51,7 @@ apps: @@NAME@@: command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ desktop: usr/share/applications/@@NAME@@.desktop + common-id: @@NAME@@.desktop environment: DISABLE_WAYLAND: 1 GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 0ff31d69dd..d4d3f2220c 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -6,12 +6,23 @@ COMMIT="@@COMMIT@@" APP_NAME="@@APPNAME@@" QUALITY="@@QUALITY@@" NAME="@@NAME@@" +DATAFOLDER="@@DATAFOLDER@@" VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" ELECTRON="$VSCODE_PATH/$NAME.exe" if grep -qi Microsoft /proc/version; then # in a wsl shell - WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-[Mm]icrosoft/\1/') - if [ $WSL_BUILD -ge 17063 ] 2> /dev/null; then + if ! [ -z "$WSL_DISTRO_NAME" ]; then + # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 + WSL_BUILD=18362 + else + WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-Microsoft/\1/') + if [ -z "$WSL_BUILD" ]; then + WSL_BUILD=0 + fi + fi + + if [ $WSL_BUILD -ge 17063 ]; then + # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 # WSLPATH is available since WSL build 17046 # WSLENV is available since WSL build 17063 export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV @@ -26,7 +37,7 @@ if grep -qi Microsoft /proc/version; then # replace \r\n with \n in WSL_EXT_WLOC WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh WIN_CODE_CMD=$(wslpath -w "$VSCODE_PATH/bin/$APP_NAME.cmd") - "$WSL_CODE" $COMMIT $QUALITY "$WIN_CODE_CMD" "$APP_NAME" "$@" + "$WSL_CODE" "$COMMIT" "$QUALITY" "$WIN_CODE_CMD" "$APP_NAME" "$DATAFOLDER" "$@" exit $? else CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") diff --git a/scripts/code-web.js b/scripts/code-web.js deleted file mode 100644 index 7721681745..0000000000 --- a/scripts/code-web.js +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const opn = require('opn'); -const cp = require('child_process'); -const path = require('path'); - -const proc = cp.execFile(path.join(__dirname, process.platform === 'win32' ? 'remoteExtensionAgent.bat' : 'remoteExtensionAgent.sh')); - -let launched = false; -proc.stdout.on("data", data => { - if (!launched && data.toString().indexOf('Extension host agent listening on 8000')) { - launched = true; - - setTimeout(() => { - const url = 'http://127.0.0.1:8000'; - console.log(`Open ${url} in your browser`); - - opn(url); - }, 100); - } -}); \ No newline at end of file diff --git a/scripts/code.sh b/scripts/code.sh index 1f7cda590a..b0b112bc23 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -58,7 +58,7 @@ function code() { function code-wsl() { # in a wsl shell - local WIN_CODE_CLI_CMD=$(wslpath -w "$ROOT/scripts/code-cli.bat") + local WIN_CODE_CLI_CMD=$(wslpath -w "$ROOT/scripts/code-cli.bat" 2>/dev/null) if ! [ -z "$WIN_CODE_CLI_CMD" ]; then local WSL_EXT_ID="ms-vscode-remote.remote-wsl" local WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CLI_CMD" --locate-extension $WSL_EXT_ID) diff --git a/scripts/env.ps1 b/scripts/env.ps1 deleted file mode 100644 index 72e094d0a6..0000000000 --- a/scripts/env.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -$env:npm_config_disturl="https://atom.io/download/electron" -$env:npm_config_target=(node -p "require('./build/lib/electron').getElectronVersion();") -$env:npm_config_runtime="electron" diff --git a/scripts/env.sh b/scripts/env.sh deleted file mode 100755 index 8f641759b2..0000000000 --- a/scripts/env.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -export npm_config_disturl=https://atom.io/download/electron -export npm_config_target=$(node -p "require('./build/lib/electron').getElectronVersion();") -export npm_config_runtime=electron -export npm_config_cache="$HOME/.npm-electron" -mkdir -p "$npm_config_cache" diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index ff579e873d..7d89aa1ab8 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -11,18 +11,18 @@ set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5% :: if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in the extension host -call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% -call .\scripts\code.bat %~dp0\..\extensions\markdown-language-features\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% -call .\scripts\code.bat %~dp0\..\extensions\azurecore\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\azurecore --extensionTestsPath=%~dp0\..\extensions\azurecore\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% +call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% +call .\scripts\code.bat %~dp0\..\extensions\markdown-language-features\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\markdown-language-features --extensionTestsPath=%~dp0\..\extensions\markdown-language-features\out\test --disableExtensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% +call .\scripts\code.bat %~dp0\..\extensions\azurecore\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\azurecore --extensionTestsPath=%~dp0\..\extensions\azurecore\out\test --disableExtensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -:: call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . +:: call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-extensions --disable-inspect --user-data-dir=%VSCODEUSERDATADIR% . :: if %errorlevel% neq 0 exit /b %errorlevel% :: Integration & performance tests in AMD @@ -32,6 +32,10 @@ call .\scripts\test.bat --runGlob **\*.integrationTest.js %* call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js if %errorlevel% neq 0 exit /b %errorlevel% +if exist ".\resources\server\test\test-remote-integration.bat" ( + call .\resources\server\test\test-remote-integration.bat +) + rmdir /s /q %VSCODEUSERDATADIR% popd diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index cab90665c4..b2e3f755c1 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -17,16 +17,20 @@ cd $ROOT # Tests in the extension host # TODO port over an re-enable API tests -#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -./scripts/code.sh $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started +#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +./scripts/code.sh $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disableExtensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR mkdir -p $ROOT/extensions/emmet/test-fixtures -./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started +./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR rm -rf $ROOT/extensions/emmet/test-fixtures +if [ -f ./resources/server/test/test-remote-integration.sh ]; then + ./resources/server/test/test-remote-integration.sh +fi + # Tests in commonJS cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js diff --git a/src/buildfile.js b/src/buildfile.js index 09b62e7f38..143b506045 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -11,8 +11,16 @@ exports.base = [{ dest: 'vs/base/worker/workerMain.js' }]; +exports.serviceWorker = [{ + name: 'vs/workbench/contrib/resources/browser/resourceServiceWorker', + // include: ['vs/editor/common/services/editorSimpleWorker'], + prepend: ['vs/loader.js'], + append: ['vs/workbench/contrib/resources/browser/resourceServiceWorkerMain'], + dest: 'vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.js' +}]; + exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.main']); -exports.workbenchWeb = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.web.main']); +exports.workbenchWeb = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.web.api']); exports.code = require('./vs/code/buildfile').collectModules(); diff --git a/src/main.js b/src/main.js index 52ece7e3ff..4f51152ae1 100644 --- a/src/main.js +++ b/src/main.js @@ -51,9 +51,11 @@ userDefinedLocale.then(locale => { } }); -// Configure command line switches +// Cached data const nodeCachedDataDir = getNodeCachedDir(); -configureCommandlineSwitches(args, nodeCachedDataDir); + +// Configure command line switches +configureCommandlineSwitches(args); // Load our code once ready app.once('ready', function () { @@ -134,31 +136,25 @@ function onReady() { * @typedef {import('minimist').ParsedArgs} ParsedArgs * * @param {ParsedArgs} cliArgs - * @param {{ jsFlags: () => string }} nodeCachedDataDir */ -function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) { +function configureCommandlineSwitches(cliArgs) { // Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) app.commandLine.appendSwitch('disable-color-correct-rendering'); // Support JS Flags - const jsFlags = resolveJSFlags(cliArgs, nodeCachedDataDir.jsFlags()); + const jsFlags = getJSFlags(cliArgs); if (jsFlags) { app.commandLine.appendSwitch('--js-flags', jsFlags); } - - // Disable smooth scrolling for Webviews - if (cliArgs['disable-smooth-scrolling']) { - app.commandLine.appendSwitch('disable-smooth-scrolling'); - } } /** * @param {ParsedArgs} cliArgs - * @param {string[]} jsFlags * @returns {string} */ -function resolveJSFlags(cliArgs, ...jsFlags) { +function getJSFlags(cliArgs) { + const jsFlags = []; // Add any existing JS flags we already got from the command line if (cliArgs['js-flags']) { @@ -205,7 +201,7 @@ function parseCLIArgs() { function setCurrentWorkingDirectory() { try { if (process.platform === 'win32') { - process.env['VSCODE_CWD'] = process.cwd(); // remember as environment letiable + process.env['VSCODE_CWD'] = process.cwd(); // remember as environment variable process.chdir(path.dirname(app.getPath('exe'))); // always set application folder as cwd } else if (process.env['VSCODE_CWD']) { process.chdir(process.env['VSCODE_CWD']); @@ -253,7 +249,7 @@ function registerListeners() { } /** - * @returns {{ jsFlags: () => string; ensureExists: () => Promise, _compute: () => string; }} + * @returns {{ ensureExists: () => Promise }} */ function getNodeCachedDir() { return new class { @@ -262,11 +258,6 @@ function getNodeCachedDir() { this.value = this._compute(); } - jsFlags() { - // return this.value ? '--nolazy' : undefined; - return undefined; - } - ensureExists() { return bootstrap.mkdirp(this.value).then(() => this.value, () => { /*ignore*/ }); } @@ -347,4 +338,4 @@ function getUserDefinedLocale() { return undefined; }); } -//#endregion \ No newline at end of file +//#endregion diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 7c56609d7b..0e4fdb120c 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -4559,8 +4559,8 @@ declare module 'azdata' { * } * ``` * @export - * @param {NotebookProvider} provider - * @returns {vscode.Disposable} + * @param notebook provider + * @returns disposable */ export function registerNotebookProvider(provider: NotebookProvider): vscode.Disposable; diff --git a/src/sql/base/browser/ui/button/button.ts b/src/sql/base/browser/ui/button/button.ts index 16d89c6569..a2e32c0bfa 100644 --- a/src/sql/base/browser/ui/button/button.ts +++ b/src/sql/base/browser/ui/button/button.ts @@ -18,7 +18,7 @@ export class Button extends vsButton { super(container, options); this._register(DOM.addDisposableListener(this.element, DOM.EventType.FOCUS, () => { - this.element.style.outlineColor = this.buttonFocusOutline ? this.buttonFocusOutline.toString() : null; + this.element.style.outlineColor = this.buttonFocusOutline ? this.buttonFocusOutline.toString() : ''; this.element.style.outlineWidth = '1px'; })); diff --git a/src/sql/base/browser/ui/dropdownList/dropdownList.ts b/src/sql/base/browser/ui/dropdownList/dropdownList.ts index 520053afa1..534e3b5842 100644 --- a/src/sql/base/browser/ui/dropdownList/dropdownList.ts +++ b/src/sql/base/browser/ui/dropdownList/dropdownList.ts @@ -41,11 +41,11 @@ export class DropdownList extends Dropdown { if (action) { this.button = new Button(_contentContainer); this.button.label = action.label; - this.toDispose.push(DOM.addDisposableListener(this.button.element, DOM.EventType.CLICK, () => { + this._register(DOM.addDisposableListener(this.button.element, DOM.EventType.CLICK, () => { action.run(); this.hide(); })); - this.toDispose.push(DOM.addDisposableListener(this.button.element, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + this._register(DOM.addDisposableListener(this.button.element, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter)) { e.stopPropagation(); @@ -75,7 +75,7 @@ export class DropdownList extends Dropdown { } })); - this.toDispose.push(this._list.onSelectionChange(() => { + this._register(this._list.onSelectionChange(() => { // focus on the dropdown label then hide the dropdown list this.element.focus(); this.hide(); diff --git a/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts b/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts index 66b506609e..3c4d1af1bf 100644 --- a/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts +++ b/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts @@ -241,7 +241,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { } this.onRemoveItems(new ArrayIterator([item.view.id!])); }); - const disposable = combinedDisposable([onChangeDisposable, containerDisposable]); + const disposable = combinedDisposable(onChangeDisposable, containerDisposable); const onAdd = view.onAdd ? () => view.onAdd!() : () => { }; const onRemove = view.onRemove ? () => view.onRemove!() : () => { }; @@ -292,7 +292,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); - const disposable = combinedDisposable([onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash]); + const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash); const sashItem: ISashItem = { sash, disposable }; this.sashItems.splice(currentIndex - 1, 0, sashItem); @@ -344,7 +344,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { } this.onRemoveItems(new ArrayIterator([item.view.id!])); }); - const disposable = combinedDisposable([onChangeDisposable, containerDisposable]); + const disposable = combinedDisposable(onChangeDisposable, containerDisposable); const onAdd = view.onAdd ? () => view.onAdd!() : () => { }; const onRemove = view.onRemove ? () => view.onRemove!() : () => { }; @@ -395,7 +395,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); - const disposable = combinedDisposable([onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash]); + const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash); const sashItem: ISashItem = { sash, disposable }; this.sashItems.splice(index - 1, 0, sashItem); @@ -527,10 +527,10 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { const index = firstIndex(this.sashItems, item => item.sash === sash); // This way, we can press Alt while we resize a sash, macOS style! - const disposable = combinedDisposable([ + const disposable = combinedDisposable( domEvent(document.body, 'keydown')(e => resetSashDragState(this.sashDragState.current, e.altKey)), domEvent(document.body, 'keyup')(() => resetSashDragState(this.sashDragState.current, false)) - ]); + ); const resetSashDragState = (start: number, alt: boolean) => { const sizes = this.viewItems.map(i => i.size); diff --git a/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts b/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts index ae6035f83d..36af00cdc0 100644 --- a/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts @@ -14,7 +14,7 @@ const defaultOptions: IAutoColumnSizeOptions = { autoSizeOnRender: false }; -export class AutoColumnSize implements Slick.Plugin { +export class AutoColumnSize implements Slick.Plugin { private _grid: Slick.Grid; private _$container: JQuery; private _context: CanvasRenderingContext2D; diff --git a/src/sql/base/browser/ui/taskbar/actionbar.ts b/src/sql/base/browser/ui/taskbar/actionbar.ts index 0b48545e73..86ae2de834 100644 --- a/src/sql/base/browser/ui/taskbar/actionbar.ts +++ b/src/sql/base/browser/ui/taskbar/actionbar.ts @@ -43,13 +43,12 @@ export class ActionBar extends ActionRunner implements IActionRunner { super(); this._options = options; this._context = options.context; - this._toDispose = []; if (this._options.actionRunner) { this._actionRunner = this._options.actionRunner; } else { this._actionRunner = new ActionRunner(); - this._toDispose.push(this._actionRunner); + this._register(this._actionRunner); } //this._toDispose.push(this.addEmitter(this._actionRunner)); @@ -365,8 +364,6 @@ export class ActionBar extends ActionRunner implements IActionRunner { lifecycle.dispose(this._items); this._items = []; - this._toDispose = lifecycle.dispose(this._toDispose); - this._domNode.remove(); super.dispose(); diff --git a/src/sql/platform/accounts/browser/accountDialog.ts b/src/sql/platform/accounts/browser/accountDialog.ts index 251738a0a7..6a41d7f37e 100644 --- a/src/sql/platform/accounts/browser/accountDialog.ts +++ b/src/sql/platform/accounts/browser/accountDialog.ts @@ -53,7 +53,7 @@ class AccountPanel extends ViewletPanel { protected renderBody(container: HTMLElement): void { this.accountList = new List(container, new AccountListDelegate(AccountDialog.ACCOUNTLIST_HEIGHT), [this.instantiationService.createInstance(AccountListRenderer)]); - this.disposables.push(attachListStyler(this.accountList, this.themeService)); + this._register(attachListStyler(this.accountList, this.themeService)); } protected layoutBody(size: number): void { diff --git a/src/sql/platform/accounts/browser/accountListStatusbarItem.ts b/src/sql/platform/accounts/browser/accountListStatusbarItem.ts deleted file mode 100644 index 34f49544a0..0000000000 --- a/src/sql/platform/accounts/browser/accountListStatusbarItem.ts +++ /dev/null @@ -1,70 +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 'vs/css!./media/accountListStatusbarItem'; -import { Action, IAction } from 'vs/base/common/actions'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { $, append } from 'vs/base/browser/dom'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { localize } from 'vs/nls'; -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { Themable, STATUS_BAR_FOREGROUND } from 'vs/workbench/common/theme'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; - -import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces'; - -export class AccountListStatusbarItem extends Themable implements IStatusbarItem { - private _manageLinkedAccountAction: IAction; - private _icon: HTMLElement; - - constructor( - @IInstantiationService private _instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService - ) { - super(themeService); - } - - protected updateStyles(): void { - super.updateStyles(); - if (this._icon) { - this._icon.style.backgroundColor = this.getColor(STATUS_BAR_FOREGROUND); - } - } - - public render(container: HTMLElement): IDisposable { - // Create root element for account list - const rootElement = append(container, $('.linked-account-staus')); - const accountElement = append(rootElement, $('a.linked-account-status-selection')); - accountElement.title = ManageLinkedAccountAction.LABEL; - accountElement.onclick = () => this._onClick(); - this._icon = append(accountElement, $('.linked-account-icon')); - - this.updateStyles(); - - return this; - } - - private _onClick() { - if (!this._manageLinkedAccountAction) { - this._manageLinkedAccountAction = this._instantiationService.createInstance(ManageLinkedAccountAction, ManageLinkedAccountAction.ID, ManageLinkedAccountAction.LABEL); - } - this._manageLinkedAccountAction.run().then(null, onUnexpectedError); - } -} - -export class ManageLinkedAccountAction extends Action { - public static ID = 'sql.action.accounts.manageLinkedAccount'; - public static LABEL = localize('manageLinedAccounts', 'Manage Linked Accounts'); - - constructor(id: string, label: string, - @IAccountManagementService protected _accountManagementService: IAccountManagementService) { - super(id, label); - } - - public run(): Promise { - return new Promise(() => this._accountManagementService.openAccountListDialog()); - } -} diff --git a/src/sql/platform/accounts/browser/accountManagement.contribution.ts b/src/sql/platform/accounts/browser/accountManagement.contribution.ts index bb90aa7915..a361420c03 100644 --- a/src/sql/platform/accounts/browser/accountManagement.contribution.ts +++ b/src/sql/platform/accounts/browser/accountManagement.contribution.ts @@ -3,9 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IExtensionPointUser, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; @@ -13,19 +10,6 @@ import { join } from 'path'; import { createCSSRule } from 'vs/base/browser/dom'; import { URI } from 'vs/base/common/uri'; -import { ManageLinkedAccountAction } from 'sql/platform/accounts/browser/accountListStatusbarItem'; - -let actionRegistry = Registry.as(Extensions.WorkbenchActions); - -actionRegistry.registerWorkbenchAction( - new SyncActionDescriptor( - ManageLinkedAccountAction, - ManageLinkedAccountAction.ID, - ManageLinkedAccountAction.LABEL - ), - ManageLinkedAccountAction.LABEL -); - export interface IAccountContrib { id: string; icon?: IUserFriendlyIcon; diff --git a/src/sql/platform/clipboard/electron-browser/clipboardService.ts b/src/sql/platform/clipboard/electron-browser/clipboardService.ts index 7ecc806237..dd29805712 100644 --- a/src/sql/platform/clipboard/electron-browser/clipboardService.ts +++ b/src/sql/platform/clipboard/electron-browser/clipboardService.ts @@ -23,14 +23,14 @@ export class ClipboardService implements IClipboardService { clipboard.writeImage(image); } - writeText(text: string): void { - this._vsClipboardService.writeText(text); + writeText(text: string): Promise { + return this._vsClipboardService.writeText(text); } /** * Reads the content of the clipboard in plain text */ - readText(): string { + readText(): Promise { return this._vsClipboardService.readText(); } /** @@ -67,4 +67,8 @@ export class ClipboardService implements IClipboardService { hasResources(): boolean { return this._vsClipboardService.hasResources(); } + + readTextSync(): string | undefined { + return this._vsClipboardService.readTextSync(); + } } diff --git a/src/sql/platform/connection/common/connectionManagementService.ts b/src/sql/platform/connection/common/connectionManagementService.ts index b25c4100a5..1d52456fa4 100644 --- a/src/sql/platform/connection/common/connectionManagementService.ts +++ b/src/sql/platform/connection/common/connectionManagementService.ts @@ -43,7 +43,6 @@ import * as platform from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { Event, Emitter } from 'vs/base/common/event'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -68,7 +67,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti private _onConnectRequestSent = new Emitter(); private _onConnectionChanged = new Emitter(); private _onLanguageFlavorChanged = new Emitter(); - private _connectionGlobalStatus = new ConnectionGlobalStatus(this._statusBarService); + private _connectionGlobalStatus = new ConnectionGlobalStatus(this._notificationService); private _mementoContext: Memento; private _mementoObj: any; @@ -85,14 +84,13 @@ export class ConnectionManagementService extends Disposable implements IConnecti @IConfigurationService private _configurationService: IConfigurationService, @ICapabilitiesService private _capabilitiesService: ICapabilitiesService, @IQuickInputService private _quickInputService: IQuickInputService, - @IStatusbarService private _statusBarService: IStatusbarService, + @INotificationService private _notificationService: INotificationService, @IResourceProviderService private _resourceProviderService: IResourceProviderService, @IAngularEventingService private _angularEventing: IAngularEventingService, @IAccountManagementService private _accountManagementService: IAccountManagementService, @ILogService private _logService: ILogService, @IStorageService private _storageService: IStorageService, - @IEnvironmentService private _environmentService: IEnvironmentService, - @INotificationService private _notificationService: INotificationService + @IEnvironmentService private _environmentService: IEnvironmentService ) { super(); diff --git a/src/sql/platform/query/common/queryModelService.ts b/src/sql/platform/query/common/queryModelService.ts index c6d81a506d..686a7d8595 100644 --- a/src/sql/platform/query/common/queryModelService.ts +++ b/src/sql/platform/query/common/queryModelService.ts @@ -9,15 +9,11 @@ import QueryRunner from 'sql/platform/query/common/queryRunner'; import { DataService } from 'sql/workbench/parts/grid/services/dataService'; import { IQueryModelService, IQueryEvent } from 'sql/platform/query/common/queryModel'; import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; -import { QueryStatusbarItem } from 'sql/workbench/parts/query/browser/queryStatus'; import { SqlFlavorStatusbarItem } from 'sql/workbench/parts/query/browser/flavorStatus'; -import { RowCountStatusBarItem } from 'sql/workbench/parts/query/browser/rowCountStatus'; -import { TimeElapsedStatusBarItem } from 'sql/workbench/parts/query/browser/timeElapsedStatus'; import * as azdata from 'azdata'; import * as nls from 'vs/nls'; -import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; import * as platform from 'vs/platform/registry/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; @@ -85,32 +81,6 @@ export class QueryModelService implements IQueryModelService { this._onRunQueryComplete = new Emitter(); this._onQueryEvent = new Emitter(); this._onEditSessionReady = new Emitter(); - - // Register Statusbar items - - (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( - TimeElapsedStatusBarItem, - StatusbarAlignment.RIGHT, - 100 /* Should appear to the right of the SQL editor status */ - )); - - (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( - RowCountStatusBarItem, - StatusbarAlignment.RIGHT, - 100 /* Should appear to the right of the SQL editor status */ - )); - - (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( - QueryStatusbarItem, - StatusbarAlignment.RIGHT, - 100 /* High Priority */ - )); - (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( - SqlFlavorStatusbarItem, - StatusbarAlignment.RIGHT, - 90 /* Should appear to the right of the SQL editor status */ - )); - } // IQUERYMODEL ///////////////////////////////////////////////////////// diff --git a/src/sql/workbench/api/browser/extensionHost.contribution.common.ts b/src/sql/workbench/api/browser/extensionHost.contribution.common.ts new file mode 100644 index 0000000000..c9b3d20ef2 --- /dev/null +++ b/src/sql/workbench/api/browser/extensionHost.contribution.common.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import './mainThreadAccountManagement'; +import './mainThreadBackgroundTaskManagement'; +import './mainThreadConnectionManagement'; +import './mainThreadCredentialManagement'; +import './mainThreadDashboard'; +import './mainThreadDashboardWebview'; +import './mainThreadDataProtocol'; +import './mainThreadExtensionManagement'; +import './mainThreadModalDialog'; +import './mainThreadModelView'; +import './mainThreadModelViewDialog'; +import './mainThreadNotebook'; +import './mainThreadNotebookDocumentsAndEditors'; +import './mainThreadObjectExplorer'; +import './mainThreadQueryEditor'; +import './mainThreadResourceProvider'; +import './mainThreadSerializationProvider'; +import './mainThreadTasks'; diff --git a/src/sql/workbench/api/node/mainThreadAccountManagement.ts b/src/sql/workbench/api/browser/mainThreadAccountManagement.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadAccountManagement.ts rename to src/sql/workbench/api/browser/mainThreadAccountManagement.ts diff --git a/src/sql/workbench/api/node/mainThreadBackgroundTaskManagement.ts b/src/sql/workbench/api/browser/mainThreadBackgroundTaskManagement.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadBackgroundTaskManagement.ts rename to src/sql/workbench/api/browser/mainThreadBackgroundTaskManagement.ts diff --git a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts b/src/sql/workbench/api/browser/mainThreadConnectionManagement.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadConnectionManagement.ts rename to src/sql/workbench/api/browser/mainThreadConnectionManagement.ts diff --git a/src/sql/workbench/api/node/mainThreadCredentialManagement.ts b/src/sql/workbench/api/browser/mainThreadCredentialManagement.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadCredentialManagement.ts rename to src/sql/workbench/api/browser/mainThreadCredentialManagement.ts diff --git a/src/sql/workbench/api/electron-browser/mainThreadDashboard.ts b/src/sql/workbench/api/browser/mainThreadDashboard.ts similarity index 100% rename from src/sql/workbench/api/electron-browser/mainThreadDashboard.ts rename to src/sql/workbench/api/browser/mainThreadDashboard.ts diff --git a/src/sql/workbench/api/node/mainThreadDashboardWebview.ts b/src/sql/workbench/api/browser/mainThreadDashboardWebview.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadDashboardWebview.ts rename to src/sql/workbench/api/browser/mainThreadDashboardWebview.ts diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/browser/mainThreadDataProtocol.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadDataProtocol.ts rename to src/sql/workbench/api/browser/mainThreadDataProtocol.ts diff --git a/src/sql/workbench/api/node/mainThreadExtensionManagement.ts b/src/sql/workbench/api/browser/mainThreadExtensionManagement.ts similarity index 83% rename from src/sql/workbench/api/node/mainThreadExtensionManagement.ts rename to src/sql/workbench/api/browser/mainThreadExtensionManagement.ts index 3500d98f72..a6a99168f7 100644 --- a/src/sql/workbench/api/node/mainThreadExtensionManagement.ts +++ b/src/sql/workbench/api/browser/mainThreadExtensionManagement.ts @@ -7,7 +7,7 @@ import { SqlMainContext, MainThreadExtensionManagementShape } from 'sql/workbenc import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; @extHostNamedCustomer(SqlMainContext.MainThreadExtensionManagement) @@ -27,6 +27,6 @@ export class MainThreadExtensionManagement implements MainThreadExtensionManagem } public $install(vsixPath: string): Thenable { - return this._extensionService.install(URI.file(vsixPath)).then((value: IExtensionIdentifier) => { return undefined; }, (reason: any) => { return reason ? reason.toString() : undefined; }); + return this._extensionService.install(URI.file(vsixPath)).then((value: ILocalExtension) => { return undefined; }, (reason: any) => { return reason ? reason.toString() : undefined; }); } } diff --git a/src/sql/workbench/api/electron-browser/mainThreadModalDialog.ts b/src/sql/workbench/api/browser/mainThreadModalDialog.ts similarity index 100% rename from src/sql/workbench/api/electron-browser/mainThreadModalDialog.ts rename to src/sql/workbench/api/browser/mainThreadModalDialog.ts diff --git a/src/sql/workbench/api/node/mainThreadModelView.ts b/src/sql/workbench/api/browser/mainThreadModelView.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadModelView.ts rename to src/sql/workbench/api/browser/mainThreadModelView.ts diff --git a/src/sql/workbench/api/node/mainThreadModelViewDialog.ts b/src/sql/workbench/api/browser/mainThreadModelViewDialog.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadModelViewDialog.ts rename to src/sql/workbench/api/browser/mainThreadModelViewDialog.ts diff --git a/src/sql/workbench/api/node/mainThreadNotebook.ts b/src/sql/workbench/api/browser/mainThreadNotebook.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadNotebook.ts rename to src/sql/workbench/api/browser/mainThreadNotebook.ts diff --git a/src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors.ts rename to src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts diff --git a/src/sql/workbench/api/node/mainThreadObjectExplorer.ts b/src/sql/workbench/api/browser/mainThreadObjectExplorer.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadObjectExplorer.ts rename to src/sql/workbench/api/browser/mainThreadObjectExplorer.ts diff --git a/src/sql/workbench/api/node/mainThreadQueryEditor.ts b/src/sql/workbench/api/browser/mainThreadQueryEditor.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadQueryEditor.ts rename to src/sql/workbench/api/browser/mainThreadQueryEditor.ts diff --git a/src/sql/workbench/api/node/mainThreadResourceProvider.ts b/src/sql/workbench/api/browser/mainThreadResourceProvider.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadResourceProvider.ts rename to src/sql/workbench/api/browser/mainThreadResourceProvider.ts diff --git a/src/sql/workbench/api/node/mainThreadSerializationProvider.ts b/src/sql/workbench/api/browser/mainThreadSerializationProvider.ts similarity index 100% rename from src/sql/workbench/api/node/mainThreadSerializationProvider.ts rename to src/sql/workbench/api/browser/mainThreadSerializationProvider.ts diff --git a/src/sql/workbench/api/electron-browser/mainThreadTasks.ts b/src/sql/workbench/api/browser/mainThreadTasks.ts similarity index 100% rename from src/sql/workbench/api/electron-browser/mainThreadTasks.ts rename to src/sql/workbench/api/browser/mainThreadTasks.ts diff --git a/src/vs/code/code.main.ts b/src/sql/workbench/api/electron-browser/extensionHost.contribution.ts similarity index 85% rename from src/vs/code/code.main.ts rename to src/sql/workbench/api/electron-browser/extensionHost.contribution.ts index 7cbce7528e..4c24e9f734 100644 --- a/src/vs/code/code.main.ts +++ b/src/sql/workbench/api/electron-browser/extensionHost.contribution.ts @@ -3,4 +3,4 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/platform/update/node/update.config.contribution'; \ No newline at end of file +import '../browser/extensionHost.contribution.common'; diff --git a/src/sql/workbench/api/node/extHostModelViewTree.ts b/src/sql/workbench/api/node/extHostModelViewTree.ts index c72576d755..a25a3d7aa2 100644 --- a/src/sql/workbench/api/node/extHostModelViewTree.ts +++ b/src/sql/workbench/api/node/extHostModelViewTree.ts @@ -160,8 +160,8 @@ export class ExtHostTreeView extends vsTreeExt.ExtHostTreeView { }); } - protected createTreeItem(element: T, extensionTreeItem: azdata.TreeComponentItem, parent?: vsTreeExt.TreeNode): ITreeComponentItem { - let item = super.createTreeItem(element, extensionTreeItem, parent); + protected createTreeNode(element: T, extensionTreeItem: azdata.TreeComponentItem, parent?: vsTreeExt.TreeNode): vsTreeExt.TreeNode { + let item = super.createTreeNode(element, extensionTreeItem, parent); item = Object.assign({}, item, { checked: extensionTreeItem.checked, enabled: extensionTreeItem.enabled }); return item; } diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 21a1273833..ac2d50e8a8 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -40,8 +40,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; -import { AzureResource } from 'sql/platform/accounts/common/interfaces'; +import { IURITransformer } from 'vs/base/common/uriIpc'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; export interface ISqlExtensionApiFactory { @@ -61,10 +60,9 @@ export function createApiFactory( extensionService: ExtHostExtensionService, logService: ExtHostLogService, extHostStorage: ExtHostStorage, - schemeTransformer: ISchemeTransformer | null, - outputChannelName: string + uriTransformer: IURITransformer | null ): ISqlExtensionApiFactory { - let vsCodeFactory = extHostApi.createApiFactory(initData, rpcProtocol, extHostWorkspace, extHostConfiguration, extensionService, logService, extHostStorage, schemeTransformer, outputChannelName); + let vsCodeFactory = extHostApi.createApiFactory(initData, rpcProtocol, extHostWorkspace, extHostConfiguration, extensionService, logService, extHostStorage, uriTransformer); // Addressable instances const extHostAccountManagement = rpcProtocol.set(SqlExtHostContext.ExtHostAccountManagement, new ExtHostAccountManagement(rpcProtocol)); diff --git a/src/sql/workbench/api/node/sqlExtHost.contribution.ts b/src/sql/workbench/api/node/sqlExtHost.contribution.ts deleted file mode 100644 index c3795ef7ba..0000000000 --- a/src/sql/workbench/api/node/sqlExtHost.contribution.ts +++ /dev/null @@ -1,46 +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 { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; - -// --- SQL contributions -import 'sql/workbench/api/node/mainThreadConnectionManagement'; -import 'sql/workbench/api/node/mainThreadCredentialManagement'; -import 'sql/workbench/api/node/mainThreadDataProtocol'; -import 'sql/workbench/api/node/mainThreadObjectExplorer'; -import 'sql/workbench/api/node/mainThreadBackgroundTaskManagement'; -import 'sql/workbench/api/node/mainThreadSerializationProvider'; -import 'sql/workbench/api/node/mainThreadResourceProvider'; -import 'sql/workbench/api/electron-browser/mainThreadTasks'; -import 'sql/workbench/api/electron-browser/mainThreadDashboard'; -import 'sql/workbench/api/node/mainThreadDashboardWebview'; -import 'sql/workbench/api/node/mainThreadQueryEditor'; -import 'sql/workbench/api/node/mainThreadModelView'; -import 'sql/workbench/api/node/mainThreadModelViewDialog'; -import 'sql/workbench/api/node/mainThreadNotebook'; -import 'sql/workbench/api/node/mainThreadNotebookDocumentsAndEditors'; -import 'sql/workbench/api/node/mainThreadAccountManagement'; -import 'sql/workbench/api/node/mainThreadExtensionManagement'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; - -export class SqlExtHostContribution implements IWorkbenchContribution { - - constructor( - @IInstantiationService private instantiationService: IInstantiationService - ) { - } - - public getId(): string { - return 'sql.api.sqlExtHost'; - } -} - -// Register File Tracker -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( - SqlExtHostContribution, - LifecyclePhase.Restored -); diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index d2fbe1af35..6566bca961 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IAction, ActionRunner, Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -14,7 +14,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views'; import { FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProgressService2 } from 'vs/platform/progress/common/progress'; +import { IProgressService } from 'vs/platform/progress/common/progress'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -44,7 +44,7 @@ import { dirname } from 'vs/base/common/resources'; import { ITreeItem, ITreeView } from 'sql/workbench/common/views'; import { IOEShimService } from 'sql/workbench/parts/objectExplorer/common/objectExplorerViewTreeShim'; import { NodeContextKey } from 'sql/workbench/parts/dataExplorer/common/nodeContext'; -import { fillInActionBarActions, fillInContextMenuActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { ContextAwareMenuEntryActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; class TitleMenus implements IDisposable { @@ -74,7 +74,7 @@ class TitleMenus implements IDisposable { const updateActions = () => { this.titleActions = []; this.titleSecondaryActions = []; - fillInActionBarActions(titleMenu, undefined, { primary: this.titleActions, secondary: this.titleSecondaryActions }); + createAndFillInActionBarActions(titleMenu, undefined, { primary: this.titleActions, secondary: this.titleSecondaryActions }); this._onDidChangeTitle.fire(); }; @@ -159,7 +159,7 @@ export class CustomTreeView extends Disposable implements ITreeView { @IInstantiationService private instantiationService: IInstantiationService, @ICommandService private commandService: ICommandService, @IConfigurationService private configurationService: IConfigurationService, - @IProgressService2 private progressService: IProgressService2 + @IProgressService private progressService: IProgressService ) { super(); this.root = new Root(); @@ -515,7 +515,7 @@ class TreeDataSource implements IDataSource { private treeView: ITreeView, private container: ViewContainer, private id: string, - @IProgressService2 private progressService: IProgressService2, + @IProgressService private progressService: IProgressService, @IOEShimService private objectExplorerService: IOEShimService ) { } @@ -853,7 +853,7 @@ class TreeMenus extends Disposable implements IDisposable { const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary }; - fillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); + createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, this.contextMenuService, g => /^inline/.test(g)); menu.dispose(); contextKeyService.dispose(); @@ -869,7 +869,7 @@ class MarkdownRenderer { ) { } - private getOptions(disposeables: IDisposable[]): RenderOptions { + private getOptions(disposeables: DisposableStore): RenderOptions { return { actionHandler: { callback: (content) => { @@ -889,7 +889,7 @@ class MarkdownRenderer { } render(markdown: IMarkdownString): IMarkdownRenderResult { - let disposeables: IDisposable[] = []; + let disposeables = new DisposableStore(); const element: HTMLElement = markdown ? renderMarkdown(markdown, this.getOptions(disposeables)) : document.createElement('span'); return { element, diff --git a/src/sql/workbench/electron-browser/modelComponents/diffeditor.component.ts b/src/sql/workbench/electron-browser/modelComponents/diffeditor.component.ts index 898dcacb30..aee6198a5f 100644 --- a/src/sql/workbench/electron-browser/modelComponents/diffeditor.component.ts +++ b/src/sql/workbench/electron-browser/modelComponents/diffeditor.component.ts @@ -18,7 +18,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ComponentBase } from 'sql/workbench/electron-browser/modelComponents/componentBase'; import { IComponent, IComponentDescriptor, IModelStore } from 'sql/workbench/electron-browser/modelComponents/interfaces'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { SimpleProgressService } from 'vs/editor/standalone/browser/simpleServices'; +import { SimpleEditorProgressService } from 'vs/editor/standalone/browser/simpleServices'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -70,7 +70,7 @@ export default class DiffEditorComponent extends ComponentBase implements ICompo } private _createEditor(): void { - this._instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleProgressService()])); + this._instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleEditorProgressService()])); this._editor = this._instantiationService.createInstance(TextDiffEditor); this._editor.reverseColoring(); this._editor.create(this._el.nativeElement); @@ -238,4 +238,4 @@ export default class DiffEditorComponent extends ComponentBase implements ICompo public set title(newValue: string) { this.setPropertyFromUI((properties, title) => { properties.title = title; }, newValue); } -} \ No newline at end of file +} diff --git a/src/sql/workbench/electron-browser/modelComponents/editor.component.ts b/src/sql/workbench/electron-browser/modelComponents/editor.component.ts index a901eb9ae8..914d53bc89 100644 --- a/src/sql/workbench/electron-browser/modelComponents/editor.component.ts +++ b/src/sql/workbench/electron-browser/modelComponents/editor.component.ts @@ -21,7 +21,7 @@ import { ComponentBase } from 'sql/workbench/electron-browser/modelComponents/co import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/workbench/electron-browser/modelComponents/interfaces'; import { QueryTextEditor } from 'sql/workbench/electron-browser/modelComponents/queryTextEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { SimpleProgressService } from 'vs/editor/standalone/browser/simpleServices'; +import { SimpleEditorProgressService } from 'vs/editor/standalone/browser/simpleServices'; import { IProgressService } from 'vs/platform/progress/common/progress'; @Component({ @@ -59,7 +59,7 @@ export default class EditorComponent extends ComponentBase implements IComponent } private _createEditor(): void { - let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleProgressService()])); + let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleEditorProgressService()])); this._editor = instantiationService.createInstance(QueryTextEditor); this._editor.create(this._el.nativeElement); this._editor.setVisible(true); diff --git a/src/sql/workbench/parts/accounts/browser/accounts.contribution.ts b/src/sql/workbench/parts/accounts/browser/accounts.contribution.ts new file mode 100644 index 0000000000..75a1484358 --- /dev/null +++ b/src/sql/workbench/parts/accounts/browser/accounts.contribution.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Registry } from 'vs/platform/registry/common/platform'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IStatusbarService, StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar'; +import { localize } from 'vs/nls'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces'; + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); + +CommandsRegistry.registerCommand('workbench.actions.modal.linkedAccount', accessor => { + const accountManagementService = accessor.get(IAccountManagementService); + accountManagementService.openAccountListDialog(); +}); + +class AccountsStatusBarContributions extends Disposable implements IWorkbenchContribution { + + constructor( + @IStatusbarService private readonly statusbarService: IStatusbarService + ) { + super(); + this._register( + this.statusbarService.addEntry({ + command: 'workbench.actions.modal.linkedAccount', + text: '$(person-filled)' + }, + 'status.accountList', + localize('status.problems', "Problems"), + StatusbarAlignment.LEFT, 15000 /* Highest Priority */) + ); + } +} + +workbenchRegistry.registerWorkbenchContribution(AccountsStatusBarContributions, LifecyclePhase.Restored); diff --git a/src/sql/workbench/parts/connection/browser/connection.contribution.ts b/src/sql/workbench/parts/connection/browser/connection.contribution.ts index 4fc97ab317..f35588555c 100644 --- a/src/sql/workbench/parts/connection/browser/connection.contribution.ts +++ b/src/sql/workbench/parts/connection/browser/connection.contribution.ts @@ -11,24 +11,19 @@ import * as azdata from 'azdata'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; -import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar'; import { ConnectionStatusbarItem } from 'sql/workbench/parts/connection/browser/connectionStatus'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { integrated, azureMFA } from 'sql/platform/connection/common/constants'; import { AuthenticationType } from 'sql/workbench/services/connection/browser/connectionWidget'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -// Register Statusbar item -(Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor( - ConnectionStatusbarItem, - StatusbarAlignment.RIGHT, - 100 /* High Priority */ -)); +workbenchRegistry.registerWorkbenchContribution(ConnectionStatusbarItem, LifecyclePhase.Restored); // Connection Dashboard registration diff --git a/src/sql/workbench/parts/connection/browser/connectionStatus.ts b/src/sql/workbench/parts/connection/browser/connectionStatus.ts index 8355534d13..809a443d59 100644 --- a/src/sql/workbench/parts/connection/browser/connectionStatus.ts +++ b/src/sql/workbench/parts/connection/browser/connectionStatus.ts @@ -3,54 +3,64 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, append, show, hide } from 'vs/base/browser/dom'; -import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService'; import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; +import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { localize } from 'vs/nls'; // Connection status bar showing the current global connection -export class ConnectionStatusbarItem implements IStatusbarItem { +export class ConnectionStatusbarItem extends Disposable implements IWorkbenchContribution { - private _element: HTMLElement; - private _connectionElement: HTMLElement; - private _toDispose: IDisposable[]; + private static readonly ID = 'status.connection.status'; + + private statusItem: IStatusbarEntryAccessor; constructor( - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @IEditorService private _editorService: IEditorService, - @IObjectExplorerService private _objectExplorerService: IObjectExplorerService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, + @IEditorService private readonly editorService: IEditorService, + @IObjectExplorerService private readonly objectExplorerService: IObjectExplorerService, ) { - } - - public render(container: HTMLElement): IDisposable { - this._element = append(container, $('.connection-statusbar-item')); - this._connectionElement = append(this._element, $('div.connection-statusbar-conninfo')); - hide(this._connectionElement); - - this._toDispose = []; - this._toDispose.push( - this._connectionManagementService.onConnect(() => this._updateStatus()), - this._connectionManagementService.onConnectionChanged(() => this._updateStatus()), - this._connectionManagementService.onDisconnect(() => this._updateStatus()), - this._editorService.onDidActiveEditorChange(() => this._updateStatus()), - this._objectExplorerService.onSelectionOrFocusChange(() => this._updateStatus()) + super(); + this.statusItem = this._register( + this.statusbarService.addEntry({ + text: '', + }, + ConnectionStatusbarItem.ID, + localize('status.connection.status', "Connection Status"), + StatusbarAlignment.RIGHT, 100) ); - return combinedDisposable(this._toDispose); + this.hide(); + + this._register(this.connectionManagementService.onConnect(() => this._updateStatus())); + this._register(this.connectionManagementService.onConnectionChanged(() => this._updateStatus())); + this._register(this.connectionManagementService.onDisconnect(() => this._updateStatus())); + this._register(this.editorService.onDidActiveEditorChange(() => this._updateStatus())); + this._register(this.objectExplorerService.onSelectionOrFocusChange(() => this._updateStatus())); + } + + private hide() { + this.statusbarService.updateEntryVisibility(ConnectionStatusbarItem.ID, false); + } + + private show() { + this.statusbarService.updateEntryVisibility(ConnectionStatusbarItem.ID, true); } // Update the connection status shown in the bar private _updateStatus(): void { - let activeConnection = TaskUtilities.getCurrentGlobalConnection(this._objectExplorerService, this._connectionManagementService, this._editorService); + let activeConnection = TaskUtilities.getCurrentGlobalConnection(this.objectExplorerService, this.connectionManagementService, this.editorService); if (activeConnection) { this._setConnectionText(activeConnection); - show(this._connectionElement); + this.show(); } else { - hide(this._connectionElement); + this.hide(); } } @@ -73,7 +83,8 @@ export class ConnectionStatusbarItem implements IStatusbarItem { tooltip = tooltip + 'Login: ' + connectionProfile.userName + '\r\n'; } - this._connectionElement.textContent = text; - this._connectionElement.title = tooltip; + this.statusItem.update({ + text, tooltip + }); } } diff --git a/src/sql/workbench/parts/connection/common/connectionGlobalStatus.ts b/src/sql/workbench/parts/connection/common/connectionGlobalStatus.ts index 856008d7d1..3f846fd3b6 100644 --- a/src/sql/workbench/parts/connection/common/connectionGlobalStatus.ts +++ b/src/sql/workbench/parts/connection/common/connectionGlobalStatus.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ConnectionSummary } from 'azdata'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import * as LocalizedConstants from 'sql/workbench/parts/connection/common/localizedConstants'; +import { INotificationService } from 'vs/platform/notification/common/notification'; // Status when making connections from the viewlet export class ConnectionGlobalStatus { @@ -12,12 +12,12 @@ export class ConnectionGlobalStatus { private _displayTime: number = 5000; // (in ms) constructor( - @IStatusbarService private _statusBarService: IStatusbarService + @INotificationService private _notificationService: INotificationService ) { } public setStatusToConnected(connectionSummary: ConnectionSummary): void { - if (this._statusBarService) { + if (this._notificationService) { let text: string; let connInfo: string = connectionSummary.serverName; if (connInfo) { @@ -28,13 +28,13 @@ export class ConnectionGlobalStatus { } text = LocalizedConstants.onDidConnectMessage + ' ' + connInfo; } - this._statusBarService.setStatusMessage(text, this._displayTime); + this._notificationService.status(text, { hideAfter: this._displayTime }); } } public setStatusToDisconnected(fileUri: string): void { - if (this._statusBarService) { - this._statusBarService.setStatusMessage(LocalizedConstants.onDidDisconnectMessage, this._displayTime); + if (this._notificationService) { + this._notificationService.status(LocalizedConstants.onDidDisconnectMessage, { hideAfter: this._displayTime }); } } } diff --git a/src/sql/workbench/parts/dashboard/common/actions.ts b/src/sql/workbench/parts/dashboard/common/actions.ts index 3a1732b5e0..2365f7d9e0 100644 --- a/src/sql/workbench/parts/dashboard/common/actions.ts +++ b/src/sql/workbench/parts/dashboard/common/actions.ts @@ -235,7 +235,7 @@ export class CollapseWidgetAction extends Action { } this.collpasedState = collapsed; this._setClass(this.collpasedState ? CollapseWidgetAction.EXPAND_ICON : CollapseWidgetAction.COLLAPSE_ICON); - this._setLabel(this.collpasedState ? CollapseWidgetAction.EXPAND_LABEL : CollapseWidgetAction.COLLPASE_LABEL); + this.label = this.collpasedState ? CollapseWidgetAction.EXPAND_LABEL : CollapseWidgetAction.COLLPASE_LABEL; } public set state(collapsed: boolean) { diff --git a/src/sql/workbench/parts/dashboard/widgets/explorer/explorerTree.ts b/src/sql/workbench/parts/dashboard/widgets/explorer/explorerTree.ts index 87cf9ecd82..920c13a28a 100644 --- a/src/sql/workbench/parts/dashboard/widgets/explorer/explorerTree.ts +++ b/src/sql/workbench/parts/dashboard/widgets/explorer/explorerTree.ts @@ -32,7 +32,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { $ } from 'vs/base/browser/dom'; import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { NewNotebookAction } from 'sql/workbench/parts/notebook/notebookActions'; export class ObjectMetadataWrapper implements ObjectMetadata { @@ -112,7 +112,7 @@ export class ExplorerController extends TreeDefaults.DefaultController { private _contextMenuService: IContextMenuService, private _capabilitiesService: ICapabilitiesService, private _instantiationService: IInstantiationService, - private _progressService: IProgressService + private _progressService: IEditorProgressService ) { super(); } @@ -426,7 +426,7 @@ class ExplorerScriptSelectAction extends ScriptSelectAction { @IQueryEditorService queryEditorService: IQueryEditorService, @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IScriptingService scriptingService: IScriptingService, - @IProgressService private progressService: IProgressService + @IEditorProgressService private progressService: IEditorProgressService ) { super(id, label, queryEditorService, connectionManagementService, scriptingService); } @@ -445,7 +445,7 @@ class ExplorerScriptCreateAction extends ScriptCreateAction { @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IScriptingService scriptingService: IScriptingService, @IErrorMessageService errorMessageService: IErrorMessageService, - @IProgressService private progressService: IProgressService + @IEditorProgressService private progressService: IEditorProgressService ) { super(id, label, queryEditorService, connectionManagementService, scriptingService, errorMessageService); } @@ -464,7 +464,7 @@ class ExplorerScriptAlterAction extends ScriptAlterAction { @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IScriptingService scriptingService: IScriptingService, @IErrorMessageService errorMessageService: IErrorMessageService, - @IProgressService private progressService: IProgressService + @IEditorProgressService private progressService: IEditorProgressService ) { super(id, label, queryEditorService, connectionManagementService, scriptingService, errorMessageService); } @@ -483,7 +483,7 @@ class ExplorerScriptExecuteAction extends ScriptExecuteAction { @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IScriptingService scriptingService: IScriptingService, @IErrorMessageService errorMessageService: IErrorMessageService, - @IProgressService private progressService: IProgressService + @IEditorProgressService private progressService: IEditorProgressService ) { super(id, label, queryEditorService, connectionManagementService, scriptingService, errorMessageService); } @@ -500,7 +500,7 @@ class ExplorerManageAction extends ManageAction { id: string, label: string, @IConnectionManagementService connectionManagementService: IConnectionManagementService, @IAngularEventingService angularEventingService: IAngularEventingService, - @IProgressService private _progressService: IProgressService + @IEditorProgressService private _progressService: IEditorProgressService ) { super(id, label, connectionManagementService, angularEventingService); } diff --git a/src/sql/workbench/parts/dashboard/widgets/explorer/explorerWidget.component.ts b/src/sql/workbench/parts/dashboard/widgets/explorer/explorerWidget.component.ts index ff2c11dcca..6f515db14c 100644 --- a/src/sql/workbench/parts/dashboard/widgets/explorer/explorerWidget.component.ts +++ b/src/sql/workbench/parts/dashboard/widgets/explorer/explorerWidget.component.ts @@ -7,7 +7,7 @@ import 'vs/css!sql/media/objectTypes/objecttypes'; import 'vs/css!sql/media/icons/common-icons'; import 'vs/css!./media/explorerWidget'; -import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { Component, Inject, forwardRef, OnInit, ViewChild, ElementRef } from '@angular/core'; import { Router } from '@angular/router'; import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/workbench/parts/dashboard/common/dashboardWidget'; @@ -26,7 +26,7 @@ import { Delayer } from 'vs/base/common/async'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; @Component({ @@ -58,7 +58,6 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, constructor( @Inject(forwardRef(() => CommonServiceInterface)) private _bootstrap: CommonServiceInterface, @Inject(forwardRef(() => Router)) private _router: Router, - @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, @Inject(WIDGET_CONFIG) protected _config: WidgetConfig, @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, @@ -66,7 +65,7 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, @Inject(IInstantiationService) private instantiationService: IInstantiationService, @Inject(IContextMenuService) private contextMenuService: IContextMenuService, @Inject(ICapabilitiesService) private capabilitiesService: ICapabilitiesService, - @Inject(IProgressService) private progressService: IProgressService + @Inject(IEditorProgressService) private progressService: IEditorProgressService ) { super(); this.init(); diff --git a/src/sql/workbench/parts/dataExplorer/browser/connectionViewletPanel.ts b/src/sql/workbench/parts/dataExplorer/browser/connectionViewletPanel.ts index 1053120c1d..371558c659 100644 --- a/src/sql/workbench/parts/dataExplorer/browser/connectionViewletPanel.ts +++ b/src/sql/workbench/parts/dataExplorer/browser/connectionViewletPanel.ts @@ -128,7 +128,6 @@ export class ConnectionViewletPanel extends ViewletPanel { } dispose(): void { - this.disposables = dispose(this.disposables); super.dispose(); } diff --git a/src/sql/workbench/parts/dataExplorer/electron-browser/nodeCommands.ts b/src/sql/workbench/parts/dataExplorer/electron-browser/nodeCommands.ts index 16d5cfff11..1b008bf3fe 100644 --- a/src/sql/workbench/parts/dataExplorer/electron-browser/nodeCommands.ts +++ b/src/sql/workbench/parts/dataExplorer/electron-browser/nodeCommands.ts @@ -13,7 +13,7 @@ import { ICustomViewDescriptor, TreeViewItemHandleArg } from 'sql/workbench/comm import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IViewsRegistry, Extensions } from 'vs/workbench/common/views'; -import { IProgressService2 } from 'vs/platform/progress/common/progress'; +import { IProgressService } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; import { NewNotebookAction } from 'sql/workbench/parts/notebook/notebookActions'; import { BackupAction, RestoreAction } from 'sql/workbench/common/actions'; @@ -98,7 +98,7 @@ CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({ id: REFRESH_COMMAND_ID, handler: (accessor, args: TreeViewItemHandleArg) => { - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); if (args.$treeItem) { const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(args.$treeViewId)); if (args.$treeContainerId) { @@ -173,4 +173,4 @@ CommandsRegistry.registerCommand({ let connectedContext: azdata.ConnectedContext = { connectionProfile: args.$treeItem.payload }; return commandService.executeCommand(RestoreAction.ID, connectedContext); } -}); \ No newline at end of file +}); diff --git a/src/sql/workbench/parts/editData/common/editDataInput.ts b/src/sql/workbench/parts/editData/common/editDataInput.ts index f19cc624a8..aacd37284d 100644 --- a/src/sql/workbench/parts/editData/common/editDataInput.ts +++ b/src/sql/workbench/parts/editData/common/editDataInput.ts @@ -56,12 +56,11 @@ export class EditDataInput extends EditorInput implements IConnectableInput { this._setup = false; this._stopButtonEnabled = false; this._refreshButtonEnabled = false; - this._toDispose = []; this._useQueryFilter = false; // re-emit sql editor events through this editor if it exists if (this._sql) { - this._toDispose.push(this._sql.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + this._register(this._sql.onDidChangeDirty(() => this._onDidChangeDirty.fire())); this._sql.disableSaving(); } this.disableSaving(); @@ -74,7 +73,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput { let self = this; // Register callbacks for the Actions - this._toDispose.push( + this._register( this._queryModelService.onRunQueryStart(uri => { if (self.uri === uri) { self.initEditStart(); @@ -82,7 +81,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput { }) ); - this._toDispose.push( + this._register( this._queryModelService.onEditSessionReady((result) => { if (self.uri === result.ownerUri) { self.initEditEnd(result); @@ -210,7 +209,6 @@ export class EditDataInput extends EditorInput implements IConnectableInput { this._queryModelService.disposeQuery(this.uri); this._sql.dispose(); this._results.dispose(); - this._toDispose = dispose(this._toDispose); super.dispose(); } diff --git a/src/sql/workbench/parts/notebook/cellViews/code.component.ts b/src/sql/workbench/parts/notebook/cellViews/code.component.ts index 0e82b0acca..62943bf146 100644 --- a/src/sql/workbench/parts/notebook/cellViews/code.component.ts +++ b/src/sql/workbench/parts/notebook/cellViews/code.component.ts @@ -17,7 +17,7 @@ import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as themeColors from 'vs/workbench/common/theme'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { SimpleProgressService } from 'vs/editor/standalone/browser/simpleServices'; +import { SimpleEditorProgressService } from 'vs/editor/standalone/browser/simpleServices'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITextModel } from 'vs/editor/common/model'; @@ -189,7 +189,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange } private async createEditor(): Promise { - let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleProgressService()])); + let instantiationService = this._instantiationService.createChild(new ServiceCollection([IProgressService, new SimpleEditorProgressService()])); this._editor = instantiationService.createInstance(QueryTextEditor); this._editor.create(this.codeElement.nativeElement); this._editor.setVisible(true); diff --git a/src/sql/workbench/parts/notebook/notebookStyles.ts b/src/sql/workbench/parts/notebook/notebookStyles.ts index 7d9122688f..8a96dda4d9 100644 --- a/src/sql/workbench/parts/notebook/notebookStyles.ts +++ b/src/sql/workbench/parts/notebook/notebookStyles.ts @@ -7,8 +7,8 @@ import 'vs/css!./notebook'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND } from 'vs/workbench/common/theme'; import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground, textLinkActiveForeground, textPreformatForeground, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IDisposable } from 'vscode-xterm'; import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BareResultsGridInfo, getBareResultsGridInfoStyles } from 'sql/workbench/parts/query/browser/queryResultsEditor'; import { getZoomLevel } from 'vs/base/browser/browser'; @@ -254,4 +254,4 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean, conf ${getBareResultsGridInfoStyles(rawOptions)} }`); }); -} \ No newline at end of file +} diff --git a/src/sql/workbench/parts/notebook/outputs/notebookMarkdown.ts b/src/sql/workbench/parts/notebook/outputs/notebookMarkdown.ts index 8edce23776..bcfbeaf9ce 100644 --- a/src/sql/workbench/parts/notebook/outputs/notebookMarkdown.ts +++ b/src/sql/workbench/parts/notebook/outputs/notebookMarkdown.ts @@ -27,7 +27,7 @@ export class NotebookMarkdownRenderer { const element: HTMLElement = markdown ? this.renderMarkdown(markdown, undefined) : document.createElement('span'); return { element, - dispose: () => dispose() + dispose: () => { } }; } diff --git a/src/sql/workbench/parts/objectExplorer/browser/treeSelectionHandler.ts b/src/sql/workbench/parts/objectExplorer/browser/treeSelectionHandler.ts index 46f51eec50..5cd58a84c9 100644 --- a/src/sql/workbench/parts/objectExplorer/browser/treeSelectionHandler.ts +++ b/src/sql/workbench/parts/objectExplorer/browser/treeSelectionHandler.ts @@ -8,30 +8,30 @@ import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService'; -import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +// import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress'; import { TreeNode } from 'sql/workbench/parts/objectExplorer/common/treeNode'; import { TreeUpdateUtils } from 'sql/workbench/parts/objectExplorer/browser/treeUpdateUtils'; export class TreeSelectionHandler { - progressRunner: IProgressRunner; + // progressRunner: IProgressRunner; private _clicks: number = 0; private _doubleClickTimeoutTimer: NodeJS.Timer = undefined; - constructor(@IProgressService private _progressService: IProgressService) { + // constructor(@IProgressService private _progressService: IProgressService) { - } + // } public onTreeActionStateChange(started: boolean): void { - if (this.progressRunner) { - this.progressRunner.done(); - } + // if (this.progressRunner) { + // this.progressRunner.done(); + // } - if (started) { - this.progressRunner = this._progressService.show(true); - } else { - this.progressRunner = null; - } + // if (started) { + // this.progressRunner = this._progressService.show(true); + // } else { + // this.progressRunner = null; + // } } private isMouseEvent(event: any): boolean { diff --git a/src/sql/workbench/parts/objectExplorer/common/objectExplorerViewTreeShimActions.ts b/src/sql/workbench/parts/objectExplorer/common/objectExplorerViewTreeShimActions.ts index 4b267c33b3..6955867c6b 100644 --- a/src/sql/workbench/parts/objectExplorer/common/objectExplorerViewTreeShimActions.ts +++ b/src/sql/workbench/parts/objectExplorer/common/objectExplorerViewTreeShimActions.ts @@ -13,7 +13,7 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; import { IScriptingService } from 'sql/platform/scripting/common/scriptingService'; import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService'; -import { IProgressService2 } from 'vs/platform/progress/common/progress'; +import { IProgressService } from 'vs/platform/progress/common/progress'; import { ScriptCreateAction, BaseActionContext, ScriptDeleteAction, ScriptSelectAction, ScriptExecuteAction, ScriptAlterAction, EditDataAction } from 'sql/workbench/common/actions'; import { VIEWLET_ID } from 'sql/workbench/parts/dataExplorer/browser/dataExplorerExtensionPoint'; @@ -88,7 +88,7 @@ CommandsRegistry.registerCommand({ const connectionManagementService = accessor.get(IConnectionManagementService); const scriptingService = accessor.get(IScriptingService); const errorMessageService = accessor.get(IErrorMessageService); - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const baseContext: BaseActionContext = { profile: profile, @@ -110,7 +110,7 @@ CommandsRegistry.registerCommand({ const connectionManagementService = accessor.get(IConnectionManagementService); const scriptingService = accessor.get(IScriptingService); const errorMessageService = accessor.get(IErrorMessageService); - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const baseContext: BaseActionContext = { profile: profile, @@ -131,7 +131,7 @@ CommandsRegistry.registerCommand({ const queryEditorService = accessor.get(IQueryEditorService); const connectionManagementService = accessor.get(IConnectionManagementService); const scriptingService = accessor.get(IScriptingService); - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const baseContext: BaseActionContext = { profile: profile, @@ -152,7 +152,7 @@ CommandsRegistry.registerCommand({ const queryEditorService = accessor.get(IQueryEditorService); const connectionManagementService = accessor.get(IConnectionManagementService); const scriptingService = accessor.get(IScriptingService); - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); const errorMessageService = accessor.get(IErrorMessageService); const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const baseContext: BaseActionContext = { @@ -174,7 +174,7 @@ CommandsRegistry.registerCommand({ const queryEditorService = accessor.get(IQueryEditorService); const connectionManagementService = accessor.get(IConnectionManagementService); const scriptingService = accessor.get(IScriptingService); - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); const errorMessageService = accessor.get(IErrorMessageService); const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const baseContext: BaseActionContext = { @@ -196,7 +196,7 @@ CommandsRegistry.registerCommand({ const queryEditorService = accessor.get(IQueryEditorService); const connectionManagementService = accessor.get(IConnectionManagementService); const scriptingService = accessor.get(IScriptingService); - const progressService = accessor.get(IProgressService2); + const progressService = accessor.get(IProgressService); const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const baseContext: BaseActionContext = { profile: profile, @@ -206,4 +206,4 @@ CommandsRegistry.registerCommand({ queryEditorService, connectionManagementService, scriptingService); return progressService.withProgress({ location: VIEWLET_ID }, () => editDataAction.run(baseContext)); } -}); \ No newline at end of file +}); diff --git a/src/sql/workbench/parts/profiler/browser/profilerActions.ts b/src/sql/workbench/parts/profiler/browser/profilerActions.ts index e49733fc2d..b7f6f8f90e 100644 --- a/src/sql/workbench/parts/profiler/browser/profilerActions.ts +++ b/src/sql/workbench/parts/profiler/browser/profilerActions.ts @@ -57,7 +57,7 @@ export class ProfilerConnect extends Action { public set connected(value: boolean) { this._connected = value; this._setClass(value ? 'disconnect' : 'connect'); - this._setLabel(value ? ProfilerConnect.DisconnectText : ProfilerConnect.ConnectText); + this.label = value ? ProfilerConnect.DisconnectText : ProfilerConnect.ConnectText; } public get connected(): boolean { @@ -131,7 +131,7 @@ export class ProfilerPause extends Action { public set paused(value: boolean) { this._paused = value; this._setClass(value ? ProfilerPause.ResumeCssClass : ProfilerPause.PauseCssClass); - this._setLabel(value ? ProfilerPause.ResumeText : ProfilerPause.PauseText); + this.label = value ? ProfilerPause.ResumeText : ProfilerPause.PauseText; } public get paused(): boolean { @@ -183,7 +183,7 @@ export class ProfilerAutoScroll extends Action { run(input: ProfilerInput): Promise { this.checked = !this.checked; - this._setLabel(this.checked ? ProfilerAutoScroll.AutoScrollOnText : ProfilerAutoScroll.AutoScrollOffText); + this.label = this.checked ? ProfilerAutoScroll.AutoScrollOnText : ProfilerAutoScroll.AutoScrollOffText; this._setClass(this.checked ? ProfilerAutoScroll.CheckedCssClass : ''); input.state.change({ autoscroll: this.checked }); return Promise.resolve(true); diff --git a/src/sql/workbench/parts/profiler/browser/profilerTableEditor.ts b/src/sql/workbench/parts/profiler/browser/profilerTableEditor.ts index 0885840e21..5419d88451 100644 --- a/src/sql/workbench/parts/profiler/browser/profilerTableEditor.ts +++ b/src/sql/workbench/parts/profiler/browser/profilerTableEditor.ts @@ -286,7 +286,7 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll : localize('ProfilerTableEditor.eventCount', 'Events: {0}', this._input.data.getLength()); this._disposeStatusbarItem(); - this._statusbarItem = this._statusbarService.addEntry({ text: message }, StatusbarAlignment.RIGHT); + this._statusbarItem = this._statusbarService.addEntry({ text: message }, 'status.eventCount', localize('status.eventCount', "Event Count"), StatusbarAlignment.RIGHT); } } diff --git a/src/sql/workbench/parts/query/browser/flavorStatus.ts b/src/sql/workbench/parts/query/browser/flavorStatus.ts index 624bf86893..de12b3bbf5 100644 --- a/src/sql/workbench/parts/query/browser/flavorStatus.ts +++ b/src/sql/workbench/parts/query/browser/flavorStatus.ts @@ -4,14 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/flavorStatus'; -import { $, append, show, hide } from 'vs/base/browser/dom'; -import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IEditorCloseEvent } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Action } from 'vs/base/common/actions'; -import errors = require('vs/base/common/errors'); import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import * as nls from 'vs/nls'; @@ -24,6 +20,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IStatusbarService, StatusbarAlignment, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar'; export interface ISqlProviderEntry extends IQuickPickItem { providerId: string; @@ -59,53 +57,54 @@ class SqlProviderEntry implements ISqlProviderEntry { } // Shows SQL flavor status in the editor -export class SqlFlavorStatusbarItem implements IStatusbarItem { +export class SqlFlavorStatusbarItem extends Disposable implements IWorkbenchContribution { + + private static readonly ID = 'status.query.flavor'; + + private statusItem: IStatusbarEntryAccessor; - private _element: HTMLElement; - private _flavorElement: HTMLElement; private _sqlStatusEditors: { [editorUri: string]: SqlProviderEntry }; - private _toDispose: IDisposable[]; constructor( - @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, - @IEditorService private _editorService: EditorServiceImpl, - @IInstantiationService private _instantiationService: IInstantiationService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IEditorService private readonly editorService: EditorServiceImpl, + @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService ) { + super(); this._sqlStatusEditors = {}; - } - public render(container: HTMLElement): IDisposable { - this._element = append(container, $('.query-statusbar-group')); - this._flavorElement = append(this._element, $('a.editor-status-selection')); - this._flavorElement.title = nls.localize('changeProvider', "Change SQL language provider"); - this._flavorElement.onclick = () => this._onSelectionClick(); - hide(this._flavorElement); + this.statusItem = this._register( + this.statusbarService.addEntry({ + text: nls.localize('changeProvider', "Change SQL language provider"), - this._toDispose = []; - this._toDispose.push( - this._connectionManagementService.onLanguageFlavorChanged((changeParams: DidChangeLanguageFlavorParams) => this._onFlavorChanged(changeParams)), - this._editorService.onDidVisibleEditorsChange(() => this._onEditorsChanged()), - this._editorService.onDidCloseEditor(event => this._onEditorClosed(event)) + }, + SqlFlavorStatusbarItem.ID, + nls.localize('status.query.flavor', "SQL Language Flavor"), + StatusbarAlignment.RIGHT, 100) ); - return combinedDisposable(this._toDispose); + + this._register(this.connectionManagementService.onLanguageFlavorChanged((changeParams: DidChangeLanguageFlavorParams) => this._onFlavorChanged(changeParams))); + this._register(this.editorService.onDidVisibleEditorsChange(() => this._onEditorsChanged())); + this._register(this.editorService.onDidCloseEditor(event => this._onEditorClosed(event))); } - private _onSelectionClick() { - const action = this._instantiationService.createInstance(ChangeFlavorAction, ChangeFlavorAction.ID, ChangeFlavorAction.LABEL); + private hide() { + this.statusbarService.updateEntryVisibility(SqlFlavorStatusbarItem.ID, false); + } - action.run().then(null, errors.onUnexpectedError); - action.dispose(); + private show() { + this.statusbarService.updateEntryVisibility(SqlFlavorStatusbarItem.ID, true); } private _onEditorClosed(event: IEditorCloseEvent): void { let uri = WorkbenchUtils.getEditorUri(event.editor); if (uri && uri in this._sqlStatusEditors) { // If active editor is being closed, hide the query status. - let activeEditor = this._editorService.activeControl; + let activeEditor = this.editorService.activeControl; if (activeEditor) { let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); if (uri === currentUri) { - hide(this._flavorElement); + this.hide(); } } // note: intentionally not removing language flavor. This is preserved across close/open events at present @@ -114,7 +113,7 @@ export class SqlFlavorStatusbarItem implements IStatusbarItem { } private _onEditorsChanged(): void { - let activeEditor = this._editorService.activeControl; + let activeEditor = this.editorService.activeControl; if (activeEditor) { let uri = WorkbenchUtils.getEditorUri(activeEditor.input); @@ -122,10 +121,10 @@ export class SqlFlavorStatusbarItem implements IStatusbarItem { if (uri) { this._showStatus(uri); } else { - hide(this._flavorElement); + this.hide(); } } else { - hide(this._flavorElement); + this.hide(); } } @@ -145,17 +144,17 @@ export class SqlFlavorStatusbarItem implements IStatusbarItem { // Show/hide query status for active editor private _showStatus(uri: string): void { - let activeEditor = this._editorService.activeControl; + let activeEditor = this.editorService.activeControl; if (activeEditor) { let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); if (uri === currentUri) { let flavor: SqlProviderEntry = this._sqlStatusEditors[uri]; if (flavor) { - this._flavorElement.textContent = flavor.label; + this.statusItem.update({ text: flavor.label }); } else { - this._flavorElement.textContent = SqlProviderEntry.getDefaultLabel(); + this.statusItem.update({ text: SqlProviderEntry.getDefaultLabel() }); } - show(this._flavorElement); + this.show(); } } } diff --git a/src/sql/workbench/parts/query/browser/query.contribution.ts b/src/sql/workbench/parts/query/browser/query.contribution.ts index d761bcb93e..96a549b581 100644 --- a/src/sql/workbench/parts/query/browser/query.contribution.ts +++ b/src/sql/workbench/parts/query/browser/query.contribution.ts @@ -29,6 +29,11 @@ import * as gridActions from 'sql/workbench/parts/grid/views/gridActions'; import * as gridCommands from 'sql/workbench/parts/grid/views/gridCommands'; import * as Constants from 'sql/workbench/parts/query/common/constants'; import { localize } from 'vs/nls'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; + +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { TimeElapsedStatusBarContributions, RowCountStatusBarContributions, QueryStatusStatusBarContributions } from 'sql/workbench/parts/query/browser/statusBarItems'; +import { SqlFlavorStatusbarItem } from 'sql/workbench/parts/query/browser/flavorStatus'; const gridCommandsWeightBonus = 100; // give our commands a little bit more weight over other default list/tree commands @@ -514,3 +519,10 @@ configurationRegistry.registerConfiguration({ 'type': 'object', 'properties': registryProperties }); + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); + +workbenchRegistry.registerWorkbenchContribution(TimeElapsedStatusBarContributions, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(RowCountStatusBarContributions, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(QueryStatusStatusBarContributions, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(SqlFlavorStatusbarItem, LifecyclePhase.Restored); diff --git a/src/sql/workbench/parts/query/browser/queryEditor.ts b/src/sql/workbench/parts/query/browser/queryEditor.ts index 8f7d6334ef..e4a59fb3ff 100644 --- a/src/sql/workbench/parts/query/browser/queryEditor.ts +++ b/src/sql/workbench/parts/query/browser/queryEditor.ts @@ -184,7 +184,7 @@ export class QueryEditor extends BaseEditor { this.setTaskbarContent(); - this._toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) { this.setTaskbarContent(); } diff --git a/src/sql/workbench/parts/query/browser/queryStatus.ts b/src/sql/workbench/parts/query/browser/queryStatus.ts deleted file mode 100644 index a4785963e5..0000000000 --- a/src/sql/workbench/parts/query/browser/queryStatus.ts +++ /dev/null @@ -1,117 +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 { $, append, show, hide } from 'vs/base/browser/dom'; -import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { IEditorCloseEvent } from 'vs/workbench/common/editor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IQueryModelService } from 'sql/platform/query/common/queryModel'; -import * as LocalizedConstants from 'sql/workbench/parts/query/common/localizedConstants'; -import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; -import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; - -// Query execution status -enum QueryExecutionStatus { - Executing, - Completed -} - -// Shows query status in the editor -export class QueryStatusbarItem implements IStatusbarItem { - - private _element: HTMLElement; - private _queryElement: HTMLElement; - private _queryStatusEditors: { [editorUri: string]: QueryExecutionStatus }; - private _toDispose: IDisposable[]; - - constructor( - @IQueryModelService private _queryModelService: IQueryModelService, - @IEditorService private _editorService: EditorServiceImpl - ) { - this._queryStatusEditors = {}; - } - - public render(container: HTMLElement): IDisposable { - this._element = append(container, $('.query-statusbar-group')); - this._queryElement = append(this._element, $('div.query-statusbar-item')); - hide(this._queryElement); - - this._toDispose = []; - this._toDispose.push( - this._queryModelService.onRunQueryStart((uri: string) => this._onRunQueryStart(uri)), - this._queryModelService.onRunQueryComplete((uri: string) => this._onRunQueryComplete(uri)), - this._editorService.onDidVisibleEditorsChange(() => this._onEditorsChanged()), - this._editorService.onDidCloseEditor(event => this._onEditorClosed(event)) - ); - - return combinedDisposable(this._toDispose); - } - - private _onEditorClosed(event: IEditorCloseEvent): void { - let uri = WorkbenchUtils.getEditorUri(event.editor); - if (uri && uri in this._queryStatusEditors) { - // If active editor is being closed, hide the query status. - let activeEditor = this._editorService.activeControl; - if (activeEditor) { - let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); - if (uri === currentUri) { - hide(this._queryElement); - } - } - delete this._queryStatusEditors[uri]; - } - } - - private _onEditorsChanged(): void { - let activeEditor = this._editorService.activeControl; - if (activeEditor) { - let uri = WorkbenchUtils.getEditorUri(activeEditor.input); - - // Show active editor's query status - if (uri && uri in this._queryStatusEditors) { - this._showStatus(uri); - } else { - hide(this._queryElement); - } - } else { - hide(this._queryElement); - } - } - - private _onRunQueryStart(uri: string): void { - this._updateStatus(uri, QueryExecutionStatus.Executing); - } - - private _onRunQueryComplete(uri: string): void { - this._updateStatus(uri, QueryExecutionStatus.Completed); - } - - // Update query status for the editor - private _updateStatus(uri: string, newStatus: QueryExecutionStatus) { - if (uri) { - this._queryStatusEditors[uri] = newStatus; - this._showStatus(uri); - } - } - - // Show/hide query status for active editor - private _showStatus(uri: string): void { - let activeEditor = this._editorService.activeControl; - if (activeEditor) { - let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); - if (uri === currentUri) { - switch (this._queryStatusEditors[uri]) { - case QueryExecutionStatus.Executing: - this._queryElement.textContent = LocalizedConstants.msgStatusRunQueryInProgress; - show(this._queryElement); - break; - default: - hide(this._queryElement); - } - } - } - } -} diff --git a/src/sql/workbench/parts/query/browser/rowCountStatus.ts b/src/sql/workbench/parts/query/browser/rowCountStatus.ts deleted file mode 100644 index f781963087..0000000000 --- a/src/sql/workbench/parts/query/browser/rowCountStatus.ts +++ /dev/null @@ -1,99 +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 WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; -import { IQueryModelService } from 'sql/platform/query/common/queryModel'; -import QueryRunner from 'sql/platform/query/common/queryRunner'; - -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorCloseEvent } from 'vs/workbench/common/editor'; -import { append, $, hide, show } from 'vs/base/browser/dom'; -import * as nls from 'vs/nls'; -import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; - -export class RowCountStatusBarItem implements IStatusbarItem { - - private _element: HTMLElement; - private _flavorElement: HTMLElement; - - private dispose: IDisposable[]; - - constructor( - @IEditorService private _editorService: EditorServiceImpl, - @IQueryModelService private _queryModelService: IQueryModelService - ) { } - - render(container: HTMLElement): IDisposable { - let disposables = [ - this._editorService.onDidVisibleEditorsChange(() => this._onEditorsChanged()), - this._editorService.onDidCloseEditor(event => this._onEditorClosed(event)) - ]; - - this._element = append(container, $('.query-statusbar-group')); - this._flavorElement = append(this._element, $('.editor-status-selection')); - this._flavorElement.title = nls.localize('rowStatus', "Row Count"); - hide(this._flavorElement); - - this._showStatus(); - - return combinedDisposable(disposables); - } - - private _onEditorsChanged() { - this._showStatus(); - } - - private _onEditorClosed(event: IEditorCloseEvent) { - hide(this._flavorElement); - } - - // Show/hide query status for active editor - private _showStatus(): void { - hide(this._flavorElement); - dispose(this.dispose); - this.dispose = []; - let activeEditor = this._editorService.activeControl; - if (activeEditor) { - let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); - if (currentUri) { - let queryRunner = this._queryModelService.getQueryRunner(currentUri); - if (queryRunner) { - if (queryRunner.hasCompleted) { - this._displayValue(queryRunner); - } - this.dispose.push(queryRunner.onQueryEnd(e => { - this._displayValue(queryRunner); - })); - this.dispose.push(queryRunner.onQueryStart(e => { - hide(this._flavorElement); - })); - } else { - this.dispose.push(this._queryModelService.onRunQueryComplete(e => { - if (e === currentUri) { - this._displayValue(this._queryModelService.getQueryRunner(currentUri)); - } - })); - this.dispose.push(this._queryModelService.onRunQueryStart(e => { - if (e === currentUri) { - hide(this._flavorElement); - } - })); - } - } - } - } - - private _displayValue(runner: QueryRunner) { - let rowCount = runner.batchSets.reduce((p, c) => { - return p + c.resultSetSummaries.reduce((rp, rc) => { - return rp + rc.rowCount; - }, 0); - }, 0); - this._flavorElement.innerText = nls.localize('rowCount', "{0} rows", rowCount); - show(this._flavorElement); - } -} diff --git a/src/sql/workbench/parts/query/browser/statusBarItems.ts b/src/sql/workbench/parts/query/browser/statusBarItems.ts new file mode 100644 index 0000000000..82e8d3c861 --- /dev/null +++ b/src/sql/workbench/parts/query/browser/statusBarItems.ts @@ -0,0 +1,238 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IQueryModelService } from 'sql/platform/query/common/queryModel'; +import { IntervalTimer } from 'vs/base/common/async'; +import { IStatusbarService, StatusbarAlignment, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { localize } from 'vs/nls'; +import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; +import QueryRunner from 'sql/platform/query/common/queryRunner'; +import { parseNumAsTimeString } from 'sql/platform/connection/common/utils'; +import { Event } from 'vs/base/common/event'; + +export class TimeElapsedStatusBarContributions extends Disposable implements IWorkbenchContribution { + + private static readonly ID = 'status.query.timeElapsed'; + + private statusItem: IStatusbarEntryAccessor; + private intervalTimer = new IntervalTimer(); + + private disposable = this._register(new DisposableStore()); + + constructor( + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IEditorService private readonly editorService: IEditorService, + @IQueryModelService private readonly queryModelService: IQueryModelService + ) { + super(); + this.statusItem = this._register( + this.statusbarService.addEntry({ + text: '', + }, + TimeElapsedStatusBarContributions.ID, + localize('status.query.timeElapsed', "Time Elapsed"), + StatusbarAlignment.RIGHT, 100) + ); + + this._register(editorService.onDidActiveEditorChange(this.update, this)); + this.update(); + } + + private hide() { + this.statusbarService.updateEntryVisibility(TimeElapsedStatusBarContributions.ID, false); + } + + private show() { + this.statusbarService.updateEntryVisibility(TimeElapsedStatusBarContributions.ID, true); + } + + private update() { + this.intervalTimer.cancel(); + this.disposable.clear(); + this.hide(); + const activeInput = this.editorService.activeEditor; + if (activeInput && activeInput instanceof QueryInput && activeInput.uri) { + const uri = activeInput.uri; + const runner = this.queryModelService.getQueryRunner(uri); + if (runner) { + if (runner.hasCompleted || runner.isExecuting) { + this._displayValue(runner); + } + this.disposable.add(runner.onQueryStart(e => { + this._displayValue(runner); + })); + this.disposable.add(runner.onQueryEnd(e => { + this._displayValue(runner); + })); + } else { + this.disposable.add(this.queryModelService.onRunQueryStart(e => { + if (e === uri) { + this._displayValue(this.queryModelService.getQueryRunner(uri)); + } + })); + this.disposable.add(this.queryModelService.onRunQueryComplete(e => { + if (e === uri) { + this._displayValue(this.queryModelService.getQueryRunner(uri)); + } + })); + } + } + } + + private _displayValue(runner: QueryRunner) { + this.intervalTimer.cancel(); + if (runner.isExecuting) { + this.intervalTimer.cancelAndSet(() => { + const value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0; + this.statusItem.update({ + text: parseNumAsTimeString(value, false) + }); + }, 1000); + + const value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0; + this.statusItem.update({ + text: parseNumAsTimeString(value, false) + }); + } else { + const value = runner.queryStartTime && runner.queryEndTime + ? runner.queryEndTime.getTime() - runner.queryStartTime.getTime() : 0; + this.statusItem.update({ + text: parseNumAsTimeString(value, false) + }); + } + this.show(); + } +} + +export class RowCountStatusBarContributions extends Disposable implements IWorkbenchContribution { + + private static readonly ID = 'status.query.rowCount'; + + private statusItem: IStatusbarEntryAccessor; + + private disposable = this._register(new DisposableStore()); + + constructor( + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IEditorService private readonly editorService: IEditorService, + @IQueryModelService private readonly queryModelService: IQueryModelService + ) { + super(); + this.statusItem = this._register( + this.statusbarService.addEntry({ + text: '', + }, + RowCountStatusBarContributions.ID, + localize('status.query.rowCount', "Row Count"), + StatusbarAlignment.RIGHT, 100) + ); + + this._register(editorService.onDidActiveEditorChange(this.update, this)); + this.update(); + } + + private hide() { + this.statusbarService.updateEntryVisibility(RowCountStatusBarContributions.ID, false); + } + + private show() { + this.statusbarService.updateEntryVisibility(RowCountStatusBarContributions.ID, true); + } + + private update() { + this.disposable.clear(); + this.hide(); + const activeInput = this.editorService.activeEditor; + if (activeInput && activeInput instanceof QueryInput && activeInput.uri) { + const uri = activeInput.uri; + const runner = this.queryModelService.getQueryRunner(uri); + if (runner) { + if (runner.hasCompleted || runner.isExecuting) { + this._displayValue(runner); + } + this.disposable.add(runner.onQueryStart(e => { + this._displayValue(runner); + })); + this.disposable.add(runner.onQueryEnd(e => { + this._displayValue(runner); + })); + } else { + this.disposable.add(this.queryModelService.onRunQueryStart(e => { + if (e === uri) { + this._displayValue(this.queryModelService.getQueryRunner(uri)); + } + })); + this.disposable.add(this.queryModelService.onRunQueryComplete(e => { + if (e === uri) { + this._displayValue(this.queryModelService.getQueryRunner(uri)); + } + })); + } + } + } + + private _displayValue(runner: QueryRunner) { + const rowCount = runner.batchSets.reduce((p, c) => { + return p + c.resultSetSummaries.reduce((rp, rc) => { + return rp + rc.rowCount; + }, 0); + }, 0); + const text = localize('rowCount', "{0} rows", rowCount); + this.statusItem.update({ text }); + this.show(); + } +} + +export class QueryStatusStatusBarContributions extends Disposable implements IWorkbenchContribution { + + private static readonly ID = 'status.query.status'; + + private visisbleUri: string | undefined; + + constructor( + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IEditorService private readonly editorService: IEditorService, + @IQueryModelService private readonly queryModelService: IQueryModelService + ) { + super(); + this._register( + this.statusbarService.addEntry({ + text: localize('query.status.executing', 'Executing query...'), + }, + QueryStatusStatusBarContributions.ID, + localize('status.query.status', "Execution Status"), + StatusbarAlignment.RIGHT, 100) + ); + + this._register(Event.filter(this.queryModelService.onRunQueryStart, uri => uri === this.visisbleUri)(this.update, this)); + this._register(Event.filter(this.queryModelService.onRunQueryComplete, uri => uri === this.visisbleUri)(this.update, this)); + this._register(this.editorService.onDidActiveEditorChange(this.update, this)); + this.update(); + } + + private update() { + this.hide(); + this.visisbleUri = undefined; + const activeInput = this.editorService.activeEditor; + if (activeInput && activeInput instanceof QueryInput && activeInput.uri) { + this.visisbleUri = activeInput.uri; + const runner = this.queryModelService.getQueryRunner(this.visisbleUri); + if (runner && runner.isExecuting) { + this.show(); + } + } + } + + private hide() { + this.statusbarService.updateEntryVisibility(QueryStatusStatusBarContributions.ID, false); + } + + private show() { + this.statusbarService.updateEntryVisibility(QueryStatusStatusBarContributions.ID, true); + } +} diff --git a/src/sql/workbench/parts/query/browser/timeElapsedStatus.ts b/src/sql/workbench/parts/query/browser/timeElapsedStatus.ts deleted file mode 100644 index 43a1238796..0000000000 --- a/src/sql/workbench/parts/query/browser/timeElapsedStatus.ts +++ /dev/null @@ -1,112 +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 WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; -import { IQueryModelService } from 'sql/platform/query/common/queryModel'; -import QueryRunner from 'sql/platform/query/common/queryRunner'; -import { parseNumAsTimeString } from 'sql/platform/connection/common/utils'; - -import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorCloseEvent } from 'vs/workbench/common/editor'; -import { append, $, hide, show } from 'vs/base/browser/dom'; -import * as nls from 'vs/nls'; -import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; -import { IntervalTimer } from 'vs/base/common/async'; - -export class TimeElapsedStatusBarItem implements IStatusbarItem { - - private _element: HTMLElement; - private _flavorElement: HTMLElement; - - private dispose: IDisposable[] = []; - private intervalTimer = new IntervalTimer(); - - constructor( - @IEditorService private _editorService: EditorServiceImpl, - @IQueryModelService private _queryModelService: IQueryModelService - ) { } - - render(container: HTMLElement): IDisposable { - let disposables = [ - this._editorService.onDidVisibleEditorsChange(() => this._onEditorsChanged()), - this._editorService.onDidCloseEditor(event => this._onEditorClosed(event)) - ]; - - this._element = append(container, $('.query-statusbar-group')); - this._flavorElement = append(this._element, $('.editor-status-selection')); - this._flavorElement.title = nls.localize('timeElapsed', "Time Elapsed"); - hide(this._flavorElement); - - this._showStatus(); - - return combinedDisposable(disposables); - } - - private _onEditorsChanged() { - this._showStatus(); - } - - private _onEditorClosed(event: IEditorCloseEvent) { - hide(this._flavorElement); - } - - // Show/hide query status for active editor - private _showStatus(): void { - this.intervalTimer.cancel(); - hide(this._flavorElement); - dispose(this.dispose); - this._flavorElement.innerText = ''; - this.dispose = []; - let activeEditor = this._editorService.activeControl; - if (activeEditor) { - let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input); - if (currentUri) { - let queryRunner = this._queryModelService.getQueryRunner(currentUri); - if (queryRunner) { - if (queryRunner.hasCompleted || queryRunner.isExecuting) { - this._displayValue(queryRunner); - } - this.dispose.push(queryRunner.onQueryStart(e => { - this._displayValue(queryRunner); - })); - this.dispose.push(queryRunner.onQueryEnd(e => { - this._displayValue(queryRunner); - })); - } else { - this.dispose.push(this._queryModelService.onRunQueryStart(e => { - if (e === currentUri) { - this._displayValue(this._queryModelService.getQueryRunner(currentUri)); - } - })); - this.dispose.push(this._queryModelService.onRunQueryComplete(e => { - if (e === currentUri) { - this._displayValue(this._queryModelService.getQueryRunner(currentUri)); - } - })); - } - } - } - } - - private _displayValue(runner: QueryRunner) { - this.intervalTimer.cancel(); - if (runner.isExecuting) { - this.intervalTimer.cancelAndSet(() => { - let value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0; - this._flavorElement.innerText = parseNumAsTimeString(value, false); - }, 1000); - - let value = runner.queryStartTime ? Date.now() - runner.queryStartTime.getTime() : 0; - this._flavorElement.innerText = parseNumAsTimeString(value, false); - } else { - let value = runner.queryStartTime && runner.queryEndTime - ? runner.queryEndTime.getTime() - runner.queryStartTime.getTime() : 0; - this._flavorElement.innerText = parseNumAsTimeString(value, false); - } - show(this._flavorElement); - } -} diff --git a/src/sql/workbench/parts/query/common/queryInput.ts b/src/sql/workbench/parts/query/common/queryInput.ts index 049c8a17f8..548dbb4d7c 100644 --- a/src/sql/workbench/parts/query/common/queryInput.ts +++ b/src/sql/workbench/parts/query/common/queryInput.ts @@ -210,6 +210,13 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec public getResource(): URI { return this._sql.getResource(); } public getEncoding(): string { return this._sql.getEncoding(); } public suggestFileName(): string { return this._sql.suggestFileName(); } + hasBackup(): boolean { + if (this.sql) { + return this.sql.hasBackup(); + } + + return false; + } public getName(longForm?: boolean): string { if (this._configurationService.getValue('sql.showConnectionInfoInTitle')) { diff --git a/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts b/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts index 81b7bf2a0a..4f1d24cda4 100644 --- a/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts +++ b/src/sql/workbench/services/accountManagement/browser/accountManagementService.ts @@ -4,9 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; -import * as platform from 'vs/platform/registry/common/platform'; -import * as statusbar from 'vs/workbench/browser/parts/statusbar/statusbar'; -import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar'; import { Event, Emitter } from 'vs/base/common/event'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -17,7 +14,6 @@ import { Memento } from 'vs/workbench/common/memento'; import AccountStore from 'sql/platform/accounts/common/accountStore'; import { AccountDialogController } from 'sql/platform/accounts/browser/accountDialogController'; import { AutoOAuthDialogController } from 'sql/platform/accounts/browser/autoOAuthDialogController'; -import { AccountListStatusbarItem } from 'sql/platform/accounts/browser/accountListStatusbarItem'; import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes'; import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces'; import { Deferred } from 'sql/base/common/promise'; @@ -65,14 +61,6 @@ export class AccountManagementService implements IAccountManagementService { this._updateAccountListEmitter = new Emitter(); _storageService.onWillSaveState(() => this.shutdown()); - - // Register status bar item - let statusbarDescriptor = new statusbar.StatusbarItemDescriptor( - AccountListStatusbarItem, - StatusbarAlignment.LEFT, - 15000 /* Highest Priority */ - ); - (platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(statusbarDescriptor); } private get autoOAuthDialogController(): AutoOAuthDialogController { diff --git a/src/sql/workbench/services/commandLine/common/commandLineService.ts b/src/sql/workbench/services/commandLine/common/commandLineService.ts index 2558f0fcdd..d65717cc5a 100644 --- a/src/sql/workbench/services/commandLine/common/commandLineService.ts +++ b/src/sql/workbench/services/commandLine/common/commandLineService.ts @@ -22,11 +22,11 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ipcRenderer as ipc } from 'electron'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { localize } from 'vs/nls'; import { QueryInput } from 'sql/workbench/parts/query/common/queryInput'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export class CommandLineService implements ICommandLineProcessing { public _serviceBrand: any; @@ -40,7 +40,7 @@ export class CommandLineService implements ICommandLineProcessing { @IEditorService private _editorService: IEditorService, @ICommandService private _commandService: ICommandService, @IConfigurationService private _configurationService: IConfigurationService, - @IStatusbarService private _statusBarService: IStatusbarService, + @INotificationService private _notificationService: INotificationService, @ILogService private logService: ILogService ) { if (ipc) { @@ -92,8 +92,8 @@ export class CommandLineService implements ICommandLineProcessing { } let connectedContext: azdata.ConnectedContext = undefined; if (profile) { - if (this._statusBarService) { - this._statusBarService.setStatusMessage(localize('connectingLabel', 'Connecting:') + profile.serverName, 2500); + if (this._notificationService) { + this._notificationService.status(localize('connectingLabel', 'Connecting: {0}', profile.serverName), { hideAfter: 2500 }); } try { await this._connectionManagementService.connectIfNotConnected(profile, 'connection', true); @@ -106,8 +106,8 @@ export class CommandLineService implements ICommandLineProcessing { } } if (commandName) { - if (this._statusBarService) { - this._statusBarService.setStatusMessage(localize('runningCommandLabel', 'Running command:') + commandName, 2500); + if (this._notificationService) { + this._notificationService.status(localize('runningCommandLabel', 'Running command: {0}', commandName), { hideAfter: 2500 }); } await this._commandService.executeCommand(commandName, connectedContext); } else if (profile) { @@ -119,8 +119,8 @@ export class CommandLineService implements ICommandLineProcessing { } else { // Default to showing new query - if (this._statusBarService) { - this._statusBarService.setStatusMessage(localize('openingNewQueryLabel', 'Opening new query:') + profile.serverName, 2500); + if (this._notificationService) { + this._notificationService.status(localize('openingNewQueryLabel', 'Opening new query: {0}', profile.serverName), { hideAfter: 2500 }); } try { await TaskUtilities.newQuery(profile, @@ -150,8 +150,8 @@ export class CommandLineService implements ICommandLineProcessing { showConnectionDialogOnError: warnOnConnectFailure, showFirewallRuleOnError: warnOnConnectFailure }; - if (this._statusBarService) { - this._statusBarService.setStatusMessage(localize('connectingQueryLabel', 'Connecting query file'), 2500); + if (this._notificationService) { + this._notificationService.status(localize('connectingQueryLabel', 'Connecting query file'), { hideAfter: 2500 }); } await this._connectionManagementService.connect(profile, uriString, options); } diff --git a/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts b/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts index bdae058258..037af8c756 100644 --- a/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts +++ b/src/sql/workbench/services/connection/browser/connectionDialogWidget.ts @@ -451,10 +451,6 @@ export class ConnectionDialogWidget extends Modal { this.onProviderTypeSelected(providerDisplayName); } - public dispose(): void { - this._toDispose.forEach(obj => obj.dispose()); - } - public set databaseDropdownExpanded(val: boolean) { this._databaseDropdownExpanded = val; } diff --git a/src/sql/workbench/services/connection/browser/connectionWidget.ts b/src/sql/workbench/services/connection/browser/connectionWidget.ts index aa8701ade7..c02a918ce8 100644 --- a/src/sql/workbench/services/connection/browser/connectionWidget.ts +++ b/src/sql/workbench/services/connection/browser/connectionWidget.ts @@ -140,9 +140,9 @@ export class ConnectionWidget { }); } - protected _handleClipboard(): void { + protected async _handleClipboard(): Promise { if (this._configurationService.getValue('connection.parseClipboardForConnectionString')) { - let paste = this._clipboardService.readText(); + let paste = await this._clipboardService.readText(); this._connectionManagementService.buildConnectionInfo(paste, this._providerName).then(e => { if (e) { let profile = new ConnectionProfile(this._capabilitiesService, this._providerName); diff --git a/src/sql/workbench/services/insights/test/common/insightsUtils.test.ts b/src/sql/workbench/services/insights/test/common/insightsUtils.test.ts index 867c60d943..943d09b2ec 100644 --- a/src/sql/workbench/services/insights/test/common/insightsUtils.test.ts +++ b/src/sql/workbench/services/insights/test/common/insightsUtils.test.ts @@ -24,7 +24,16 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IFileService } from 'vs/platform/files/common/files'; class TestEnvironmentService implements IWorkbenchEnvironmentService { - machineSettingsHome: string; + webviewCspSource: string; + webviewCspRule: string; + localeResource: URI; + userRoamingDataHome: URI; + webviewEndpoint?: string; + webviewResourceRoot: string; + keyboardLayoutResource: URI; + machineSettingsResource: URI; + keybindingsResource: URI; + machineSettingsHome: URI; machineSettingsPath: string; extensionDevelopmentLocationURI?: URI[]; @@ -47,14 +56,15 @@ class TestEnvironmentService implements IWorkbenchEnvironmentService { userDataPath: string; appNameLong: string; appQuality?: string; - appSettingsHome: string; - appSettingsPath: string; + appSettingsHome: URI; + + settingsResource: URI; appKeybindingsPath: string; settingsSearchBuildId?: number; settingsSearchUrl?: string; globalStorageHome: string; workspaceStorageHome: string; - backupHome: string; + backupHome: URI; backupWorkspacesPath: string; untitledWorkspacesHome: URI; isExtensionDevelopment: boolean; diff --git a/src/sqltest/common/telemetryUtilities.test.ts b/src/sqltest/common/telemetryUtilities.test.ts index 119b090123..59ce8cb013 100644 --- a/src/sqltest/common/telemetryUtilities.test.ts +++ b/src/sqltest/common/telemetryUtilities.test.ts @@ -8,7 +8,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryServiceStub } from 'sqltest/stubs/telemetryServiceStub'; import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; -import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('SQL Telemetry Utilities tests', () => { let telemetryService: TypeMoq.Mock; @@ -42,7 +42,7 @@ suite('SQL Telemetry Utilities tests', () => { test('addTelemetry should add provider id using the connection', (done) => { let data: TelemetryUtils.IConnectionTelemetryData = { }; - const logService = new TestLogService(); + const logService = new NullLogService(); TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => { telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === providerName)), TypeMoq.Times.once()); done(); @@ -59,7 +59,7 @@ suite('SQL Telemetry Utilities tests', () => { }; data.test1 = '1'; - const logService = new TestLogService(); + const logService = new NullLogService(); TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => { telemetryService.verify(x => x.publicLog( TypeMoq.It.is(a => a === telemetryKey), @@ -77,7 +77,7 @@ suite('SQL Telemetry Utilities tests', () => { test('addTelemetry should not crash not given data', (done) => { - const logService = new TestLogService(); + const logService = new NullLogService(); TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey).then(() => { telemetryService.verify(x => x.publicLog( TypeMoq.It.is(a => a === telemetryKey), @@ -95,7 +95,7 @@ suite('SQL Telemetry Utilities tests', () => { }; data.provider = providerName + '1'; - const logService = new TestLogService(); + const logService = new NullLogService(); TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => { telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === data.provider)), TypeMoq.Times.once()); done(); diff --git a/src/sqltest/parts/commandLine/commandLineService.test.ts b/src/sqltest/parts/commandLine/commandLineService.test.ts index b49fdff1fa..321dd5f783 100644 --- a/src/sqltest/parts/commandLine/commandLineService.test.ts +++ b/src/sqltest/parts/commandLine/commandLineService.test.ts @@ -22,10 +22,10 @@ import { WorkspaceConfigurationTestService } from 'sqltest/stubs/workspaceConfig import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { assertThrowsAsync } from 'sqltest/utils/testUtils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestEditorService, TestLogService } from 'vs/workbench/test/workbenchTestServices'; +import { TestEditorService } from 'vs/workbench/test/workbenchTestServices'; import { QueryInput, QueryEditorState } from 'sql/workbench/parts/query/common/queryInput'; import { URI } from 'vs/base/common/uri'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; class TestParsedArgs implements ParsedArgs { [arg: string]: any; @@ -199,7 +199,7 @@ suite('commandLineService tests', () => { .verifiable(TypeMoq.Times.once()); connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile); const configurationService = getConfigurationServiceMock(true); - const logService = new TestLogService(); + const logService = new NullLogService(); let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService); await service.processCommandLine(args); connectionManagementService.verifyAll(); @@ -304,7 +304,7 @@ suite('commandLineService tests', () => { connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile); connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []); const configurationService = getConfigurationServiceMock(true); - const logService = new TestLogService(); + const logService = new NullLogService(); let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService); await service.processCommandLine(args); connectionManagementService.verifyAll(); @@ -347,7 +347,7 @@ suite('commandLineService tests', () => { connectionManagementService.setup(c => c.getConnectionProfileById('testID')).returns(() => originalProfile).verifiable(TypeMoq.Times.once()); connectionManagementService.setup(x => x.getConnectionGroups(TypeMoq.It.isAny())).returns(() => [conProfGroup]); const configurationService = getConfigurationServiceMock(true); - const logService = new TestLogService(); + const logService = new NullLogService(); let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService); await service.processCommandLine(args); connectionManagementService.verifyAll(); diff --git a/src/sqltest/parts/connection/connectionManagementService.test.ts b/src/sqltest/parts/connection/connectionManagementService.test.ts index 34b0277b74..59306192d4 100644 --- a/src/sqltest/parts/connection/connectionManagementService.test.ts +++ b/src/sqltest/parts/connection/connectionManagementService.test.ts @@ -32,8 +32,9 @@ import * as TypeMoq from 'typemoq'; import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { AccountManagementTestService } from 'sqltest/stubs/accountManagementStubs'; -import { TestStorageService, TestEnvironmentService, TestLogService } from 'vs/workbench/test/workbenchTestServices'; +import { TestStorageService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('SQL ConnectionManagementService tests', () => { @@ -86,7 +87,7 @@ suite('SQL ConnectionManagementService tests', () => { connectionStore = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService()); workbenchEditorService = TypeMoq.Mock.ofType(WorkbenchEditorTestService); editorGroupService = TypeMoq.Mock.ofType(EditorGroupTestService); - connectionStatusManager = new ConnectionStatusManager(capabilitiesService, new TestLogService(), TestEnvironmentService, new TestNotificationService()); + connectionStatusManager = new ConnectionStatusManager(capabilitiesService, new NullLogService(), TestEnvironmentService, new TestNotificationService()); mssqlConnectionProvider = TypeMoq.Mock.ofType(ConnectionProviderStub); let resourceProviderStub = new ResourceProviderStub(); resourceProviderStubMock = TypeMoq.Mock.ofInstance(resourceProviderStub); @@ -160,14 +161,13 @@ suite('SQL ConnectionManagementService tests', () => { workspaceConfigurationServiceMock.object, capabilitiesService, undefined, // IQuickInputService - undefined, // IStatusbarService + new TestNotificationService(), resourceProviderStubMock.object, undefined, // IAngularEventingService accountManagementService.object, - new TestLogService(), // ILogService + new NullLogService(), // ILogService undefined, // IStorageService - TestEnvironmentService, - new TestNotificationService() + TestEnvironmentService ); return connectionManagementService; } diff --git a/src/sqltest/parts/connection/connectionStatusManager.test.ts b/src/sqltest/parts/connection/connectionStatusManager.test.ts index d012b355f7..cbcdd6628f 100644 --- a/src/sqltest/parts/connection/connectionStatusManager.test.ts +++ b/src/sqltest/parts/connection/connectionStatusManager.test.ts @@ -10,9 +10,10 @@ import * as Utils from 'sql/platform/connection/common/utils'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; -import { TestEnvironmentService, TestLogService } from 'vs/workbench/test/workbenchTestServices'; +import { TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; +import { NullLogService } from 'vs/platform/log/common/log'; let connections: ConnectionStatusManager; let capabilitiesService: CapabilitiesTestService; @@ -77,7 +78,7 @@ suite('SQL ConnectionStatusManager tests', () => { setup(() => { capabilitiesService = new CapabilitiesTestService(); connectionProfileObject = new ConnectionProfile(capabilitiesService, connectionProfile); - connections = new ConnectionStatusManager(capabilitiesService, new TestLogService(), TestEnvironmentService, new TestNotificationService()); + connections = new ConnectionStatusManager(capabilitiesService, new NullLogService(), TestEnvironmentService, new TestNotificationService()); connection1Id = Utils.generateUri(connectionProfile); connection2Id = 'connection2Id'; connection3Id = 'connection3Id'; @@ -254,4 +255,4 @@ suite('SQL ConnectionStatusManager tests', () => { assert.equal(activeConnections.length, 4); assert.equal(activeConnections.filter(connection => connection.matches(newConnection)).length, 1, 'Did not find newConnection in active connections'); }); -}); \ No newline at end of file +}); diff --git a/src/sqltest/parts/connection/objectExplorerService.test.ts b/src/sqltest/parts/connection/objectExplorerService.test.ts index 9a9c736444..e6d6c89892 100644 --- a/src/sqltest/parts/connection/objectExplorerService.test.ts +++ b/src/sqltest/parts/connection/objectExplorerService.test.ts @@ -18,8 +18,8 @@ import { ServerTreeView } from 'sql/workbench/parts/objectExplorer/browser/serve import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes'; import { Event, Emitter } from 'vs/base/common/event'; import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; -import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('SQL Object Explorer Service tests', () => { let sqlOEProvider: TypeMoq.Mock; @@ -270,7 +270,7 @@ suite('SQL Object Explorer Service tests', () => { } }; - const logService = new TestLogService(); + const logService = new NullLogService(); objectExplorerService = new ObjectExplorerService(connectionManagementService.object, undefined, capabilitiesService, logService); objectExplorerService.registerProvider(mssqlProviderName, sqlOEProvider.object); sqlOEProvider.setup(x => x.createNewSession(TypeMoq.It.is(x => x.options['serverName'] === connection.serverName))).returns(() => new Promise((resolve) => { diff --git a/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts b/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts index c78dedd460..2fdcc0e541 100644 --- a/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts +++ b/src/sqltest/parts/dashboard/widgets/propertiesWidget.component.test.ts @@ -15,8 +15,8 @@ import { ConnectionManagementInfo } from 'sql/platform/connection/common/connect import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; -import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; +import { NullLogService } from 'vs/platform/log/common/log'; class TestChangeDetectorRef extends ChangeDetectorRef { reattach(): void { @@ -98,7 +98,7 @@ suite('Dashboard Properties Widget Tests', () => { dashboardService.setup(x => x.connectionManagementService).returns(() => singleConnectionService.object); - const testLogService = new class extends TestLogService { + const testLogService = new class extends NullLogService { error() { assert.fail('Called console Error unexpectedly'); } diff --git a/src/sqltest/parts/notebook/model/notebookModel.test.ts b/src/sqltest/parts/notebook/model/notebookModel.test.ts index fed9b629cf..17f133b6bd 100644 --- a/src/sqltest/parts/notebook/model/notebookModel.test.ts +++ b/src/sqltest/parts/notebook/model/notebookModel.test.ts @@ -24,11 +24,12 @@ import { Memento } from 'vs/workbench/common/memento'; import { Emitter } from 'vs/base/common/event'; import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; -import { TestStorageService, TestLogService } from 'vs/workbench/test/workbenchTestServices'; +import { TestStorageService } from 'vs/workbench/test/workbenchTestServices'; 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 { mssqlProviderName } from 'sql/platform/connection/common/constants'; +import { NullLogService } from 'vs/platform/log/common/log'; let expectedNotebookContent: nb.INotebookContents = { cells: [{ @@ -83,7 +84,7 @@ suite('notebook model', function (): void { let memento: TypeMoq.Mock; let queryConnectionService: TypeMoq.Mock; let defaultModelOptions: INotebookModelOptions; - const logService = new TestLogService(); + const logService = new NullLogService(); setup(() => { sessionReady = new Deferred(); notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose); diff --git a/src/sqltest/services/connectionManagement/connectionManagementService.test.ts b/src/sqltest/services/connectionManagement/connectionManagementService.test.ts index c668de355c..e3b2a6923b 100644 --- a/src/sqltest/services/connectionManagement/connectionManagementService.test.ts +++ b/src/sqltest/services/connectionManagement/connectionManagementService.test.ts @@ -36,7 +36,7 @@ suite('ConnectionManagementService Tests:', () => { connectionStoreMock.setup(x => x.getConnectionProfileGroups(TypeMoq.It.isAny(), undefined)).returns(() => { return [group1]; }); - const connectionManagementService = new ConnectionManagementService(connectionStoreMock.object, connectionStatusManagerMock.object, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const connectionManagementService = new ConnectionManagementService(connectionStoreMock.object, connectionStatusManagerMock.object, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); // dupe connections have been seeded the numbers below already reflected the de-duped results @@ -80,4 +80,4 @@ function createConnectionProfile(id: string): ConnectionProfile { function createConnectionGroup(id: string): ConnectionProfileGroup { return new ConnectionProfileGroup(id, undefined, id, undefined, undefined); -} \ No newline at end of file +} diff --git a/src/sqltest/stubs/editorGroupService.ts b/src/sqltest/stubs/editorGroupService.ts index e769fb3dce..ccd14a7777 100644 --- a/src/sqltest/stubs/editorGroupService.ts +++ b/src/sqltest/stubs/editorGroupService.ts @@ -13,6 +13,15 @@ import { IDimension } from 'vs/editor/common/editorCommon'; import { IDisposable } from 'vs/base/common/lifecycle'; export class EditorGroupTestService implements IEditorGroupsService { + getSize(group: number | IEditorGroup): { + width: number; height: number; /** + * Move an editor from this group either within this group or to another group. + */ } { + throw new Error('Method not implemented.'); + } + setSize(group: number | IEditorGroup, size: { width: number; height: number; }): void { + throw new Error('Method not implemented.'); + } willRestoreEditors: boolean; dimension: IDimension; whenRestored: Promise; @@ -212,19 +221,6 @@ export class EditorGroupTestService implements IEditorGroupsService { } - /** - * Returns the size of a group. - */ - getSize(group: IEditorGroup | GroupIdentifier): number { - return 0; - } - - /** - * Sets the size of a group. - */ - setSize(group: IEditorGroup | GroupIdentifier, size: number): void { - } - /** * Applies the provided layout by either moving existing groups or creating new groups. */ diff --git a/src/sqltest/stubs/storageTestService.ts b/src/sqltest/stubs/storageTestService.ts index 9d16e5cae8..4fb68245c4 100644 --- a/src/sqltest/stubs/storageTestService.ts +++ b/src/sqltest/stubs/storageTestService.ts @@ -7,6 +7,9 @@ import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent, IWillSaveS import { Event } from 'vs/base/common/event'; export class StorageTestService implements IStorageService { + logStorage(): void { + + } _serviceBrand: any; /** diff --git a/src/sqltest/stubs/telemetryServiceStub.ts b/src/sqltest/stubs/telemetryServiceStub.ts index 5edf619833..041247e2db 100644 --- a/src/sqltest/stubs/telemetryServiceStub.ts +++ b/src/sqltest/stubs/telemetryServiceStub.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; // Test stubs for commonly used objects @@ -21,6 +22,10 @@ export class TelemetryServiceStub implements ITelemetryService { return undefined; } + publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck, anonymizeFilePaths?: boolean): Promise { + return undefined; + } + getTelemetryInfo(): Promise { return undefined; } diff --git a/src/sqltest/workbench/api/extHostAccountManagement.test.ts b/src/sqltest/workbench/api/extHostAccountManagement.test.ts index d09743d523..7d1b613e0e 100644 --- a/src/sqltest/workbench/api/extHostAccountManagement.test.ts +++ b/src/sqltest/workbench/api/extHostAccountManagement.test.ts @@ -12,7 +12,7 @@ import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCP import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { SqlMainContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; -import { MainThreadAccountManagement } from 'sql/workbench/api/node/mainThreadAccountManagement'; +import { MainThreadAccountManagement } from 'sql/workbench/api/browser/mainThreadAccountManagement'; import { IAccountManagementService, AzureResource } from 'sql/platform/accounts/common/interfaces'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/sqltest/workbench/api/extHostCredentialManagement.test.ts b/src/sqltest/workbench/api/extHostCredentialManagement.test.ts index 9668980dab..a6a7d3c5fa 100644 --- a/src/sqltest/workbench/api/extHostCredentialManagement.test.ts +++ b/src/sqltest/workbench/api/extHostCredentialManagement.test.ts @@ -9,7 +9,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { ExtHostCredentialManagement } from 'sql/workbench/api/node/extHostCredentialManagement'; import { SqlMainContext } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; -import { MainThreadCredentialManagement } from 'sql/workbench/api/node/mainThreadCredentialManagement'; +import { MainThreadCredentialManagement } from 'sql/workbench/api/browser/mainThreadCredentialManagement'; import { CredentialsTestProvider, CredentialsTestService } from 'sqltest/stubs/credentialsTestStubs'; import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService'; import { Credential, CredentialProvider } from 'azdata'; diff --git a/src/sqltest/workbench/api/mainThreadBackgroundTaskManagement.test.ts b/src/sqltest/workbench/api/mainThreadBackgroundTaskManagement.test.ts index 078d66771a..656039a6a6 100644 --- a/src/sqltest/workbench/api/mainThreadBackgroundTaskManagement.test.ts +++ b/src/sqltest/workbench/api/mainThreadBackgroundTaskManagement.test.ts @@ -5,7 +5,7 @@ import * as azdata from 'azdata'; import { Mock, It, Times } from 'typemoq'; -import { MainThreadBackgroundTaskManagement, TaskStatus } from 'sql/workbench/api/node/mainThreadBackgroundTaskManagement'; +import { MainThreadBackgroundTaskManagement, TaskStatus } from 'sql/workbench/api/browser/mainThreadBackgroundTaskManagement'; import { ExtHostBackgroundTaskManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { ITaskService } from 'sql/platform/tasks/common/tasksService'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; diff --git a/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts b/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts index 5d3676eef9..694461c91b 100644 --- a/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts +++ b/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Mock, It, Times } from 'typemoq'; -import { MainThreadModelViewDialog } from 'sql/workbench/api/node/mainThreadModelViewDialog'; +import { MainThreadModelViewDialog } from 'sql/workbench/api/browser/mainThreadModelViewDialog'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails, IModelViewWizardPageDetails, IModelViewWizardDetails, DialogMessage, MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes'; import { CustomDialogService } from 'sql/platform/dialog/customDialogService'; diff --git a/src/sqltest/workbench/api/mainThreadNotebook.test.ts b/src/sqltest/workbench/api/mainThreadNotebook.test.ts index 45f039684f..94878a0451 100644 --- a/src/sqltest/workbench/api/mainThreadNotebook.test.ts +++ b/src/sqltest/workbench/api/mainThreadNotebook.test.ts @@ -12,7 +12,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostNotebookShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; -import { MainThreadNotebook } from 'sql/workbench/api/node/mainThreadNotebook'; +import { MainThreadNotebook } from 'sql/workbench/api/browser/mainThreadNotebook'; import { NotebookService } from 'sql/workbench/services/notebook/common/notebookServiceImpl'; import { INotebookProvider } from 'sql/workbench/services/notebook/common/notebookService'; import { INotebookManagerDetails, INotebookSessionDetails, INotebookKernelDetails, INotebookFutureDetails } from 'sql/workbench/api/common/sqlExtHostTypes'; diff --git a/src/tsconfig.json b/src/tsconfig.json index 5a3597bf75..2e95f74519 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -9,7 +9,8 @@ "lib": [ "dom", "es5", - "es2015.iterable" + "es2015.iterable", + "webworker" ] }, "include": [ diff --git a/src/typings/chokidar.d.ts b/src/typings/chokidar.d.ts index 60ba996d5f..1584d20fbb 100644 --- a/src/typings/chokidar.d.ts +++ b/src/typings/chokidar.d.ts @@ -5,84 +5,196 @@ declare module 'vscode-chokidar' { + // TypeScript Version: 3.0 + + import * as fs from "fs"; + import { EventEmitter } from "events"; + /** - * takes paths to be watched recursively and options + * The object's keys are all the directories (using absolute paths unless the `cwd` option was + * used), and the values are arrays of the names of the items contained in each directory. */ - export function watch(paths: string | string[], options: IOptions): FSWatcher; - - export interface IOptions { - - /** - * (regexp or function) files to be ignored. This function or regexp is tested against the whole path, not just filename. - * If it is a function with two arguments, it gets called twice per path - once with a single argument (the path), second time with two arguments (the path and the fs.Stats object of that path). - */ - ignored?: any; - - /** - * (default: false). Indicates whether the process should continue to run as long as files are being watched. - */ - persistent?: boolean; - - /** - * (default: false). Indicates whether to watch files that don't have read permissions. - */ - ignorePermissionErrors?: boolean; - - /** - * (default: false). Indicates whether chokidar should ignore the initial add events or not. - */ - ignoreInitial?: boolean; - - /** - * (default: 100). Interval of file system polling. - */ - interval?: number; - - /** - * (default: 300). Interval of file system polling for binary files (see extensions in src/is-binary). - */ - binaryInterval?: number; - - /** - * (default: false on Windows, true on Linux and OS X). Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU utilization, consider setting this to false. - */ - usePolling?: boolean; - - /** - * (default: true on OS X). Whether to use the fsevents watching interface if available. When set to true explicitly and fsevents is available this supercedes the usePolling setting. When set to false on OS X, usePolling: true becomes the default. - */ - useFsEvents?: boolean; - - /** - * (default: true). When false, only the symlinks themselves will be watched for changes instead of following the link references and bubbling events through the link's path. - */ - followSymlinks?: boolean; - - /** - * (default: false). If set to true then the strings passed to .watch() and .add() are treated as literal path names, even if they look like globs. - */ - disableGlobbing?: boolean; + export interface WatchedPaths { + [directory: string]: string[]; } - export interface FSWatcher { + export class FSWatcher extends EventEmitter implements fs.FSWatcher { - add(fileDirOrGlob: string): void; - add(filesDirsOrGlobs: Array): void; - - unwatch(fileDirOrGlob: string): void; - unwatch(filesDirsOrGlobs: Array): void; + readonly options?: WatchOptions; /** - * Listen for an FS event. Available events: add, addDir, change, unlink, unlinkDir, error. Additionally all is available which gets emitted for every non-error event. + * Constructs a new FSWatcher instance with optional WatchOptions parameter. */ - on(event: string, clb: (type: string, path: string) => void): void; - on(event: string, clb: (error: Error) => void): void; + constructor(options?: WatchOptions); + + /** + * Add files, directories, or glob patterns for tracking. Takes an array of strings or just one + * string. + */ + add(paths: string | string[]): void; + + /** + * Stop watching files, directories, or glob patterns. Takes an array of strings or just one + * string. + */ + unwatch(paths: string | string[]): void; + + /** + * Returns an object representing all the paths on the file system being watched by this + * `FSWatcher` instance. The object's keys are all the directories (using absolute paths unless + * the `cwd` option was used), and the values are arrays of the names of the items contained in + * each directory. + */ + getWatched(): WatchedPaths; /** * Removes all listeners from watched files. */ close(): void; - options: IOptions; + on(event: 'add' | 'addDir' | 'change', listener: (path: string, stats?: fs.Stats) => void): this; + + on(event: 'all', listener: (eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir', path: string, stats?: fs.Stats) => void): this; + + /** + * Error occured + */ + on(event: 'error', listener: (error: Error) => void): this; + + /** + * Exposes the native Node `fs.FSWatcher events` + */ + on(event: 'raw', listener: (eventName: string, path: string, details: any) => void): this; + + /** + * Fires when the initial scan is complete + */ + on(event: 'ready', listener: () => void): this; + + on(event: 'unlink' | 'unlinkDir', listener: (path: string) => void): this; + + on(event: string, listener: (...args: any[]) => void): this; } + + export interface WatchOptions { + /** + * Indicates whether the process should continue to run as long as files are being watched. If + * set to `false` when using `fsevents` to watch, no more events will be emitted after `ready`, + * even if the process continues to run. + */ + persistent?: boolean; + + /** + * ([anymatch](https://github.com/es128/anymatch)-compatible definition) Defines files/paths to + * be ignored. The whole relative or absolute path is tested, not just filename. If a function + * with two arguments is provided, it gets called twice per path - once with a single argument + * (the path), second time with two arguments (the path and the + * [`fs.Stats`](http://nodejs.org/api/fs.html#fs_class_fs_stats) object of that path). + */ + ignored?: any; + + /** + * If set to `false` then `add`/`addDir` events are also emitted for matching paths while + * instantiating the watching as chokidar discovers these file paths (before the `ready` event). + */ + ignoreInitial?: boolean; + + /** + * When `false`, only the symlinks themselves will be watched for changes instead of following + * the link references and bubbling events through the link's path. + */ + followSymlinks?: boolean; + + /** + * The base directory from which watch `paths` are to be derived. Paths emitted with events will + * be relative to this. + */ + cwd?: string; + + /** + * If set to true then the strings passed to .watch() and .add() are treated as literal path + * names, even if they look like globs. Default: false. + */ + disableGlobbing?: boolean; + + /** + * Whether to use fs.watchFile (backed by polling), or fs.watch. If polling leads to high CPU + * utilization, consider setting this to `false`. It is typically necessary to **set this to + * `true` to successfully watch files over a network**, and it may be necessary to successfully + * watch files in other non-standard situations. Setting to `true` explicitly on OS X overrides + * the `useFsEvents` default. + */ + usePolling?: boolean; + + /** + * Whether to use the `fsevents` watching interface if available. When set to `true` explicitly + * and `fsevents` is available this supercedes the `usePolling` setting. When set to `false` on + * OS X, `usePolling: true` becomes the default. + */ + useFsEvents?: boolean; + + /** + * If relying upon the [`fs.Stats`](http://nodejs.org/api/fs.html#fs_class_fs_stats) object that + * may get passed with `add`, `addDir`, and `change` events, set this to `true` to ensure it is + * provided even in cases where it wasn't already available from the underlying watch events. + */ + alwaysStat?: boolean; + + /** + * If set, limits how many levels of subdirectories will be traversed. + */ + depth?: number; + + /** + * Interval of file system polling. + */ + interval?: number; + + /** + * Interval of file system polling for binary files. ([see list of binary extensions](https://gi + * thub.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json)) + */ + binaryInterval?: number; + + /** + * Indicates whether to watch files that don't have read permissions if possible. If watching + * fails due to `EPERM` or `EACCES` with this set to `true`, the errors will be suppressed + * silently. + */ + ignorePermissionErrors?: boolean; + + /** + * `true` if `useFsEvents` and `usePolling` are `false`). Automatically filters out artifacts + * that occur when using editors that use "atomic writes" instead of writing directly to the + * source file. If a file is re-added within 100 ms of being deleted, Chokidar emits a `change` + * event rather than `unlink` then `add`. If the default of 100 ms does not work well for you, + * you can override it by setting `atomic` to a custom value, in milliseconds. + */ + atomic?: boolean | number; + + /** + * can be set to an object in order to adjust timing params: + */ + awaitWriteFinish?: AwaitWriteFinishOptions | boolean; + } + + export interface AwaitWriteFinishOptions { + /** + * Amount of time in milliseconds for a file size to remain constant before emitting its event. + */ + stabilityThreshold?: number; + + /** + * File size polling interval. + */ + pollInterval?: number; + } + + /** + * produces an instance of `FSWatcher`. + */ + export function watch( + paths: string | string[], + options?: WatchOptions + ): FSWatcher; } \ No newline at end of file diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 20e58bcd73..c52a879c36 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 3.1.8 +// Type definitions for Electron 4.2.5 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -86,7 +86,7 @@ declare namespace Electron { webviewTag: WebviewTag; } - interface AllElectron extends MainInterface, RendererInterface {} + interface AllElectron extends MainInterface, RendererInterface { } const app: App; const autoUpdater: AutoUpdater; @@ -119,7 +119,7 @@ declare namespace Electron { interface App extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/app + // Docs: http://electronjs.org/docs/api/app /** * Emitted when Chrome's accessibility support changes. This event fires when @@ -472,6 +472,100 @@ declare namespace Electron { once(event: 'ready', listener: (launchInfo: any) => void): this; addListener(event: 'ready', listener: (launchInfo: any) => void): this; removeListener(event: 'ready', listener: (launchInfo: any) => void): this; + /** + * Emitted when remote.getBuiltin() is called in the renderer process of + * webContents. Calling event.preventDefault() will prevent the module from being + * returned. Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-builtin', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + once(event: 'remote-get-builtin', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + addListener(event: 'remote-get-builtin', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + removeListener(event: 'remote-get-builtin', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + /** + * Emitted when remote.getCurrentWebContents() is called in the renderer process of + * webContents. Calling event.preventDefault() will prevent the object from being + * returned. Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-current-web-contents', listener: (event: Event, + webContents: WebContents) => void): this; + once(event: 'remote-get-current-web-contents', listener: (event: Event, + webContents: WebContents) => void): this; + addListener(event: 'remote-get-current-web-contents', listener: (event: Event, + webContents: WebContents) => void): this; + removeListener(event: 'remote-get-current-web-contents', listener: (event: Event, + webContents: WebContents) => void): this; + /** + * Emitted when remote.getCurrentWindow() is called in the renderer process of + * webContents. Calling event.preventDefault() will prevent the object from being + * returned. Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-current-window', listener: (event: Event, + webContents: WebContents) => void): this; + once(event: 'remote-get-current-window', listener: (event: Event, + webContents: WebContents) => void): this; + addListener(event: 'remote-get-current-window', listener: (event: Event, + webContents: WebContents) => void): this; + removeListener(event: 'remote-get-current-window', listener: (event: Event, + webContents: WebContents) => void): this; + /** + * Emitted when remote.getGlobal() is called in the renderer process of + * webContents. Calling event.preventDefault() will prevent the global from being + * returned. Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-global', listener: (event: Event, + webContents: WebContents, + globalName: string) => void): this; + once(event: 'remote-get-global', listener: (event: Event, + webContents: WebContents, + globalName: string) => void): this; + addListener(event: 'remote-get-global', listener: (event: Event, + webContents: WebContents, + globalName: string) => void): this; + removeListener(event: 'remote-get-global', listener: (event: Event, + webContents: WebContents, + globalName: string) => void): this; + /** + * Emitted when .getWebContents() is called in the renderer process of + * webContents. Calling event.preventDefault() will prevent the object from being + * returned. Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-guest-web-contents', listener: (event: Event, + webContents: WebContents, + guestWebContents: WebContents) => void): this; + once(event: 'remote-get-guest-web-contents', listener: (event: Event, + webContents: WebContents, + guestWebContents: WebContents) => void): this; + addListener(event: 'remote-get-guest-web-contents', listener: (event: Event, + webContents: WebContents, + guestWebContents: WebContents) => void): this; + removeListener(event: 'remote-get-guest-web-contents', listener: (event: Event, + webContents: WebContents, + guestWebContents: WebContents) => void): this; + /** + * Emitted when remote.require() is called in the renderer process of webContents. + * Calling event.preventDefault() will prevent the module from being returned. + * Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-require', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + once(event: 'remote-require', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + addListener(event: 'remote-require', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; + removeListener(event: 'remote-require', listener: (event: Event, + webContents: WebContents, + moduleName: string) => void): this; /** * This event will be emitted inside the primary instance of your application when * a second instance has been executed. argv is an Array of the second instance's @@ -690,6 +784,11 @@ declare namespace Electron { * is ready. */ enableMixedSandbox(): void; + /** + * Enables full sandbox mode on the app. This method can only be called before app + * is ready. + */ + enableSandbox(): void; /** * Exits immediately with exitCode. exitCode defaults to 0. All windows will be * closed immediately without asking user and the before-quit and will-quit events @@ -709,13 +808,22 @@ declare namespace Electron { * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; /** * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; getGPUFeatureStatus(): GPUFeatureStatus; + /** + * For infoType equal to complete: Promise is fulfilled with Object containing all + * the GPU Information as in chromium's GPUInfo object. This includes the version + * and driver information that's shown on chrome://gpu page. For infoType equal to + * basic: Promise is fulfilled with Object containing fewer attributes than when + * requested with complete. Here's an example of basic response: Using basic should + * be preferred if only basic information like vendorId or driverId is needed. + */ + getGPUInfo(infoType: string): Promise; getJumpListSettings(): JumpListSettings; /** * To set the locale, you'll want to use a command line switch at app startup, @@ -754,7 +862,7 @@ declare namespace Electron { /** * Imports the certificate in pkcs12 format into the platform certificate store. * callback is called with the result of import operation, a value of 0 indicates - * success while any other value indicates failure according to chromium + * success while any other value indicates failure according to Chromium * net_error_list. */ importCertificate(options: ImportCertificateOptions, callback: (result: number) => void): void; @@ -843,9 +951,9 @@ declare namespace Electron { setAboutPanelOptions(options: AboutPanelOptionsOptions): void; /** * Manually enables Chrome's accessibility support, allowing to expose - * accessibility switch to users in application settings. - * https://www.chromium.org/developers/design-documents/accessibility for more - * details. Disabled by default. Note: Rendering accessibility tree can + * accessibility switch to users in application settings. See Chromium's + * accessibility docs for more details. Disabled by default. This API must be + * called after the ready event is emitted. Note: Rendering accessibility tree can * significantly affect the performance of your app. It should not be enabled by * default. */ @@ -929,7 +1037,12 @@ declare namespace Electron { */ show(): void; /** - * Start accessing a security scoped resource. With this method electron + * Show the about panel with the values defined in the app's .plist file or with + * the options set via app.setAboutPanelOptions(options). + */ + showAboutPanel(): void; + /** + * Start accessing a security scoped resource. With this method Electron * applications that are packaged for the Mac App Store may reach outside their * sandbox to access files chosen by the user. See Apple's documentation for a * description of how this system works. @@ -940,7 +1053,7 @@ declare namespace Electron { * userInfo into its current userInfo dictionary. */ updateCurrentActivity(type: string, userInfo: any): void; - whenReady(): Promise; + whenReady(): Promise; commandLine: CommandLine; dock: Dock; /** @@ -949,11 +1062,19 @@ declare namespace Electron { * production environments. */ isPackaged?: boolean; + /** + * A String which is the user agent string Electron will use as a global fallback. + * This is the user agent that will be used when no user agent is set at the + * webContents or session level. Useful for ensuring your entire app has the same + * user agent. Set to a custom value as early as possible in your apps + * initialization to ensure that your overridden value is used. + */ + userAgentFallback?: string; } interface AutoUpdater extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/auto-updater + // Docs: http://electronjs.org/docs/api/auto-updater /** * This event is emitted after a user calls quitAndInstall(). When this API is @@ -990,7 +1111,9 @@ declare namespace Electron { removeListener(event: 'update-available', listener: Function): this; /** * Emitted when an update has been downloaded. On Windows only releaseName is - * available. + * available. Note: It is not strictly necessary to handle this event. A + * successfully downloaded update will still be applied the next time the + * application starts. */ on(event: 'update-downloaded', listener: (event: Event, releaseNotes: string, @@ -1029,10 +1152,10 @@ declare namespace Electron { * Restarts the app and installs the update after it has been downloaded. It should * only be called after update-downloaded has been emitted. Under the hood calling * autoUpdater.quitAndInstall() will close all application windows first, and - * automatically call app.quit() after all windows have been closed. Note: If the - * application is quit without calling this API after the update-downloaded event - * has been emitted, the application will still be replaced by the updated one on - * the next run. + * automatically call app.quit() after all windows have been closed. Note: It is + * not strictly necessary to call this function to apply an update, as a + * successfully downloaded update will always be applied the next time the + * application starts. */ quitAndInstall(): void; /** @@ -1043,7 +1166,7 @@ declare namespace Electron { interface BluetoothDevice { - // Docs: http://electron.atom.io/docs/api/structures/bluetooth-device + // Docs: http://electronjs.org/docs/api/structures/bluetooth-device deviceId: string; deviceName: string; @@ -1051,11 +1174,11 @@ declare namespace Electron { class BrowserView extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/browser-view + // Docs: http://electronjs.org/docs/api/browser-view constructor(options?: BrowserViewConstructorOptions); static fromId(id: number): BrowserView; - static fromWebContents(webContents: WebContents): BrowserView | null; + static fromWebContents(webContents: WebContents): (BrowserView) | (null); static getAllViews(): BrowserView[]; /** * Force closing the view, the unload and beforeunload events won't be emitted for @@ -1076,8 +1199,19 @@ declare namespace Electron { class BrowserWindow extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/browser-window + // Docs: http://electronjs.org/docs/api/browser-window + /** + * Emitted when the window is set or unset to show always on top of other windows. + */ + on(event: 'always-on-top-changed', listener: (event: Event, + isAlwaysOnTop: boolean) => void): this; + once(event: 'always-on-top-changed', listener: (event: Event, + isAlwaysOnTop: boolean) => void): this; + addListener(event: 'always-on-top-changed', listener: (event: Event, + isAlwaysOnTop: boolean) => void): this; + removeListener(event: 'always-on-top-changed', listener: (event: Event, + isAlwaysOnTop: boolean) => void): this; /** * Emitted when an App Command is invoked. These are typically related to keyboard * media keys or browser commands, as well as the "Back" button built into some @@ -1204,16 +1338,21 @@ declare namespace Electron { removeListener(event: 'new-window-for-tab', listener: Function): this; /** * Emitted when the document changed its title, calling event.preventDefault() will - * prevent the native window's title from changing. + * prevent the native window's title from changing. explicitSet is false when title + * is synthesized from file url. */ on(event: 'page-title-updated', listener: (event: Event, - title: string) => void): this; + title: string, + explicitSet: boolean) => void): this; once(event: 'page-title-updated', listener: (event: Event, - title: string) => void): this; + title: string, + explicitSet: boolean) => void): this; addListener(event: 'page-title-updated', listener: (event: Event, - title: string) => void): this; + title: string, + explicitSet: boolean) => void): this; removeListener(event: 'page-title-updated', listener: (event: Event, - title: string) => void): this; + title: string, + explicitSet: boolean) => void): this; /** * Emitted when the web page has been rendered (while not being shown) and window * can be displayed without a visual flash. @@ -1223,7 +1362,7 @@ declare namespace Electron { addListener(event: 'ready-to-show', listener: Function): this; removeListener(event: 'ready-to-show', listener: Function): this; /** - * Emitted when the window is being resized. + * Emitted after the window has been resized. */ on(event: 'resize', listener: Function): this; once(event: 'resize', listener: Function): this; @@ -1318,6 +1457,58 @@ declare namespace Electron { once(event: 'unresponsive', listener: Function): this; addListener(event: 'unresponsive', listener: Function): this; removeListener(event: 'unresponsive', listener: Function): this; + /** + * Emitted before the window is moved. Calling event.preventDefault() will prevent + * the window from being moved. Note that this is only emitted when the window is + * being resized manually. Resizing the window with setBounds/setSize will not emit + * this event. + */ + on(event: 'will-move', listener: (event: Event, + /** + * ` Location the window is being moved to. + */ + newBounds: Rectangle) => void): this; + once(event: 'will-move', listener: (event: Event, + /** + * ` Location the window is being moved to. + */ + newBounds: Rectangle) => void): this; + addListener(event: 'will-move', listener: (event: Event, + /** + * ` Location the window is being moved to. + */ + newBounds: Rectangle) => void): this; + removeListener(event: 'will-move', listener: (event: Event, + /** + * ` Location the window is being moved to. + */ + newBounds: Rectangle) => void): this; + /** + * Emitted before the window is resized. Calling event.preventDefault() will + * prevent the window from being resized. Note that this is only emitted when the + * window is being resized manually. Resizing the window with setBounds/setSize + * will not emit this event. + */ + on(event: 'will-resize', listener: (event: Event, + /** + * ` Size the window is being resized to. + */ + newBounds: Rectangle) => void): this; + once(event: 'will-resize', listener: (event: Event, + /** + * ` Size the window is being resized to. + */ + newBounds: Rectangle) => void): this; + addListener(event: 'will-resize', listener: (event: Event, + /** + * ` Size the window is being resized to. + */ + newBounds: Rectangle) => void): this; + removeListener(event: 'will-resize', listener: (event: Event, + /** + * ` Size the window is being resized to. + */ + newBounds: Rectangle) => void): this; constructor(options?: BrowserWindowConstructorOptions); /** * Adds DevTools extension located at path, and returns extension's name. The @@ -1335,7 +1526,7 @@ declare namespace Electron { * This API cannot be called before the ready event of the app module is emitted. */ static addExtension(path: string): void; - static fromBrowserView(browserView: BrowserView): BrowserWindow | null; + static fromBrowserView(browserView: BrowserView): (BrowserWindow) | (null); static fromId(id: number): BrowserWindow; static fromWebContents(webContents: WebContents): BrowserWindow; static getAllWindows(): BrowserWindow[]; @@ -1349,7 +1540,7 @@ declare namespace Electron { * emitted. */ static getExtensions(): Extensions; - static getFocusedWindow(): BrowserWindow | null; + static getFocusedWindow(): (BrowserWindow) | (null); /** * Remove a DevTools extension by name. Note: This API cannot be called before the * ready event of the app module is emitted. @@ -1411,7 +1602,7 @@ declare namespace Electron { * Note: The BrowserView API is currently experimental and may change or be removed * in future Electron releases. */ - getBrowserView(): BrowserView | null; + getBrowserView(): (BrowserView) | (null); getChildWindows(): BrowserWindow[]; getContentBounds(): Rectangle; getContentSize(): number[]; @@ -1422,6 +1613,13 @@ declare namespace Electron { * (unsigned long) on Linux. */ getNativeWindowHandle(): Buffer; + /** + * Note: whatever the current state of the window : maximized, minimized or in + * fullscreen, this function always returns the position and size of the window in + * normal state. In normal state, getBounds and getNormalBounds returns the same + * Rectangle. + */ + getNormalBounds(): Rectangle; getOpacity(): number; getParentWindow(): BrowserWindow; getPosition(): number[]; @@ -1473,6 +1671,7 @@ declare namespace Electron { * On Linux always returns true. */ isMovable(): boolean; + isNormal(): boolean; isResizable(): boolean; isSimpleFullScreen(): boolean; isVisible(): boolean; @@ -1485,7 +1684,7 @@ declare namespace Electron { * Same as webContents.loadFile, filePath should be a path to an HTML file relative * to the root of your application. See the webContents docs for more information. */ - loadFile(filePath: string): void; + loadFile(filePath: string, options?: LoadFileOptions): void; /** * Same as webContents.loadURL(url[, options]). The url can be a remote address * (e.g. http://) or a path to a local HTML file using the file:// protocol. To @@ -1579,7 +1778,12 @@ declare namespace Electron { */ setAutoHideMenuBar(hide: boolean): void; /** - * Resizes and moves the window to the supplied bounds + * Sets the background color of the window. See Setting backgroundColor. + */ + setBackgroundColor(backgroundColor: string): void; + /** + * Resizes and moves the window to the supplied bounds. Any properties that are not + * supplied will default to their current values. */ setBounds(bounds: Rectangle, animate?: boolean): void; setBrowserView(browserView: BrowserView): void; @@ -1655,7 +1859,7 @@ declare namespace Electron { * Sets the menu as the window's menu bar, setting it to null will remove the menu * bar. */ - setMenu(menu: Menu | null): void; + setMenu(menu: (Menu) | (null)): void; /** * Sets whether the menu bar should be visible. If the menu bar is auto-hide, users * can still bring up the menu bar by pressing the single Alt key. @@ -1682,7 +1886,7 @@ declare namespace Electron { * Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to * convey some sort of application status or to passively notify the user. */ - setOverlayIcon(overlay: NativeImage | null, description: string): void; + setOverlayIcon(overlay: (NativeImage) | (null), description: string): void; /** * Sets parent as current window's parent window, passing null will turn current * window into a top-level window. @@ -1754,7 +1958,8 @@ declare namespace Electron { /** * Sets the region of the window to show as the thumbnail image displayed when * hovering over the window in the taskbar. You can reset the thumbnail to be the - * entire window by specifying an empty region: {x: 0, y: 0, width: 0, height: 0}. + * entire window by specifying an empty region: { x: 0, y: 0, width: 0, height: 0 + * }. */ setThumbnailClip(region: Rectangle): void; /** @@ -1782,7 +1987,12 @@ declare namespace Electron { * Sets whether the window should be visible on all workspaces. Note: This API does * nothing on Windows. */ - setVisibleOnAllWorkspaces(visible: boolean): void; + setVisibleOnAllWorkspaces(visible: boolean, options?: VisibleOnAllWorkspacesOptions): void; + /** + * Sets whether the window traffic light buttons should be visible. This cannot be + * called when titleBarStyle is set to customButtonsOnHover. + */ + setWindowButtonVisibility(visible: boolean): void; /** * Shows and gives focus to the window. */ @@ -1818,7 +2028,7 @@ declare namespace Electron { class BrowserWindowProxy extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/browser-window-proxy + // Docs: http://electronjs.org/docs/api/browser-window-proxy /** * Removes focus from the child window. @@ -1851,7 +2061,7 @@ declare namespace Electron { interface Certificate { - // Docs: http://electron.atom.io/docs/api/structures/certificate + // Docs: http://electronjs.org/docs/api/structures/certificate /** * PEM encoded data @@ -1897,37 +2107,37 @@ declare namespace Electron { interface CertificatePrincipal { - // Docs: http://electron.atom.io/docs/api/structures/certificate-principal + // Docs: http://electronjs.org/docs/api/structures/certificate-principal /** - * Common Name + * Common Name. */ commonName: string; /** - * Country or region + * Country or region. */ country: string; /** - * Locality + * Locality. */ locality: string; /** - * Organization names + * Organization names. */ organizations: string[]; /** - * Organization Unit names + * Organization Unit names. */ organizationUnits: string[]; /** - * State or province + * State or province. */ state: string; } class ClientRequest extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/client-request + // Docs: http://electronjs.org/docs/api/client-request /** * Emitted when the request is aborted. The abort event will not be fired if the @@ -2045,7 +2255,7 @@ declare namespace Electron { * Sends the last chunk of the request data. Subsequent write or end operations * will not be allowed. The finish event is emitted just after the end operation. */ - end(chunk?: string | Buffer, encoding?: string, callback?: Function): void; + end(chunk?: (string) | (Buffer), encoding?: string, callback?: Function): void; /** * Continues any deferred redirection request when the redirection mode is manual. */ @@ -2078,13 +2288,13 @@ declare namespace Electron { * issued on the wire. After the first write operation, it is not allowed to add or * remove a custom header. */ - write(chunk: string | Buffer, encoding?: string, callback?: Function): void; + write(chunk: (string) | (Buffer), encoding?: string, callback?: Function): void; chunkedEncoding: boolean; } interface Clipboard extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/clipboard + // Docs: http://electronjs.org/docs/api/clipboard availableFormats(type?: string): string[]; /** @@ -2144,7 +2354,7 @@ declare namespace Electron { interface ContentTracing extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/content-tracing + // Docs: http://electronjs.org/docs/api/content-tracing /** * Get the current monitoring traced data. Child processes typically cache trace @@ -2181,7 +2391,7 @@ declare namespace Electron { * request. The callback will be called once all child processes have acknowledged * the startRecording request. */ - startRecording(options: TraceCategoriesAndOptions | TraceConfig, callback: Function): void; + startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig), callback: Function): void; /** * Stop monitoring on all processes. Once all child processes have acknowledged the * stopMonitoring request the callback is called. @@ -2203,10 +2413,11 @@ declare namespace Electron { interface Cookie { - // Docs: http://electron.atom.io/docs/api/structures/cookie + // Docs: http://electronjs.org/docs/api/structures/cookie /** - * The domain of the cookie. + * The domain of the cookie; this will be normalized with a preceding dot so that + * it's also valid for subdomains. */ domain?: string; /** @@ -2215,7 +2426,8 @@ declare namespace Electron { */ expirationDate?: number; /** - * Whether the cookie is a host-only cookie. + * Whether the cookie is a host-only cookie; this will only be true if no domain + * was passed. */ hostOnly?: boolean; /** @@ -2247,7 +2459,7 @@ declare namespace Electron { class Cookies extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/cookies + // Docs: http://electronjs.org/docs/api/cookies /** * Emitted when a cookie is changed because it was added, edited, removed, or @@ -2328,7 +2540,7 @@ declare namespace Electron { interface CPUUsage { - // Docs: http://electron.atom.io/docs/api/structures/cpu-usage + // Docs: http://electronjs.org/docs/api/structures/cpu-usage /** * The number of average idle cpu wakeups per second since the last call to @@ -2343,7 +2555,7 @@ declare namespace Electron { interface CrashReport { - // Docs: http://electron.atom.io/docs/api/structures/crash-report + // Docs: http://electronjs.org/docs/api/structures/crash-report date: Date; id: string; @@ -2351,7 +2563,7 @@ declare namespace Electron { interface CrashReporter extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/crash-reporter + // Docs: http://electronjs.org/docs/api/crash-reporter /** * Set an extra parameter to be sent with the crash report. The values specified @@ -2362,8 +2574,10 @@ declare namespace Electron { */ addExtraParameter(key: string, value: string): void; /** - * Returns the date and ID of the last crash report. If no crash reports have been - * sent or the crash reporter has not been started, null is returned. + * Returns the date and ID of the last crash report. Only crash reports that have + * been uploaded will be returned; even if a crash report is present on disk it + * will not be returned until it is uploaded. In the case that there are no + * uploaded reports, null is returned. */ getLastCrashReport(): CrashReport; /** @@ -2419,7 +2633,7 @@ declare namespace Electron { class Debugger extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/debugger + // Docs: http://electronjs.org/docs/api/debugger /** * Emitted when debugging session is terminated. This happens either when @@ -2505,7 +2719,7 @@ declare namespace Electron { interface DesktopCapturer extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/desktop-capturer + // Docs: http://electronjs.org/docs/api/desktop-capturer /** * Starts gathering information about all available desktop media sources, and @@ -2518,7 +2732,7 @@ declare namespace Electron { interface DesktopCapturerSource { - // Docs: http://electron.atom.io/docs/api/structures/desktop-capturer-source + // Docs: http://electronjs.org/docs/api/structures/desktop-capturer-source /** * A unique identifier that will correspond to the id of the matching returned by @@ -2534,8 +2748,8 @@ declare namespace Electron { */ id: string; /** - * A screen source will be named either Entire Screen or Screen , while the - * name of a window source will match the window title. + * A screen source will be named either Entire Screen or Screen , while the name of + * a window source will match the window title. */ name: string; /** @@ -2549,7 +2763,7 @@ declare namespace Electron { interface Dialog extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/dialog + // Docs: http://electronjs.org/docs/api/dialog /** * On macOS, this displays a modal dialog that shows a message and certificate @@ -2610,7 +2824,7 @@ declare namespace Electron { * file selector and a directory selector, so if you set properties to ['openFile', * 'openDirectory'] on these platforms, a directory selector will be shown. */ - showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): string[]; + showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): (string[]) | (undefined); /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can @@ -2623,7 +2837,7 @@ declare namespace Electron { * file selector and a directory selector, so if you set properties to ['openFile', * 'openDirectory'] on these platforms, a directory selector will be shown. */ - showOpenDialog(options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): string[]; + showOpenDialog(options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): (string[]) | (undefined); /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can @@ -2631,7 +2845,7 @@ declare namespace Electron { * the API call will be asynchronous and the result will be passed via * callback(filename). */ - showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): string; + showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): (string) | (undefined); /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can @@ -2639,12 +2853,12 @@ declare namespace Electron { * the API call will be asynchronous and the result will be passed via * callback(filename). */ - showSaveDialog(options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): string; + showSaveDialog(options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): (string) | (undefined); } interface Display { - // Docs: http://electron.atom.io/docs/api/structures/display + // Docs: http://electronjs.org/docs/api/structures/display bounds: Rectangle; /** @@ -2670,7 +2884,7 @@ declare namespace Electron { class DownloadItem extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/download-item + // Docs: http://electronjs.org/docs/api/download-item /** * Emitted when the download is in a terminal state. This includes a completed @@ -2773,7 +2987,7 @@ declare namespace Electron { interface FileFilter { - // Docs: http://electron.atom.io/docs/api/structures/file-filter + // Docs: http://electronjs.org/docs/api/structures/file-filter extensions: string[]; name: string; @@ -2781,7 +2995,7 @@ declare namespace Electron { interface GlobalShortcut extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/global-shortcut + // Docs: http://electronjs.org/docs/api/global-shortcut /** * When the accelerator is already taken by other applications, this call will @@ -2811,65 +3025,65 @@ declare namespace Electron { interface GPUFeatureStatus { - // Docs: http://electron.atom.io/docs/api/structures/gpu-feature-status + // Docs: http://electronjs.org/docs/api/structures/gpu-feature-status /** - * Canvas + * Canvas. */ '2d_canvas': string; /** - * Flash + * Flash. */ flash_3d: string; /** - * Flash Stage3D + * Flash Stage3D. */ flash_stage3d: string; /** - * Flash Stage3D Baseline profile + * Flash Stage3D Baseline profile. */ flash_stage3d_baseline: string; /** - * Compositing + * Compositing. */ gpu_compositing: string; /** - * Multiple Raster Threads + * Multiple Raster Threads. */ multiple_raster_threads: string; /** - * Native GpuMemoryBuffers + * Native GpuMemoryBuffers. */ native_gpu_memory_buffers: string; /** - * Rasterization + * Rasterization. */ rasterization: string; /** - * Video Decode + * Video Decode. */ video_decode: string; /** - * Video Encode + * Video Encode. */ video_encode: string; /** - * VPx Video Decode + * VPx Video Decode. */ vpx_decode: string; /** - * WebGL + * WebGL. */ webgl: string; /** - * WebGL2 + * WebGL2. */ webgl2: string; } interface InAppPurchase extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/in-app-purchase + // Docs: http://electronjs.org/docs/api/in-app-purchase /** * Emitted when one or more transactions have been updated. @@ -2917,7 +3131,7 @@ declare namespace Electron { class IncomingMessage extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/incoming-message + // Docs: http://electronjs.org/docs/api/incoming-message /** * Emitted when a request has been canceled during an ongoing HTTP transaction. @@ -2978,7 +3192,7 @@ declare namespace Electron { interface IOCounters { - // Docs: http://electron.atom.io/docs/api/structures/io-counters + // Docs: http://electronjs.org/docs/api/structures/io-counters /** * Then number of I/O other operations. @@ -3008,7 +3222,7 @@ declare namespace Electron { interface IpcMain extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/ipc-main + // Docs: http://electronjs.org/docs/api/ipc-main /** * Listens to channel, when a new message arrives listener would be called with @@ -3033,7 +3247,7 @@ declare namespace Electron { interface IpcRenderer extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/ipc-renderer + // Docs: http://electronjs.org/docs/api/ipc-renderer /** * Listens to channel, when a new message arrives listener would be called with @@ -3083,7 +3297,7 @@ declare namespace Electron { interface JumpListCategory { - // Docs: http://electron.atom.io/docs/api/structures/jump-list-category + // Docs: http://electronjs.org/docs/api/structures/jump-list-category /** * Array of objects if type is tasks or custom, otherwise it should be omitted. @@ -3101,7 +3315,7 @@ declare namespace Electron { interface JumpListItem { - // Docs: http://electron.atom.io/docs/api/structures/jump-list-item + // Docs: http://electronjs.org/docs/api/structures/jump-list-item /** * The command line arguments when program is executed. Should only be set if type @@ -3146,38 +3360,9 @@ declare namespace Electron { type?: ('task' | 'separator' | 'file'); } - interface MemoryInfo { - - // Docs: http://electron.atom.io/docs/api/structures/memory-info - - /** - * The maximum amount of memory that has ever been pinned to actual physical RAM. - * On macOS its value will always be 0. - */ - peakWorkingSetSize: number; - /** - * Process id of the process. - */ - pid: number; - /** - * The amount of memory not shared by other processes, such as JS heap or HTML - * content. - */ - privateBytes: number; - /** - * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself - */ - sharedBytes: number; - /** - * The amount of memory currently pinned to actual physical RAM. - */ - workingSetSize: number; - } - interface MemoryUsageDetails { - // Docs: http://electron.atom.io/docs/api/structures/memory-usage-details + // Docs: http://electronjs.org/docs/api/structures/memory-usage-details count: number; liveSize: number; @@ -3186,7 +3371,7 @@ declare namespace Electron { class Menu { - // Docs: http://electron.atom.io/docs/api/menu + // Docs: http://electronjs.org/docs/api/menu /** * Emitted when a popup is closed either manually or with menu.closePopup(). @@ -3213,7 +3398,7 @@ declare namespace Electron { * Note: The returned Menu instance doesn't support dynamic addition or removal of * menu items. Instance properties can still be dynamically modified. */ - static getApplicationMenu(): Menu | null; + static getApplicationMenu(): (Menu) | (null); /** * Sends the action to the first responder of application. This is used for * emulating default macOS menu behaviors. Usually you would use the role property @@ -3227,7 +3412,7 @@ declare namespace Electron { * Windows and Linux but has no effect on macOS. Note: This API has to be called * after the ready event of app module. */ - static setApplicationMenu(menu: Menu | null): void; + static setApplicationMenu(menu: (Menu) | (null)): void; /** * Appends the menuItem to the menu. */ @@ -3244,13 +3429,13 @@ declare namespace Electron { /** * Pops up this menu as a context menu in the BrowserWindow. */ - popup(options: PopupOptions): void; + popup(options?: PopupOptions): void; items: MenuItem[]; } class MenuItem { - // Docs: http://electron.atom.io/docs/api/menu-item + // Docs: http://electronjs.org/docs/api/menu-item constructor(options: MenuItemConstructorOptions); checked: boolean; @@ -3262,21 +3447,21 @@ declare namespace Electron { interface MimeTypedBuffer { - // Docs: http://electron.atom.io/docs/api/structures/mime-typed-buffer + // Docs: http://electronjs.org/docs/api/structures/mime-typed-buffer /** - * The actual Buffer content + * The actual Buffer content. */ data: Buffer; /** - * The mimeType of the Buffer that you are sending + * The mimeType of the Buffer that you are sending. */ mimeType: string; } class NativeImage { - // Docs: http://electron.atom.io/docs/api/native-image + // Docs: http://electronjs.org/docs/api/native-image /** * Creates an empty NativeImage instance. @@ -3294,7 +3479,14 @@ declare namespace Electron { * Creates a new NativeImage instance from the NSImage that maps to the given image * name. See NSImageName for a list of possible values. The hslShift is applied to * the image with the following rules This means that [-1, 0, 1] will make the - * image completely white and [-1, 1, 0] will make the image completely black. + * image completely white and [-1, 1, 0] will make the image completely black. In + * some cases, the NSImageName doesn't match its string representation; one example + * of this is NSFolderImageName, whose string representation would actually be + * NSFolder. Therefore, you'll need to determine the correct string representation + * for your image before passing it in. This can be done with the following: echo + * -e '#import \nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' | + * clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME + * should be replaced with any value from this list. */ static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage; /** @@ -3343,7 +3535,7 @@ declare namespace Electron { interface Net extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/net + // Docs: http://electronjs.org/docs/api/net /** * Creates a ClientRequest instance using the provided options which are directly @@ -3351,12 +3543,12 @@ declare namespace Electron { * to issue both secure and insecure HTTP requests according to the specified * protocol scheme in the options object. */ - request(options: any | string): ClientRequest; + request(options: (any) | (string)): ClientRequest; } interface NetLog extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/net-log + // Docs: http://electronjs.org/docs/api/net-log /** * Starts recording network events to path. @@ -3379,7 +3571,7 @@ declare namespace Electron { class Notification extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/notification + // Docs: http://electronjs.org/docs/api/notification on(event: 'action', listener: (event: Event, /** @@ -3469,7 +3661,7 @@ declare namespace Electron { interface NotificationAction { - // Docs: http://electron.atom.io/docs/api/structures/notification-action + // Docs: http://electronjs.org/docs/api/structures/notification-action /** * The label for the given action. @@ -3483,7 +3675,7 @@ declare namespace Electron { interface Point { - // Docs: http://electron.atom.io/docs/api/structures/point + // Docs: http://electronjs.org/docs/api/structures/point x: number; y: number; @@ -3491,7 +3683,7 @@ declare namespace Electron { interface PowerMonitor extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/power-monitor + // Docs: http://electronjs.org/docs/api/power-monitor /** * Emitted when the system is about to lock the screen. @@ -3549,7 +3741,7 @@ declare namespace Electron { interface PowerSaveBlocker extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/power-save-blocker + // Docs: http://electronjs.org/docs/api/power-save-blocker isStarted(id: number): boolean; /** @@ -3571,7 +3763,7 @@ declare namespace Electron { interface PrinterInfo { - // Docs: http://electron.atom.io/docs/api/structures/printer-info + // Docs: http://electronjs.org/docs/api/structures/printer-info description: string; isDefault: boolean; @@ -3581,16 +3773,12 @@ declare namespace Electron { interface ProcessMetric { - // Docs: http://electron.atom.io/docs/api/structures/process-metric + // Docs: http://electronjs.org/docs/api/structures/process-metric /** * CPU usage of the process. */ cpu: CPUUsage; - /** - * Memory information for the process. - */ - memory: MemoryInfo; /** * Process id of the process. */ @@ -3603,7 +3791,7 @@ declare namespace Electron { interface Product { - // Docs: http://electron.atom.io/docs/api/structures/product + // Docs: http://electronjs.org/docs/api/structures/product /** * The total size of the content, in bytes. @@ -3642,7 +3830,7 @@ declare namespace Electron { interface Protocol extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/protocol + // Docs: http://electronjs.org/docs/api/protocol /** * Intercepts scheme protocol and uses handler as the protocol's new handler which @@ -3663,7 +3851,7 @@ declare namespace Electron { * Same as protocol.registerStreamProtocol, except that it replaces an existing * protocol handler. */ - interceptStreamProtocol(scheme: string, handler: (request: InterceptStreamProtocolRequest, callback: (stream?: ReadableStream | StreamProtocolResponse) => void) => void, completion?: (error: Error) => void): void; + interceptStreamProtocol(scheme: string, handler: (request: InterceptStreamProtocolRequest, callback: (stream?: (NodeJS.ReadableStream) | (StreamProtocolResponse)) => void) => void, completion?: (error: Error) => void): void; /** * Intercepts scheme protocol and uses handler as the protocol's new handler which * sends a String as a response. @@ -3680,15 +3868,15 @@ declare namespace Electron { * with either a Buffer object or an object that has the data, mimeType, and * charset properties. Example: */ - registerBufferProtocol(scheme: string, handler: (request: RegisterBufferProtocolRequest, callback: (buffer?: Buffer | MimeTypedBuffer) => void) => void, completion?: (error: Error) => void): void; + registerBufferProtocol(scheme: string, handler: (request: RegisterBufferProtocolRequest, callback: (buffer?: (Buffer) | (MimeTypedBuffer)) => void) => void, completion?: (error: Error) => void): void; /** * Registers a protocol of scheme that will send the file as a response. The * handler will be called with handler(request, callback) when a request is going * to be created with scheme. completion will be called with completion(null) when * scheme is successfully registered or completion(error) when failed. To handle * the request, the callback should be called with either the file's path or an - * object that has a path property, e.g. callback(filePath) or callback({path: - * filePath}). When callback is called with nothing, a number, or an object that + * object that has a path property, e.g. callback(filePath) or callback({ path: + * filePath }). When callback is called with nothing, a number, or an object that * has an error property, the request will fail with the error number you * specified. For the available error numbers you can use, please see the net error * list. By default the scheme is treated like http:, which is parsed differently @@ -3732,7 +3920,7 @@ declare namespace Electron { * that implements the readable stream API (emits data/end/error events). For * example, here's how a file could be returned: */ - registerStreamProtocol(scheme: string, handler: (request: RegisterStreamProtocolRequest, callback: (stream?: ReadableStream | StreamProtocolResponse) => void) => void, completion?: (error: Error) => void): void; + registerStreamProtocol(scheme: string, handler: (request: RegisterStreamProtocolRequest, callback: (stream?: (NodeJS.ReadableStream) | (StreamProtocolResponse)) => void) => void, completion?: (error: Error) => void): void; /** * Registers a protocol of scheme that will send a String as a response. The usage * is the same with registerFileProtocol, except that the callback should be called @@ -3752,29 +3940,29 @@ declare namespace Electron { interface Rectangle { - // Docs: http://electron.atom.io/docs/api/structures/rectangle + // Docs: http://electronjs.org/docs/api/structures/rectangle /** - * The height of the rectangle (must be an integer) + * The height of the rectangle (must be an integer). */ height: number; /** - * The width of the rectangle (must be an integer) + * The width of the rectangle (must be an integer). */ width: number; /** - * The x coordinate of the origin of the rectangle (must be an integer) + * The x coordinate of the origin of the rectangle (must be an integer). */ x: number; /** - * The y coordinate of the origin of the rectangle (must be an integer) + * The y coordinate of the origin of the rectangle (must be an integer). */ y: number; } interface Referrer { - // Docs: http://electron.atom.io/docs/api/structures/referrer + // Docs: http://electronjs.org/docs/api/structures/referrer /** * Can be default, unsafe-url, no-referrer-when-downgrade, no-referrer, origin, @@ -3790,7 +3978,7 @@ declare namespace Electron { interface Remote extends MainInterface { - // Docs: http://electron.atom.io/docs/api/remote + // Docs: http://electronjs.org/docs/api/remote getCurrentWebContents(): WebContents; /** @@ -3813,7 +4001,7 @@ declare namespace Electron { interface RemoveClientCertificate { - // Docs: http://electron.atom.io/docs/api/structures/remove-client-certificate + // Docs: http://electronjs.org/docs/api/structures/remove-client-certificate /** * Origin of the server whose associated client certificate must be removed from @@ -3828,7 +4016,7 @@ declare namespace Electron { interface RemovePassword { - // Docs: http://electron.atom.io/docs/api/structures/remove-password + // Docs: http://electronjs.org/docs/api/structures/remove-password /** * When provided, the authentication info related to the origin will only be @@ -3860,7 +4048,7 @@ declare namespace Electron { interface Screen extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/screen + // Docs: http://electronjs.org/docs/api/screen /** * Emitted when newDisplay has been added. @@ -3911,7 +4099,7 @@ declare namespace Electron { * relative to the display nearest to window. If window is null, scaling will be * performed to the display nearest to rect. */ - dipToScreenRect(window: BrowserWindow | null, rect: Rectangle): Rectangle; + dipToScreenRect(window: (BrowserWindow) | (null), rect: Rectangle): Rectangle; getAllDisplays(): Display[]; /** * The current absolute position of the mouse pointer. @@ -3930,44 +4118,44 @@ declare namespace Electron { * relative to the display nearest to window. If window is null, scaling will be * performed to the display nearest to rect. */ - screenToDipRect(window: BrowserWindow | null, rect: Rectangle): Rectangle; + screenToDipRect(window: (BrowserWindow) | (null), rect: Rectangle): Rectangle; } interface ScrubberItem { - // Docs: http://electron.atom.io/docs/api/structures/scrubber-item + // Docs: http://electronjs.org/docs/api/structures/scrubber-item /** - * The image to appear in this item + * The image to appear in this item. */ icon?: NativeImage; /** - * The text to appear in this item + * The text to appear in this item. */ label?: string; } interface SegmentedControlSegment { - // Docs: http://electron.atom.io/docs/api/structures/segmented-control-segment + // Docs: http://electronjs.org/docs/api/structures/segmented-control-segment /** - * Whether this segment is selectable. Default: true + * Whether this segment is selectable. Default: true. */ enabled?: boolean; /** - * The image to appear in this segment + * The image to appear in this segment. */ icon?: NativeImage; /** - * The text to appear in this segment + * The text to appear in this segment. */ label?: string; } class Session extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/session + // Docs: http://electronjs.org/docs/api/session /** * If partition starts with persist:, the page will use a persistent session @@ -4007,7 +4195,7 @@ declare namespace Electron { /** * Clears the session’s HTTP authentication cache. */ - clearAuthCache(options: RemovePassword | RemoveClientCertificate, callback?: Function): void; + clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback?: Function): void; /** * Clears the session’s HTTP cache. */ @@ -4066,12 +4254,18 @@ declare namespace Electron { * Downloads under the respective app folder. */ setDownloadPath(path: string): void; + /** + * Sets the handler which can be used to respond to permission checks for the + * session. Returning true will allow the permission and false will reject it. To + * clear the handler, call setPermissionCheckHandler(null). + */ + setPermissionCheckHandler(handler: ((webContents: WebContents, permission: string, requestingOrigin: string, details: PermissionCheckHandlerDetails) => boolean) | (null)): void; /** * Sets the handler which can be used to respond to permission requests for the * session. Calling callback(true) will allow the permission and callback(false) * will reject it. To clear the handler, call setPermissionRequestHandler(null). */ - setPermissionRequestHandler(handler: (webContents: WebContents, permission: string, callback: (permissionGranted: boolean) => void, details: PermissionRequestHandlerDetails) => void | null): void; + setPermissionRequestHandler(handler: ((webContents: WebContents, permission: string, callback: (permissionGranted: boolean) => void, details: PermissionRequestHandlerDetails) => void) | (null)): void; /** * Adds scripts that will be executed on ALL web contents that are associated with * this session just before normal preload scripts run. @@ -4100,7 +4294,7 @@ declare namespace Electron { interface Shell { - // Docs: http://electron.atom.io/docs/api/shell + // Docs: http://electronjs.org/docs/api/shell /** * Play the beep sound. @@ -4140,7 +4334,7 @@ declare namespace Electron { interface ShortcutDetails { - // Docs: http://electron.atom.io/docs/api/structures/shortcut-details + // Docs: http://electronjs.org/docs/api/structures/shortcut-details /** * The Application User Model ID. Default is empty. @@ -4176,7 +4370,7 @@ declare namespace Electron { interface Size { - // Docs: http://electron.atom.io/docs/api/structures/size + // Docs: http://electronjs.org/docs/api/structures/size height: number; width: number; @@ -4184,25 +4378,25 @@ declare namespace Electron { interface StreamProtocolResponse { - // Docs: http://electron.atom.io/docs/api/structures/stream-protocol-response + // Docs: http://electronjs.org/docs/api/structures/stream-protocol-response /** - * A Node.js readable stream representing the response body + * A Node.js readable stream representing the response body. */ - data: ReadableStream; + data: NodeJS.ReadableStream; /** - * An object containing the response headers + * An object containing the response headers. */ headers: Headers; /** - * The HTTP response code + * The HTTP response code. */ statusCode: number; } interface SystemPreferences extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/system-preferences + // Docs: http://electronjs.org/docs/api/system-preferences on(event: 'accent-color-changed', listener: (event: Event, /** @@ -4224,6 +4418,30 @@ declare namespace Electron { * The new RGBA color the user assigned to be their system accent color. */ newColor: string) => void): this; + /** + * NOTE: This event is only emitted after you have called + * startAppLevelAppearanceTrackingOS + */ + on(event: 'appearance-changed', listener: ( + /** + * Can be `dark` or `light` + */ + newAppearance: ('dark' | 'light')) => void): this; + once(event: 'appearance-changed', listener: ( + /** + * Can be `dark` or `light` + */ + newAppearance: ('dark' | 'light')) => void): this; + addListener(event: 'appearance-changed', listener: ( + /** + * Can be `dark` or `light` + */ + newAppearance: ('dark' | 'light')) => void): this; + removeListener(event: 'appearance-changed', listener: ( + /** + * Can be `dark` or `light` + */ + newAppearance: ('dark' | 'light')) => void): this; on(event: 'color-changed', listener: (event: Event) => void): this; once(event: 'color-changed', listener: (event: Event) => void): this; addListener(event: 'color-changed', listener: (event: Event) => void): this; @@ -4252,8 +4470,41 @@ declare namespace Electron { * used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; + /** + * Important: In order to properly leverage this API, you must set the + * NSMicrophoneUsageDescription and NSCameraUsageDescription strings in your app's + * Info.plist file. The values for these keys will be used to populate the + * permission dialogs so that the user will be properly informed as to the purpose + * of the permission request. See Electron Application Distribution for more + * information about how to set these in the context of Electron. This user consent + * was not required until macOS 10.14 Mojave, so this method will always return + * true if your system is running 10.13 High Sierra or lower. + */ + askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; getAccentColor(): string; + /** + * Gets the macOS appearance setting that you have declared you want for your + * application, maps to NSApplication.appearance. You can use the + * setAppLevelAppearance API to set this value. + */ + getAppLevelAppearance(): ('dark' | 'light' | 'unknown'); getColor(color: '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text'): string; + /** + * Gets the macOS appearance setting that is currently applied to your application, + * maps to NSApplication.effectiveAppearance Please note that until Electron is + * built targeting the 10.14 SDK, your application's effectiveAppearance will + * default to 'light' and won't inherit the OS preference. In the interim in order + * for your application to inherit the OS preference you must set the + * NSRequiresAquaSystemAppearance key in your apps Info.plist to false. If you are + * using electron-packager or electron-forge just set the enableDarwinDarkMode + * packager option to true. See the Electron Packager API for more details. + */ + getEffectiveAppearance(): ('dark' | 'light' | 'unknown'); + /** + * This user consent was not required until macOS 10.14 Mojave, so this method will + * always return granted if your system is running 10.13 High Sierra or lower. + */ + getMediaAccessStatus(mediaType: string): ('not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'); /** * Some popular key and types are: */ @@ -4266,6 +4517,7 @@ declare namespace Electron { isDarkMode(): boolean; isInvertedColorScheme(): boolean; isSwipeTrackingFromScrollEventsEnabled(): boolean; + isTrustedAccessibilityClient(prompt: boolean): boolean; /** * Posts event as native notifications of macOS. The userInfo is an Object that * contains the user information dictionary sent along with the notification. @@ -4290,6 +4542,11 @@ declare namespace Electron { * global value of a key previously set with setUserDefault. */ removeUserDefault(key: string): void; + /** + * Sets the appearance setting for your application, this should override the + * system default and override the value of getEffectiveAppearance. + */ + setAppLevelAppearance(appearance: 'dark' | 'light'): void; /** * Set the value of key in NSUserDefaults. Note that type should match actual type * of value. An exception is thrown if they don't. Some popular key and types are: @@ -4333,7 +4590,7 @@ declare namespace Electron { interface Task { - // Docs: http://electron.atom.io/docs/api/structures/task + // Docs: http://electronjs.org/docs/api/structures/task /** * The command line arguments when program is executed. @@ -4368,7 +4625,7 @@ declare namespace Electron { interface ThumbarButton { - // Docs: http://electron.atom.io/docs/api/structures/thumbar-button + // Docs: http://electronjs.org/docs/api/structures/thumbar-button click: Function; /** @@ -4388,7 +4645,7 @@ declare namespace Electron { class TouchBarButton extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-button + // Docs: http://electronjs.org/docs/api/touch-bar-button constructor(options: TouchBarButtonConstructorOptions); backgroundColor: string; @@ -4398,7 +4655,7 @@ declare namespace Electron { class TouchBarColorPicker extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-color-picker + // Docs: http://electronjs.org/docs/api/touch-bar-color-picker constructor(options: TouchBarColorPickerConstructorOptions); availableColors: string[]; @@ -4407,14 +4664,14 @@ declare namespace Electron { class TouchBarGroup extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-group + // Docs: http://electronjs.org/docs/api/touch-bar-group constructor(options: TouchBarGroupConstructorOptions); } class TouchBarLabel extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-label + // Docs: http://electronjs.org/docs/api/touch-bar-label constructor(options: TouchBarLabelConstructorOptions); label: string; @@ -4423,7 +4680,7 @@ declare namespace Electron { class TouchBarPopover extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-popover + // Docs: http://electronjs.org/docs/api/touch-bar-popover constructor(options: TouchBarPopoverConstructorOptions); icon: NativeImage; @@ -4432,7 +4689,7 @@ declare namespace Electron { class TouchBarScrubber extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-scrubber + // Docs: http://electronjs.org/docs/api/touch-bar-scrubber constructor(options: TouchBarScrubberConstructorOptions); continuous: boolean; @@ -4445,7 +4702,7 @@ declare namespace Electron { class TouchBarSegmentedControl extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-segmented-control + // Docs: http://electronjs.org/docs/api/touch-bar-segmented-control constructor(options: TouchBarSegmentedControlConstructorOptions); segments: SegmentedControlSegment[]; @@ -4455,7 +4712,7 @@ declare namespace Electron { class TouchBarSlider extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-slider + // Docs: http://electronjs.org/docs/api/touch-bar-slider constructor(options: TouchBarSliderConstructorOptions); label: string; @@ -4466,14 +4723,14 @@ declare namespace Electron { class TouchBarSpacer extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar-spacer + // Docs: http://electronjs.org/docs/api/touch-bar-spacer constructor(options: TouchBarSpacerConstructorOptions); } class TouchBar extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/touch-bar + // Docs: http://electronjs.org/docs/api/touch-bar constructor(options: TouchBarConstructorOptions); escapeItem: (TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer | null); @@ -4490,7 +4747,7 @@ declare namespace Electron { interface TraceCategoriesAndOptions { - // Docs: http://electron.atom.io/docs/api/structures/trace-categories-and-options + // Docs: http://electronjs.org/docs/api/structures/trace-categories-and-options /** * – is a filter to control what category groups should be traced. A filter can @@ -4517,7 +4774,7 @@ declare namespace Electron { interface TraceConfig { - // Docs: http://electron.atom.io/docs/api/structures/trace-config + // Docs: http://electronjs.org/docs/api/structures/trace-config excluded_categories?: string[]; included_categories?: string[]; @@ -4526,7 +4783,7 @@ declare namespace Electron { interface Transaction { - // Docs: http://electron.atom.io/docs/api/structures/transaction + // Docs: http://electronjs.org/docs/api/structures/transaction /** * The error code if an error occurred while processing the transaction. @@ -4558,7 +4815,7 @@ declare namespace Electron { class Tray extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/tray + // Docs: http://electronjs.org/docs/api/tray /** * Emitted when the tray balloon is clicked. @@ -4810,7 +5067,7 @@ declare namespace Electron { * The bounds of tray icon. */ bounds: Rectangle) => void): this; - constructor(image: NativeImage | string); + constructor(image: (NativeImage) | (string)); /** * Destroys the tray icon immediately. */ @@ -4834,7 +5091,7 @@ declare namespace Electron { /** * Sets the context menu for this icon. */ - setContextMenu(menu: Menu | null): void; + setContextMenu(menu: (Menu) | (null)): void; /** * Sets when the tray's icon background becomes highlighted (in blue). Note: You * can use highlightMode with a BrowserWindow by toggling between 'never' and @@ -4850,11 +5107,11 @@ declare namespace Electron { /** * Sets the image associated with this tray icon. */ - setImage(image: NativeImage | string): void; + setImage(image: (NativeImage) | (string)): void; /** * Sets the image associated with this tray icon when pressed on macOS. */ - setPressedImage(image: NativeImage | string): void; + setPressedImage(image: (NativeImage) | (string)): void; /** * Sets the title displayed aside of the tray icon in the status bar (Support ANSI * colors). @@ -4868,7 +5125,7 @@ declare namespace Electron { interface UploadBlob { - // Docs: http://electron.atom.io/docs/api/structures/upload-blob + // Docs: http://electronjs.org/docs/api/structures/upload-blob /** * UUID of blob data to upload. @@ -4882,7 +5139,7 @@ declare namespace Electron { interface UploadData { - // Docs: http://electron.atom.io/docs/api/structures/upload-data + // Docs: http://electronjs.org/docs/api/structures/upload-data /** * UUID of blob data. Use method to retrieve the data. @@ -4900,7 +5157,7 @@ declare namespace Electron { interface UploadFile { - // Docs: http://electron.atom.io/docs/api/structures/upload-file + // Docs: http://electronjs.org/docs/api/structures/upload-file /** * Path of file to be uploaded. @@ -4926,7 +5183,7 @@ declare namespace Electron { interface UploadRawData { - // Docs: http://electron.atom.io/docs/api/structures/upload-raw-data + // Docs: http://electronjs.org/docs/api/structures/upload-raw-data /** * Data to be uploaded. @@ -4940,7 +5197,7 @@ declare namespace Electron { class WebContents extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/web-contents + // Docs: http://electronjs.org/docs/api/web-contents static fromId(id: number): WebContents; static getAllWebContents(): WebContents[]; @@ -5164,22 +5421,22 @@ declare namespace Electron { */ on(event: 'did-attach-webview', listener: (event: Event, /** - * The guest web contents that is used by the ``. + * The guest web contents that is used by the ` */ webContents: WebContents) => void): this; once(event: 'did-attach-webview', listener: (event: Event, /** - * The guest web contents that is used by the ``. + * The guest web contents that is used by the ` */ webContents: WebContents) => void): this; addListener(event: 'did-attach-webview', listener: (event: Event, /** - * The guest web contents that is used by the ``. + * The guest web contents that is used by the ` */ webContents: WebContents) => void): this; removeListener(event: 'did-attach-webview', listener: (event: Event, /** - * The guest web contents that is used by the ``. + * The guest web contents that is used by the ` */ webContents: WebContents) => void): this; /** @@ -5190,22 +5447,22 @@ declare namespace Electron { /** * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. */ - color: string | null) => void): this; + color: (string) | (null)) => void): this; once(event: 'did-change-theme-color', listener: (event: Event, /** * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. */ - color: string | null) => void): this; + color: (string) | (null)) => void): this; addListener(event: 'did-change-theme-color', listener: (event: Event, /** * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. */ - color: string | null) => void): this; + color: (string) | (null)) => void): this; removeListener(event: 'did-change-theme-color', listener: (event: Event, /** * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. */ - color: string | null) => void): this; + color: (string) | (null)) => void): this; /** * This event is like did-finish-load but emitted when the load failed or was * cancelled, e.g. window.stop() is invoked. The full list of error codes and their @@ -5394,6 +5651,35 @@ declare namespace Electron { isMainFrame: boolean, frameProcessId: number, frameRoutingId: number) => void): this; + /** + * Emitted after a server side redirect occurs during navigation. For example a + * 302 redirect. This event can not be prevented, if you want to prevent redirects + * you should checkout out the will-redirect event above. + */ + on(event: 'did-redirect-navigation', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + once(event: 'did-redirect-navigation', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + addListener(event: 'did-redirect-navigation', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + removeListener(event: 'did-redirect-navigation', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Corresponds to the points in time when the spinner of the tab started spinning. */ @@ -5405,22 +5691,26 @@ declare namespace Electron { * Emitted when any frame (including main) starts navigating. isInplace will be * true for in-page navigations. */ - on(event: 'did-start-navigation', listener: (url: string, + on(event: 'did-start-navigation', listener: (event: Event, + url: string, isInPlace: boolean, isMainFrame: boolean, frameProcessId: number, frameRoutingId: number) => void): this; - once(event: 'did-start-navigation', listener: (url: string, + once(event: 'did-start-navigation', listener: (event: Event, + url: string, isInPlace: boolean, isMainFrame: boolean, frameProcessId: number, frameRoutingId: number) => void): this; - addListener(event: 'did-start-navigation', listener: (url: string, + addListener(event: 'did-start-navigation', listener: (event: Event, + url: string, isInPlace: boolean, isMainFrame: boolean, frameProcessId: number, frameRoutingId: number) => void): this; - removeListener(event: 'did-start-navigation', listener: (url: string, + removeListener(event: 'did-start-navigation', listener: (event: Event, + url: string, isInPlace: boolean, isMainFrame: boolean, frameProcessId: number, @@ -5605,6 +5895,22 @@ declare namespace Electron { * Array of URLs. */ favicons: string[]) => void): this; + /** + * Fired when page title is set during navigation. explicitSet is false when title + * is synthesized from file url. + */ + on(event: 'page-title-updated', listener: (event: Event, + title: string, + explicitSet: boolean) => void): this; + once(event: 'page-title-updated', listener: (event: Event, + title: string, + explicitSet: boolean) => void): this; + addListener(event: 'page-title-updated', listener: (event: Event, + title: string, + explicitSet: boolean) => void): this; + removeListener(event: 'page-title-updated', listener: (event: Event, + title: string, + explicitSet: boolean) => void): this; /** * Emitted when a new frame is generated. Only the dirty area is passed in the * buffer. @@ -5648,6 +5954,76 @@ declare namespace Electron { removeListener(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when remote.getBuiltin() is called in the renderer process. Calling + * event.preventDefault() will prevent the module from being returned. Custom value + * can be returned by setting event.returnValue. + */ + on(event: 'remote-get-builtin', listener: (event: Event, + moduleName: string) => void): this; + once(event: 'remote-get-builtin', listener: (event: Event, + moduleName: string) => void): this; + addListener(event: 'remote-get-builtin', listener: (event: Event, + moduleName: string) => void): this; + removeListener(event: 'remote-get-builtin', listener: (event: Event, + moduleName: string) => void): this; + /** + * Emitted when remote.getCurrentWebContents() is called in the renderer process. + * Calling event.preventDefault() will prevent the object from being returned. + * Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; + once(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; + addListener(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; + removeListener(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; + /** + * Emitted when remote.getCurrentWindow() is called in the renderer process. + * Calling event.preventDefault() will prevent the object from being returned. + * Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-current-window', listener: (event: Event) => void): this; + once(event: 'remote-get-current-window', listener: (event: Event) => void): this; + addListener(event: 'remote-get-current-window', listener: (event: Event) => void): this; + removeListener(event: 'remote-get-current-window', listener: (event: Event) => void): this; + /** + * Emitted when remote.getGlobal() is called in the renderer process. Calling + * event.preventDefault() will prevent the global from being returned. Custom value + * can be returned by setting event.returnValue. + */ + on(event: 'remote-get-global', listener: (event: Event, + globalName: string) => void): this; + once(event: 'remote-get-global', listener: (event: Event, + globalName: string) => void): this; + addListener(event: 'remote-get-global', listener: (event: Event, + globalName: string) => void): this; + removeListener(event: 'remote-get-global', listener: (event: Event, + globalName: string) => void): this; + /** + * Emitted when .getWebContents() is called in the renderer process. + * Calling event.preventDefault() will prevent the object from being returned. + * Custom value can be returned by setting event.returnValue. + */ + on(event: 'remote-get-guest-web-contents', listener: (event: Event, + guestWebContents: WebContents) => void): this; + once(event: 'remote-get-guest-web-contents', listener: (event: Event, + guestWebContents: WebContents) => void): this; + addListener(event: 'remote-get-guest-web-contents', listener: (event: Event, + guestWebContents: WebContents) => void): this; + removeListener(event: 'remote-get-guest-web-contents', listener: (event: Event, + guestWebContents: WebContents) => void): this; + /** + * Emitted when remote.require() is called in the renderer process. Calling + * event.preventDefault() will prevent the module from being returned. Custom value + * can be returned by setting event.returnValue. + */ + on(event: 'remote-require', listener: (event: Event, + moduleName: string) => void): this; + once(event: 'remote-require', listener: (event: Event, + moduleName: string) => void): this; + addListener(event: 'remote-require', listener: (event: Event, + moduleName: string) => void): this; + removeListener(event: 'remote-require', listener: (event: Event, + moduleName: string) => void): this; /** * Emitted when the unresponsive web page becomes responsive again. */ @@ -5727,8 +6103,7 @@ declare namespace Electron { */ webPreferences: any, /** - * The other `` parameters such as the `src` URL. This object can be - * modified to adjust the parameters of the guest page. + * The other ` */ params: any) => void): this; once(event: 'will-attach-webview', listener: (event: Event, @@ -5738,8 +6113,7 @@ declare namespace Electron { */ webPreferences: any, /** - * The other `` parameters such as the `src` URL. This object can be - * modified to adjust the parameters of the guest page. + * The other ` */ params: any) => void): this; addListener(event: 'will-attach-webview', listener: (event: Event, @@ -5749,8 +6123,7 @@ declare namespace Electron { */ webPreferences: any, /** - * The other `` parameters such as the `src` URL. This object can be - * modified to adjust the parameters of the guest page. + * The other ` */ params: any) => void): this; removeListener(event: 'will-attach-webview', listener: (event: Event, @@ -5760,8 +6133,7 @@ declare namespace Electron { */ webPreferences: any, /** - * The other `` parameters such as the `src` URL. This object can be - * modified to adjust the parameters of the guest page. + * The other ` */ params: any) => void): this; /** @@ -5790,6 +6162,36 @@ declare namespace Electron { once(event: 'will-prevent-unload', listener: (event: Event) => void): this; addListener(event: 'will-prevent-unload', listener: (event: Event) => void): this; removeListener(event: 'will-prevent-unload', listener: (event: Event) => void): this; + /** + * Emitted as a server side redirect occurs during navigation. For example a 302 + * redirect. This event will be emitted after did-start-navigation and always + * before the did-redirect-navigation event for the same navigation. Calling + * event.preventDefault() will prevent the navigation (not just the redirect). + */ + on(event: 'will-redirect', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + once(event: 'will-redirect', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + addListener(event: 'will-redirect', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + removeListener(event: 'will-redirect', listener: (event: Event, + url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Adds the specified path to DevTools workspace. Must be used after DevTools * creation: @@ -5895,6 +6297,7 @@ declare namespace Electron { getPrinters(): PrinterInfo[]; getProcessId(): number; getTitle(): string; + getType(): ('backgroundPage' | 'window' | 'browserView' | 'remote' | 'webview' | 'offscreen'); getURL(): string; getUserAgent(): string; getWebRTCIPHandlingPolicy(): string; @@ -5953,6 +6356,7 @@ declare namespace Electron { invalidate(): void; isAudioMuted(): boolean; isCrashed(): boolean; + isCurrentlyAudible(): boolean; isDestroyed(): boolean; isDevToolsFocused(): boolean; isDevToolsOpened(): boolean; @@ -5967,7 +6371,7 @@ declare namespace Electron { * relative to the root of your application. For instance an app structure like * this: Would require code like this */ - loadFile(filePath: string): void; + loadFile(filePath: string, options?: LoadFileOptions): void; /** * Loads the url in the window. The url must contain the protocol prefix, e.g. the * http:// or file://. If the load should bypass http cache then use the pragma @@ -5992,8 +6396,8 @@ declare namespace Electron { * Prints window's web page. When silent is set to true, Electron will pick the * system's default printer if deviceName is empty and the default settings for * printing. Calling window.print() in web page is equivalent to calling - * webContents.print({silent: false, printBackground: false, deviceName: ''}). Use - * page-break-before: always; CSS style to force to print to a new page. + * webContents.print({ silent: false, printBackground: false, deviceName: '' }). + * Use page-break-before: always; CSS style to force to print to a new page. */ print(options?: PrintOptions, callback?: (success: boolean) => void): void; /** @@ -6054,6 +6458,11 @@ declare namespace Electron { * Mute the audio on the current web page. */ setAudioMuted(muted: boolean): void; + /** + * Controls whether or not this WebContents will throttle animations and timers + * when the page becomes backgrounded. This also affects the Page Visibility API. + */ + setBackgroundThrottling(allowed: boolean): void; /** * Uses the devToolsWebContents as the target WebContents to show devtools. The * devToolsWebContents must not have done any navigation, and it should not be used @@ -6131,6 +6540,10 @@ declare namespace Electron { * If offscreen rendering is enabled and painting, stop painting. */ stopPainting(): void; + /** + * Takes a V8 heap snapshot and saves it to filePath. + */ + takeHeapSnapshot(filePath: string): Promise; /** * Toggles the developer tools. */ @@ -6158,7 +6571,7 @@ declare namespace Electron { interface WebFrame extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/web-frame + // Docs: http://electronjs.org/docs/api/web-frame /** * Attempts to free memory that is no longer being used (like images from a @@ -6176,7 +6589,7 @@ declare namespace Electron { */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; /** - * Work like executeJavaScript but evaluates scripts in isolated context. + * Work like executeJavaScript but evaluates scripts in an isolated context. */ executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): void; findFrameByName(name: string): WebFrame; @@ -6279,7 +6692,7 @@ declare namespace Electron { class WebRequest extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/web-request + // Docs: http://electronjs.org/docs/api/web-request /** * The listener will be called with listener(details) when a server initiated @@ -6373,7 +6786,7 @@ declare namespace Electron { interface WebSource { - // Docs: http://electron.atom.io/docs/api/structures/web-source + // Docs: http://electronjs.org/docs/api/structures/web-source code: string; /** @@ -6385,7 +6798,7 @@ declare namespace Electron { interface WebviewTag extends HTMLElement { - // Docs: http://electron.atom.io/docs/api/webview-tag + // Docs: http://electronjs.org/docs/api/webview-tag /** * Fired when a load has committed. This includes navigation within the current @@ -6509,11 +6922,6 @@ declare namespace Electron { */ addEventListener(event: 'crashed', listener: (event: Event) => void, useCapture?: boolean): this; removeEventListener(event: 'crashed', listener: (event: Event) => void): this; - /** - * Fired when the gpu process is crashed. - */ - addEventListener(event: 'gpu-crashed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'gpu-crashed', listener: (event: Event) => void): this; /** * Fired when a plugin process is crashed. */ @@ -6597,6 +7005,10 @@ declare namespace Electron { * Executes editing command delete in page. */ delete(): void; + /** + * Initiates a download of the resource at url without navigating. + */ + downloadURL(url: string): void; /** * Evaluates code in page. If userGesture is set, it will create the user gesture * context in the page. HTML APIs like requestFullScreen, which require user @@ -6611,7 +7023,21 @@ declare namespace Electron { getTitle(): string; getURL(): string; getUserAgent(): string; + /** + * It depends on the remote module, it is therefore not available when this module + * is disabled. + */ getWebContents(): WebContents; + /** + * Sends a request to get current zoom factor, the callback will be called with + * callback(zoomFactor). + */ + getZoomFactor(callback: (zoomFactor: number) => void): void; + /** + * Sends a request to get current zoom level, the callback will be called with + * callback(zoomLevel). + */ + getZoomLevel(callback: (zoomLevel: number) => void): void; /** * Makes the guest page go back. */ @@ -6646,9 +7072,11 @@ declare namespace Electron { inspectServiceWorker(): void; isAudioMuted(): boolean; isCrashed(): boolean; + isCurrentlyAudible(): boolean; isDevToolsFocused(): boolean; isDevToolsOpened(): boolean; isLoading(): boolean; + isLoadingMainFrame(): boolean; isWaitingForResponse(): boolean; /** * Loads the url in the webview, the url must contain the protocol prefix, e.g. the @@ -6716,10 +7144,18 @@ declare namespace Electron { * Set guest page muted. */ setAudioMuted(muted: boolean): void; + /** + * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. + */ + setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; /** * Overrides the user agent for the guest page. */ setUserAgent(userAgent: string): void; + /** + * Sets the maximum and minimum pinch-to-zoom level. + */ + setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; /** * Changes the zoom factor to the specified factor. Zoom factor is zoom percent * divided by 100, so 300% = 3.0. @@ -6728,7 +7164,8 @@ declare namespace Electron { /** * Changes the zoom level to the specified level. The original size is 0 and each * increment above or below represents zooming 20% larger or smaller to default - * limits of 300% and 50% of original size, respectively. + * limits of 300% and 50% of original size, respectively. The formula for this is + * scale := 1.2 ^ level. */ setZoomLevel(level: number): void; /** @@ -6781,6 +7218,11 @@ declare namespace Electron { * RuntimeEnabledFeatures.json5 file. */ enableblinkfeatures?: string; + /** + * When this attribute is false the guest page in webview will not have access to + * the remote module. The remote module is avaiable by default. + */ + enableremotemodule?: string; /** * Sets the referrer URL for the guest page. */ @@ -7054,7 +7496,7 @@ declare namespace Electron { * visual effects, you can also leave it undefined so the executable's icon will be * used. */ - icon?: NativeImage | string; + icon?: (NativeImage) | (string); /** * Whether window should be shown when created. Default is true. */ @@ -7091,9 +7533,8 @@ declare namespace Electron { enableLargerThanScreen?: boolean; /** * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha is supported). Default is #FFF (white). If transparent is set - * to true, only values with transparent (#00-------) or opaque (#FF-----) alpha - * values are respected. + * #80FFFFFF (alpha is supported if transparent is set to true). Default is #FFF + * (white). */ backgroundColor?: string; /** @@ -7320,7 +7761,7 @@ declare namespace Electron { } interface CrashReporterStartOptions { - companyName?: string; + companyName: string; /** * URL that crash reports will be sent to as POST. */ @@ -7422,7 +7863,8 @@ declare namespace Electron { */ value?: string; /** - * The domain of the cookie. Empty by default if omitted. + * The domain of the cookie; this will be normalized with a preceding dot so that + * it's also valid for subdomains. Empty by default if omitted. */ domain?: string; /** @@ -7476,7 +7918,7 @@ declare namespace Electron { /** * - */ - icon?: NativeImage | string; + icon?: (NativeImage) | (string); title: string; content: string; } @@ -7518,7 +7960,7 @@ declare namespace Electron { /** * Sets the image associated with this dock icon. */ - setIcon: (image: NativeImage | string) => void; + setIcon: (image: (NativeImage) | (string)) => void; } interface EnableNetworkEmulationOptions { @@ -7716,6 +8158,7 @@ declare namespace Electron { interface InterceptHttpProtocolRequest { url: string; + headers: Headers; referrer: string; method: string; uploadData: UploadData[]; @@ -7772,11 +8215,26 @@ declare namespace Electron { isMainFrame: boolean; } + interface LoadFileOptions { + /** + * Passed to url.format(). + */ + query?: Query; + /** + * Passed to url.format(). + */ + search?: string; + /** + * Passed to url.format(). + */ + hash?: string; + } + interface LoadURLOptions { /** * An HTTP Referrer url. */ - httpReferrer?: string | Referrer; + httpReferrer?: (string) | (Referrer); /** * A user agent originating the request. */ @@ -7785,7 +8243,7 @@ declare namespace Electron { * Extra headers separated by "\n" */ extraHeaders?: string; - postData?: UploadRawData[] | UploadFile[] | UploadBlob[]; + postData?: (UploadRawData[]) | (UploadFile[]) | (UploadBlob[]); /** * Base url (with trailing path separator) for files to be loaded by the data url. * This is needed only if the specified url is a data url and needs to load other @@ -7856,7 +8314,7 @@ declare namespace Electron { label?: string; sublabel?: string; accelerator?: Accelerator; - icon?: NativeImage | string; + icon?: (NativeImage) | (string); /** * If false, the menu item will be greyed out and unclickable. */ @@ -7879,17 +8337,36 @@ declare namespace Electron { * type: 'submenu' can be omitted. If the value is not a then it will be * automatically converted to one using Menu.buildFromTemplate. */ - submenu?: MenuItemConstructorOptions[] | Menu; + submenu?: (MenuItemConstructorOptions[]) | (Menu); /** * Unique within a single menu. If defined then it can be used as a reference to * this item by the position attribute. */ id?: string; /** - * This field allows fine-grained definition of the specific location within a - * given menu. + * Inserts this item before the item with the specified label. If the referenced + * item doesn't exist the item will be inserted at the end of the menu. Also + * implies that the menu item in question should be placed in the same “group” as + * the item. */ - position?: string; + before?: string[]; + /** + * Inserts this item after the item with the specified label. If the referenced + * item doesn't exist the item will be inserted at the end of the menu. + */ + after?: string[]; + /** + * Provides a means for a single context menu to declare the placement of their + * containing group before the containing group of the item with the specified + * label. + */ + beforeGroupContaining?: string[]; + /** + * Provides a means for a single context menu to declare the placement of their + * containing group after the containing group of the item with the specified + * label. + */ + afterGroupContaining?: string[]; } interface MessageBoxOptions { @@ -7935,7 +8412,7 @@ declare namespace Electron { * The index of the button to be used to cancel the dialog, via the Esc key. By * default this is assigned to the first button with "cancel" or "no" as the label. * If no such labeled buttons exist and this option is not set, 0 will be used as - * the return value or callback response. This option is ignored on Windows. + * the return value or callback response. */ cancelId?: number; /** @@ -7993,7 +8470,7 @@ declare namespace Electron { /** * An icon to use in the notification. */ - icon?: string | NativeImage; + icon?: (string) | (NativeImage); /** * Whether or not to add an inline reply option to the notification. */ @@ -8093,6 +8570,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; responseHeaders: ResponseHeaders; fromCache: boolean; @@ -8151,7 +8629,7 @@ declare namespace Electron { } interface OnHeadersReceivedResponse { - cancel: boolean; + cancel?: boolean; /** * When provided, the server is assumed to have responded with these headers. */ @@ -8242,7 +8720,11 @@ declare namespace Electron { /** * true to bring the opened application to the foreground. The default is true. */ - activate: boolean; + activate?: boolean; + /** + * The working directory. + */ + workingDirectory?: string; } interface PageFaviconUpdatedEvent extends Event { @@ -8267,8 +8749,8 @@ declare namespace Electron { */ screenSize: Size; /** - * Position the view on the screen (screenPosition == mobile) (default: {x: 0, y: - * 0}). + * Position the view on the screen (screenPosition == mobile) (default: { x: 0, y: + * 0 }). */ viewPosition: Point; /** @@ -8298,11 +8780,26 @@ declare namespace Electron { quantity: number; } + interface PermissionCheckHandlerDetails { + /** + * The security orign of the media check. + */ + securityOrigin: string; + /** + * The type of media access being requested, can be video, audio or unknown + */ + mediaType: ('video' | 'audio' | 'unknown'); + } + interface PermissionRequestHandlerDetails { /** * The url of the openExternal request. */ externalURL: string; + /** + * The types of media access being requested, elements can be video or audio + */ + mediaTypes: Array<'video' | 'audio'>; } interface PluginCrashedEvent extends Event { @@ -8359,7 +8856,7 @@ declare namespace Electron { * Specify page size of the generated PDF. Can be A3, A4, A5, Legal, Letter, * Tabloid or an Object containing height and width in microns. */ - pageSize?: string | Size; + pageSize?: (string) | (Size); /** * Whether to print CSS backgrounds. */ @@ -8376,23 +8873,19 @@ declare namespace Electron { interface ProcessMemoryInfo { /** - * The amount of memory currently pinned to actual physical RAM. + * and The amount of memory currently pinned to actual physical RAM in Kilobytes. */ - workingSetSize: number; - /** - * The maximum amount of memory that has ever been pinned to actual physical RAM. - */ - peakWorkingSetSize: number; + residentSet: number; /** * The amount of memory not shared by other processes, such as JS heap or HTML - * content. + * content in Kilobytes. */ - privateBytes: number; + private: number; /** * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself. + * Electron code itself in Kilobytes. */ - sharedBytes: number; + shared: number; } interface ProgressBarOptions { @@ -8437,6 +8930,7 @@ declare namespace Electron { interface RegisterHttpProtocolRequest { url: string; + headers: Headers; referrer: string; method: string; uploadData: UploadData[]; @@ -8704,8 +9198,8 @@ declare namespace Electron { } interface TouchBarConstructorOptions { - items: Array; - escapeItem?: TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer | null; + items: Array<(TouchBarButton) | (TouchBarColorPicker) | (TouchBarGroup) | (TouchBarLabel) | (TouchBarPopover) | (TouchBarScrubber) | (TouchBarSegmentedControl) | (TouchBarSlider) | (TouchBarSpacer)>; + escapeItem?: (TouchBarButton) | (TouchBarColorPicker) | (TouchBarGroup) | (TouchBarLabel) | (TouchBarPopover) | (TouchBarScrubber) | (TouchBarSegmentedControl) | (TouchBarSlider) | (TouchBarSpacer) | (null); } interface TouchBarGroupConstructorOptions { @@ -8871,6 +9365,13 @@ declare namespace Electron { electron?: string; } + interface VisibleOnAllWorkspacesOptions { + /** + * Sets whether the window should be visible above fullscreen windows + */ + visibleOnFullScreen?: boolean; + } + interface WillNavigateEvent extends Event { url: string; } @@ -8964,6 +9465,9 @@ declare namespace Electron { interface Options { } + interface Query { + } + interface RequestHeaders { } @@ -9004,6 +9508,10 @@ declare namespace Electron { * currently experimental and may change or be removed in future Electron releases. */ sandbox?: boolean; + /** + * Whether to enable the module. Default is true. + */ + enableRemoteModule?: boolean; /** * Sets the session used by the page. Instead of passing the Session object * directly, you can also choose to use the partition option instead, which accepts @@ -9072,10 +9580,6 @@ declare namespace Electron { * Enables Chromium's experimental features. Default is false. */ // experimentalFeatures?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Enables Chromium's experimental canvas features. Default is false. - */ - experimentalCanvasFeatures?: boolean; /** * Enables scroll bounce (rubber banding) effect on macOS. Default is false. */ @@ -9131,8 +9635,7 @@ declare namespace Electron { * content to ensure the loaded content cannot tamper with the preload script and * any Electron APIs being used. This option uses the same technique used by . You * can access this context in the dev tools by selecting the 'Electron Isolated - * Context' entry in the combo box at the top of the Console tab. This option is - * currently experimental and may change or be removed in future Electron releases. + * Context' entry in the combo box at the top of the Console tab. */ contextIsolation?: boolean; /** @@ -9144,11 +9647,11 @@ declare namespace Electron { nativeWindowOpen?: boolean; /** * Whether to enable the . Defaults to the value of the nodeIntegration option. The - * preload script configured for the will have node integration enabled - * when it is executed so you should ensure remote/untrusted content is not able to - * create a tag with a possibly malicious preload script. You can use the + * preload script configured for the will have node integration enabled when it is + * executed so you should ensure remote/untrusted content is not able to create a + * tag with a possibly malicious preload script. You can use the * will-attach-webview event on to strip away the preload script and to validate or - * alter the 's initial settings. + * alter the 's initial settings. */ webviewTag?: boolean; /** @@ -9230,7 +9733,7 @@ interface Document { declare namespace NodeJS { interface Process extends EventEmitter { - // Docs: http://electron.atom.io/docs/api/process + // Docs: http://electronjs.org/docs/api/process // ### BEGIN VSCODE MODIFICATION ### // /** @@ -9250,6 +9753,12 @@ declare namespace NodeJS { */ crash(): void; getCPUUsage(): Electron.CPUUsage; + /** + * Indicates the creation time of the application. The time is represented as + * number of milliseconds since epoch. It returns null if it is unable to get the + * process creation time. + */ + getCreationTime(): (number) | (null); /** * Returns an object with V8 heap statistics. Note that all statistics are reported * in Kilobytes. @@ -9258,7 +9767,12 @@ declare namespace NodeJS { getIOCounters(): Electron.IOCounters; /** * Returns an object giving memory usage statistics about the current process. Note - * that all statistics are reported in Kilobytes. + * that all statistics are reported in Kilobytes. This api should be called after + * app ready. Chromium does not provide residentSet value for macOS. This is + * because macOS performs in-memory compression of pages that haven't been recently + * used. As a result the resident set size value is not what one would expect. + * private memory is more representative of the actual pre-compression memory usage + * of the process on macOS. */ getProcessMemoryInfo(): Electron.ProcessMemoryInfo; /** @@ -9275,6 +9789,10 @@ declare namespace NodeJS { * whichever is lower for the current process. */ setFdLimit(maxDescriptors: number): void; + /** + * Takes a V8 heap snapshot and saves it to filePath. + */ + takeHeapSnapshot(filePath: string): boolean; /** * A Boolean. When app is started by being passed as parameter to the default app, * this property is true in the main process, otherwise it is undefined. @@ -9300,6 +9818,11 @@ declare namespace NodeJS { * A String representing the path to the resources directory. */ resourcesPath?: string; + /** + * A Boolean. When the renderer process is sandboxed, this property is true, + * otherwise it is undefined. + */ + sandboxed?: boolean; /** * A Boolean that controls whether or not deprecation warnings will be thrown as * exceptions. Setting this to true will throw errors for deprecations. This @@ -9335,4 +9858,4 @@ declare namespace NodeJS { electron: string; chrome: string; } -} +} \ No newline at end of file diff --git a/src/typings/gc-signals.d.ts b/src/typings/gc-signals.d.ts deleted file mode 100644 index 15af041177..0000000000 --- a/src/typings/gc-signals.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -declare module 'gc-signals' { - export interface GCSignal { - } - /** - * Create a new GC signal. When being garbage collected the passed - * value is stored for later consumption. - */ - export const GCSignal: { - new(id: number): GCSignal; - }; - /** - * Consume ids of garbage collected signals. - */ - export function consumeSignals(): number[]; - export function onDidGarbageCollectSignals(callback: (ids: number[]) => any): { - dispose(): void; - }; - export function trackGarbageCollection(obj: any, id: number): number; -} \ No newline at end of file diff --git a/src/typings/lib.array-ext.d.ts b/src/typings/lib.array-ext.d.ts index d1d98a1b6f..5b592172d7 100644 --- a/src/typings/lib.array-ext.d.ts +++ b/src/typings/lib.array-ext.d.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ interface ArrayConstructor { + isArray(arg: ReadonlyArray | null | undefined): arg is ReadonlyArray; + isArray(arg: Array | null | undefined): arg is Array; isArray(arg: any): arg is Array; isArray(arg: any): arg is Array; } \ No newline at end of file diff --git a/src/typings/vscode-nsfw.d.ts b/src/typings/nsfw.d.ts similarity index 95% rename from src/typings/vscode-nsfw.d.ts rename to src/typings/nsfw.d.ts index c9863ca887..37c4c2ec92 100644 --- a/src/typings/vscode-nsfw.d.ts +++ b/src/typings/nsfw.d.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module 'vscode-nsfw' { +declare module 'nsfw' { interface NsfwWatcher { start(): any; stop(): any; @@ -22,6 +22,7 @@ declare module 'vscode-nsfw' { directory: string; file?: string; newFile?: string; + newDirectory?: string; oldFile?: string; } diff --git a/src/typings/onigasm-umd.d.ts b/src/typings/onigasm-umd.d.ts new file mode 100644 index 0000000000..24396aafa9 --- /dev/null +++ b/src/typings/onigasm-umd.d.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module "onigasm-umd" { + + function loadWASM(data: string | ArrayBuffer): Promise; + + class OnigString { + constructor(content: string); + readonly content: string; + readonly dispose?: () => void; + } + + class OnigScanner { + constructor(patterns: string[]); + findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch; + } + + export interface IOnigCaptureIndex { + index: number + start: number + end: number + length: number + } + + export interface IOnigMatch { + index: number + captureIndices: IOnigCaptureIndex[] + scanner: OnigScanner + } +} \ No newline at end of file diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index 76916d8513..5b98dc2f4b 100644 --- a/src/typings/require.d.ts +++ b/src/typings/require.d.ts @@ -17,7 +17,12 @@ declare const enum LoaderEventType { NodeEndEvaluatingScript = 32, NodeBeginNativeRequire = 33, - NodeEndNativeRequire = 34 + NodeEndNativeRequire = 34, + + CachedDataFound = 60, + CachedDataMissed = 61, + CachedDataRejected = 62, + CachedDataCreated = 63, } declare class LoaderEvent { diff --git a/src/typings/vscode-sqlite3.d.ts b/src/typings/vscode-sqlite3.d.ts index 6c48ee2d01..6791f2e967 100644 --- a/src/typings/vscode-sqlite3.d.ts +++ b/src/typings/vscode-sqlite3.d.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Type definitions for sqlite3 3.1 -// Project: https://github.com/mapbox/node-sqlite3 +// Project: http://github.com/mapbox/node-sqlite3 // Definitions by: Nick Malaguti // Sumant Manne // Behind The Math @@ -18,6 +18,9 @@ declare module 'vscode-sqlite3' { export const OPEN_READONLY: number; export const OPEN_READWRITE: number; export const OPEN_CREATE: number; + export const OPEN_SHAREDCACHE: number; + export const OPEN_PRIVATECACHE: number; + export const OPEN_URI: number; export const cached: { Database(filename: string, callback?: (this: Database, err: Error | null) => void): Database; @@ -100,6 +103,9 @@ declare module 'vscode-sqlite3' { OPEN_READONLY: number; OPEN_READWRITE: number; OPEN_CREATE: number; + OPEN_SHAREDCACHE: number; + OPEN_PRIVATECACHE: number; + OPEN_URI: number; cached: typeof cached; RunResult: RunResult; Statement: typeof Statement; diff --git a/src/typings/vscode-textmate.d.ts b/src/typings/vscode-textmate.d.ts index d92fa13d9e..28f28fbbf1 100644 --- a/src/typings/vscode-textmate.d.ts +++ b/src/typings/vscode-textmate.d.ts @@ -30,7 +30,7 @@ declare module "vscode-textmate" { */ export interface RegistryOptions { theme?: IRawTheme; - loadGrammar(scopeName: string): Thenable | null; + loadGrammar(scopeName: string): Thenable; getInjections?(scopeName: string): string[]; getOnigLib?(): Thenable; } @@ -85,7 +85,7 @@ declare module "vscode-textmate" { * Load the grammar for `scopeName` and all referenced included grammars asynchronously. */ loadGrammar(initialScopeName: string): Thenable; - private _loadGrammar(initialScopeName, initialLanguage, embeddedLanguages, tokenTypes); + private _loadGrammar; /** * Adds a rawGrammar. */ @@ -182,7 +182,7 @@ declare module "vscode-textmate" { equals(other: StackElement): boolean; } export const INITIAL: StackElement; - export const parseRawGrammar: (content: string, filePath: string) => IRawGrammar; + export const parseRawGrammar: (content: string, filePath?: string) => IRawGrammar; export interface ILocation { readonly filename: string; readonly line: number; diff --git a/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css b/src/typings/vscode-windows-ca-certs.d.ts similarity index 88% rename from src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css rename to src/typings/vscode-windows-ca-certs.d.ts index 06e13f322f..37b2282827 100644 --- a/src/vs/workbench/contrib/codeinset/electron-browser/codeInsetWidget.css +++ b/src/typings/vscode-windows-ca-certs.d.ts @@ -3,6 +3,4 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .code-inset { - z-index: 10; -} +declare module 'vscode-windows-ca-certs'; diff --git a/src/vs/workbench/contrib/update/electron-browser/media/update.contribution.css b/src/typings/vsda.d.ts similarity index 82% rename from src/vs/workbench/contrib/update/electron-browser/media/update.contribution.css rename to src/typings/vsda.d.ts index 19aa75b356..5a0ca7212e 100644 --- a/src/vs/workbench/contrib/update/electron-browser/media/update.contribution.css +++ b/src/typings/vsda.d.ts @@ -3,6 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.update-activity { - -webkit-mask: url('update.svg') no-repeat 50% 50%; +declare module 'vsda' { + export class signer { + sign(arg: any): any; + } } diff --git a/src/typings/xterm-addon-search.d.ts b/src/typings/xterm-addon-search.d.ts new file mode 100644 index 0000000000..2f3be6f82b --- /dev/null +++ b/src/typings/xterm-addon-search.d.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +// HACK: gulp-tsb doesn't play nice with importing from typings +// import { Terminal, ITerminalAddon } from 'xterm'; + +declare module 'xterm-addon-search' { + /** + * Options for a search. + */ + export interface ISearchOptions { + /** + * Whether the search term is a regex. + */ + regex?: boolean; + + /** + * Whether to search for a whole word, the result is only valid if it's + * suppounded in "non-word" characters such as `_`, `(`, `)` or space. + */ + wholeWord?: boolean; + + /** + * Whether the search is case sensitive. + */ + caseSensitive?: boolean; + + /** + * Whether to do an indcremental search, this will expand the selection if it + * still matches the term the user typed. Note that this only affects + * `findNext`, not `findPrevious`. + */ + incremental?: boolean; + } + + /** + * An xterm.js addon that provides search functionality. + */ + export class SearchAddon { + /** + * Activates the addon + * @param terminal The terminal the addon is being loaded in. + */ + public activate(terminal: any): void; + + /** + * Disposes the addon. + */ + public dispose(): void; + + /** + * Search forwards for the next result that matches the search term and + * options. + * @param term The search term. + * @param searchOptions The options for the search. + */ + public findNext(term: string, searchOptions?: ISearchOptions): boolean; + + /** + * Search backwards for the previous result that matches the search term and + * options. + * @param term The search term. + * @param searchOptions The options for the search. + */ + public findPrevious(term: string, searchOptions?: ISearchOptions): boolean; + } +} diff --git a/src/typings/xterm-addon-web-links.d.ts b/src/typings/xterm-addon-web-links.d.ts new file mode 100644 index 0000000000..da348f8c82 --- /dev/null +++ b/src/typings/xterm-addon-web-links.d.ts @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +// HACK: gulp-tsb doesn't play nice with importing from typings +// import { Terminal, ITerminalAddon } from 'xterm'; +interface ILinkMatcherOptions { + /** + * The index of the link from the regex.match(text) call. This defaults to 0 + * (for regular expressions without capture groups). + */ + matchIndex?: number; + + /** + * A callback that validates whether to create an individual link, pass + * whether the link is valid to the callback. + */ + validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; + + /** + * A callback that fires when the mouse hovers over a link for a moment. + */ + tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; + + /** + * A callback that fires when the mouse leaves a link. Note that this can + * happen even when tooltipCallback hasn't fired for the link yet. + */ + leaveCallback?: () => void; + + /** + * The priority of the link matcher, this defines the order in which the link + * matcher is evaluated relative to others, from highest to lowest. The + * default value is 0. + */ + priority?: number; + + /** + * A callback that fires when the mousedown and click events occur that + * determines whether a link will be activated upon click. This enables + * only activating a link when a certain modifier is held down, if not the + * mouse event will continue propagation (eg. double click to select word). + */ + willLinkActivate?: (event: MouseEvent, uri: string) => boolean; +} + +declare module 'xterm-addon-web-links' { + /** + * An xterm.js addon that enables web links. + */ + export class WebLinksAddon { + /** + * Creates a new web links addon. + * @param handler The callback when the link is called. + * @param options Options for the link matcher. + */ + constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions); + + /** + * Activates the addon + * @param terminal The terminal the addon is being loaded in. + */ + public activate(terminal: any): void; + + /** + * Disposes the addon. + */ + public dispose(): void; + } +} diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/xterm.d.ts similarity index 76% rename from src/typings/vscode-xterm.d.ts rename to src/typings/xterm.d.ts index 01e47b16b2..3372f63435 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -9,7 +9,7 @@ /// -declare module 'vscode-xterm' { +declare module 'xterm' { /** * A string representing text font weight. */ @@ -26,13 +26,14 @@ declare module 'vscode-xterm' { export interface ITerminalOptions { /** * Whether background should support non-opaque color. It must be set before - * executing open() method and can't be changed later without excuting it again. - * Warning: Enabling this option can reduce performances somewhat. + * executing the `Terminal.open()` method and can't be changed later without + * executing it again. Note that enabling this can negatively impact + * performance. */ allowTransparency?: boolean; /** - * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). + * A data uri of the sound to use for the bell when `bellStyle = 'sound'`. */ bellSound?: string; @@ -76,31 +77,6 @@ declare module 'vscode-xterm' { */ drawBoldTextInBrightColors?: boolean; - /** - * Whether to enable the rendering of bold text. - * - * @deprecated Use fontWeight and fontWeightBold instead. - */ - enableBold?: boolean; - - /** - * What character atlas implementation to use. The character atlas caches drawn characters, - * speeding up rendering significantly. However, it can introduce some minor rendering - * artifacts. - * - * - 'none': Don't use an atlas. - * - 'static': Generate an atlas when the terminal starts or is reconfigured. This atlas will - * only contain ASCII characters in 16 colors. - * - 'dynamic': Generate an atlas using a LRU cache as characters are requested. Limited to - * ASCII characters (for now), but supports 256 colors. For characters covered by the static - * cache, it's slightly slower in comparison, since there's more overhead involved in - * managing the cache. - * - * Currently defaults to 'static'. This option may be removed in the future. If it is, passed - * parameters will be ignored. - */ - experimentalCharAtlas?: 'none' | 'static' | 'dynamic'; - /** * The font size used to render text. */ @@ -139,9 +115,9 @@ declare module 'vscode-xterm' { /** * Whether holding a modifier key will force normal selection behavior, * regardless of whether the terminal is in mouse events mode. This will - * also prevent mouse events from being emitted by the terminal. For example, - * this allows you to use xterm.js' regular selection inside tmux with - * mouse mode enabled. + * also prevent mouse events from being emitted by the terminal. For + * example, this allows you to use xterm.js' regular selection inside tmux + * with mouse mode enabled. */ macOptionClickForcesSelection?: boolean; @@ -174,8 +150,9 @@ declare module 'vscode-xterm' { screenReaderMode?: boolean; /** - * The amount of scrollback in the terminal. Scrollback is the amount of rows - * that are retained when lines are scrolled beyond the initial viewport. + * The amount of scrollback in the terminal. Scrollback is the amount of + * rows that are retained when lines are scrolled beyond the initial + * viewport. */ scrollback?: number; @@ -212,7 +189,7 @@ declare module 'vscode-xterm' { background?: string, /** The cursor color */ cursor?: string, - /** The accent color of the cursor (used as the foreground color for a block cursor) */ + /** The accent color of the cursor (fg color for a block cursor) */ cursorAccent?: string, /** The selection color (can be transparent) */ selection?: string, @@ -278,8 +255,8 @@ declare module 'vscode-xterm' { leaveCallback?: () => void; /** - * The priority of the link matcher, this defines the order in which the link - * matcher is evaluated relative to others, from highest to lowest. The + * The priority of the link matcher, this defines the order in which the + * link matcher is evaluated relative to others, from highest to lowest. The * default value is 0. */ priority?: number; @@ -293,13 +270,6 @@ declare module 'vscode-xterm' { willLinkActivate?: (event: MouseEvent, uri: string) => boolean; } - export interface IEventEmitter { - on(type: string, listener: (...args: any[]) => void): void; - off(type: string, listener: (...args: any[]) => void): void; - emit(type: string, data?: any): void; - addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; - } - /** * An object that can be disposed via a dispose function. */ @@ -315,22 +285,47 @@ declare module 'vscode-xterm' { (listener: (e: T) => any): IDisposable; } + /** + * Represents a specific line in the terminal that is tracked when scrollback + * is trimmed and lines are added or removed. + */ export interface IMarker extends IDisposable { + /** + * A unique identifier for this marker. + */ readonly id: number; + + /** + * Whether this marker is disposed. + */ readonly isDisposed: boolean; + + /** + * The actual line index in the buffer at this point in time. + */ readonly line: number; } + /** + * The set of localizable strings. + */ export interface ILocalizableStrings { - blankLine: string; + /** + * The aria label for the underlying input textarea for the terminal. + */ promptLabel: string; + + /** + * Announcement for when line reading is suppressed due to too many lines + * being printed to the terminal when `screenReaderMode` is enabled. + */ tooMuchOutput: string; } /** * The class that represents an xterm.js terminal. */ - export class Terminal implements IEventEmitter, IDisposable { + export class Terminal implements IDisposable { /** * The element containing the terminal. */ @@ -454,96 +449,6 @@ declare module 'vscode-xterm' { */ focus(): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'data', listener: (...args: any[]) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'key', listener: (key: string, event: KeyboardEvent) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'keypress' | 'keydown', listener: (event: KeyboardEvent) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'refresh', listener: (data: { start: number, end: number }) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'resize', listener: (data: { cols: number, rows: number }) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'scroll', listener: (ydisp: number) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: 'title', listener: (title: string) => void): void; - /** - * Registers an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - on(type: string, listener: (...args: any[]) => void): void; - - /** - * Deregisters an event listener. - * @param type The type of the event. - * @param listener The listener. - * @deprecated use `Terminal.onEvent(listener).dispose()` instead. - */ - off(type: 'blur' | 'focus' | 'linefeed' | 'selection' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; - - /** - * Emits an event on the terminal. - * @param type The type of event - * @param data data associated with the event. - * @deprecated This is being removed from the API with no replacement, see - * issue #1505. - */ - emit(type: string, data?: any): void; - - /** - * Adds an event listener to the Terminal, returning an IDisposable that can - * be used to conveniently remove the event listener. - * @param type The type of event. - * @param handler The event handler. - * @deprecated use `Terminal.onEvent(listener)` instead. - */ - addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; - /** * Resizes the terminal. It's best practice to debounce calls to resize, * this will help ensure that the pty can respond to the resize event @@ -706,13 +611,6 @@ declare module 'vscode-xterm' { */ dispose(): void; - /** - * Destroys the terminal and detaches it from the DOM. - * - * @deprecated Use dispose() instead. - */ - destroy(): void; - /** * Scroll the display of the terminal * @param amount The number of lines to scroll down (negative scroll up). @@ -759,9 +657,9 @@ declare module 'vscode-xterm' { writeln(data: string): void; /** - * Writes UTF8 data to the terminal. - * This has a slight performance advantage over the string based write method - * due to lesser data conversions needed on the way from the pty to xterm.js. + * Writes UTF8 data to the terminal. This has a slight performance advantage + * over the string based write method due to lesser data conversions needed + * on the way from the pty to xterm.js. * @param data The data to write to the terminal. */ writeUtf8(data: Uint8Array): void; @@ -775,7 +673,7 @@ declare module 'vscode-xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; + getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -826,7 +724,7 @@ declare module 'vscode-xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; + setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. @@ -897,13 +795,13 @@ declare module 'vscode-xterm' { */ export interface ITerminalAddon extends IDisposable { /** - * (EXPERIMENTAL) This is called when the addon is activated within xterm.js. + * (EXPERIMENTAL) This is called when the addon is activated. */ activate(terminal: Terminal): void; } /** - * An object representing a selecrtion within the terminal. + * An object representing a selection within the terminal. */ interface ISelectionPosition { /** @@ -927,6 +825,9 @@ declare module 'vscode-xterm' { endRow: number; } + /** + * Represents a terminal buffer. + */ interface IBuffer { /** * The y position of the cursor. This ranges between `0` (when the @@ -958,16 +859,21 @@ declare module 'vscode-xterm' { readonly length: number; /** - * Gets a line from the buffer, or undefined if the line index does not exist. + * Gets a line from the buffer, or undefined if the line index does not + * exist. * - * Note that the result of this function should be used immediately after calling as when the - * terminal updates it could lead to unexpected behavior. + * Note that the result of this function should be used immediately after + * calling as when the terminal updates it could lead to unexpected + * behavior. * * @param y The line index to get. */ getLine(y: number): IBufferLine | undefined; } + /** + * Represents a line in the terminal's buffer. + */ interface IBufferLine { /** * Whether the line is wrapped from the previous line. @@ -977,16 +883,17 @@ declare module 'vscode-xterm' { /** * Gets a cell from the line, or undefined if the line index does not exist. * - * Note that the result of this function should be used immediately after calling as when the - * terminal updates it could lead to unexpected behavior. + * Note that the result of this function should be used immediately after + * calling as when the terminal updates it could lead to unexpected + * behavior. * * @param x The character index to get. */ - getCell(x: number): IBufferCell; + getCell(x: number): IBufferCell | undefined; /** - * Gets the line as a string. Note that this is gets only the string for the line, not taking - * isWrapped into account. + * Gets the line as a string. Note that this is gets only the string for the + * line, not taking isWrapped into account. * * @param trimRight Whether to trim any whitespace at the right of the line. * @param startColumn The column to start from (inclusive). @@ -995,6 +902,9 @@ declare module 'vscode-xterm' { translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string; } + /** + * Represents a single cell in the terminal's buffer. + */ interface IBufferCell { /** * The character within the cell. @@ -1013,19 +923,24 @@ declare module 'vscode-xterm' { } + + // Modifications to official .d.ts below -declare module 'vscode-xterm' { +declare module 'xterm' { interface TerminalCore { debug: boolean; handler(text: string): void; - _onScroll: IEventEmitter2; - _onKey: IEventEmitter2<{ key: string }>; + _onScroll: IEventEmitter; + _onKey: IEventEmitter<{ key: string }>; - charMeasure?: { height: number, width: number }; + _charSizeService: { + width: number; + height: number; + } - _renderCoordinator: { + _renderService: { _renderer: { _renderLayers: any[]; }; @@ -1033,46 +948,11 @@ declare module 'vscode-xterm' { }; } - interface IEventEmitter2 { + interface IEventEmitter { fire(e: T): void; } - interface ISearchOptions { - /** - * Whether the find should be done as a regex. - */ - regex?: boolean; - /** - * Whether only whole words should match. - */ - wholeWord?: boolean; - /** - * Whether find should pay attention to case. - */ - caseSensitive?: boolean; - } - interface Terminal { _core: TerminalCore; - - webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void; - - /** - * Find the next instance of the term, then scroll to and select it. If it - * doesn't exist, do nothing. - * @param term The search term. - * @param findOptions Regex, whole word, and case sensitive options. - * @return Whether a result was found. - */ - findNext(term: string, findOptions: ISearchOptions): boolean; - - /** - * Find the previous instance of the term, then scroll to and select it. If it - * doesn't exist, do nothing. - * @param term The search term. - * @param findOptions Regex, whole word, and case sensitive options. - * @return Whether a result was found. - */ - findPrevious(term: string, findOptions: ISearchOptions): boolean; } -} +} \ No newline at end of file diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index b388c4ead7..63b7dabf19 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -122,6 +122,7 @@ export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0)); export const isWebkitWebView = (!isChrome && !isSafari && isWebKit); export const isIPad = (userAgent.indexOf('iPad') >= 0); export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0); +export const isStandalone = (window.matchMedia('(display-mode: standalone)').matches); export function hasClipboardSupport() { if (isIE) { diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index a4434d722a..c6cb3b7a80 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -24,7 +24,7 @@ export class ContextSubMenu extends SubmenuAction { export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): Array; + getActions(): ReadonlyArray; getActionViewItem?(action: IAction): IActionViewItem | undefined; getActionsContext?(event?: IContextMenuEvent): any; getKeyBinding?(action: IAction): ResolvedKeybinding | undefined; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 02a6055c3a..be2b8aa9cd 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -11,9 +11,11 @@ import { TimeoutTimer } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { coalesce } from 'vs/base/common/arrays'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -38,12 +40,12 @@ export function isInDOM(node: Node | null): boolean { } interface IDomClassList { - hasClass(node: HTMLElement, className: string): boolean; - addClass(node: HTMLElement, className: string): void; - addClasses(node: HTMLElement, ...classNames: string[]): void; - removeClass(node: HTMLElement, className: string): void; - removeClasses(node: HTMLElement, ...classNames: string[]): void; - toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void; + hasClass(node: HTMLElement | SVGElement, className: string): boolean; + addClass(node: HTMLElement | SVGElement, className: string): void; + addClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void; + removeClass(node: HTMLElement | SVGElement, className: string): void; + removeClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void; + toggleClass(node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean): void; } const _manualClassList = new class implements IDomClassList { @@ -191,12 +193,12 @@ const _nativeClassList = new class implements IDomClassList { // In IE11 there is only partial support for `classList` which makes us keep our // custom implementation. Otherwise use the native implementation, see: http://caniuse.com/#search=classlist const _classList: IDomClassList = browser.isIE ? _manualClassList : _nativeClassList; -export const hasClass: (node: HTMLElement, className: string) => boolean = _classList.hasClass.bind(_classList); -export const addClass: (node: HTMLElement, className: string) => void = _classList.addClass.bind(_classList); -export const addClasses: (node: HTMLElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList); -export const removeClass: (node: HTMLElement, className: string) => void = _classList.removeClass.bind(_classList); -export const removeClasses: (node: HTMLElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList); -export const toggleClass: (node: HTMLElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList); +export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList); +export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList); +export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList); +export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList); +export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList); +export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList); class DomListener implements IDisposable { @@ -768,6 +770,10 @@ export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClaz return null; } +export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): boolean { + return !!findParentWithClass(node, clazz, stopAtClazzOrNode); +} + export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement { let style = document.createElement('style'); style.type = 'text/css'; @@ -800,8 +806,7 @@ export function createCSSRule(selector: string, cssText: string, style: HTMLStyl if (!style || !cssText) { return; } - - (style.sheet).insertRule(selector + '{' + cssText + '}', 0); + style.textContent = `${selector}{${cssText}}\n${style.textContent}`; } export function removeCSSRulesContainingSelector(ruleName: string, style: HTMLStyleElement = getSharedStyleSheet()): void { @@ -854,6 +859,8 @@ export const EventType = { ERROR: 'error', RESIZE: 'resize', SCROLL: 'scroll', + FULLSCREEN_CHANGE: 'fullscreenchange', + WK_FULLSCREEN_CHANGE: 'webkitfullscreenchange', // Form SELECT: 'select', CHANGE: 'change', @@ -905,10 +912,9 @@ export const EventHelper = { } }; -export interface IFocusTracker { +export interface IFocusTracker extends Disposable { onDidFocus: Event; onDidBlur: Event; - dispose(): void; } export function saveParentsScrollTop(node: Element): number[] { @@ -929,21 +935,20 @@ export function restoreParentsScrollTop(node: Element, state: number[]): void { } } -class FocusTracker implements IFocusTracker { +class FocusTracker extends Disposable implements IFocusTracker { - private _onDidFocus = new Emitter(); - readonly onDidFocus: Event = this._onDidFocus.event; + private readonly _onDidFocus = this._register(new Emitter()); + public readonly onDidFocus: Event = this._onDidFocus.event; - private _onDidBlur = new Emitter(); - readonly onDidBlur: Event = this._onDidBlur.event; - - private disposables: IDisposable[] = []; + private readonly _onDidBlur = this._register(new Emitter()); + public readonly onDidBlur: Event = this._onDidBlur.event; constructor(element: HTMLElement | Window) { + super(); let hasFocus = isAncestor(document.activeElement, element); let loosingFocus = false; - let onFocus = () => { + const onFocus = () => { loosingFocus = false; if (!hasFocus) { hasFocus = true; @@ -951,7 +956,7 @@ class FocusTracker implements IFocusTracker { } }; - let onBlur = () => { + const onBlur = () => { if (hasFocus) { loosingFocus = true; window.setTimeout(() => { @@ -964,14 +969,8 @@ class FocusTracker implements IFocusTracker { } }; - domEvent(element, EventType.FOCUS, true)(onFocus, null, this.disposables); - domEvent(element, EventType.BLUR, true)(onBlur, null, this.disposables); - } - - dispose(): void { - this.disposables = dispose(this.disposables); - this._onDidFocus.dispose(); - this._onDidBlur.dispose(); + this._register(domEvent(element, EventType.FOCUS, true)(onFocus)); + this._register(domEvent(element, EventType.BLUR, true)(onBlur)); } } @@ -991,14 +990,28 @@ export function prepend(parent: HTMLElement, child: T): T { const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/; -export function $(description: string, attrs?: { [key: string]: any; }, ...children: Array): T { +export enum Namespace { + HTML = 'http://www.w3.org/1999/xhtml', + SVG = 'http://www.w3.org/2000/svg' +} + +function _$(namespace: Namespace, description: string, attrs?: { [key: string]: any; }, ...children: Array): T { let match = SELECTOR_REGEX.exec(description); if (!match) { throw new Error('Bad use of emmet'); } - let result = document.createElement(match[1] || 'div'); + attrs = { ...(attrs || {}) }; + + let tagName = match[1] || 'div'; + let result: T; + + if (namespace !== Namespace.HTML) { + result = document.createElementNS(namespace as string, tagName) as T; + } else { + result = document.createElement(tagName) as unknown as T; + } if (match[3]) { result.id = match[3]; @@ -1007,7 +1020,6 @@ export function $(description: string, attrs?: { [key: st result.className = match[4].replace(/\./g, ' ').trim(); } - attrs = attrs || {}; Object.keys(attrs).forEach(name => { const value = attrs![name]; if (/^on\w+$/.test(name)) { @@ -1034,6 +1046,14 @@ export function $(description: string, attrs?: { [key: st return result as T; } +export function $(description: string, attrs?: { [key: string]: any; }, ...children: Array): T { + return _$(Namespace.HTML, description, attrs, ...children); +} + +$.SVG = function (description: string, attrs?: { [key: string]: any; }, ...children: Array): T { + return _$(Namespace.SVG, description, attrs, ...children); +}; + export function join(nodes: Node[], separator: Node | string): Node[] { const result: Node[] = []; @@ -1163,3 +1183,23 @@ export function animate(fn: () => void): IDisposable { let stepDisposable = scheduleAtNextAnimationFrame(step); return toDisposable(() => stepDisposable.dispose()); } + + + +const _location = URI.parse(window.location.href); + +export function asDomUri(uri: URI): URI { + if (!uri) { + return uri; + } + if (!platform.isWeb) { + //todo@joh remove this once we have sw in electron going + return uri; + } + if (Schemas.vscodeRemote === uri.scheme) { + // rewrite vscode-remote-uris to uris of the window location + // so that they can be intercepted by the service worker + return _location.with({ path: '/vscode-resources/fetch', query: JSON.stringify({ u: uri.toJSON(), i: 1 }) }); + } + return uri; +} diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index f4fd8b5382..b29d574cfd 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { IframeUtils } from 'vs/base/browser/iframe'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; export interface IStandardMouseMoveEventData { leftButton: boolean; @@ -36,24 +36,16 @@ export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData, }; } -export class GlobalMouseMoveMonitor extends Disposable { +export class GlobalMouseMoveMonitor implements IDisposable { - private hooks: IDisposable[]; - private mouseMoveEventMerger: IEventMerger | null; - private mouseMoveCallback: IMouseMoveCallback | null; - private onStopCallback: IOnStopCallback | null; - - constructor() { - super(); - this.hooks = []; - this.mouseMoveEventMerger = null; - this.mouseMoveCallback = null; - this.onStopCallback = null; - } + private readonly hooks = new DisposableStore(); + private mouseMoveEventMerger: IEventMerger | null = null; + private mouseMoveCallback: IMouseMoveCallback | null = null; + private onStopCallback: IOnStopCallback | null = null; public dispose(): void { this.stopMonitoring(false); - super.dispose(); + this.hooks.dispose(); } public stopMonitoring(invokeStopCallback: boolean): void { @@ -63,10 +55,10 @@ export class GlobalMouseMoveMonitor extends Disposable { } // Unhook - this.hooks = dispose(this.hooks); + this.hooks.clear(); this.mouseMoveEventMerger = null; this.mouseMoveCallback = null; - let onStopCallback = this.onStopCallback; + const onStopCallback = this.onStopCallback; this.onStopCallback = null; if (invokeStopCallback && onStopCallback) { @@ -74,8 +66,8 @@ export class GlobalMouseMoveMonitor extends Disposable { } } - public isMonitoring() { - return this.hooks.length > 0; + public isMonitoring(): boolean { + return !!this.mouseMoveEventMerger; } public startMonitoring( @@ -93,32 +85,32 @@ export class GlobalMouseMoveMonitor extends Disposable { let windowChain = IframeUtils.getSameOriginWindowChain(); for (const element of windowChain) { - this.hooks.push(dom.addDisposableThrottledListener(element.window.document, 'mousemove', + this.hooks.add(dom.addDisposableThrottledListener(element.window.document, 'mousemove', (data: R) => this.mouseMoveCallback!(data), (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); - this.hooks.push(dom.addDisposableListener(element.window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); + this.hooks.add(dom.addDisposableListener(element.window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); } if (IframeUtils.hasDifferentOriginAncestor()) { let lastSameOriginAncestor = windowChain[windowChain.length - 1]; // We might miss a mouse up if it happens outside the iframe // This one is for Chrome - this.hooks.push(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseout', (browserEvent: MouseEvent) => { + this.hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseout', (browserEvent: MouseEvent) => { let e = new StandardMouseEvent(browserEvent); if (e.target.tagName.toLowerCase() === 'html') { this.stopMonitoring(true); } })); // This one is for FF - this.hooks.push(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseover', (browserEvent: MouseEvent) => { + this.hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseover', (browserEvent: MouseEvent) => { let e = new StandardMouseEvent(browserEvent); if (e.target.tagName.toLowerCase() === 'html') { this.stopMonitoring(true); } })); // This one is for IE - this.hooks.push(dom.addDisposableListener(lastSameOriginAncestor.window.document.body, 'mouseleave', (browserEvent: MouseEvent) => { + this.hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document.body, 'mouseleave', (browserEvent: MouseEvent) => { this.stopMonitoring(true); })); } diff --git a/src/vs/base/browser/hash.ts b/src/vs/base/browser/hash.ts deleted file mode 100644 index 222332b2f8..0000000000 --- a/src/vs/base/browser/hash.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function createSHA1(content: string): Thenable { - if (typeof require !== 'undefined') { - const _crypto: typeof crypto = require.__$__nodeRequire('crypto'); - return Promise.resolve(_crypto['createHash']('sha1').update(content).digest('hex')); - } - return crypto.subtle.digest('SHA-1', new TextEncoder().encode(content)).then(buffer => { - // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string - return Array.prototype.map.call(new Uint8Array(buffer), (value: number) => `00${value.toString(16)}`.slice(-2)).join(''); - }); -} \ No newline at end of file diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index d400bbadf3..631038c5b5 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -6,10 +6,10 @@ import * as DOM from 'vs/base/browser/dom'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { escape } from 'vs/base/common/strings'; -import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent'; +import { removeMarkdownEscapes, IMarkdownString, parseHrefAndDimensions } from 'vs/base/common/htmlContent'; import * as marked from 'vs/base/common/marked/marked'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { parse } from 'vs/base/common/marshalling'; @@ -17,7 +17,7 @@ import { cloneAndChange } from 'vs/base/common/objects'; export interface IContentActionHandler { callback: (content: string, event?: IMouseEvent) => void; - disposeables: IDisposable[]; + readonly disposeables: DisposableStore; } export interface RenderOptions { @@ -75,12 +75,15 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions return encodeURIComponent(JSON.stringify(data)); }; - const _href = function (href: string): string { + const _href = function (href: string, isDomUri: boolean): string { const data = markdown.uris && markdown.uris[href]; if (!data) { return href; } let uri = URI.revive(data); + if (isDomUri) { + uri = DOM.asDomUri(uri); + } if (uri.query) { uri = uri.with({ query: _uriMassage(uri.query) }); } @@ -97,29 +100,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions const renderer = new marked.Renderer(); renderer.image = (href: string, title: string, text: string) => { - href = _href(href); let dimensions: string[] = []; - if (href) { - const splitted = href.split('|').map(s => s.trim()); - href = splitted[0]; - const parameters = splitted[1]; - if (parameters) { - const heightFromParams = /height=(\d+)/.exec(parameters); - const widthFromParams = /width=(\d+)/.exec(parameters); - const height = heightFromParams ? heightFromParams[1] : ''; - const width = widthFromParams ? widthFromParams[1] : ''; - const widthIsFinite = isFinite(parseInt(width)); - const heightIsFinite = isFinite(parseInt(height)); - if (widthIsFinite) { - dimensions.push(`width="${width}"`); - } - if (heightIsFinite) { - dimensions.push(`height="${height}"`); - } - } - } let attributes: string[] = []; if (href) { + ({ href, dimensions } = parseHrefAndDimensions(href)); + href = _href(href, true); attributes.push(`src="${href}"`); } if (text) { @@ -138,7 +123,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions if (href === text) { // raw link case text = removeMarkdownEscapes(text); } - href = _href(href); + href = _href(href, false); title = removeMarkdownEscapes(title); href = removeMarkdownEscapes(href); if ( @@ -203,7 +188,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions } if (options.actionHandler) { - options.actionHandler.disposeables.push(DOM.addStandardDisposableListener(element, 'click', event => { + options.actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { let target: HTMLElement | null = event.target; if (target.tagName !== 'A') { target = target.parentElement; @@ -298,7 +283,7 @@ function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionH else if (treeNode.type === FormatType.Action && actionHandler) { const a = document.createElement('a'); a.href = '#'; - actionHandler.disposeables.push(DOM.addStandardDisposableListener(a, 'click', (event) => { + actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => { actionHandler.callback(String(treeNode.index), event); })); diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index ea3d32745f..52c15c9652 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -6,7 +6,7 @@ import 'vs/css!./actionbar'; import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; -import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { SelectBox, ISelectOptionItem, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; @@ -16,16 +16,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { Event, Emitter } from 'vs/base/common/event'; -import { asArray } from 'vs/base/common/arrays'; -export interface IActionViewItem { +export interface IActionViewItem extends IDisposable { actionRunner: IActionRunner; setActionContext(context: any): void; render(element: HTMLElement): void; isEnabled(): boolean; focus(fromRight?: boolean): void; blur(): void; - dispose(): void; } export interface IBaseActionViewItemOptions { @@ -407,16 +405,16 @@ export class ActionBar extends Disposable implements IActionRunner { protected actionsList: HTMLElement; private _onDidBlur = this._register(new Emitter()); - get onDidBlur(): Event { return this._onDidBlur.event; } + readonly onDidBlur: Event = this._onDidBlur.event; private _onDidCancel = this._register(new Emitter()); - get onDidCancel(): Event { return this._onDidCancel.event; } + readonly onDidCancel: Event = this._onDidCancel.event; private _onDidRun = this._register(new Emitter()); - get onDidRun(): Event { return this._onDidRun.event; } + readonly onDidRun: Event = this._onDidRun.event; private _onDidBeforeRun = this._register(new Emitter()); - get onDidBeforeRun(): Event { return this._onDidBeforeRun.event; } + readonly onDidBeforeRun: Event = this._onDidBeforeRun.event; constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) { super(); @@ -593,8 +591,8 @@ export class ActionBar extends Disposable implements IActionRunner { return this.domNode; } - push(arg: IAction | IAction[], options: IActionOptions = {}): void { - const actions: IAction[] = asArray(arg); + push(arg: IAction | ReadonlyArray, options: IActionOptions = {}): void { + const actions: ReadonlyArray = Array.isArray(arg) ? arg : [arg]; let index = types.isNumber(options.index) ? options.index : null; diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index 8f0da6bb42..c9ceb0a65c 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -31,12 +31,16 @@ } .monaco-breadcrumbs .monaco-breadcrumb-item:not(:nth-child(2))::before { - background-image: url(./collapsed.svg); + background-image: url(./tree-collapsed-light.svg); opacity: .7; background-size: 16px; background-position: 50% 50%; } .vs-dark .monaco-breadcrumbs .monaco-breadcrumb-item:not(:nth-child(2))::before { - background-image: url(./collpased-dark.svg); + background-image: url(./tree-collapsed-dark.svg); +} + +.hc-black .monaco-breadcrumbs .monaco-breadcrumb-item:not(:nth-child(2))::before { + background-image: url(./tree-collapsed-hc.svg); } diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 24f6dafa8a..ae2a205025 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -9,7 +9,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { commonPrefixLength } from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import 'vs/css!./breadcrumbsWidget'; @@ -57,7 +57,7 @@ export interface IBreadcrumbsItemEvent { export class BreadcrumbsWidget { - private readonly _disposables = new Array(); + private readonly _disposables = new DisposableStore(); private readonly _domNode: HTMLDivElement; private readonly _styleElement: HTMLStyleElement; private readonly _scrollable: DomScrollableElement; @@ -94,26 +94,25 @@ export class BreadcrumbsWidget { useShadows: false, scrollYToX: true }); - this._disposables.push(this._scrollable); - this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); + this._disposables.add(this._scrollable); + this._disposables.add(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); container.appendChild(this._scrollable.getDomNode()); this._styleElement = dom.createStyleSheet(this._domNode); - let focusTracker = dom.trackFocus(this._domNode); - this._disposables.push(focusTracker); - this._disposables.push(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false))); - this._disposables.push(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true))); + const focusTracker = dom.trackFocus(this._domNode); + this._disposables.add(focusTracker); + this._disposables.add(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false))); + this._disposables.add(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true))); } dispose(): void { - dispose(this._disposables); + this._disposables.dispose(); dispose(this._pendingLayout); this._onDidSelectItem.dispose(); this._onDidFocusItem.dispose(); this._onDidChangeFocus.dispose(); this._domNode.remove(); - this._disposables.length = 0; this._nodes.length = 0; this._freeNodes.length = 0; } @@ -134,14 +133,14 @@ export class BreadcrumbsWidget { } private _updateDimensions(dim: dom.Dimension): IDisposable { - let disposables: IDisposable[] = []; - disposables.push(dom.modify(() => { + const disposables = new DisposableStore(); + disposables.add(dom.modify(() => { this._dimension = dim; this._domNode.style.width = `${dim.width}px`; this._domNode.style.height = `${dim.height}px`; - disposables.push(this._updateScrollbar()); + disposables.add(this._updateScrollbar()); })); - return combinedDisposable(disposables); + return disposables; } private _updateScrollbar(): IDisposable { diff --git a/src/vs/base/browser/ui/breadcrumbs/collapsed.svg b/src/vs/base/browser/ui/breadcrumbs/collapsed.svg deleted file mode 100755 index 3a63808c35..0000000000 --- a/src/vs/base/browser/ui/breadcrumbs/collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg b/src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg deleted file mode 100755 index cf5c3641aa..0000000000 --- a/src/vs/base/browser/ui/breadcrumbs/collpased-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg new file mode 100644 index 0000000000..243be1451c --- /dev/null +++ b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg new file mode 100644 index 0000000000..40ba72b708 --- /dev/null +++ b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg new file mode 100644 index 0000000000..0d746558a4 --- /dev/null +++ b/src/vs/base/browser/ui/breadcrumbs/tree-collapsed-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index d51cb04707..e0fb0490dc 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -7,7 +7,7 @@ import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } fro import { $ } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; export interface CenteredViewState { @@ -48,7 +48,7 @@ export interface ICenteredViewStyles extends ISplitViewStyles { background: Color; } -export class CenteredViewLayout { +export class CenteredViewLayout implements IDisposable { private splitView?: SplitView; private width: number = 0; @@ -56,7 +56,7 @@ export class CenteredViewLayout { private style: ICenteredViewStyles; private didLayout = false; private emptyViews: ISplitViewView[] | undefined; - private splitViewDisposables: IDisposable[] = []; + private readonly splitViewDisposables = new DisposableStore(); constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = { leftMarginRatio: GOLDEN_RATIO.leftMarginRatio, rightMarginRatio: GOLDEN_RATIO.rightMarginRatio }) { this.container.appendChild(this.view.element); @@ -117,13 +117,13 @@ export class CenteredViewLayout { styles: this.style }); - this.splitViewDisposables.push(this.splitView.onDidSashChange(() => { + this.splitViewDisposables.add(this.splitView.onDidSashChange(() => { if (this.splitView) { this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.width; this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.width; } })); - this.splitViewDisposables.push(this.splitView.onDidSashReset(() => { + this.splitViewDisposables.add(this.splitView.onDidSashReset(() => { this.state.leftMarginRatio = GOLDEN_RATIO.leftMarginRatio; this.state.rightMarginRatio = GOLDEN_RATIO.rightMarginRatio; this.resizeMargins(); @@ -138,7 +138,7 @@ export class CenteredViewLayout { if (this.splitView) { this.container.removeChild(this.splitView.el); } - this.splitViewDisposables = dispose(this.splitViewDisposables); + this.splitViewDisposables.clear(); if (this.splitView) { this.splitView.dispose(); } @@ -153,7 +153,7 @@ export class CenteredViewLayout { } dispose(): void { - this.splitViewDisposables = dispose(this.splitViewDisposables); + this.splitViewDisposables.dispose(); if (this.splitView) { this.splitView.dispose(); diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 47adfc1499..e8c1b2b17f 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -12,7 +12,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as objects from 'vs/base/common/objects'; import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export interface ICheckboxOpts extends ICheckboxStyles { readonly actionClassName?: string; @@ -31,19 +31,19 @@ const defaultOpts = { export class CheckboxActionViewItem extends BaseActionViewItem { private checkbox: Checkbox; - private disposables: IDisposable[] = []; + private readonly disposables = new DisposableStore(); render(container: HTMLElement): void { this.element = container; - this.disposables = dispose(this.disposables); + this.disposables.clear(); this.checkbox = new Checkbox({ actionClassName: this._action.class, isChecked: this._action.checked, title: this._action.label }); - this.disposables.push(this.checkbox); - this.checkbox.onChange(() => this._action.checked = this.checkbox.checked, this, this.disposables); + this.disposables.add(this.checkbox); + this.disposables.add(this.checkbox.onChange(() => this._action.checked = this.checkbox.checked, this)); this.element.appendChild(this.checkbox.domNode); } @@ -64,19 +64,18 @@ export class CheckboxActionViewItem extends BaseActionViewItem { } dipsose(): void { - this.disposables = dispose(this.disposables); + this.disposables.dispose(); super.dispose(); } - } export class Checkbox extends Widget { private readonly _onChange = this._register(new Emitter()); - get onChange(): Event { return this._onChange.event; } + readonly onChange: Event = this._onChange.event; private readonly _onKeyDown = this._register(new Emitter()); - get onKeyDown(): Event { return this._onKeyDown.event; } + readonly onKeyDown: Event = this._onKeyDown.event; private readonly _opts: ICheckboxOpts; readonly domNode: HTMLElement; diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index bc28c7e3fd..5fca8286bb 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -5,7 +5,7 @@ import 'vs/css!./contextview'; import * as DOM from 'vs/base/browser/dom'; -import { IDisposable, dispose, toDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Range } from 'vs/base/common/range'; export interface IAnchor { @@ -128,21 +128,21 @@ export class ContextView extends Disposable { this.container = container; this.container.appendChild(this.view); - const toDisposeOnSetContainer: IDisposable[] = []; + const toDisposeOnSetContainer = new DisposableStore(); ContextView.BUBBLE_UP_EVENTS.forEach(event => { - toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { + toDisposeOnSetContainer.add(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { this.onDOMEvent(e, false); })); }); ContextView.BUBBLE_DOWN_EVENTS.forEach(event => { - toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { + toDisposeOnSetContainer.add(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { this.onDOMEvent(e, true); }, true)); }); - this.toDisposeOnSetContainer = combinedDisposable(toDisposeOnSetContainer); + this.toDisposeOnSetContainer = toDisposeOnSetContainer; } } @@ -260,12 +260,13 @@ export class ContextView extends Disposable { } hide(data?: any): void { - if (this.delegate && this.delegate.onHide) { - this.delegate.onHide(data); - } - + const delegate = this.delegate; this.delegate = null; + if (delegate && delegate.onHide) { + delegate.onHide(data); + } + if (this.toDisposeOnClean) { this.toDisposeOnClean.dispose(); this.toDisposeOnClean = null; diff --git a/src/vs/base/browser/ui/countBadge/countBadge.css b/src/vs/base/browser/ui/countBadge/countBadge.css index ab7717b041..f7bab5b08e 100644 --- a/src/vs/base/browser/ui/countBadge/countBadge.css +++ b/src/vs/base/browser/ui/countBadge/countBadge.css @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ .monaco-count-badge { - padding: 0.3em 0.5em; - border-radius: 1em; - font-size: 85%; - min-width: 1.6em; - line-height: 1em; + padding: 3px 5px; + border-radius: 11px; + font-size: 11px; + min-width: 18px; + min-height: 18px; + line-height: 11px; font-weight: normal; text-align: center; display: inline-block; diff --git a/src/vs/base/browser/ui/dialog/close-dark.svg b/src/vs/base/browser/ui/dialog/close-dark.svg new file mode 100644 index 0000000000..7305a8f099 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/close-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/dialog/close-inverse.svg b/src/vs/base/browser/ui/dialog/close-inverse.svg deleted file mode 100644 index a174033d91..0000000000 --- a/src/vs/base/browser/ui/dialog/close-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/close-light.svg b/src/vs/base/browser/ui/dialog/close-light.svg new file mode 100644 index 0000000000..ecddcd665b --- /dev/null +++ b/src/vs/base/browser/ui/dialog/close-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/dialog/close.svg b/src/vs/base/browser/ui/dialog/close.svg deleted file mode 100644 index f4038b8bfa..0000000000 --- a/src/vs/base/browser/ui/dialog/close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 617f46bf43..9c736c3b20 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -47,12 +47,12 @@ .monaco-workbench .dialog-box .dialog-close-action { - background: url('close.svg') center center no-repeat; + background: url('close-light.svg') center center no-repeat; } .vs-dark .monaco-workbench .dialog-box .dialog-close-action, .hc-black .monaco-workbench .dialog-box .dialog-close-action { - background: url('close-inverse.svg') center center no-repeat; + background: url('close-dark.svg') center center no-repeat; } /** Dialog: Message Row */ @@ -64,12 +64,12 @@ } .monaco-workbench .dialog-box .dialog-message-row .dialog-icon { - flex: 0 0 30px; - height: 30px; - padding-right: 4px; - padding-left: 4px; + flex: 0 0 40px; + height: 40px; + align-self: baseline; background-position: center; background-repeat: no-repeat; + background-size: 40px; } .vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { @@ -77,15 +77,15 @@ } .vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info { - background-image: url('info.svg'); + background-image: url('info-light.svg'); } .vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning { - background-image: url('warning.svg'); + background-image: url('warning-light.svg'); } .vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error { - background-image: url('error.svg'); + background-image: url('error-light.svg'); } .vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { @@ -94,17 +94,17 @@ .vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info, .hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info { - background-image: url('info-inverse.svg'); + background-image: url('info-dark.svg'); } .vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning, .hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning { - background-image: url('warning-inverse.svg'); + background-image: url('warning-dark.svg'); } .vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error, .hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error { - background-image: url('error-inverse.svg'); + background-image: url('error-dark.svg'); } .hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 04a10067f0..e0ae7ed4ad 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -15,7 +15,7 @@ import { ButtonGroup, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; export interface IDialogOptions { cancelId?: number; @@ -97,9 +97,17 @@ export class Dialog extends Disposable { clearNode(this.buttonsContainer); let focusedButton = 0; - this.buttonGroup = new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true }); + const buttonGroup = this.buttonGroup = new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true }); const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId); - this.buttonGroup.buttons.forEach((button, index) => { + + // Set focused button to UI index + buttonMap.forEach((value, index) => { + if (value.index === 0) { + focusedButton = index; + } + }); + + buttonGroup.buttons.forEach((button, index) => { button.label = mnemonicButtonLabel(buttonMap[index].label, true); this._register(button.onDidClick(e => { @@ -115,18 +123,16 @@ export class Dialog extends Disposable { } let eventHandled = false; - if (this.buttonGroup) { - if (evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { - focusedButton = focusedButton + this.buttonGroup.buttons.length - 1; - focusedButton = focusedButton % this.buttonGroup.buttons.length; - this.buttonGroup.buttons[focusedButton].focus(); - eventHandled = true; - } else if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { - focusedButton++; - focusedButton = focusedButton % this.buttonGroup.buttons.length; - this.buttonGroup.buttons[focusedButton].focus(); - eventHandled = true; - } + if (evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { + focusedButton = focusedButton + buttonGroup.buttons.length - 1; + focusedButton = focusedButton % buttonGroup.buttons.length; + buttonGroup.buttons[focusedButton].focus(); + eventHandled = true; + } else if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { + focusedButton++; + focusedButton = focusedButton % buttonGroup.buttons.length; + buttonGroup.buttons[focusedButton].focus(); + eventHandled = true; } if (eventHandled) { @@ -192,7 +198,7 @@ export class Dialog extends Disposable { show(this.element); // Focus first element - this.buttonGroup.buttons[focusedButton].focus(); + buttonGroup.buttons[focusedButton].focus(); }); } @@ -243,7 +249,8 @@ export class Dialog extends Disposable { buttonMap.push({ label: button, index: index }); }); - if (isMacintosh) { + // macOS/linux: reverse button order + if (isMacintosh || isLinux) { if (cancelId !== undefined) { const cancelButton = buttonMap.splice(cancelId, 1)[0]; buttonMap.reverse(); diff --git a/src/vs/base/browser/ui/dialog/error-dark.svg b/src/vs/base/browser/ui/dialog/error-dark.svg new file mode 100644 index 0000000000..efdc5f2ae2 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/error-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/dialog/error-inverse.svg b/src/vs/base/browser/ui/dialog/error-inverse.svg deleted file mode 100644 index 51e9dc81b9..0000000000 --- a/src/vs/base/browser/ui/dialog/error-inverse.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/error-light.svg b/src/vs/base/browser/ui/dialog/error-light.svg new file mode 100644 index 0000000000..d646c72c74 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/error-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/dialog/error.svg b/src/vs/base/browser/ui/dialog/error.svg deleted file mode 100644 index 04b6668901..0000000000 --- a/src/vs/base/browser/ui/dialog/error.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/info-dark.svg b/src/vs/base/browser/ui/dialog/info-dark.svg new file mode 100644 index 0000000000..bb851afdfe --- /dev/null +++ b/src/vs/base/browser/ui/dialog/info-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/dialog/info-inverse.svg b/src/vs/base/browser/ui/dialog/info-inverse.svg deleted file mode 100644 index 64b801a63b..0000000000 --- a/src/vs/base/browser/ui/dialog/info-inverse.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/info-light.svg b/src/vs/base/browser/ui/dialog/info-light.svg new file mode 100644 index 0000000000..6faf670ccc --- /dev/null +++ b/src/vs/base/browser/ui/dialog/info-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/base/browser/ui/dialog/info.svg b/src/vs/base/browser/ui/dialog/info.svg deleted file mode 100644 index 3c603528a7..0000000000 --- a/src/vs/base/browser/ui/dialog/info.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/warning-dark.svg b/src/vs/base/browser/ui/dialog/warning-dark.svg new file mode 100644 index 0000000000..a267963e58 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/warning-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/dialog/warning-inverse.svg b/src/vs/base/browser/ui/dialog/warning-inverse.svg deleted file mode 100644 index a7f4afbcc9..0000000000 --- a/src/vs/base/browser/ui/dialog/warning-inverse.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - -StatusWarning_16x - - - - - diff --git a/src/vs/base/browser/ui/dialog/warning-light.svg b/src/vs/base/browser/ui/dialog/warning-light.svg new file mode 100644 index 0000000000..f2e2aa741e --- /dev/null +++ b/src/vs/base/browser/ui/dialog/warning-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/base/browser/ui/dialog/warning.svg b/src/vs/base/browser/ui/dialog/warning.svg deleted file mode 100644 index 6d8cffe913..0000000000 --- a/src/vs/base/browser/ui/dialog/warning.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - -StatusWarning_16x - - - - - diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index a3bd38835b..296fa86c0e 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -108,6 +108,10 @@ export class BaseDropdown extends ActionRunner { this.visible = false; } + isVisible(): boolean { + return this.visible; + } + protected onEvent(e: Event, activeElement: HTMLElement): void { this.hide(); } @@ -192,12 +196,12 @@ export interface IContextMenuProvider { } export interface IActionProvider { - getActions(): IAction[]; + getActions(): ReadonlyArray; } export interface IDropdownMenuOptions extends IBaseDropdownOptions { contextMenuProvider: IContextMenuProvider; - actions?: IAction[]; + actions?: ReadonlyArray; actionProvider?: IActionProvider; menuClassName?: string; } @@ -205,7 +209,7 @@ export interface IDropdownMenuOptions extends IBaseDropdownOptions { export class DropdownMenu extends BaseDropdown { private _contextMenuProvider: IContextMenuProvider; private _menuOptions: IMenuOptions; - private _actions: IAction[]; + private _actions: ReadonlyArray; private actionProvider?: IActionProvider; private menuClassName: string; @@ -226,7 +230,7 @@ export class DropdownMenu extends BaseDropdown { return this._menuOptions; } - private get actions(): IAction[] { + private get actions(): ReadonlyArray { if (this.actionProvider) { return this.actionProvider.getActions(); } @@ -234,7 +238,7 @@ export class DropdownMenu extends BaseDropdown { return this._actions; } - private set actions(actions: IAction[]) { + private set actions(actions: ReadonlyArray) { this._actions = actions; } @@ -275,7 +279,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { private clazz: string | undefined; private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; - constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, menuActions: ReadonlyArray, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment) { super(null, action); diff --git a/src/vs/base/browser/ui/findinput/case-sensitive-dark.svg b/src/vs/base/browser/ui/findinput/case-sensitive-dark.svg index ae54017202..40c9ed3262 100644 --- a/src/vs/base/browser/ui/findinput/case-sensitive-dark.svg +++ b/src/vs/base/browser/ui/findinput/case-sensitive-dark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/base/browser/ui/findinput/case-sensitive-hc.svg b/src/vs/base/browser/ui/findinput/case-sensitive-hc.svg new file mode 100644 index 0000000000..82501418da --- /dev/null +++ b/src/vs/base/browser/ui/findinput/case-sensitive-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/findinput/case-sensitive-light.svg b/src/vs/base/browser/ui/findinput/case-sensitive-light.svg new file mode 100644 index 0000000000..aa1dbd353e --- /dev/null +++ b/src/vs/base/browser/ui/findinput/case-sensitive-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/findinput/case-sensitive.svg b/src/vs/base/browser/ui/findinput/case-sensitive.svg deleted file mode 100644 index 1f3b1a2c57..0000000000 --- a/src/vs/base/browser/ui/findinput/case-sensitive.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/findinput/findInputCheckboxes.css b/src/vs/base/browser/ui/findinput/findInputCheckboxes.css index e1c91264cb..0aa5e6d55e 100644 --- a/src/vs/base/browser/ui/findinput/findInputCheckboxes.css +++ b/src/vs/base/browser/ui/findinput/findInputCheckboxes.css @@ -4,28 +4,40 @@ *--------------------------------------------------------------------------------------------*/ .vs .monaco-custom-checkbox.monaco-case-sensitive { - background: url('case-sensitive.svg') center center no-repeat; + background: url('case-sensitive-light.svg') center center no-repeat; } -.hc-black .monaco-custom-checkbox.monaco-case-sensitive, -.hc-black .monaco-custom-checkbox.monaco-case-sensitive:hover, + .vs-dark .monaco-custom-checkbox.monaco-case-sensitive { background: url('case-sensitive-dark.svg') center center no-repeat; } -.vs .monaco-custom-checkbox.monaco-whole-word { - background: url('whole-word.svg') center center no-repeat; +.hc-black .monaco-custom-checkbox.monaco-case-sensitive, +.hc-black .monaco-custom-checkbox.monaco-case-sensitive:hover { + background: url('case-sensitive-hc.svg') center center no-repeat; } -.hc-black .monaco-custom-checkbox.monaco-whole-word, -.hc-black .monaco-custom-checkbox.monaco-whole-word:hover, + +.vs .monaco-custom-checkbox.monaco-whole-word { + background: url('whole-word-light.svg') center center no-repeat; +} + .vs-dark .monaco-custom-checkbox.monaco-whole-word { background: url('whole-word-dark.svg') center center no-repeat; } -.vs .monaco-custom-checkbox.monaco-regex { - background: url('regex.svg') center center no-repeat; +.hc-black .monaco-custom-checkbox.monaco-whole-word, +.hc-black .monaco-custom-checkbox.monaco-whole-word:hover { + background: url('whole-word-hc.svg') center center no-repeat; } -.hc-black .monaco-custom-checkbox.monaco-regex, -.hc-black .monaco-custom-checkbox.monaco-regex:hover, + +.vs .monaco-custom-checkbox.monaco-regex { + background: url('regex-light.svg') center center no-repeat; +} + .vs-dark .monaco-custom-checkbox.monaco-regex { background: url('regex-dark.svg') center center no-repeat; } + +.hc-black .monaco-custom-checkbox.monaco-regex, +.hc-black .monaco-custom-checkbox.monaco-regex:hover { + background: url('regex-hc.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/findinput/regex-dark.svg b/src/vs/base/browser/ui/findinput/regex-dark.svg index c303032e6a..cf43e9d952 100644 --- a/src/vs/base/browser/ui/findinput/regex-dark.svg +++ b/src/vs/base/browser/ui/findinput/regex-dark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/base/browser/ui/findinput/regex-hc.svg b/src/vs/base/browser/ui/findinput/regex-hc.svg new file mode 100644 index 0000000000..116c547d15 --- /dev/null +++ b/src/vs/base/browser/ui/findinput/regex-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/findinput/regex-light.svg b/src/vs/base/browser/ui/findinput/regex-light.svg new file mode 100644 index 0000000000..b329c104fa --- /dev/null +++ b/src/vs/base/browser/ui/findinput/regex-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/findinput/regex.svg b/src/vs/base/browser/ui/findinput/regex.svg deleted file mode 100644 index c677843bee..0000000000 --- a/src/vs/base/browser/ui/findinput/regex.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/findinput/whole-word-dark.svg b/src/vs/base/browser/ui/findinput/whole-word-dark.svg index d18e144f31..6c38eb215d 100644 --- a/src/vs/base/browser/ui/findinput/whole-word-dark.svg +++ b/src/vs/base/browser/ui/findinput/whole-word-dark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/base/browser/ui/findinput/whole-word-hc.svg b/src/vs/base/browser/ui/findinput/whole-word-hc.svg new file mode 100644 index 0000000000..fc1ff43268 --- /dev/null +++ b/src/vs/base/browser/ui/findinput/whole-word-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/findinput/whole-word-light.svg b/src/vs/base/browser/ui/findinput/whole-word-light.svg new file mode 100644 index 0000000000..345e65b2a5 --- /dev/null +++ b/src/vs/base/browser/ui/findinput/whole-word-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/findinput/whole-word.svg b/src/vs/base/browser/ui/findinput/whole-word.svg deleted file mode 100644 index 8244d95abd..0000000000 --- a/src/vs/base/browser/ui/findinput/whole-word.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 4e2d1a196e..6bd266b3a7 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -5,14 +5,12 @@ import 'vs/css!./gridview'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; -import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles } from './gridview'; -import { Event, Emitter } from 'vs/base/common/event'; -import { $ } from 'vs/base/browser/dom'; -import { LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; +import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview'; +import { Event } from 'vs/base/common/event'; -export { Orientation } from './gridview'; +export { Orientation, Sizing as GridViewSizing } from './gridview'; export const enum Direction { Up, @@ -117,10 +115,6 @@ function getDirectionOrientation(direction: Direction): Orientation { return direction === Direction.Up || direction === Direction.Down ? Orientation.VERTICAL : Orientation.HORIZONTAL; } -function getSize(dimensions: { width: number; height: number; }, orientation: Orientation) { - return orientation === Orientation.HORIZONTAL ? dimensions.width : dimensions.height; -} - export function getRelativeLocation(rootOrientation: Orientation, location: number[], direction: Direction): number[] { const orientation = getLocationOrientation(rootOrientation, location); const directionOrientation = getDirectionOrientation(direction); @@ -191,12 +185,10 @@ export interface IGridOptions { proportionalLayout?: boolean; } -export class Grid implements IDisposable { +export class Grid extends Disposable { protected gridview: GridView; private views = new Map(); - private disposables: IDisposable[] = []; - get orientation(): Orientation { return this.gridview.orientation; } set orientation(orientation: Orientation) { this.gridview.orientation = orientation; } @@ -211,13 +203,12 @@ export class Grid implements IDisposable { get element(): HTMLElement { return this.gridview.element; } - sashResetSizing: Sizing = Sizing.Distribute; - constructor(view: T, options: IGridOptions = {}) { + super(); this.gridview = new GridView(options); - this.disposables.push(this.gridview); + this._register(this.gridview); - this.gridview.onDidSashReset(this.doResetViewSize, this, this.disposables); + this._register(this.gridview.onDidSashReset(this.doResetViewSize, this)); this._addView(view, 0, [0]); } @@ -299,15 +290,14 @@ export class Grid implements IDisposable { return this.gridview.swapViews(fromLocation, toLocation); } - resizeView(view: T, size: number): void { + resizeView(view: T, size: IViewSize): void { const location = this.getViewLocation(view); return this.gridview.resizeView(location, size); } - getViewSize(view: T): number { + getViewSize(view: T): IViewSize { const location = this.getViewLocation(view); - const viewSize = this.gridview.getViewSize(location); - return getLocationOrientation(this.orientation, location) === Orientation.HORIZONTAL ? viewSize.width : viewSize.height; + return this.gridview.getViewSize(location); } // TODO@joao cleanup @@ -325,6 +315,16 @@ export class Grid implements IDisposable { this.gridview.distributeViewSizes(); } + isViewVisible(view: T): boolean { + const location = this.getViewLocation(view); + return this.gridview.isViewVisible(location); + } + + setViewVisible(view: T, visible: boolean): void { + const location = this.getViewLocation(view); + this.gridview.setViewVisible(location, visible); + } + getViews(): GridBranchNode { return this.gridview.getViews() as GridBranchNode; } @@ -362,22 +362,8 @@ export class Grid implements IDisposable { } private doResetViewSize(location: number[]): void { - if (this.sashResetSizing === Sizing.Split) { - const orientation = getLocationOrientation(this.orientation, location); - const firstViewSize = getSize(this.gridview.getViewSize(location), orientation); - const [parentLocation, index] = tail(location); - const secondViewSize = getSize(this.gridview.getViewSize([...parentLocation, index + 1]), orientation); - const totalSize = firstViewSize + secondViewSize; - this.gridview.resizeView(location, Math.floor(totalSize / 2)); - - } else { - const [parentLocation,] = tail(location); - this.gridview.distributeViewSizes(parentLocation); - } - } - - dispose(): void { - this.disposables = dispose(this.disposables); + const [parentLocation,] = tail(location); + this.gridview.distributeViewSizes(parentLocation); } } @@ -568,8 +554,11 @@ export class SerializableGrid extends Grid { const childLocation = [...location, i]; if (i < node.children.length - 1) { - const size = orientation === Orientation.VERTICAL ? child.box.height : child.box.width; - this.gridview.resizeView(childLocation, Math.floor(size * scale)); + const size = orientation === Orientation.VERTICAL + ? { height: Math.floor(child.box.height * scale) } + : { width: Math.floor(child.box.width * scale) }; + + this.gridview.resizeView(childLocation, size); } this.restoreViewsSize(childLocation, child, orthogonal(orientation), widthScale, heightScale); @@ -653,63 +642,3 @@ export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerialize height: height || 1 }; } - -export class View implements IView { - - readonly element = $('.grid-view-view'); - - private visible = false; - private width: number | undefined; - private height: number | undefined; - private orientation: Orientation = Orientation.HORIZONTAL; - - get minimumWidth(): number { return this.visible ? this.view.minimumWidth : 0; } - get maximumWidth(): number { return this.visible ? this.view.maximumWidth : (this.orientation === Orientation.HORIZONTAL ? 0 : Number.POSITIVE_INFINITY); } - get minimumHeight(): number { return this.visible ? this.view.minimumHeight : 0; } - get maximumHeight(): number { return this.visible ? this.view.maximumHeight : (this.orientation === Orientation.VERTICAL ? 0 : Number.POSITIVE_INFINITY); } - - private onDidChangeVisibility = new Emitter<{ width: number; height: number; } | undefined>(); - readonly onDidChange: Event<{ width: number; height: number; } | undefined>; - - get priority(): LayoutPriority | undefined { return this.view.priority; } - get snapSize(): number | undefined { return this.visible ? this.view.snapSize : undefined; } - - constructor(private view: IView) { - this.show(); - this.onDidChange = Event.any(this.onDidChangeVisibility.event, Event.filter(view.onDidChange, () => this.visible)); - } - - show(): void { - if (this.visible) { - return; - } - - this.visible = true; - - this.element.appendChild(this.view.element); - this.onDidChangeVisibility.fire(typeof this.width === 'number' ? { width: this.width, height: this.height! } : undefined); - } - - hide(): void { - if (!this.visible) { - return; - } - - this.visible = false; - - this.element.removeChild(this.view.element); - this.onDidChangeVisibility.fire(undefined); - } - - layout(width: number, height: number, orientation: Orientation): void { - this.orientation = orientation; - - if (!this.visible) { - return; - } - - this.view.layout(width, height, orientation); - this.width = width; - this.height = height; - } -} \ No newline at end of file diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 9a67e9ae26..374c9c97fe 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -15,16 +15,22 @@ import { Color } from 'vs/base/common/color'; export { Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; +export interface IViewSize { + readonly width: number; + readonly height: number; +} + export interface IView { readonly element: HTMLElement; readonly minimumWidth: number; readonly maximumWidth: number; readonly minimumHeight: number; readonly maximumHeight: number; - readonly onDidChange: Event<{ width: number; height: number; } | undefined>; + readonly onDidChange: Event; readonly priority?: LayoutPriority; - readonly snapSize?: number; + readonly snap?: boolean; layout(width: number, height: number, orientation: Orientation): void; + setVisible?(visible: boolean): void; } export function orthogonal(orientation: Orientation): Orientation { @@ -173,6 +179,12 @@ class BranchNode implements ISplitView, IDisposable { } } + setVisible(visible: boolean): void { + for (const child of this.children) { + child.setVisible(visible); + } + } + orthogonalLayout(size: number): void { this._size = size; this.splitview.layout(size); @@ -299,6 +311,22 @@ class BranchNode implements ISplitView, IDisposable { return this.splitview.getViewSize(index); } + isChildVisible(index: number): boolean { + if (index < 0 || index >= this.children.length) { + throw new Error('Invalid index'); + } + + return this.splitview.isViewVisible(index); + } + + setChildVisible(index: number, visible: boolean): void { + if (index < 0 || index >= this.children.length) { + throw new Error('Invalid index'); + } + + this.splitview.setViewVisible(index, visible); + } + private onDidChildrenChange(): void { const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined); this.childrenChangeDisposable.dispose(); @@ -458,8 +486,8 @@ class LeafNode implements ISplitView, IDisposable { return this.view.priority; } - get snapSize(): number | undefined { - return this.view.snapSize; + get snap(): boolean | undefined { + return this.view.snap; } get minimumOrthogonalSize(): number { @@ -483,6 +511,12 @@ class LeafNode implements ISplitView, IDisposable { return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } + setVisible(visible: boolean): void { + if (this.view.setVisible) { + this.view.setVisible(visible); + } + } + orthogonalLayout(size: number): void { this._orthogonalSize = size; return this.view.layout(this.width, this.height, orthogonal(this.orientation)); @@ -573,7 +607,7 @@ export class GridView implements IDisposable { get maximumWidth(): number { return this.root.maximumHeight; } get maximumHeight(): number { return this.root.maximumHeight; } - private _onDidChange = new Relay<{ width: number; height: number; } | undefined>(); + private _onDidChange = new Relay(); readonly onDidChange = this._onDidChange.event; constructor(options: IGridViewOptions = {}) { @@ -747,18 +781,33 @@ export class GridView implements IDisposable { } } - resizeView(location: number[], size: number): void { + resizeView(location: number[], { width, height }: Partial): void { const [rest, index] = tail(location); - const [, parent] = this.getNode(rest); + const [pathToParent, parent] = this.getNode(rest); if (!(parent instanceof BranchNode)) { throw new Error('Invalid location'); } - parent.resizeChild(index, size); + if (!width && !height) { + return; + } + + const [parentSize, grandParentSize] = parent.orientation === Orientation.HORIZONTAL ? [width, height] : [height, width]; + + if (typeof grandParentSize === 'number' && pathToParent.length > 0) { + const [, grandParent] = tail(pathToParent); + const [, parentIndex] = tail(rest); + + grandParent.resizeChild(parentIndex, grandParentSize); + } + + if (typeof parentSize === 'number') { + parent.resizeChild(index, parentSize); + } } - getViewSize(location: number[]): { width: number; height: number; } { + getViewSize(location: number[]): IViewSize { const [, node] = this.getNode(location); return { width: node.width, height: node.height }; } @@ -790,6 +839,28 @@ export class GridView implements IDisposable { node.distributeViewSizes(); } + isViewVisible(location: number[]): boolean { + const [rest, index] = tail(location); + const [, parent] = this.getNode(rest); + + if (!(parent instanceof BranchNode)) { + throw new Error('Invalid from location'); + } + + return parent.isChildVisible(index); + } + + setViewVisible(location: number[], visible: boolean): void { + const [rest, index] = tail(location); + const [, parent] = this.getNode(rest); + + if (!(parent instanceof BranchNode)) { + throw new Error('Invalid from location'); + } + + parent.setChildVisible(index, visible); + } + getViews(): GridBranchNode { return this._getViews(this.root, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }) as GridBranchNode; } diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 1d72186b6b..44fc80ba56 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -28,7 +28,8 @@ export interface IInputOptions extends IInputBoxStyles { readonly type?: string; readonly validationOptions?: IInputValidationOptions; readonly flexibleHeight?: boolean; - readonly actions?: IAction[]; + readonly actions?: ReadonlyArray; + // {{SQL CARBON EDIT}} Candidate for addition to vscode readonly min?: string; @@ -99,7 +100,7 @@ export class InputBox extends Widget { private placeholder: string; private ariaLabel: string; private validation?: IInputValidator; - private state: string | null = 'idle'; + private state: 'idle' | 'open' | 'closed' = 'idle'; private cachedHeight: number | null; // {{SQL CARBON EDIT}} - Add showValidationMessage and set inputBackground, inputForeground, and inputBorder as protected @@ -422,8 +423,6 @@ export class InputBox extends Widget { let div: HTMLElement; let layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px'; - this.state = 'open'; - this.contextViewProvider.showContextView({ getAnchor: () => this.element, anchorAlignment: AnchorAlignment.RIGHT, @@ -454,18 +453,25 @@ export class InputBox extends Widget { return null; }, + onHide: () => { + this.state = 'closed'; + }, layout: layout }); + + this.state = 'open'; } private _hideMessage(): void { - if (!this.contextViewProvider || this.state !== 'open') { + if (!this.contextViewProvider) { return; } - this.state = 'idle'; + if (this.state === 'open') { + this.contextViewProvider.hideContextView(); + } - this.contextViewProvider.hideContextView(); + this.state = 'idle'; } private onValueChange(): void { @@ -555,7 +561,7 @@ export class InputBox extends Widget { this.contextViewProvider = undefined; this.message = null; this.validation = undefined; - this.state = null; + this.state = null!; // StrictNullOverride: nulling out ok in dispose this.actionbar = undefined; super.dispose(); diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index b7314d9306..24111a0134 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -186,9 +186,9 @@ /* Electron */ .monaco-list-type-filter { - cursor: -webkit-grab; + cursor: grab; } .monaco-list-type-filter.dragging { - cursor: -webkit-grabbing; + cursor: grabbing; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 8d205598ff..8a23e82a54 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -41,9 +41,11 @@ export interface IListViewDragAndDrop extends IListDragAndDrop { getDragElements(element: T): T[]; } -export interface IAriaSetProvider { +export interface IAriaProvider { getSetSize(element: T, index: number, listLength: number): number; getPosInSet(element: T, index: number): number; + getRole?(element: T): string; + isChecked?(element: T): boolean; } export interface IListViewOptions { @@ -54,7 +56,7 @@ export interface IListViewOptions { readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; - readonly ariaSetProvider?: IAriaSetProvider; + readonly ariaProvider?: IAriaProvider; } const DefaultOptions = { @@ -174,7 +176,7 @@ export class ListView implements ISpliceable, IDisposable { private setRowLineHeight: boolean; private supportDynamicHeights: boolean; private horizontalScrolling: boolean; - private ariaSetProvider: IAriaSetProvider; + private ariaProvider: IAriaProvider; private scrollWidth: number | undefined; private canUseTranslate3d: boolean | undefined = undefined; @@ -227,7 +229,7 @@ export class ListView implements ISpliceable, IDisposable { this.horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); DOM.toggleClass(this.domNode, 'horizontal-scrolling', this.horizontalScrolling); - this.ariaSetProvider = options.ariaSetProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 }; + this.ariaProvider = options.ariaProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 }; this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; @@ -566,7 +568,12 @@ export class ListView implements ISpliceable, IDisposable { if (!item.row) { item.row = this.cache.alloc(item.templateId); - item.row!.domNode!.setAttribute('role', 'treeitem'); + const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'treeitem'; + item.row!.domNode!.setAttribute('role', role); + const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined; + if (typeof checked !== 'undefined') { + item.row!.domNode!.setAttribute('aria-checked', String(checked)); + } } if (!item.row.domNode!.parentElement) { @@ -634,8 +641,8 @@ export class ListView implements ISpliceable, IDisposable { item.row!.domNode!.setAttribute('data-index', `${index}`); item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); - item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaSetProvider.getSetSize(item.element, index, this.length))); - item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaSetProvider.getPosInSet(item.element, index))); + item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaProvider.getSetSize(item.element, index, this.length))); + item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaProvider.getPosInSet(item.element, index))); item.row!.domNode!.setAttribute('id', this.getElementDomId(index)); DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 2c60e666a6..462f115a76 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -5,7 +5,7 @@ import 'vs/css!./list'; import { localize } from 'vs/nls'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; import { range, firstIndex, binarySearch } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; @@ -17,7 +17,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaSetProvider } from './listView'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaProvider } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; @@ -111,7 +111,7 @@ class Trait implements ISpliceable, IDisposable { private sortedIndexes: number[] = []; private _onChange = new Emitter(); - get onChange(): Event { return this._onChange.event; } + readonly onChange: Event = this._onChange.event; get trait(): string { return this._trait; } @@ -228,7 +228,7 @@ function isInputElement(e: HTMLElement): boolean { class KeyboardController implements IDisposable { - private disposables: IDisposable[]; + private readonly disposables = new DisposableStore(); private openController: IOpenController; constructor( @@ -237,7 +237,6 @@ class KeyboardController implements IDisposable { options: IListOptions ) { const multipleSelectionSupport = !(options.multipleSelectionSupport === false); - this.disposables = []; this.openController = options.openController || DefaultOpenController; @@ -314,7 +313,7 @@ class KeyboardController implements IDisposable { } dispose() { - this.disposables = dispose(this.disposables); + this.disposables.dispose(); } } @@ -330,6 +329,7 @@ export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean { return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9) + || (event.keyCode >= KeyCode.NUMPAD_0 && event.keyCode <= KeyCode.NUMPAD_9) || (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE); } @@ -341,8 +341,8 @@ class TypeLabelController implements IDisposable { private automaticKeyboardNavigation = true; private triggered = false; - private enabledDisposables: IDisposable[] = []; - private disposables: IDisposable[] = []; + private readonly enabledDisposables = new DisposableStore(); + private readonly disposables = new DisposableStore(); constructor( private list: List, @@ -398,7 +398,7 @@ class TypeLabelController implements IDisposable { return; } - this.enabledDisposables = dispose(this.enabledDisposables); + this.enabledDisposables.clear(); this.enabled = false; this.triggered = false; } @@ -430,20 +430,19 @@ class TypeLabelController implements IDisposable { dispose() { this.disable(); - this.disposables = dispose(this.disposables); + this.enabledDisposables.dispose(); + this.disposables.dispose(); } } class DOMFocusController implements IDisposable { - private disposables: IDisposable[] = []; + private readonly disposables = new DisposableStore(); constructor( private list: List, private view: ListView ) { - this.disposables = []; - const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -486,7 +485,7 @@ class DOMFocusController implements IDisposable { } dispose() { - this.disposables = dispose(this.disposables); + this.disposables.dispose(); } } @@ -523,7 +522,7 @@ export class MouseController implements IDisposable { readonly multipleSelectionController: IMultipleSelectionController; private openController: IOpenController; private mouseSupport: boolean; - private disposables: IDisposable[] = []; + private readonly disposables = new DisposableStore(); constructor(protected list: List) { this.multipleSelectionSupport = !(list.options.multipleSelectionSupport === false); @@ -663,7 +662,7 @@ export class MouseController implements IDisposable { } dispose() { - this.disposables = dispose(this.disposables); + this.disposables.dispose(); } } @@ -833,7 +832,7 @@ export interface IListOptions extends IListStyles { readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; - readonly ariaSetProvider?: IAriaSetProvider; + readonly ariaProvider?: IAriaProvider; } export interface IListStyles { @@ -857,6 +856,7 @@ export interface IListStyles { listFilterWidgetOutline?: Color; listFilterWidgetNoMatchesOutline?: Color; listMatchesShadow?: Color; + treeIndentGuidesStroke?: Color; } const defaultStyles: IListStyles = { @@ -867,7 +867,8 @@ const defaultStyles: IListStyles = { listFocusAndSelectionForeground: Color.fromHex('#FFFFFF'), listInactiveSelectionBackground: Color.fromHex('#3F3F46'), listHoverBackground: Color.fromHex('#2A2D2E'), - listDropBackground: Color.fromHex('#383B3D') + listDropBackground: Color.fromHex('#383B3D'), + treeIndentGuidesStroke: Color.fromHex('#a9a9a9') }; const DefaultOptions = { @@ -1094,7 +1095,7 @@ export class List implements ISpliceable, IDisposable { private styleController: IStyleController; private typeLabelController?: TypeLabelController; - protected disposables: IDisposable[]; + protected readonly disposables = new DisposableStore(); @memoize get onFocusChange(): Event> { return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); @@ -1112,6 +1113,7 @@ export class List implements ISpliceable, IDisposable { return Event.map(this._onPin.event, indexes => this.toListEvent({ indexes })); } + get domId(): string { return this.view.domId; } get onDidScroll(): Event { return this.view.onDidScroll; } get onMouseClick(): Event> { return this.view.onMouseClick; } get onMouseDblClick(): Event> { return this.view.onMouseDblClick; } @@ -1163,7 +1165,7 @@ export class List implements ISpliceable, IDisposable { readonly onDidBlur: Event; private _onDidDispose = new Emitter(); - get onDidDispose(): Event { return this._onDidDispose.event; } + readonly onDidDispose: Event = this._onDidDispose.event; constructor( container: HTMLElement, @@ -1207,24 +1209,27 @@ export class List implements ISpliceable, IDisposable { this.view ]); - this.disposables = [this.focus, this.selection, this.view, this._onDidDispose]; + this.disposables.add(this.focus); + this.disposables.add(this.selection); + this.disposables.add(this.view); + this.disposables.add(this._onDidDispose); this.onDidFocus = Event.map(domEvent(this.view.domNode, 'focus', true), () => null!); this.onDidBlur = Event.map(domEvent(this.view.domNode, 'blur', true), () => null!); - this.disposables.push(new DOMFocusController(this, this.view)); + this.disposables.add(new DOMFocusController(this, this.view)); if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) { const controller = new KeyboardController(this, this.view, _options); - this.disposables.push(controller); + this.disposables.add(controller); } if (_options.keyboardNavigationLabelProvider) { this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider); - this.disposables.push(this.typeLabelController); + this.disposables.add(this.typeLabelController); } - this.disposables.push(this.createMouseController(_options)); + this.disposables.add(this.createMouseController(_options)); this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); @@ -1607,7 +1612,7 @@ export class List implements ISpliceable, IDisposable { dispose(): void { this._onDidDispose.fire(); - this.disposables = dispose(this.disposables); + this.disposables.dispose(); this._onDidOpen.dispose(); this._onPin.dispose(); diff --git a/src/vs/base/browser/ui/menu/check.svg b/src/vs/base/browser/ui/menu/check.svg index 3f365c4800..865cc83c34 100644 --- a/src/vs/base/browser/ui/menu/check.svg +++ b/src/vs/base/browser/ui/menu/check.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/base/browser/ui/menu/ellipsis.svg b/src/vs/base/browser/ui/menu/ellipsis.svg index e3f8562335..2c52e359f6 100644 --- a/src/vs/base/browser/ui/menu/ellipsis.svg +++ b/src/vs/base/browser/ui/menu/ellipsis.svg @@ -1 +1,5 @@ -Ellipsis_bold_16x \ No newline at end of file + + + + + diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index aec27d7afd..15660767c2 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -133,7 +133,7 @@ } .monaco-menu .monaco-action-bar.vertical .action-item { - border: 1px solid transparent; /* prevents jumping behaviour on hover or focus */ + border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index d65d930df3..bf4f136f53 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -12,13 +12,13 @@ import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { Event, Emitter } from 'vs/base/common/event'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -import { isLinux } from 'vs/base/common/platform'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; function createMenuMnemonicRegExp() { try { @@ -59,7 +59,7 @@ export interface IMenuStyles { } export class SubmenuAction extends Action { - constructor(label: string, public entries: Array, cssClass?: string) { + constructor(label: string, public entries: ReadonlyArray, cssClass?: string) { super(!!cssClass ? cssClass : 'submenu', label, '', true); } } @@ -71,15 +71,14 @@ interface ISubMenuData { export class Menu extends ActionBar { private mnemonics: Map>; - private menuDisposables: IDisposable[]; + private readonly menuDisposables: DisposableStore; private scrollableElement: DomScrollableElement; private menuElement: HTMLElement; private scrollTopHold: number | undefined; private readonly _onScroll: Emitter; - constructor(container: HTMLElement, actions: IAction[], options: IMenuOptions = {}) { - + constructor(container: HTMLElement, actions: ReadonlyArray, options: IMenuOptions = {}) { addClass(container, 'monaco-menu-container'); container.setAttribute('role', 'presentation'); const menuElement = document.createElement('div'); @@ -92,7 +91,7 @@ export class Menu extends ActionBar { context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, - triggerKeys: { keys: [KeyCode.Enter], keyDown: true } + triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh ? [KeyCode.Space] : [])], keyDown: true } }); this.menuElement = menuElement; @@ -103,19 +102,19 @@ export class Menu extends ActionBar { this.actionsList.tabIndex = 0; - this.menuDisposables = []; + this.menuDisposables = this._register(new DisposableStore()); addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { const event = new StandardKeyboardEvent(e); // Stop tab navigation of menus if (event.equals(KeyCode.Tab)) { - EventHelper.stop(e, true); + e.preventDefault(); } }); if (options.enableMnemonics) { - this.menuDisposables.push(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { + this.menuDisposables.add(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { const key = e.key.toLocaleLowerCase(); if (this.mnemonics.has(key)) { EventHelper.stop(e, true); @@ -217,9 +216,9 @@ export class Menu extends ActionBar { menuElement.style.maxHeight = `${Math.max(10, window.innerHeight - container.getBoundingClientRect().top - 30)}px`; - this.scrollableElement.onScroll(() => { + this.menuDisposables.add(this.scrollableElement.onScroll(() => { this._onScroll.fire(); - }, this, this.menuDisposables); + }, this)); this._register(addDisposableListener(this.menuElement, EventType.SCROLL, (e: ScrollEvent) => { if (this.scrollTopHold !== undefined) { @@ -557,7 +556,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; - const border = isSelected && this.menuStyle.selectionBorderColor ? `1px solid ${this.menuStyle.selectionBorderColor}` : null; + const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : null; this.item.style.color = fgColor ? `${fgColor}` : null; this.check.style.backgroundColor = fgColor ? `${fgColor}` : null; @@ -575,14 +574,14 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { private mysubmenu: Menu | null; private submenuContainer: HTMLElement | undefined; private submenuIndicator: HTMLElement; - private submenuDisposables: IDisposable[] = []; + private readonly submenuDisposables = this._register(new DisposableStore()); private mouseOver: boolean; private showScheduler: RunOnceScheduler; private hideScheduler: RunOnceScheduler; constructor( action: IAction, - private submenuActions: IAction[], + private submenuActions: ReadonlyArray, private parentData: ISubMenuData, private submenuOptions?: IMenuOptions ) { @@ -675,7 +674,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.parentData.submenu = undefined; if (this.submenuContainer) { - this.submenuDisposables = dispose(this.submenuDisposables); + this.submenuDisposables.clear(); this.submenuContainer = undefined; } } @@ -708,7 +707,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; } - this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { + this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.LeftArrow)) { EventHelper.stop(e, true); @@ -720,12 +719,12 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.parentData.submenu = undefined; } - this.submenuDisposables = dispose(this.submenuDisposables); + this.submenuDisposables.clear(); this.submenuContainer = undefined; } })); - this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, e => { + this.submenuDisposables.add(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, e => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.LeftArrow)) { EventHelper.stop(e, true); @@ -733,7 +732,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { })); - this.submenuDisposables.push(this.parentData.submenu.onDidCancel(() => { + this.submenuDisposables.add(this.parentData.submenu.onDidCancel(() => { this.parentData.parent.focus(); if (this.parentData.submenu) { @@ -741,7 +740,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.parentData.submenu = undefined; } - this.submenuDisposables = dispose(this.submenuDisposables); + this.submenuDisposables.clear(); this.submenuContainer = undefined; })); @@ -781,7 +780,6 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { } if (this.submenuContainer) { - this.submenuDisposables = dispose(this.submenuDisposables); this.submenuContainer = undefined; } } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index c534ba5ba0..af6b70784f 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -14,22 +14,25 @@ import { cleanMnemonic, IMenuOptions, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MN import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; -import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { KeyCode, ResolvedKeybinding, KeyMod } from 'vs/base/common/keyCodes'; +import { Disposable, dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { asArray } from 'vs/base/common/arrays'; +import { ScanCodeUtils, ScanCode } from 'vs/base/common/scanCode'; +import { isMacintosh } from 'vs/base/common/platform'; const $ = DOM.$; export interface IMenuBarOptions { enableMnemonics?: boolean; + disableAltFocus?: boolean; visibility?: string; getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined; alwaysOnMnemonics?: boolean; } export interface MenuBarMenu { - actions: IAction[]; + actions: ReadonlyArray; label: string; } @@ -48,7 +51,7 @@ export class MenuBar extends Disposable { buttonElement: HTMLElement; titleElement: HTMLElement; label: string; - actions?: IAction[]; + actions?: ReadonlyArray; }[]; private overflowMenu: { @@ -114,9 +117,9 @@ export class MenuBar extends Disposable { let eventHandled = true; const key = !!e.key ? e.key.toLocaleLowerCase() : ''; - if (event.equals(KeyCode.LeftArrow)) { + if (event.equals(KeyCode.LeftArrow) || (isMacintosh && event.equals(KeyCode.Tab | KeyMod.Shift))) { this.focusPrevious(); - } else if (event.equals(KeyCode.RightArrow)) { + } else if (event.equals(KeyCode.RightArrow) || (isMacintosh && event.equals(KeyCode.Tab))) { this.focusNext(); } else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) { this.setUnfocusedState(); @@ -127,6 +130,11 @@ export class MenuBar extends Disposable { eventHandled = false; } + // Never allow default tab behavior + if (event.equals(KeyCode.Tab | KeyMod.Shift) || event.equals(KeyCode.Tab)) { + event.preventDefault(); + } + if (eventHandled) { event.preventDefault(); event.stopPropagation(); @@ -776,6 +784,13 @@ export class MenuBar extends Disposable { return; } + // Prevent alt-key default if the menu is not hidden and we use alt to focus + if (modifierKeyStatus.event && !this.options.disableAltFocus) { + if (ScanCodeUtils.toEnum(modifierKeyStatus.event.code) === ScanCode.AltLeft) { + modifierKeyStatus.event.preventDefault(); + } + } + // Alt key pressed while menu is focused. This should return focus away from the menubar if (this.isFocused && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.altKey) { this.setUnfocusedState(); @@ -786,7 +801,7 @@ export class MenuBar extends Disposable { // Clean alt key press and release if (allModifiersReleased && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.lastKeyReleased === 'alt') { if (!this.awaitingAltRelease) { - if (!this.isFocused) { + if (!this.isFocused && !(this.options.disableAltFocus && this.options.visibility !== 'toggle')) { this.mnemonicsInUse = true; this.focusedMenu = { index: this.numMenusShown > 0 ? 0 : MenuBar.OVERFLOW_INDEX }; this.focusState = MenubarState.FOCUSED; @@ -891,12 +906,13 @@ interface IModifierKeyStatus { ctrlKey: boolean; lastKeyPressed?: ModifierKey; lastKeyReleased?: ModifierKey; + event?: KeyboardEvent; } class ModifierKeyEmitter extends Emitter { - private _subscriptions: IDisposable[] = []; + private readonly _subscriptions = new DisposableStore(); private _keyStatus: IModifierKeyStatus; private static instance: ModifierKeyEmitter; @@ -909,7 +925,7 @@ class ModifierKeyEmitter extends Emitter { ctrlKey: false }; - this._subscriptions.push(domEvent(document.body, 'keydown', true)(e => { + this._subscriptions.add(domEvent(document.body, 'keydown', true)(e => { const event = new StandardKeyboardEvent(e); if (e.altKey && !this._keyStatus.altKey) { @@ -929,11 +945,12 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.shiftKey = e.shiftKey; if (this._keyStatus.lastKeyPressed) { + this._keyStatus.event = e; this.fire(this._keyStatus); } })); - this._subscriptions.push(domEvent(document.body, 'keyup', true)(e => { + this._subscriptions.add(domEvent(document.body, 'keyup', true)(e => { if (!e.altKey && this._keyStatus.altKey) { this._keyStatus.lastKeyReleased = 'alt'; } else if (!e.ctrlKey && this._keyStatus.ctrlKey) { @@ -953,25 +970,26 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.shiftKey = e.shiftKey; if (this._keyStatus.lastKeyReleased) { + this._keyStatus.event = e; this.fire(this._keyStatus); } })); - this._subscriptions.push(domEvent(document.body, 'mousedown', true)(e => { + this._subscriptions.add(domEvent(document.body, 'mousedown', true)(e => { this._keyStatus.lastKeyPressed = undefined; })); - this._subscriptions.push(domEvent(document.body, 'mouseup', true)(e => { + this._subscriptions.add(domEvent(document.body, 'mouseup', true)(e => { this._keyStatus.lastKeyPressed = undefined; })); - this._subscriptions.push(domEvent(document.body, 'mousemove', true)(e => { + this._subscriptions.add(domEvent(document.body, 'mousemove', true)(e => { if (e.buttons) { this._keyStatus.lastKeyPressed = undefined; } })); - this._subscriptions.push(domEvent(window, 'blur')(e => { + this._subscriptions.add(domEvent(window, 'blur')(e => { this._keyStatus.lastKeyPressed = undefined; this._keyStatus.lastKeyReleased = undefined; this._keyStatus.altKey = false; @@ -992,6 +1010,6 @@ class ModifierKeyEmitter extends Emitter { dispose() { super.dispose(); - this._subscriptions = dispose(this._subscriptions); + this._subscriptions.dispose(); } } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index d583733048..d9cc1b7a4f 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons"; - src: url("./octicons.ttf?91284a5a76ea88faeb754359b7f7cd03") format("truetype"), -url("./octicons.svg?91284a5a76ea88faeb754359b7f7cd03#octicons") format("svg"); + src: url("./octicons.ttf?1b0f2a9535896866c74dd24eedeb4374") format("truetype"), +url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); } .octicon, .mega-octicon { @@ -235,10 +235,14 @@ url("./octicons.svg?91284a5a76ea88faeb754359b7f7cd03#octicons") format("svg"); .octicon-zap:before { content: "\26a1" } .octicon-archive:before { content: "\f101" } .octicon-arrow-both:before { content: "\f102" } -.octicon-eye-closed:before { content: "\f103" } -.octicon-fold-down:before { content: "\f104" } -.octicon-fold-up:before { content: "\f105" } -.octicon-github-action:before { content: "\f106" } -.octicon-play:before { content: "\f107" } -.octicon-remote:before { content: "\f108" } -.octicon-request-changes:before { content: "\f109" } +.octicon-error:before { content: "\f103" } +.octicon-eye-closed:before { content: "\f104" } +.octicon-fold-down:before { content: "\f105" } +.octicon-fold-up:before { content: "\f106" } +.octicon-github-action:before { content: "\f107" } +.octicon-info-outline:before { content: "\f108" } +.octicon-play:before { content: "\f109" } +.octicon-remote:before { content: "\f10a" } +.octicon-request-changes:before { content: "\f10b" } +.octicon-smiley-outline:before { content: "\f10c" } +.octicon-warning:before { content: "\f10d" } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg index af83960978..3f4ab4f180 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg @@ -42,10 +42,10 @@ horiz-adv-x="625" d=" M312.5 632.5L0 257.5H187.5V7.5H437.5V257.5H625L312.5 632.5z" /> + horiz-adv-x="1000" d=" M881.8625 -40.39375L697.0187500000001 374.6875V593.4375H751.7062500000001V648.125H259.51625V593.4375H314.20375V374.6875L129.906875 -40.39375C113.500625 -76.4874999999999 140.2975 -117.5 179.6725 -117.5H832.6437500000001C872.01875 -117.5 898.26875 -76.4874999999999 882.40625 -40.39375H881.8625zM300.531875 210.625L368.89125 374.6875V593.4375H642.3312500000001V374.6875L710.6875 210.625H300.531875V210.625zM532.95375 320H587.64125V265.3125H532.95375V320V320zM478.26625 374.6875H423.57875V429.375H478.26625V374.6875V374.6875zM478.26625 538.75H532.95375V484.0625H478.26625V538.75V538.75zM478.26625 702.8125H423.57875V757.5H478.26625V702.8125V702.8125z" /> + horiz-adv-x="875" d=" M816.66875 115.83125V57.5H0V115.83125L42.5833125 149.6687499999999C87.5 194.58125 89.833125 298.416875 112 407.5C156.916875 627.416875 350 699.166875 350 699.166875C350 731.25 376.25 757.5 408.333125 757.5C440.416875 757.5 466.666875 731.25 466.666875 699.166875C466.666875 699.166875 664.4187499999999 627.416875 709.33125 407.5C731.5 297.833125 733.8312500000001 194 778.75 149.6687499999999L817.25 115.83125H816.66875zM408.333125 -117.5C473.083125 -117.5 525 -65.5812499999999 525 -0.8312500000001H291.666875C291.666875 -65.5812499999999 343.583125 -117.5 408.333125 -117.5V-117.5z" /> @@ -166,8 +166,11 @@ - + + + horiz-adv-x="1000" d=" M500 757.5C257.6925 757.5 62.5 562.3075 62.5 320C62.5 77.69375 257.6925 -117.5 500 -117.5C742.30625 -117.5 937.5 77.69375 937.5 320C937.5 562.3075 742.30625 757.5 500 757.5zM560.57625 10.3812499999999H432.6925V447.884375H560.57625V10.3812499999999zM560.57625 508.460625H432.6925V636.346875H560.57625V508.460625z" /> @@ -392,7 +398,7 @@ unicode="" horiz-adv-x="1000" d=" M625 745V695L656.25 632.5L375 445H137.5C110 445 95.625 411.875 116.25 391.25L312.5 195L62.5 -117.5L375 132.5L571.25 -63.75A31.25 31.25 0 0 1 625 -42.5V195L812.5 476.25L875 445H925C952.5 445 966.875 478.125 946.25 498.75L678.75 766.25A31.25 31.25 0 0 1 625 745z" /> + horiz-adv-x="937.5" d=" M605.46875 101.25H769.53125V648.125H605.46875V101.25V101.25zM386.71875 210.625H550.78125V648.125H386.71875V210.625V210.625zM167.96875 -8.125H332.03125V648.125H167.96875V-8.125V-8.125zM113.28125 -62.8125H824.21875V702.8125H113.28125V-62.8125V-62.8125zM824.21875 757.5H113.28125C83.09375 757.5 58.59375 733 58.59375 702.8125V-62.8125C58.59375 -93 83.09375 -117.5 113.28125 -117.5H824.21875C854.40625 -117.5 878.90625 -93 878.90625 -62.8125V702.8125C878.90625 733 854.40625 757.5 824.21875 757.5V757.5V757.5z" /> @@ -422,7 +428,7 @@ unicode="" horiz-adv-x="1000" d=" M299.375 438.125C315 453.75 315 480 299.375 495.625C279.375 516.25 269.3750000000001 543.125 269.3750000000001 570C269.3750000000001 596.875 279.3750000000001 623.75 299.3750000000001 644.375C315.0000000000001 660.625 315.0000000000001 686.25 299.3750000000001 701.875A38.3125 38.3125 0 0 1 271.2500000000001 713.75C261.2500000000001 713.75 250.6250000000001 710 243.1250000000001 701.875C207.5000000000001 665.625 190 617.5 190 570C190 522.5 208.125 474.375 243.1250000000001 438.1250000000001C258.7500000000001 422.5000000000001 284.3750000000001 422.5000000000001 299.3750000000001 438.1250000000001zM145.625 787.5A40.6875 40.6875 0 0 1 88.125 787.5C30 727.5 0.625 648.75 0.625 570.625C0.625 491.875 30 413.125 88.125 353.125C103.75 336.875 129.375 336.875 145 353.125S160.625 395.625 145 411.875C102.5 455.6249999999999 81.25 513.125 81.25 570.625C81.25 628.1249999999999 102.5 685.6249999999999 145 729.3749999999999A41.25 41.25 0 0 1 145.625 787.4999999999999zM501.25 468.7500000000001A101.25 101.25 0 1 1 400 570C399.3750000000001 514.375 445 468.75 501.25 468.75zM911.875 786.875A39.25 39.25 0 0 1 855 786.875C839.375 770.625 839.375 744.375 855 728.125C897.5 684.375 918.75 626.875 918.75 569.375C918.75 511.875 897.5 455 855 410.625C839.375 394.375 839.375 368.1250000000001 855 351.875A40.6875 40.6875 0 0 1 912.5 351.875C970.625 411.875 1000 490.625 1000 569.375A315.5 315.5 0 0 1 911.875 786.875zM501.25 387.5C475.6249999999999 387.5 449.375 393.75 426.25 406.25L229.375 -116.8749999999999H322.5L376.25 -54.3749999999999H626.25L678.75 -116.8749999999999H771.875L575.625 406.25C551.875 393.75 526.8750000000001 387.5 501.2500000000001 387.5zM500.625 357.5L563.75 132.5H438.75L500.625 357.5zM376.25 8.125L438.75 70.625H563.75L626.25 8.125H376.25zM700.625 701.875C685 686.25 685 660 700.625 644.375C720.6250000000001 623.75 730.6250000000001 596.875 730.6250000000001 570C730.6250000000001 543.125 720.6250000000001 516.25 700.625 495.6250000000001C685 479.3750000000001 685 453.7500000000001 700.625 438.1250000000001A39.375 39.375 0 0 1 756.8750000000001 438.1250000000001C792.5000000000001 474.3750000000001 810 522.5 810 570C810 617.5 792.5000000000001 665.625 756.8750000000001 701.875A39.625 39.625 0 0 1 700.625 701.875z" /> + + horiz-adv-x="1000" d=" M500 757.5C258.375 757.5 62.5 561.625 62.5 320C62.5 78.375 258.375 -117.5 500 -117.5C741.625 -117.5 937.5 78.375 937.5 320C937.5 561.625 741.625 757.5 500 757.5zM653.125 582.5C689.375 582.5 718.75 543.3125 718.75 495C718.75 446.6875 689.375 407.5 653.125 407.5C616.875 407.5 587.5 446.6875 587.5 495C587.5 543.3125 616.875 582.5 653.125 582.5zM346.875 582.5C383.125 582.5 412.5 543.3125 412.5 495C412.5 446.6875 383.125 407.5 346.875 407.5C310.625 407.5 281.25 446.6875 281.25 495C281.25 543.3125 310.625 582.5 346.875 582.5zM778.1875 221.5625C736.4375 103.8125 624.6875 24.6875 500 24.6875C375.3125 24.6875 263.5 103.8125 221.8125 221.5C215.75 238.5625 224.6875 257.3125 241.75 263.375C258.9375 269.4375 277.625 260.4375 283.625 243.375C316.0625 151.8125 403 90.25 499.9375 90.25C596.875 90.25 683.75 151.75 716.25 243.375C722.3125 260.4375 741.25 269.4375 758.125 263.375C775.25 257.375 784.25 238.625 778.1875 221.5625z" /> @@ -525,7 +534,7 @@ horiz-adv-x="1000" d=" M998.75 309.375L938.125 -62.5000000000001C927.5 -148.75 820.625 -180 750 -180H355.625C343.125 -180 331.8750000000001 -176.875 322.5 -171.25L232.5 -117.5H125C58.75 -117.5 0 -58.75 0 7.5V257.5C0 323.7500000000001 58.75 383.75 125 382.5H250C306.875 382.5 336.875 410.625 399.3750000000001 479.375C456.2500000000001 541.875 454.3750000000001 591.875 438.7500000000001 683.75C433.75 715 442.5 746.25 465 772.5C489.375 801.875 526.25 820 562.5 820C676.875 820 750 588.125 750 506.875L748.75 445.625H876.25C948.75 445.625 998.1249999999998 395.625 1000 322.5C1000 315.625 998.75 309.375 998.75 309.375zM875.625 383.75H751.25C707.5 383.75 686.875 401.25 686.875 444.375L688.75 508.75C688.75 588.125 615.625 758.75 563.75 758.75C532.5 758.75 496.2499999999999 727.5 501.25 696.25C516.875 597.5 522.5 522.5 445.625 437.5C381.875 366.875 335 320 250 320V-55L354.375 -117.5H750C795.625 -117.5 871.875 -98.125 875 -55L876.25 -53.75L938.75 321.25C936.875 361.25 914.9999999999998 383.75 876.25 383.75H875.625z" /> + horiz-adv-x="1000" d=" M307.5 359.920625C321.71875 345.701875 377.5 287.18625 377.5 287.18625L408.125 318.9050000000001L360 368.6706250000001L452.421875 467.108125C452.421875 467.108125 410.859375 507.576875 428.90625 491.7175C446.40625 556.795625 430.546875 628.983125 381.328125 679.8425C332.109375 730.155 262.65625 746.56125 200.3125 729.608125L305.859375 620.233125L277.96875 513.045625L174.609375 484.608125L69.0625 593.983125C52.1095625 529.451875 67.96875 457.81125 117.1875 407.4987500000001C168.59375 353.9050000000001 242.421875 338.5925000000001 307.5 359.920625V359.920625zM659.6875 253.826875L532.265625 128.0437499999999L742.2687500000001 -89.6125000000001C759.21875 -107.65625 782.1875 -116.40625 804.6125 -116.40625C827.03125 -116.40625 849.4562500000001 -107.65625 866.95625 -89.6125000000001C901.40625 -54.0625 901.40625 3.35625 866.95625 38.90625L659.6875 253.826875V253.826875zM937.5 619.140625L803.51875 757.5L408.671875 349.53125L456.796875 299.765625L221.09375 55.8625L166.953125 26.875L90.9375 -97.2687499999999L110.078125 -117.5L230.390625 -38.75L258.28125 17.03125L494.53125 260.9375L542.65625 211.171875L937.5 619.140625V619.140625z" /> @@ -556,6 +565,9 @@ + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf index 8c29d3383086aa5e948dae2b66b2fe4c8e04c5e8..4eb17f9045c32dfdff2f5489fe76b3ed7323c733 100644 GIT binary patch delta 4137 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 R-qPK-bd@n++uGd={0DE3c%1+M diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index 37a058e709..a69252d55b 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./sash'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { isIPad } from 'vs/base/browser/browser'; import { isMacintosh } from 'vs/base/common/platform'; import * as types from 'vs/base/common/types'; @@ -95,14 +95,14 @@ export class Sash extends Disposable { linkedSash: Sash | undefined = undefined; - private orthogonalStartSashDisposables: IDisposable[] = []; + private readonly orthogonalStartSashDisposables = this._register(new DisposableStore()); private _orthogonalStartSash: Sash | undefined; get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; } set orthogonalStartSash(sash: Sash | undefined) { - this.orthogonalStartSashDisposables = dispose(this.orthogonalStartSashDisposables); + this.orthogonalStartSashDisposables.clear(); if (sash) { - sash.onDidEnablementChange(this.onOrthogonalStartSashEnablementChange, this, this.orthogonalStartSashDisposables); + this.orthogonalStartSashDisposables.add(sash.onDidEnablementChange(this.onOrthogonalStartSashEnablementChange, this)); this.onOrthogonalStartSashEnablementChange(sash.state); } else { this.onOrthogonalStartSashEnablementChange(SashState.Disabled); @@ -111,14 +111,14 @@ export class Sash extends Disposable { this._orthogonalStartSash = sash; } - private orthogonalEndSashDisposables: IDisposable[] = []; + private readonly orthogonalEndSashDisposables = this._register(new DisposableStore()); private _orthogonalEndSash: Sash | undefined; get orthogonalEndSash(): Sash | undefined { return this._orthogonalEndSash; } set orthogonalEndSash(sash: Sash | undefined) { - this.orthogonalEndSashDisposables = dispose(this.orthogonalEndSashDisposables); + this.orthogonalEndSashDisposables.clear(); if (sash) { - sash.onDidEnablementChange(this.onOrthogonalEndSashEnablementChange, this, this.orthogonalEndSashDisposables); + this.orthogonalEndSashDisposables.add(sash.onDidEnablementChange(this.onOrthogonalEndSashEnablementChange, this)); this.onOrthogonalEndSashEnablementChange(sash.state); } else { this.onOrthogonalEndSashEnablementChange(SashState.Disabled); @@ -212,7 +212,13 @@ export class Sash extends Disposable { return; } - const iframes = getElementsByTagName('iframe'); + // Select both iframes and webviews; internally Electron nests an iframe + // in its component, but this isn't queryable. + const iframes = [ + ...getElementsByTagName('iframe'), + ...getElementsByTagName('webview'), + ]; + for (const iframe of iframes) { iframe.style.pointerEvents = 'none'; // disable mouse events on iframes as long as we drag the sash } @@ -254,7 +260,7 @@ export class Sash extends Disposable { style.innerHTML = `* { cursor: ${cursor} !important; }`; }; - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); updateStyle(); @@ -278,9 +284,8 @@ export class Sash extends Disposable { removeClass(this.el, 'active'); this._onDidEnd.fire(); - dispose(disposables); + disposables.dispose(); - const iframes = getElementsByTagName('iframe'); for (const iframe of iframes) { iframe.style.pointerEvents = 'auto'; } @@ -384,9 +389,6 @@ export class Sash extends Disposable { dispose(): void { super.dispose(); - this.orthogonalStartSashDisposables = dispose(this.orthogonalStartSashDisposables); - this.orthogonalEndSashDisposables = dispose(this.orthogonalEndSashDisposables); - if (this.el && this.el.parentElement) { this.el.parentElement.removeChild(this.el); } diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index eda689c79a..10c3d6dc1a 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -108,6 +108,12 @@ export abstract class AbstractScrollbar extends Widget { this._sliderMouseDown(e, () => { /*nothing to do*/ }); } }); + + this.onclick(this.slider.domNode, e => { + if (e.leftButton) { + e.stopPropagation(); + } + }); } // ----------------- Update state diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 21c83e4cb1..2ce97a42ce 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -14,11 +14,12 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { SelectBoxNative } from 'vs/base/browser/ui/selectBox/selectBoxNative'; import { SelectBoxList } from 'vs/base/browser/ui/selectBox/selectBoxCustom'; import { isMacintosh } from 'vs/base/common/platform'; +import { IDisposable } from 'vs/base/common/lifecycle'; // Public SelectBox interface - Calls routed to appropriate select implementation class -export interface ISelectBoxDelegate { +export interface ISelectBoxDelegate extends IDisposable { // Public SelectBox Interface readonly onDidSelect: Event; @@ -27,7 +28,6 @@ export interface ISelectBoxDelegate { setAriaLabel(label: string): void; focus(): void; blur(): void; - dispose(): void; // Delegated Widget interface render(container: HTMLElement): void; @@ -147,5 +147,4 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { return option; } - } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index ed5149a0cd..0c80f04675 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -5,7 +5,7 @@ import 'vs/css!./selectBoxCustom'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -85,7 +85,7 @@ class SelectListRenderer implements IListRenderer { +export class SelectBoxList extends Disposable implements ISelectBoxDelegate, IListVirtualDelegate { private static readonly DEFAULT_DROPDOWN_MINIMUM_BOTTOM_MARGIN = 32; private static readonly DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN = 2; @@ -98,7 +98,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate; - private toDispose: IDisposable[]; private styles: ISelectBoxStyles; private listRenderer: SelectListRenderer; private contextViewProvider: IContextViewProvider; @@ -117,7 +116,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate(); - this.toDispose.push(this._onDidSelect); + this._register(this._onDidSelect); this.styles = styles; @@ -191,18 +190,21 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { + this._register(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => { this.selected = e.target.selectedIndex; this._onDidSelect.fire({ index: e.target.selectedIndex, selected: e.target.value }); + if (!!this.options[this.selected] && !!this.options[this.selected].text) { + this.selectElement.title = this.options[this.selected].text; + } })); // Have to implement both keyboard and mouse controllers to handle disabled options // Intercept mouse events to override normal select actions on parents - this.toDispose.push(dom.addDisposableListener(this.selectElement, dom.EventType.CLICK, (e) => { + this._register(dom.addDisposableListener(this.selectElement, dom.EventType.CLICK, (e) => { dom.EventHelper.stop(e); if (this._isVisible) { @@ -212,13 +214,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { + this._register(dom.addDisposableListener(this.selectElement, dom.EventType.MOUSE_DOWN, (e) => { dom.EventHelper.stop(e); })); // Intercept keyboard handling - this.toDispose.push(dom.addDisposableListener(this.selectElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + this._register(dom.addDisposableListener(this.selectElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); let showDropDown = false; @@ -288,6 +290,9 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selectList.length > 0) .map(e => new StandardKeyboardEvent(e)); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this, this.toDispose); - onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.End).on(this.onEnd, this, this.toDispose); - onSelectDropDownKeyDown.filter(e => (e.keyCode >= KeyCode.KEY_0 && e.keyCode <= KeyCode.KEY_Z) || (e.keyCode >= KeyCode.US_SEMICOLON && e.keyCode <= KeyCode.NUMPAD_DIVIDE)).on(this.onCharacter, this, this.toDispose); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this)); + this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.End).on(this.onEnd, this)); + this._register(onSelectDropDownKeyDown.filter(e => (e.keyCode >= KeyCode.KEY_0 && e.keyCode <= KeyCode.KEY_Z) || (e.keyCode >= KeyCode.US_SEMICOLON && e.keyCode <= KeyCode.NUMPAD_DIVIDE)).on(this.onCharacter, this)); // SetUp list mouse controller - control navigation, disabled items, focus - Event.chain(domEvent(this.selectList.getHTMLElement(), 'mouseup')) + this._register(Event.chain(domEvent(this.selectList.getHTMLElement(), 'mouseup')) .filter(() => this.selectList.length > 0) - .on(e => this.onMouseUp(e), this, this.toDispose); + .on(e => this.onMouseUp(e), this)); - this.toDispose.push( - this.selectList.onDidBlur(_ => this.onListBlur()), - this.selectList.onMouseOver(e => typeof e.index !== 'undefined' && this.selectList.setFocus([e.index])), - this.selectList.onFocusChange(e => this.onListFocus(e)) - ); + + this._register(this.selectList.onDidBlur(_ => this.onListBlur())); + this._register(this.selectList.onMouseOver(e => typeof e.index !== 'undefined' && this.selectList.setFocus([e.index]))); + this._register(this.selectList.onFocusChange(e => this.onListFocus(e))); this.selectList.getHTMLElement().setAttribute('aria-label', this.selectBoxOptions.ariaLabel || ''); this.selectList.getHTMLElement().setAttribute('aria-expanded', 'true'); @@ -816,7 +820,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate; - private toDispose: IDisposable[]; private styles: ISelectBoxStyles; constructor(options: ISelectOptionItem[], selected: number, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { - this.toDispose = []; + super(); this.selectBoxOptions = selectBoxOptions || Object.create(null); this.options = []; @@ -36,8 +35,7 @@ export class SelectBoxNative implements ISelectBoxDelegate { this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } - this._onDidSelect = new Emitter(); - this.toDispose.push(this._onDidSelect); + this._onDidSelect = this._register(new Emitter()); this.styles = styles; @@ -47,7 +45,7 @@ export class SelectBoxNative implements ISelectBoxDelegate { private registerListeners() { - this.toDispose.push(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => { + this._register(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => { this.selectElement.title = e.target.value; this._onDidSelect.fire({ index: e.target.selectedIndex, @@ -55,7 +53,7 @@ export class SelectBoxNative implements ISelectBoxDelegate { }); })); - this.toDispose.push(dom.addStandardDisposableListener(this.selectElement, 'keydown', (e) => { + this._register(dom.addStandardDisposableListener(this.selectElement, 'keydown', (e) => { let showSelect = false; if (isMacintosh) { @@ -169,8 +167,4 @@ export class SelectBoxNative implements ISelectBoxDelegate { return option; } - - public dispose(): void { - this.toDispose = dispose(this.toDispose); - } } diff --git a/src/vs/base/browser/ui/splitview/arrow-collapse-dark.svg b/src/vs/base/browser/ui/splitview/arrow-collapse-dark.svg deleted file mode 100644 index 6f3abfce78..0000000000 --- a/src/vs/base/browser/ui/splitview/arrow-collapse-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/splitview/arrow-collapse.svg b/src/vs/base/browser/ui/splitview/arrow-collapse.svg deleted file mode 100644 index 5dcb87c772..0000000000 --- a/src/vs/base/browser/ui/splitview/arrow-collapse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/splitview/arrow-expand-dark.svg b/src/vs/base/browser/ui/splitview/arrow-expand-dark.svg deleted file mode 100644 index 22dfac04f1..0000000000 --- a/src/vs/base/browser/ui/splitview/arrow-expand-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/splitview/arrow-expand.svg b/src/vs/base/browser/ui/splitview/arrow-expand.svg deleted file mode 100644 index e55ccd923e..0000000000 --- a/src/vs/base/browser/ui/splitview/arrow-expand.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/splitview/panelview.css b/src/vs/base/browser/ui/splitview/panelview.css index 1569abdd1f..7fd452238f 100644 --- a/src/vs/base/browser/ui/splitview/panelview.css +++ b/src/vs/base/browser/ui/splitview/panelview.css @@ -27,23 +27,31 @@ } .monaco-panel-view .panel > .panel-header { - background-image: url('arrow-collapse.svg'); + background-image: url('tree-collapsed-light.svg'); background-position: 2px center; background-repeat: no-repeat; } .monaco-panel-view .panel > .panel-header.expanded { - background-image: url('arrow-expand.svg'); + background-image: url('tree-expanded-light.svg'); background-position: 2px center; background-repeat: no-repeat; } .vs-dark .monaco-panel-view .panel > .panel-header { - background-image: url('arrow-collapse-dark.svg'); + background-image: url('tree-collapsed-dark.svg'); } .vs-dark .monaco-panel-view .panel > .panel-header.expanded { - background-image: url('arrow-expand-dark.svg'); + background-image: url('tree-expanded-dark.svg'); +} + +.hc-black .monaco-panel-view .panel > .panel-header { + background-image: url('tree-collapsed-hc.svg'); +} + +.hc-black .monaco-panel-view .panel > .panel-header.expanded { + background-image: url('tree-expanded-hc.svg'); } /* TODO: actions should be part of the panel, but they aren't yet */ diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index ba6bec16d7..621eaff0f0 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./panelview'; -import { IDisposable, dispose, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -37,7 +37,7 @@ export interface IPanelStyles { * Subclasses wouldn't be able to set own properties * before the `render()` call, thus forbiding their use. */ -export abstract class Panel implements IView { +export abstract class Panel extends Disposable implements IView { private static readonly HEADER_SIZE = 22; @@ -55,11 +55,9 @@ export abstract class Panel implements IView { private styles: IPanelStyles = {}; private animationTimer: number | undefined = undefined; - private _onDidChange = new Emitter(); + private readonly _onDidChange = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - protected disposables: IDisposable[] = []; - get draggableElement(): HTMLElement { return this.header; } @@ -114,6 +112,7 @@ export abstract class Panel implements IView { width: number; constructor(options: IPanelOptions = {}) { + super(); this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; this.ariaHeaderLabel = options.ariaHeaderLabel || ''; this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120; @@ -172,26 +171,26 @@ export abstract class Panel implements IView { this.renderHeader(this.header); const focusTracker = trackFocus(this.header); - this.disposables.push(focusTracker); - focusTracker.onDidFocus(() => addClass(this.header, 'focused'), null, this.disposables); - focusTracker.onDidBlur(() => removeClass(this.header, 'focused'), null, this.disposables); + this._register(focusTracker); + this._register(focusTracker.onDidFocus(() => addClass(this.header, 'focused'), null)); + this._register(focusTracker.onDidBlur(() => removeClass(this.header, 'focused'), null)); this.updateHeader(); const onHeaderKeyDown = Event.chain(domEvent(this.header, 'keydown')) .map(e => new StandardKeyboardEvent(e)); - onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) - .event(() => this.setExpanded(!this.isExpanded()), null, this.disposables); + this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) + .event(() => this.setExpanded(!this.isExpanded()), null)); - onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow) - .event(() => this.setExpanded(false), null, this.disposables); + this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow) + .event(() => this.setExpanded(false), null)); - onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow) - .event(() => this.setExpanded(true), null, this.disposables); + this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow) + .event(() => this.setExpanded(true), null)); - domEvent(this.header, 'click') - (() => this.setExpanded(!this.isExpanded()), null, this.disposables); + this._register(domEvent(this.header, 'click') + (() => this.setExpanded(!this.isExpanded()), null)); this.body = append(this.element, $('.panel-body')); this.renderBody(this.body); @@ -234,12 +233,6 @@ export abstract class Panel implements IView { protected abstract renderHeader(container: HTMLElement): void; protected abstract renderBody(container: HTMLElement): void; protected abstract layoutBody(height: number, width: number): void; - - dispose(): void { - this.disposables = dispose(this.disposables); - - this._onDidChange.dispose(); - } } interface IDndContext { @@ -397,24 +390,24 @@ export class PanelView extends Disposable { } addPanel(panel: Panel, size: number, index = this.splitview.length): void { - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); // https://github.com/Microsoft/vscode/issues/59950 let shouldAnimate = false; - disposables.push(scheduleAtNextAnimationFrame(() => shouldAnimate = true)); + disposables.add(scheduleAtNextAnimationFrame(() => shouldAnimate = true)); - Event.filter(panel.onDidChange, () => shouldAnimate) - (this.setupAnimation, this, disposables); + disposables.add(Event.filter(panel.onDidChange, () => shouldAnimate) + (this.setupAnimation, this)); - const panelItem = { panel, disposable: combinedDisposable(disposables) }; + const panelItem = { panel, disposable: disposables }; this.panelItems.splice(index, 0, panelItem); panel.width = this.width; this.splitview.addView(panel, size, index); if (this.dnd) { const draggable = new PanelDraggable(panel, this.dnd, this.dndContext); - disposables.push(draggable); - draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop, disposables); + disposables.add(draggable); + disposables.add(draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop)); } } diff --git a/src/vs/base/browser/ui/splitview/splitview.css b/src/vs/base/browser/ui/splitview/splitview.css index 45ac7b00f1..f9882a893a 100644 --- a/src/vs/base/browser/ui/splitview/splitview.css +++ b/src/vs/base/browser/ui/splitview/splitview.css @@ -41,6 +41,10 @@ position: relative; } +.monaco-split-view2 > .split-view-container > .split-view-view:not(.visible) { + display: none; +} + .monaco-split-view2.vertical > .split-view-container > .split-view-view { width: 100%; } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 2f33b06bdd..30f35bdf46 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./splitview'; -import { IDisposable, combinedDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; @@ -47,7 +47,9 @@ export interface IView { readonly maximumSize: number; readonly onDidChange: Event; readonly priority?: LayoutPriority; + readonly snap?: boolean; layout(size: number, orientation: Orientation): void; + setVisible?(visible: boolean): void; } interface ISashEvent { @@ -57,12 +59,81 @@ interface ISashEvent { alt: boolean; } -interface IViewItem { - view: IView; - size: number; - container: HTMLElement; - disposable: IDisposable; - layout(): void; +abstract class ViewItem { + + set size(size: number) { + this._size = size; + } + + get size(): number { + return this._size; + } + + private cachedSize: number | undefined = undefined; + + get visible(): boolean { + return typeof this.cachedSize === 'undefined'; + } + + set visible(visible: boolean) { + if (visible === this.visible) { + return; + } + + if (visible) { + this.size = this.cachedSize!; + this.cachedSize = undefined; + } else { + this.cachedSize = this.size; + this.size = 0; + } + + dom.toggleClass(this.container, 'visible', visible); + + if (this.view.setVisible) { + this.view.setVisible(visible); + } + } + + get minimumSize(): number { return this.visible ? this.view.minimumSize : 0; } + get viewMinimumSize(): number { return this.view.minimumSize; } + + get maximumSize(): number { return this.visible ? this.view.maximumSize : 0; } + get viewMaximumSize(): number { return this.view.maximumSize; } + + get priority(): LayoutPriority | undefined { return this.view.priority; } + get snap(): boolean { return !!this.view.snap; } + + constructor(protected container: HTMLElement, private view: IView, private _size: number, private disposable: IDisposable) { + dom.addClass(container, 'visible'); + } + + abstract layout(): void; + + layoutView(orientation: Orientation): void { + this.view.layout(this.size, orientation); + } + + dispose(): IView { + this.disposable.dispose(); + return this.view; + } +} + +class VerticalViewItem extends ViewItem { + + layout(): void { + this.container.style.height = `${this.size}px`; + this.layoutView(Orientation.VERTICAL); + } +} + +class HorizontalViewItem extends ViewItem { + + layout(): void { + this.container.style.width = `${this.size}px`; + this.layoutView(Orientation.HORIZONTAL); + } } interface ISashItem { @@ -70,6 +141,11 @@ interface ISashItem { disposable: IDisposable; } +interface ISashDragSnapState { + readonly index: number; + readonly limitDelta: number; +} + interface ISashDragState { index: number; start: number; @@ -78,6 +154,8 @@ interface ISashDragState { minDelta: number; maxDelta: number; alt: boolean; + snapBefore: ISashDragSnapState | undefined; + snapAfter: ISashDragSnapState | undefined; disposable: IDisposable; } @@ -104,7 +182,7 @@ export class SplitView extends Disposable { private size = 0; private contentSize = 0; private proportions: undefined | number[] = undefined; - private viewItems: IViewItem[] = []; + private viewItems: ViewItem[] = []; private sashItems: ISashItem[] = []; private sashDragState: ISashDragState; private state: State = State.Idle; @@ -122,11 +200,11 @@ export class SplitView extends Disposable { } get minimumSize(): number { - return this.viewItems.reduce((r, item) => r + item.view.minimumSize, 0); + return this.viewItems.reduce((r, item) => r + item.minimumSize, 0); } get maximumSize(): number { - return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.view.maximumSize, 0); + return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.maximumSize, 0); } private _orthogonalStartSash: Sash | undefined; @@ -199,16 +277,7 @@ export class SplitView extends Disposable { const onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size)); const containerDisposable = toDisposable(() => this.viewContainer.removeChild(container)); - const disposable = combinedDisposable([onChangeDisposable, containerDisposable]); - - const layoutContainer = this.orientation === Orientation.VERTICAL - ? () => item.container.style.height = `${item.size}px` - : () => item.container.style.width = `${item.size}px`; - - const layout = () => { - layoutContainer(); - item.view.layout(item.size, this.orientation); - }; + const disposable = combinedDisposable(onChangeDisposable, containerDisposable); let viewSize: number; @@ -220,7 +289,10 @@ export class SplitView extends Disposable { viewSize = view.minimumSize; } - const item: IViewItem = { view, container, size: viewSize, layout, disposable }; + const item = this.orientation === Orientation.VERTICAL + ? new VerticalViewItem(container, view, viewSize, disposable) + : new HorizontalViewItem(container, view, viewSize, disposable); + this.viewItems.splice(index, 0, item); // Add sash @@ -245,7 +317,7 @@ export class SplitView extends Disposable { const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); - const disposable = combinedDisposable([onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash]); + const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash); const sashItem: ISashItem = { sash, disposable }; this.sashItems.splice(index - 1, 0, sashItem); @@ -280,7 +352,7 @@ export class SplitView extends Disposable { // Remove view const viewItem = this.viewItems.splice(index, 1)[0]; - viewItem.disposable.dispose(); + const view = viewItem.dispose(); // Remove sash if (this.viewItems.length >= 1) { @@ -296,7 +368,7 @@ export class SplitView extends Disposable { this.distributeViewSizes(); } - return viewItem.view; + return view; } moveView(from: number, to: number): void { @@ -327,6 +399,27 @@ export class SplitView extends Disposable { this.addView(fromView, toSize, to); } + isViewVisible(index: number): boolean { + if (index < 0 || index >= this.viewItems.length) { + throw new Error('Index out of bounds'); + } + + const viewItem = this.viewItems[index]; + return viewItem.visible; + } + + setViewVisible(index: number, visible: boolean): void { + if (index < 0 || index >= this.viewItems.length) { + throw new Error('Index out of bounds'); + } + + const viewItem = this.viewItems[index]; + viewItem.visible = visible; + + this.distributeEmptySpace(index); + this.layoutViews(); + } + private relayout(lowPriorityIndex?: number, highPriorityIndex?: number): void { const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); const lowPriorityIndexes = typeof lowPriorityIndex === 'number' ? [lowPriorityIndex] : undefined; @@ -344,14 +437,14 @@ export class SplitView extends Disposable { if (!this.proportions) { const indexes = range(this.viewItems.length); - const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].view.priority === LayoutPriority.Low); - const highPriorityIndexes = indexes.filter(i => this.viewItems[i].view.priority === LayoutPriority.High); + const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.Low); + const highPriorityIndexes = indexes.filter(i => this.viewItems[i].priority === LayoutPriority.High); this.resize(this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes); } else { for (let i = 0; i < this.viewItems.length; i++) { const item = this.viewItems[i]; - item.size = clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize); + item.size = clamp(Math.round(this.proportions[i] * size), item.minimumSize, item.maximumSize); } } @@ -369,10 +462,10 @@ export class SplitView extends Disposable { const index = firstIndex(this.sashItems, item => item.sash === sash); // This way, we can press Alt while we resize a sash, macOS style! - const disposable = combinedDisposable([ + const disposable = combinedDisposable( domEvent(document.body, 'keydown')(e => resetSashDragState(this.sashDragState.current, e.altKey)), domEvent(document.body, 'keyup')(() => resetSashDragState(this.sashDragState.current, false)) - ]); + ); const resetSashDragState = (start: number, alt: boolean) => { const sizes = this.viewItems.map(i => i.size); @@ -391,35 +484,71 @@ export class SplitView extends Disposable { if (isLastSash) { const viewItem = this.viewItems[index]; - minDelta = (viewItem.view.minimumSize - viewItem.size) / 2; - maxDelta = (viewItem.view.maximumSize - viewItem.size) / 2; + minDelta = (viewItem.minimumSize - viewItem.size) / 2; + maxDelta = (viewItem.maximumSize - viewItem.size) / 2; } else { const viewItem = this.viewItems[index + 1]; - minDelta = (viewItem.size - viewItem.view.maximumSize) / 2; - maxDelta = (viewItem.size - viewItem.view.minimumSize) / 2; + minDelta = (viewItem.size - viewItem.maximumSize) / 2; + maxDelta = (viewItem.size - viewItem.minimumSize) / 2; } } - this.sashDragState = { start, current: start, index, sizes, minDelta, maxDelta, alt, disposable }; + let snapBefore: ISashDragSnapState | undefined; + let snapAfter: ISashDragSnapState | undefined; + + if (!alt) { + const upIndexes = range(index, -1); + const downIndexes = range(index + 1, this.viewItems.length); + const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].minimumSize - sizes[i]), 0); + const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].viewMaximumSize - sizes[i]), 0); + const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].minimumSize), 0); + const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].viewMaximumSize), 0); + const minDelta = Math.max(minDeltaUp, minDeltaDown); + const maxDelta = Math.min(maxDeltaDown, maxDeltaUp); + const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); + const snapAfterIndex = this.findFirstSnapIndex(downIndexes); + + if (typeof snapBeforeIndex === 'number') { + const viewItem = this.viewItems[snapBeforeIndex]; + const halfSize = Math.floor(viewItem.viewMinimumSize / 2); + + snapBefore = { + index: snapBeforeIndex, + limitDelta: viewItem.visible ? minDelta - halfSize : minDelta + halfSize + }; + } + + if (typeof snapAfterIndex === 'number') { + const viewItem = this.viewItems[snapAfterIndex]; + const halfSize = Math.floor(viewItem.viewMinimumSize / 2); + + snapAfter = { + index: snapAfterIndex, + limitDelta: viewItem.visible ? maxDelta + halfSize : maxDelta - halfSize + }; + } + } + + this.sashDragState = { start, current: start, index, sizes, minDelta, maxDelta, alt, snapBefore, snapAfter, disposable }; }; resetSashDragState(start, alt); } private onSashChange({ current }: ISashEvent): void { - const { index, start, sizes, alt, minDelta, maxDelta } = this.sashDragState; + const { index, start, sizes, alt, minDelta, maxDelta, snapBefore, snapAfter } = this.sashDragState; this.sashDragState.current = current; const delta = current - start; - const newDelta = this.resize(index, delta, sizes, undefined, undefined, minDelta, maxDelta); + const newDelta = this.resize(index, delta, sizes, undefined, undefined, minDelta, maxDelta, snapBefore, snapAfter); if (alt) { const isLastSash = index === this.sashItems.length - 1; const newSizes = this.viewItems.map(i => i.size); const viewItemIndex = isLastSash ? index : index + 1; const viewItem = this.viewItems[viewItemIndex]; - const newMinDelta = viewItem.size - viewItem.view.maximumSize; - const newMaxDelta = viewItem.size - viewItem.view.minimumSize; + const newMinDelta = viewItem.size - viewItem.maximumSize; + const newMaxDelta = viewItem.size - viewItem.minimumSize; const resizeIndex = isLastSash ? index - 1 : index + 1; this.resize(resizeIndex, -newDelta, newSizes, undefined, undefined, newMinDelta, newMaxDelta); @@ -435,7 +564,7 @@ export class SplitView extends Disposable { this.saveProportions(); } - private onViewChange(item: IViewItem, size: number | undefined): void { + private onViewChange(item: ViewItem, size: number | undefined): void { const index = this.viewItems.indexOf(item); if (index < 0 || index >= this.viewItems.length) { @@ -443,7 +572,7 @@ export class SplitView extends Disposable { } size = typeof size === 'number' ? size : item.size; - size = clamp(size, item.view.minimumSize, item.view.maximumSize); + size = clamp(size, item.minimumSize, item.maximumSize); if (this.inverseAltBehavior && index > 0) { // In this case, we want the view to grow or shrink both sides equally @@ -470,13 +599,13 @@ export class SplitView extends Disposable { const item = this.viewItems[index]; size = Math.round(size); - size = clamp(size, item.view.minimumSize, item.view.maximumSize); + size = clamp(size, item.minimumSize, item.maximumSize); let delta = size - item.size; if (delta !== 0 && index < this.viewItems.length - 1) { const downIndexes = range(index + 1, this.viewItems.length); - const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0); - const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0); + const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].minimumSize), 0); + const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].maximumSize - this.viewItems[i].size), 0); const deltaDown = clamp(delta, -expandDown, collapseDown); this.resize(index, deltaDown); @@ -485,8 +614,8 @@ export class SplitView extends Disposable { if (delta !== 0 && index > 0) { const upIndexes = range(index - 1, -1); - const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0); - const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0); + const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].minimumSize), 0); + const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].maximumSize - this.viewItems[i].size), 0); const deltaUp = clamp(-delta, -collapseUp, expandUp); this.resize(index - 1, deltaUp); @@ -521,7 +650,9 @@ export class SplitView extends Disposable { lowPriorityIndexes?: number[], highPriorityIndexes?: number[], overloadMinDelta: number = Number.NEGATIVE_INFINITY, - overloadMaxDelta: number = Number.POSITIVE_INFINITY + overloadMaxDelta: number = Number.POSITIVE_INFINITY, + snapBefore?: ISashDragSnapState, + snapAfter?: ISashDragSnapState ): number { if (index < 0 || index >= this.viewItems.length) { return 0; @@ -550,18 +681,38 @@ export class SplitView extends Disposable { const downItems = downIndexes.map(i => this.viewItems[i]); const downSizes = downIndexes.map(i => sizes[i]); - const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.minimumSize - sizes[i]), 0); - const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); - const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); - const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.maximumSize), 0); + const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].minimumSize - sizes[i]), 0); + const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].maximumSize - sizes[i]), 0); + const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].minimumSize), 0); + const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].maximumSize), 0); const minDelta = Math.max(minDeltaUp, minDeltaDown, overloadMinDelta); const maxDelta = Math.min(maxDeltaDown, maxDeltaUp, overloadMaxDelta); + let snapped = false; + + if (snapBefore) { + const snapView = this.viewItems[snapBefore.index]; + const visible = delta >= snapBefore.limitDelta; + snapped = visible !== snapView.visible; + snapView.visible = visible; + } + + if (!snapped && snapAfter) { + const snapView = this.viewItems[snapAfter.index]; + const visible = delta < snapAfter.limitDelta; + snapped = visible !== snapView.visible; + snapView.visible = visible; + } + + if (snapped) { + return this.resize(index, delta, sizes, lowPriorityIndexes, highPriorityIndexes, overloadMinDelta, overloadMaxDelta); + } + delta = clamp(delta, minDelta, maxDelta); for (let i = 0, deltaUp = delta; i < upItems.length; i++) { const item = upItems[i]; - const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize); + const size = clamp(upSizes[i] + deltaUp, item.minimumSize, item.maximumSize); const viewDelta = size - upSizes[i]; deltaUp -= viewDelta; @@ -570,7 +721,7 @@ export class SplitView extends Disposable { for (let i = 0, deltaDown = delta; i < downItems.length; i++) { const item = downItems[i]; - const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize); + const size = clamp(downSizes[i] - deltaDown, item.minimumSize, item.maximumSize); const viewDelta = size - downSizes[i]; deltaDown += viewDelta; @@ -580,13 +731,19 @@ export class SplitView extends Disposable { return delta; } - private distributeEmptySpace(): void { - let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); + private distributeEmptySpace(lowPriorityIndex?: number): void { + const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let emptyDelta = this.size - contentSize; - for (let i = this.viewItems.length - 1; emptyDelta !== 0 && i >= 0; i--) { - const item = this.viewItems[i]; - const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize); + const indexes = range(this.viewItems.length - 1, -1); + + if (typeof lowPriorityIndex === 'number') { + pushToEnd(indexes, lowPriorityIndex); + } + + for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) { + const item = this.viewItems[indexes[i]]; + const size = clamp(item.size + emptyDelta, item.minimumSize, item.maximumSize); const viewDelta = size - item.size; emptyDelta -= viewDelta; @@ -606,31 +763,43 @@ export class SplitView extends Disposable { // Update sashes enablement let previous = false; - const collapsesDown = this.viewItems.map(i => previous = (i.size - i.view.minimumSize > 0) || previous); + const collapsesDown = this.viewItems.map(i => previous = (i.size - i.minimumSize > 0) || previous); previous = false; - const expandsDown = this.viewItems.map(i => previous = (i.view.maximumSize - i.size > 0) || previous); + const expandsDown = this.viewItems.map(i => previous = (i.maximumSize - i.size > 0) || previous); const reverseViews = [...this.viewItems].reverse(); previous = false; - const collapsesUp = reverseViews.map(i => previous = (i.size - i.view.minimumSize > 0) || previous).reverse(); + const collapsesUp = reverseViews.map(i => previous = (i.size - i.minimumSize > 0) || previous).reverse(); previous = false; - const expandsUp = reverseViews.map(i => previous = (i.view.maximumSize - i.size > 0) || previous).reverse(); + const expandsUp = reverseViews.map(i => previous = (i.maximumSize - i.size > 0) || previous).reverse(); - this.sashItems.forEach((s, i) => { - const min = !(collapsesDown[i] && expandsUp[i + 1]); - const max = !(expandsDown[i] && collapsesUp[i + 1]); + this.sashItems.forEach(({ sash }, index) => { + const min = !(collapsesDown[index] && expandsUp[index + 1]); + const max = !(expandsDown[index] && collapsesUp[index + 1]); if (min && max) { - s.sash.state = SashState.Disabled; + const upIndexes = range(index, -1); + const downIndexes = range(index + 1, this.viewItems.length); + const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); + const snapAfterIndex = this.findFirstSnapIndex(downIndexes); + + if (typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible) { + sash.state = SashState.Minimum; + } else if (typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible) { + sash.state = SashState.Maximum; + } else { + sash.state = SashState.Disabled; + } } else if (min && !max) { - s.sash.state = SashState.Minimum; + sash.state = SashState.Minimum; } else if (!min && max) { - s.sash.state = SashState.Maximum; + sash.state = SashState.Maximum; } else { - s.sash.state = SashState.Enabled; + sash.state = SashState.Enabled; } + // } }); } @@ -648,10 +817,32 @@ export class SplitView extends Disposable { return 0; } + private findFirstSnapIndex(indexes: number[]): number | undefined { + // visible views first + for (const index of indexes) { + if (!this.viewItems[index].visible) { + continue; + } + + if (this.viewItems[index].snap) { + return index; + } + } + + // then, hidden views + for (const index of indexes) { + if (!this.viewItems[index].visible && this.viewItems[index].snap) { + return index; + } + } + + return undefined; + } + dispose(): void { super.dispose(); - this.viewItems.forEach(i => i.disposable.dispose()); + this.viewItems.forEach(i => i.dispose()); this.viewItems = []; this.sashItems.forEach(i => i.disposable.dispose()); diff --git a/src/vs/base/browser/ui/splitview/tree-collapsed-dark.svg b/src/vs/base/browser/ui/splitview/tree-collapsed-dark.svg new file mode 100644 index 0000000000..c2c2298dd5 --- /dev/null +++ b/src/vs/base/browser/ui/splitview/tree-collapsed-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/splitview/tree-collapsed-hc.svg b/src/vs/base/browser/ui/splitview/tree-collapsed-hc.svg new file mode 100644 index 0000000000..3732cbc04b --- /dev/null +++ b/src/vs/base/browser/ui/splitview/tree-collapsed-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/splitview/tree-collapsed-light.svg b/src/vs/base/browser/ui/splitview/tree-collapsed-light.svg new file mode 100644 index 0000000000..1952ad63f8 --- /dev/null +++ b/src/vs/base/browser/ui/splitview/tree-collapsed-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/splitview/tree-expanded-dark.svg b/src/vs/base/browser/ui/splitview/tree-expanded-dark.svg new file mode 100644 index 0000000000..5570923e17 --- /dev/null +++ b/src/vs/base/browser/ui/splitview/tree-expanded-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/splitview/tree-expanded-hc.svg b/src/vs/base/browser/ui/splitview/tree-expanded-hc.svg new file mode 100644 index 0000000000..b370009330 --- /dev/null +++ b/src/vs/base/browser/ui/splitview/tree-expanded-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/splitview/tree-expanded-light.svg b/src/vs/base/browser/ui/splitview/tree-expanded-light.svg new file mode 100644 index 0000000000..939ebc8b96 --- /dev/null +++ b/src/vs/base/browser/ui/splitview/tree-expanded-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/toolbar/ellipsis-dark.svg b/src/vs/base/browser/ui/toolbar/ellipsis-dark.svg new file mode 100644 index 0000000000..2c52e359f6 --- /dev/null +++ b/src/vs/base/browser/ui/toolbar/ellipsis-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/toolbar/ellipsis-hc.svg b/src/vs/base/browser/ui/toolbar/ellipsis-hc.svg new file mode 100644 index 0000000000..3d7068f6b4 --- /dev/null +++ b/src/vs/base/browser/ui/toolbar/ellipsis-hc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/toolbar/ellipsis-inverse.svg b/src/vs/base/browser/ui/toolbar/ellipsis-inverse.svg deleted file mode 100644 index e3337557a2..0000000000 --- a/src/vs/base/browser/ui/toolbar/ellipsis-inverse.svg +++ /dev/null @@ -1 +0,0 @@ -Ellipsis_bold_16x \ No newline at end of file diff --git a/src/vs/base/browser/ui/toolbar/ellipsis-light.svg b/src/vs/base/browser/ui/toolbar/ellipsis-light.svg new file mode 100644 index 0000000000..883d2722ce --- /dev/null +++ b/src/vs/base/browser/ui/toolbar/ellipsis-light.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/toolbar/ellipsis.svg b/src/vs/base/browser/ui/toolbar/ellipsis.svg deleted file mode 100644 index e3f8562335..0000000000 --- a/src/vs/base/browser/ui/toolbar/ellipsis.svg +++ /dev/null @@ -1 +0,0 @@ -Ellipsis_bold_16x \ No newline at end of file diff --git a/src/vs/base/browser/ui/toolbar/toolbar.css b/src/vs/base/browser/ui/toolbar/toolbar.css index 10cb35fed0..d23a05bcb3 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.css +++ b/src/vs/base/browser/ui/toolbar/toolbar.css @@ -9,10 +9,13 @@ } .vs .monaco-toolbar .action-label.toolbar-toggle-more { - background-image: url('ellipsis.svg'); + background-image: url('ellipsis-light.svg'); } -.hc-black .monaco-toolbar .action-label.toolbar-toggle-more, .vs-dark .monaco-toolbar .action-label.toolbar-toggle-more { - background-image: url('ellipsis-inverse.svg'); + background-image: url('ellipsis-dark.svg'); +} + +.hc-black .monaco-toolbar .action-label.toolbar-toggle-more { + background-image: url('ellipsis-hc.svg'); } \ No newline at end of file diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 7d08f03117..1e676d6d37 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -9,7 +9,7 @@ import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuProvider, DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { withNullAsUndefined } from 'vs/base/common/types'; @@ -32,7 +32,7 @@ export class ToolBar extends Disposable { private options: IToolBarOptions; private actionBar: ActionBar; private toggleMenuAction: ToggleMenuAction; - private toggleMenuActionViewItem?: DropdownMenuActionViewItem; + private toggleMenuActionViewItem = this._register(new MutableDisposable()); private hasSecondaryActions: boolean; private lookupKeybindings: boolean; @@ -42,7 +42,7 @@ export class ToolBar extends Disposable { this.options = options; this.lookupKeybindings = typeof this.options.getKeyBinding === 'function'; - this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem && this.toggleMenuActionViewItem.show(), options.toggleMenuTitle)); + this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem.value && this.toggleMenuActionViewItem.value.show(), options.toggleMenuTitle)); let element = document.createElement('div'); element.className = 'monaco-toolbar'; @@ -57,13 +57,8 @@ export class ToolBar extends Disposable { // Return special action item for the toggle menu action if (action.id === ToggleMenuAction.ID) { - // Dispose old - if (this.toggleMenuActionViewItem) { - this.toggleMenuActionViewItem.dispose(); - } - // Create new - this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( + this.toggleMenuActionViewItem.value = new DropdownMenuActionViewItem( action, (action).menuActions, contextMenuProvider, @@ -73,9 +68,9 @@ export class ToolBar extends Disposable { 'toolbar-toggle-more', this.options.anchorAlignmentProvider ); - this.toggleMenuActionViewItem!.setActionContext(this.actionBar.context); + this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context); - return this.toggleMenuActionViewItem; + return this.toggleMenuActionViewItem.value; } return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; @@ -93,8 +88,8 @@ export class ToolBar extends Disposable { set context(context: any) { this.actionBar.context = context; - if (this.toggleMenuActionViewItem) { - this.toggleMenuActionViewItem.setActionContext(context); + if (this.toggleMenuActionViewItem.value) { + this.toggleMenuActionViewItem.value.setActionContext(context); } } @@ -114,7 +109,7 @@ export class ToolBar extends Disposable { this.actionBar.setAriaLabel(label); } - setActions(primaryActions: IAction[], secondaryActions?: IAction[]): () => void { + setActions(primaryActions: ReadonlyArray, secondaryActions?: ReadonlyArray): () => void { return () => { let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; @@ -154,22 +149,13 @@ export class ToolBar extends Disposable { } }; } - - dispose(): void { - if (this.toggleMenuActionViewItem) { - this.toggleMenuActionViewItem.dispose(); - this.toggleMenuActionViewItem = undefined; - } - - super.dispose(); - } } class ToggleMenuAction extends Action { static readonly ID = 'toolbar.toggle.more'; - private _menuActions: IAction[]; + private _menuActions: ReadonlyArray; private toggleDropdownMenu: () => void; constructor(toggleDropdownMenu: () => void, title?: string) { @@ -185,11 +171,11 @@ class ToggleMenuAction extends Action { return Promise.resolve(true); } - get menuActions() { + get menuActions(): ReadonlyArray { return this._menuActions; } - set menuActions(actions: IAction[]) { + set menuActions(actions: ReadonlyArray) { this._menuActions = actions; } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 327c09ac6b..5a2705ad09 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom'; +import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent, TreeMouseEventTarget } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; import { range, equals, distinctES6 } from 'vs/base/common/arrays'; @@ -25,6 +25,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { values } from 'vs/base/common/map'; import { clamp } from 'vs/base/common/numbers'; import { ScrollEvent } from 'vs/base/common/scrollable'; +import { SetMap } from 'vs/base/common/collections'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -152,7 +153,7 @@ function asListOptions(modelProvider: () => ITreeModel implements IListV interface ITreeListTemplateData { readonly container: HTMLElement; + readonly indent: HTMLElement; readonly twistie: HTMLElement; + indentGuidesDisposable: IDisposable; readonly templateData: T; } +export enum RenderIndentGuides { + None = 'none', + OnHover = 'onHover', + Always = 'always' +} + interface ITreeRendererOptions { readonly indent?: number; + readonly renderIndentGuides?: RenderIndentGuides; +} + +interface IRenderData { + templateData: ITreeListTemplateData; + height: number; +} + +interface Collection { + readonly elements: T[]; + readonly onDidChange: Event; +} + +class EventCollection implements Collection { + + private disposables = new DisposableStore(); + + get elements(): T[] { + return this._elements; + } + + constructor(readonly onDidChange: Event, private _elements: T[] = []) { + onDidChange(e => this._elements = e, null, this.disposables); + } + + dispose() { + this.disposables.dispose(); + } } class TreeRenderer implements IListRenderer, ITreeListTemplateData> { @@ -202,13 +239,20 @@ class TreeRenderer implements IListRenderer>(); - private renderedNodes = new Map, ITreeListTemplateData>(); + private renderedNodes = new Map, IRenderData>(); private indent: number = TreeRenderer.DefaultIndent; + + private _renderIndentGuides: RenderIndentGuides = RenderIndentGuides.None; + private renderedIndentGuides = new SetMap, HTMLDivElement>(); + private activeParentNodes = new Set>(); + private indentGuidesDisposable: IDisposable = Disposable.None; + private disposables: IDisposable[] = []; constructor( private renderer: ITreeRenderer, onDidChangeCollapseState: Event>, + private activeNodes: Collection>, options: ITreeRendererOptions = {} ) { this.templateId = renderer.templateId; @@ -226,34 +270,57 @@ class TreeRenderer implements IListRenderer { - templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; - }); + if (typeof options.renderIndentGuides !== 'undefined') { + const renderIndentGuides = options.renderIndentGuides; + + if (renderIndentGuides !== this._renderIndentGuides) { + this._renderIndentGuides = renderIndentGuides; + + if (renderIndentGuides) { + const disposables = new DisposableStore(); + this.activeNodes.onDidChange(this._onDidChangeActiveNodes, this, disposables); + this.indentGuidesDisposable = disposables; + + this._onDidChangeActiveNodes(this.activeNodes.elements); + } else { + this.indentGuidesDisposable.dispose(); + } + } + } } renderTemplate(container: HTMLElement): ITreeListTemplateData { const el = append(container, $('.monaco-tl-row')); + const indent = append(el, $('.monaco-tl-indent')); const twistie = append(el, $('.monaco-tl-twistie')); const contents = append(el, $('.monaco-tl-contents')); const templateData = this.renderer.renderTemplate(contents); - return { container, twistie, templateData }; + return { container, indent, twistie, indentGuidesDisposable: Disposable.None, templateData }; } renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, height: number | undefined): void { if (typeof height === 'number') { - this.renderedNodes.set(node, templateData); + this.renderedNodes.set(node, { templateData, height }); this.renderedElements.set(node.element, node); } const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; templateData.twistie.style.marginLeft = `${indent}px`; - this.update(node, templateData); + templateData.indent.style.width = `${indent + this.indent - 16}px`; + + this.renderTwistie(node, templateData); + + if (typeof height === 'number') { + this.renderIndentGuides(node, templateData); + } this.renderer.renderElement(node, index, templateData.templateData, height); } 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); } @@ -279,16 +346,17 @@ class TreeRenderer implements IListRenderer): void { - const templateData = this.renderedNodes.get(node); + const data = this.renderedNodes.get(node); - if (!templateData) { + if (!data) { return; } - this.update(node, templateData); + this.renderTwistie(node, data.templateData); + this.renderIndentGuides(node, data.templateData); } - private update(node: ITreeNode, templateData: ITreeListTemplateData) { + private renderTwistie(node: ITreeNode, templateData: ITreeListTemplateData) { if (this.renderer.renderTwistie) { this.renderer.renderTwistie(node.element, templateData.twistie); } @@ -303,9 +371,72 @@ class TreeRenderer implements IListRenderer, templateData: ITreeListTemplateData): void { + clearNode(templateData.indent); + templateData.indentGuidesDisposable.dispose(); + + if (this._renderIndentGuides === RenderIndentGuides.None) { + return; + } + + const disposableStore = new DisposableStore(); + let node = target; + + while (node.parent && node.parent.parent) { + const parent = node.parent; + const guide = $('.indent-guide', { style: `width: ${this.indent}px` }); + + if (this.activeParentNodes.has(parent)) { + addClass(guide, 'active'); + } + + if (templateData.indent.childElementCount === 0) { + templateData.indent.appendChild(guide); + } else { + templateData.indent.insertBefore(guide, templateData.indent.firstElementChild); + } + + this.renderedIndentGuides.add(parent, guide); + disposableStore.add(toDisposable(() => this.renderedIndentGuides.delete(parent, guide))); + + node = parent; + } + + templateData.indentGuidesDisposable = disposableStore; + } + + private _onDidChangeActiveNodes(nodes: ITreeNode[]): void { + if (this._renderIndentGuides === RenderIndentGuides.None) { + return; + } + + const set = new Set>(); + + nodes.forEach(node => { + if (node.parent) { + set.add(node.parent); + } + }); + + this.activeParentNodes.forEach(node => { + if (!set.has(node)) { + this.renderedIndentGuides.forEach(node, line => removeClass(line, 'active')); + } + }); + + set.forEach(node => { + if (!this.activeParentNodes.has(node)) { + this.renderedIndentGuides.forEach(node, line => addClass(line, 'active')); + } + }); + + this.activeParentNodes = set; + } + dispose(): void { this.renderedNodes.clear(); this.renderedElements.clear(); + this.indentGuidesDisposable.dispose(); this.disposables = dispose(this.disposables); } } @@ -722,9 +853,18 @@ function asTreeEvent(event: IListEvent>): ITreeEvent { } function asTreeMouseEvent(event: IListMouseEvent>): ITreeMouseEvent { + let target: TreeMouseEventTarget = TreeMouseEventTarget.Unknown; + + if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-twistie', 'monaco-tl-row')) { + target = TreeMouseEventTarget.Twistie; + } else if (hasParentWithClass(event.browserEvent.target as HTMLElement, 'monaco-tl-contents', 'monaco-tl-row')) { + target = TreeMouseEventTarget.Element; + } + return { browserEvent: event.browserEvent, - element: event.element ? event.element.element : null + element: event.element ? event.element.element : null, + target }; } @@ -811,6 +951,10 @@ class Trait { return [...this.elements]; } + getNodes(): readonly ITreeNode[] { + return this.nodes; + } + has(node: ITreeNode): boolean { return this.nodeSet.has(node); } @@ -999,6 +1143,7 @@ export abstract class AbstractTree implements IDisposable private eventBufferer = new EventBufferer(); private typeFilterController?: TypeFilterController; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; + private styleElement: HTMLStyleElement; protected disposables: IDisposable[] = []; get onDidScroll(): Event { return this.view.onDidScroll; } @@ -1027,7 +1172,6 @@ export abstract class AbstractTree implements IDisposable get filterOnType(): boolean { return !!this._options.filterOnType; } get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } - // Options TODO@joao expose options only, not Optional<> get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; } get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } @@ -1045,7 +1189,11 @@ export abstract class AbstractTree implements IDisposable const treeDelegate = new ComposedTreeDelegate>(delegate); const onDidChangeCollapseStateRelay = new Relay>(); - this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, _options)); + const onDidChangeActiveNodes = new Relay[]>(); + const activeNodes = new EventCollection(onDidChangeActiveNodes.event); + this.disposables.push(activeNodes); + + this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, activeNodes, _options)); this.disposables.push(...this.renderers); let filter: TypeFilter | undefined; @@ -1068,6 +1216,8 @@ export abstract class AbstractTree implements IDisposable this.selection.onDidModelSplice(e); }, null, this.disposables); + onDidChangeActiveNodes.input = Event.map(Event.any(this.focus.onDidChange, this.selection.onDidChange, this.model.onDidSplice), () => [...this.focus.getNodes(), ...this.selection.getNodes()]); + if (_options.keyboardSupport !== false) { const onKeyDown = Event.chain(this.view.onKeyDown) .filter(e => !isInputElement(e.target as HTMLElement)) @@ -1083,6 +1233,9 @@ export abstract class AbstractTree implements IDisposable this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node); this.disposables.push(this.typeFilterController!); } + + this.styleElement = createStyleSheet(this.view.getHTMLElement()); + toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always); } updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void { @@ -1102,6 +1255,8 @@ export abstract class AbstractTree implements IDisposable } this._onDidUpdateOptions.fire(this._options); + + toggleClass(this.getHTMLElement(), 'always', this._options.renderIndentGuides === RenderIndentGuides.Always); } get options(): IAbstractTreeOptions { @@ -1183,6 +1338,19 @@ export abstract class AbstractTree implements IDisposable } style(styles: IListStyles): void { + const suffix = `.${this.view.domId}`; + const content: string[] = []; + + if (styles.treeIndentGuidesStroke) { + content.push(`.monaco-list${suffix}:hover .monaco-tl-indent > .indent-guide, .monaco-list${suffix}.always .monaco-tl-indent > .indent-guide { border-color: ${styles.treeIndentGuidesStroke.transparent(0.4)}; }`); + content.push(`.monaco-list${suffix} .monaco-tl-indent > .indent-guide.active { border-color: ${styles.treeIndentGuidesStroke}; }`); + } + + const newStyles = content.join('\n'); + if (newStyles !== this.styleElement.innerHTML) { + this.styleElement.innerHTML = newStyles; + } + this.view.style(styles); } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index c7c31b8a63..0ff0353db0 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -28,6 +28,7 @@ interface IAsyncDataTreeNode { hasChildren: boolean; stale: boolean; slow: boolean; + collapsedByDefault: boolean | undefined; } interface IAsyncDataTreeNodeRequiredProps extends Partial> { @@ -42,7 +43,8 @@ function createAsyncDataTreeNode(props: IAsyncDataTreeNodeRequiredPro children: [], loading: false, stale: true, - slow: false + slow: false, + collapsedByDefault: undefined }; } @@ -133,7 +135,8 @@ function asTreeEvent(e: ITreeEvent>): I function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent { return { browserEvent: e.browserEvent, - element: e.element && e.element.element as T + element: e.element && e.element.element as T, + target: e.target }; } @@ -224,6 +227,7 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt } }, keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && { + ...options.keyboardNavigationLabelProvider, getKeyboardNavigationLabel(e) { return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element as T); } @@ -234,17 +238,21 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) ) ), - ariaSetProvider: undefined + ariaProvider: undefined }; } function asTreeElement(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): ITreeElement> { let collapsed: boolean | undefined; - if (viewStateContext && viewStateContext.viewState.expanded && node.id) { - collapsed = viewStateContext.viewState.expanded.indexOf(node.id) === -1; + if (viewStateContext && viewStateContext.viewState.expanded && node.id && viewStateContext.viewState.expanded.indexOf(node.id) > -1) { + collapsed = false; + } else { + collapsed = node.collapsedByDefault; } + node.collapsedByDefault = undefined; + return { element: node, children: node.hasChildren ? Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)) : [], @@ -255,10 +263,11 @@ function asTreeElement(node: IAsyncDataTreeNode, viewState export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { } -export interface IAsyncDataTreeOptions extends IAsyncDataTreeOptionsUpdate, IAbstractTreeOptions { - identityProvider?: IIdentityProvider; - sorter?: ITreeSorter; - autoExpandSingleChildren?: boolean; +export interface IAsyncDataTreeOptions extends IAsyncDataTreeOptionsUpdate, Pick, Exclude, 'collapseByDefault'>> { + readonly collapseByDefault?: { (e: T): boolean; }; + readonly identityProvider?: IIdentityProvider; + readonly sorter?: ITreeSorter; + readonly autoExpandSingleChildren?: boolean; } export interface IAsyncDataTreeViewState { @@ -285,6 +294,7 @@ export class AsyncDataTree implements IDisposable private readonly root: IAsyncDataTreeNode; private readonly nodes = new Map>(); private readonly sorter?: ITreeSorter; + private readonly collapseByDefault?: { (e: T): boolean; }; private readonly subTreeRefreshPromises = new Map, Promise>(); private readonly refreshPromises = new Map, CancelablePromise>(); @@ -310,10 +320,20 @@ export class AsyncDataTree implements IDisposable get onDidFocus(): Event { return this.tree.onDidFocus; } get onDidBlur(): Event { return this.tree.onDidBlur; } + get onDidChangeCollapseState(): Event, TFilterData>> { return this.tree.onDidChangeCollapseState; } + get onDidUpdateOptions(): Event { return this.tree.onDidUpdateOptions; } get filterOnType(): boolean { return this.tree.filterOnType; } get openOnSingleClick(): boolean { return this.tree.openOnSingleClick; } + get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { + if (typeof this.tree.expandOnlyOnTwistieClick === 'boolean') { + return this.tree.expandOnlyOnTwistieClick; + } + + const fn = this.tree.expandOnlyOnTwistieClick; + return element => fn(this.nodes.get((element === this.root.element ? null : element) as T) || null); + } get onDidDispose(): Event { return this.tree.onDidDispose; } @@ -327,6 +347,7 @@ export class AsyncDataTree implements IDisposable this.identityProvider = options.identityProvider; this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; this.sorter = options.sorter; + this.collapseByDefault = options.collapseByDefault; const objectTreeDelegate = new ComposedTreeDelegate>(delegate); const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event)); @@ -762,12 +783,17 @@ export class AsyncDataTree implements IDisposable const childrenToRefresh: IAsyncDataTreeNode[] = []; const children = childrenElements.map>(element => { + const hasChildren = !!this.dataSource.hasChildren(element); + if (!this.identityProvider) { - return createAsyncDataTreeNode({ - element, - parent: node, - hasChildren: !!this.dataSource.hasChildren(element) - }); + const asyncDataTreeNode = createAsyncDataTreeNode({ element, parent: node, hasChildren }); + + if (hasChildren && this.collapseByDefault && !this.collapseByDefault(element)) { + asyncDataTreeNode.collapsedByDefault = false; + childrenToRefresh.push(asyncDataTreeNode); + } + + return asyncDataTreeNode; } const id = this.identityProvider.getId(element).toString(); @@ -781,7 +807,7 @@ export class AsyncDataTree implements IDisposable this.nodes.set(element, asyncDataTreeNode); asyncDataTreeNode.element = element; - asyncDataTreeNode.hasChildren = !!this.dataSource.hasChildren(element); + asyncDataTreeNode.hasChildren = hasChildren; if (recursive) { if (childNode.collapsed) { @@ -789,17 +815,15 @@ export class AsyncDataTree implements IDisposable } else { childrenToRefresh.push(asyncDataTreeNode); } + } else if (hasChildren && this.collapseByDefault && !this.collapseByDefault(element)) { + asyncDataTreeNode.collapsedByDefault = false; + childrenToRefresh.push(asyncDataTreeNode); } return asyncDataTreeNode; } - const childAsyncDataTreeNode = createAsyncDataTreeNode({ - element, - parent: node, - id, - hasChildren: !!this.dataSource.hasChildren(element) - }); + const childAsyncDataTreeNode = createAsyncDataTreeNode({ element, parent: node, id, hasChildren }); if (viewStateContext && viewStateContext.viewState.focus && viewStateContext.viewState.focus.indexOf(id) > -1) { viewStateContext.focus.push(childAsyncDataTreeNode); @@ -811,6 +835,9 @@ export class AsyncDataTree implements IDisposable if (viewStateContext && viewStateContext.viewState.expanded && viewStateContext.viewState.expanded.indexOf(id) > -1) { childrenToRefresh.push(childAsyncDataTreeNode); + } else if (hasChildren && this.collapseByDefault && !this.collapseByDefault(element)) { + childAsyncDataTreeNode.collapsedByDefault = false; + childrenToRefresh.push(childAsyncDataTreeNode); } return childAsyncDataTreeNode; diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index a7d1208cfd..4d6500dc23 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -18,6 +18,7 @@ export interface IDataTreeViewState { readonly focus: string[]; readonly selection: string[]; readonly expanded: string[]; + readonly scrollTop: number; } export class DataTree extends AbstractTree { @@ -80,6 +81,10 @@ export class DataTree extends AbstractTree extends AbstractTree \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/media/collapsed-hc.svg b/src/vs/base/browser/ui/tree/media/collapsed-hc.svg deleted file mode 100644 index 145c763338..0000000000 --- a/src/vs/base/browser/ui/tree/media/collapsed-hc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/media/collapsed.svg b/src/vs/base/browser/ui/tree/media/collapsed.svg deleted file mode 100755 index 3a63808c35..0000000000 --- a/src/vs/base/browser/ui/tree/media/collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/media/expanded-dark.svg b/src/vs/base/browser/ui/tree/media/expanded-dark.svg deleted file mode 100755 index 73d41e6399..0000000000 --- a/src/vs/base/browser/ui/tree/media/expanded-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/media/expanded-hc.svg b/src/vs/base/browser/ui/tree/media/expanded-hc.svg deleted file mode 100644 index d38d4abc89..0000000000 --- a/src/vs/base/browser/ui/tree/media/expanded-hc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/media/expanded.svg b/src/vs/base/browser/ui/tree/media/expanded.svg deleted file mode 100755 index 75f73adbb0..0000000000 --- a/src/vs/base/browser/ui/tree/media/expanded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg b/src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg new file mode 100644 index 0000000000..c2c2298dd5 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/tree-collapsed-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg b/src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg new file mode 100644 index 0000000000..3732cbc04b --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/tree-collapsed-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg b/src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg new file mode 100644 index 0000000000..1952ad63f8 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/tree-collapsed-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg b/src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg new file mode 100644 index 0000000000..5570923e17 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/tree-expanded-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg b/src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg new file mode 100644 index 0000000000..b370009330 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/tree-expanded-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/tree/media/tree-expanded-light.svg b/src/vs/base/browser/ui/tree/media/tree-expanded-light.svg new file mode 100644 index 0000000000..939ebc8b96 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/tree-expanded-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index aaf730c914..2d458bf0f2 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -7,6 +7,30 @@ display: flex; height: 100%; align-items: center; + position: relative; +} + +.monaco-tl-indent { + height: 100%; + position: absolute; + top: 0; + left: 18px; + pointer-events: none; +} + +.hide-arrows .monaco-tl-indent { + left: 12px; +} + +.monaco-tl-indent > .indent-guide { + display: inline-block; + box-sizing: border-box; + height: 100%; + border-left: 1px solid transparent; +} + +.monaco-tl-indent > .indent-guide { + transition: border-color 0.1s linear; } .monaco-tl-twistie, @@ -31,28 +55,28 @@ background-size: 16px; background-position: 3px 50%; background-repeat: no-repeat; - background-image: url("expanded.svg"); + background-image: url("tree-expanded-light.svg"); } .monaco-tl-twistie.collapsible.collapsed:not(.loading) { display: inline-block; - background-image: url("collapsed.svg"); + background-image: url("tree-collapsed-light.svg"); } .vs-dark .monaco-tl-twistie.collapsible:not(.loading) { - background-image: url("expanded-dark.svg"); + background-image: url("tree-expanded-dark.svg"); } .vs-dark .monaco-tl-twistie.collapsible.collapsed:not(.loading) { - background-image: url("collapsed-dark.svg"); + background-image: url("tree-collapsed-dark.svg"); } .hc-black .monaco-tl-twistie.collapsible:not(.loading) { - background-image: url("expanded-hc.svg"); + background-image: url("tree-expanded-hc.svg"); } .hc-black .monaco-tl-twistie.collapsible.collapsed:not(.loading) { - background-image: url("collapsed-hc.svg"); + background-image: url("tree-collapsed-hc.svg"); } .monaco-tl-twistie.loading { @@ -66,4 +90,4 @@ .hc-black .monaco-tl-twistie.loading { background-image: url("loading-hc.svg"); -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 0fa4a67f2c..a84399ce5c 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -6,9 +6,10 @@ import { Iterator, ISequence } from 'vs/base/common/iterator'; import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { Event } from 'vs/base/common/event'; export interface IObjectTreeOptions extends IAbstractTreeOptions { sorter?: ITreeSorter; @@ -18,6 +19,8 @@ export class ObjectTree, TFilterData = void> extends protected model: ObjectTreeModel; + get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } + constructor( container: HTMLElement, delegate: IListVirtualDelegate, diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index c597490189..881ae62dcd 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -8,9 +8,11 @@ import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterato import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; +import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { readonly sorter?: ITreeSorter; + readonly identityProvider?: IIdentityProvider; } export class ObjectTreeModel, TFilterData extends NonNullable = void> implements ITreeModel { @@ -19,6 +21,8 @@ export class ObjectTreeModel, TFilterData extends Non private model: IndexTreeModel; private nodes = new Map>(); + private readonly nodesByIdentity = new Map>(); + private readonly identityProvider?: IIdentityProvider; private sorter?: ITreeSorter<{ element: T; }>; readonly onDidSplice: Event>; @@ -40,6 +44,8 @@ export class ObjectTreeModel, TFilterData extends Non } }; } + + this.identityProvider = options.identityProvider; } setChildren( @@ -59,11 +65,18 @@ export class ObjectTreeModel, TFilterData extends Non onDidDeleteNode?: (node: ITreeNode) => void ): Iterator> { const insertedElements = new Set(); + const insertedElementIds = new Set(); const _onDidCreateNode = (node: ITreeNode) => { insertedElements.add(node.element); this.nodes.set(node.element, node); + if (this.identityProvider) { + const id = this.identityProvider.getId(node.element).toString(); + insertedElementIds.add(id); + this.nodesByIdentity.set(id, node); + } + if (onDidCreateNode) { onDidCreateNode(node); } @@ -74,18 +87,27 @@ export class ObjectTreeModel, TFilterData extends Non this.nodes.delete(node.element); } + if (this.identityProvider) { + const id = this.identityProvider.getId(node.element).toString(); + if (!insertedElementIds.has(id)) { + this.nodesByIdentity.delete(id); + } + } + if (onDidDeleteNode) { onDidDeleteNode(node); } }; - return this.model.splice( + const result = this.model.splice( [...location, 0], Number.MAX_VALUE, children, _onDidCreateNode, _onDidDeleteNode ); + + return result; } private preserveCollapseState(elements: ISequence> | undefined): ISequence> { @@ -96,7 +118,12 @@ export class ObjectTreeModel, TFilterData extends Non } return Iterator.map(iterator, treeElement => { - const node = this.nodes.get(treeElement.element); + let node = this.nodes.get(treeElement.element); + + if (!node && this.identityProvider) { + const id = this.identityProvider.getId(treeElement.element).toString(); + node = this.nodesByIdentity.get(id); + } if (!node) { return { diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index d67539192a..41c38c1192 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -137,9 +137,16 @@ export interface ITreeEvent { browserEvent?: UIEvent; } +export enum TreeMouseEventTarget { + Unknown, + Twistie, + Element +} + export interface ITreeMouseEvent { browserEvent: MouseEvent; element: T | null; + target: TreeMouseEventTarget; } export interface ITreeContextMenuEvent { diff --git a/src/vs/base/browser/ui/tree/treeDefaults.ts b/src/vs/base/browser/ui/tree/treeDefaults.ts new file mode 100644 index 0000000000..cf32280eda --- /dev/null +++ b/src/vs/base/browser/ui/tree/treeDefaults.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; + +export class CollapseAllAction extends Action { + + constructor(private viewer: AsyncDataTree, enabled: boolean) { + super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled); + } + + public run(context?: any): Promise { + this.viewer.collapseAll(); + this.viewer.setSelection([]); + this.viewer.setFocus([]); + this.viewer.domFocus(); + this.viewer.focusFirst(); + + return Promise.resolve(); + } +} \ No newline at end of file diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 8a75a27d5c..0621c5ca1f 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -3,17 +3,27 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; export interface ITelemetryData { - from?: string; - target?: string; + readonly from?: string; + readonly target?: string; [key: string]: any; } -export interface IAction extends IDisposable { +export type WorkbenchActionExecutedClassification = { + id: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + from: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + +export type WorkbenchActionExecutedEvent = { id: string; + from: string; +}; + +export interface IAction extends IDisposable { + readonly id: string; label: string; tooltip: string; class: string | undefined; @@ -25,44 +35,44 @@ export interface IAction extends IDisposable { export interface IActionRunner extends IDisposable { run(action: IAction, context?: any): Promise; - onDidRun: Event; - onDidBeforeRun: Event; + readonly onDidRun: Event; + readonly onDidBeforeRun: Event; } -export interface IActionViewItem { - actionRunner: IActionRunner; +export interface IActionViewItem extends IDisposable { + readonly actionRunner: IActionRunner; setActionContext(context: any): void; render(element: any /* HTMLElement */): void; isEnabled(): boolean; focus(): void; blur(): void; - dispose(): void; } export interface IActionChangeEvent { - label?: string; - tooltip?: string; - class?: string; - enabled?: boolean; - checked?: boolean; - radio?: boolean; + readonly label?: string; + readonly tooltip?: string; + readonly class?: string; + readonly enabled?: boolean; + readonly checked?: boolean; + readonly radio?: boolean; } -export class Action implements IAction { +export class Action extends Disposable implements IAction { - protected _onDidChange = new Emitter(); + protected _onDidChange = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - protected _id: string; + protected readonly _id: string; protected _label: string; protected _tooltip: string; protected _cssClass: string | undefined; protected _enabled: boolean; protected _checked: boolean; protected _radio: boolean; - protected _actionCallback?: (event?: any) => Promise; + protected readonly _actionCallback?: (event?: any) => Promise; constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Promise) { + super(); this._id = id; this._label = label; this._cssClass = cssClass; @@ -82,7 +92,7 @@ export class Action implements IAction { this._setLabel(value); } - protected _setLabel(value: string): void { + private _setLabel(value: string): void { if (this._label !== value) { this._label = value; this._onDidChange.fire({ label: value }); @@ -171,16 +181,12 @@ export class Action implements IAction { return Promise.resolve(true); } - - dispose() { - this._onDidChange.dispose(); - } } export interface IRunEvent { - action: IAction; - result?: any; - error?: any; + readonly action: IAction; + readonly result?: any; + readonly error?: any; } export class ActionRunner extends Disposable implements IActionRunner { @@ -217,8 +223,8 @@ export class RadioGroup extends Disposable { constructor(readonly actions: Action[]) { super(); - this._register(combinedDisposable(actions.map(action => { - return action.onDidChange(e => { + for (const action of actions) { + this._register(action.onDidChange(e => { if (e.checked && action.checked) { for (const candidate of actions) { if (candidate !== action) { @@ -226,7 +232,7 @@ export class RadioGroup extends Disposable { } } } - }); - }))); + })); + } } } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 6b894d7d1f..9821106225 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -295,9 +295,9 @@ function topStep(array: ReadonlyArray, compare: (a: T, b: T) => number, re /** * @returns a new array with all falsy values removed. The original array IS NOT modified. */ -export function coalesce(array: Array): T[] { +export function coalesce(array: ReadonlyArray): T[] { if (!array) { - return array; + return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-checks } return array.filter(e => !!e); } @@ -336,7 +336,9 @@ export function isFalsyOrEmpty(obj: any): boolean { /** * @returns True if the provided object is an array and has at least one element. */ -export function isNonEmptyArray(obj: ReadonlyArray | undefined | null): obj is Array { +export function isNonEmptyArray(obj: T[] | undefined | null): obj is T[]; +export function isNonEmptyArray(obj: readonly T[] | undefined | null): obj is readonly T[]; +export function isNonEmptyArray(obj: T[] | readonly T[] | undefined | null): obj is T[] | readonly T[] { return Array.isArray(obj) && obj.length > 0; } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 27dde86362..131df405be 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -6,7 +6,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; export function isThenable(obj: any): obj is Promise { @@ -415,11 +415,11 @@ export class Limiter { this._onFinished = new Emitter(); } - public get onFinished(): Event { + get onFinished(): Event { return this._onFinished.event; } - public get size(): number { + get size(): number { return this._size; // return this.runningPromises + this.outstandingPromises.length; } @@ -455,7 +455,7 @@ export class Limiter { } } - public dispose(): void { + dispose(): void { this._onFinished.dispose(); } } @@ -475,35 +475,30 @@ export class Queue extends Limiter { * by disposing them once the queue is empty. */ export class ResourceQueue { - private queues: { [path: string]: Queue }; + private queues: Map> = new Map(); - constructor() { - this.queues = Object.create(null); - } - - public queueFor(resource: URI): Queue { + queueFor(resource: URI): Queue { const key = resource.toString(); - if (!this.queues[key]) { + if (!this.queues.has(key)) { const queue = new Queue(); queue.onFinished(() => { queue.dispose(); - delete this.queues[key]; + this.queues.delete(key); }); - this.queues[key] = queue; + this.queues.set(key, queue); } - return this.queues[key]; + return this.queues.get(key)!; } } -export class TimeoutTimer extends Disposable { +export class TimeoutTimer implements IDisposable { private _token: any; constructor(); constructor(runner: () => void, timeout: number); constructor(runner?: () => void, timeout?: number) { - super(); this._token = -1; if (typeof runner === 'function' && typeof timeout === 'number') { @@ -513,7 +508,6 @@ export class TimeoutTimer extends Disposable { dispose(): void { this.cancel(); - super.dispose(); } cancel(): void { @@ -543,18 +537,16 @@ export class TimeoutTimer extends Disposable { } } -export class IntervalTimer extends Disposable { +export class IntervalTimer implements IDisposable { private _token: any; constructor() { - super(); this._token = -1; } dispose(): void { this.cancel(); - super.dispose(); } cancel(): void { diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index ea20ac81a4..8758b8a585 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -78,7 +78,10 @@ export class VSBuffer { } slice(start?: number, end?: number): VSBuffer { - return new VSBuffer(this.buffer.slice(start, end)); + // IMPORTANT: use subarray instead of slice because TypedArray#slice + // creates shallow copy and NodeBuffer#slice doesn't. The use of subarray + // ensures the same, performant, behaviour. + return new VSBuffer(this.buffer.subarray(start!/*bad lib.d.ts*/, end)); } set(array: VSBuffer, offset?: number): void { @@ -138,18 +141,13 @@ export interface VSBufferReadable { read(): VSBuffer | null; } -/** - * A buffer readable stream emits data to listeners. The stream - * will only start emitting when the first data listener has - * been added or the resume() method has been called. - */ -export interface VSBufferReadableStream { +export interface ReadableStream { /** * The 'data' event is emitted whenever the stream is * relinquishing ownership of a chunk of data to a consumer. */ - on(event: 'data', callback: (chunk: VSBuffer) => void): void; + on(event: 'data', callback: (chunk: T) => void): void; /** * Emitted when any error occurs. @@ -166,19 +164,34 @@ export interface VSBufferReadableStream { /** * Stops emitting any events until resume() is called. */ - pause(): void; + pause?(): void; /** * Starts emitting events again after pause() was called. */ - resume(): void; + resume?(): void; /** * Destroys the stream and stops emitting any event. */ + destroy?(): void; +} + +/** + * A readable stream that sends data via VSBuffer. + */ +export interface VSBufferReadableStream extends ReadableStream { + pause(): void; + resume(): void; destroy(): void; } +export function isVSBufferReadableStream(obj: any): obj is VSBufferReadableStream { + const candidate: VSBufferReadableStream = obj; + + return candidate && [candidate.on, candidate.pause, candidate.resume, candidate.destroy].every(fn => typeof fn === 'function'); +} + /** * Helper to fully read a VSBuffer readable into a single buffer. */ @@ -236,6 +249,19 @@ export function bufferToStream(buffer: VSBuffer): VSBufferReadableStream { return stream; } +/** + * Helper to create a VSBufferStream from a Uint8Array stream. + */ +export function toVSBufferReadableStream(stream: ReadableStream): VSBufferReadableStream { + const vsbufferStream = writeableBufferStream(); + + stream.on('data', data => vsbufferStream.write(typeof data === 'string' ? VSBuffer.fromString(data) : VSBuffer.wrap(data))); + stream.on('end', () => vsbufferStream.end()); + stream.on('error', error => vsbufferStream.error(error)); + + return vsbufferStream; +} + /** * Helper to create a VSBufferStream that can be pushed * buffers to. Will only start to emit data when a listener diff --git a/src/vs/base/common/cache.ts b/src/vs/base/common/cache.ts index 74ef0199ff..bb43d54730 100644 --- a/src/vs/base/common/cache.ts +++ b/src/vs/base/common/cache.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IDisposable } from 'vs/base/common/lifecycle'; -export interface CacheResult { +export interface CacheResult extends IDisposable { promise: Promise; - dispose(): void; } export class Cache { diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 84d731afbc..ed2afa0809 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -46,9 +46,9 @@ export function size(from: IStringDictionary | INumberDictionary): numb } export function first(from: IStringDictionary | INumberDictionary): T | undefined { - for (let key in from) { + for (const key in from) { if (hasOwnProperty.call(from, key)) { - return from[key]; + return (from as any)[key]; } } return undefined; @@ -99,3 +99,43 @@ export function fromMap(original: Map): IStringDictionary { } return result; } + +export class SetMap { + + private map = new Map>(); + + add(key: K, value: V): void { + let values = this.map.get(key); + + if (!values) { + values = new Set(); + this.map.set(key, values); + } + + values.add(value); + } + + delete(key: K, value: V): void { + const values = this.map.get(key); + + if (!values) { + return; + } + + values.delete(value); + + if (values.size === 0) { + this.map.delete(key); + } + } + + forEach(key: K, fn: (value: V) => void): void { + const values = this.map.get(key); + + if (!values) { + return; + } + + values.forEach(fn); + } +} \ No newline at end of file diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index 898369543e..377d61e3ed 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -7,28 +7,26 @@ import * as strings from 'vs/base/common/strings'; import { sep } from 'vs/base/common/path'; import { IdleValue } from 'vs/base/common/async'; -let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>; - -export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>): void { - intlFileNameCollator = collator; -} +const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => { + const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); + return { + collator: collator, + collatorIsNumeric: collator.resolvedOptions().numeric + }; +}); export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number { - if (intlFileNameCollator) { - const a = one || ''; - const b = other || ''; - const result = intlFileNameCollator.getValue().collator.compare(a, b); + const a = one || ''; + const b = other || ''; + const result = intlFileNameCollator.getValue().collator.compare(a, b); - // Using the numeric option in the collator will - // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) { - return a < b ? -1 : 1; - } - - return result; + // Using the numeric option in the collator will + // make compare(`foo1`, `foo01`) === 0. We must disambiguate. + if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) { + return a < b ? -1 : 1; } - return noIntlCompareFileNames(one, other, caseSensitive); + return result; } const FileNameMatch = /^(.*?)(\.([^.]*))?$/; @@ -54,46 +52,27 @@ export function noIntlCompareFileNames(one: string | null, other: string | null, } export function compareFileExtensions(one: string | null, other: string | null): number { - if (intlFileNameCollator) { - const [oneName, oneExtension] = extractNameAndExtension(one); - const [otherName, otherExtension] = extractNameAndExtension(other); + const [oneName, oneExtension] = extractNameAndExtension(one); + const [otherName, otherExtension] = extractNameAndExtension(other); - let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension); + let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension); - if (result === 0) { - // Using the numeric option in the collator will - // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) { - return oneExtension < otherExtension ? -1 : 1; - } - - // Extensions are equal, compare filenames - result = intlFileNameCollator.getValue().collator.compare(oneName, otherName); - - if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) { - return oneName < otherName ? -1 : 1; - } + if (result === 0) { + // Using the numeric option in the collator will + // make compare(`foo1`, `foo01`) === 0. We must disambiguate. + if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) { + return oneExtension < otherExtension ? -1 : 1; } - return result; + // Extensions are equal, compare filenames + result = intlFileNameCollator.getValue().collator.compare(oneName, otherName); + + if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) { + return oneName < otherName ? -1 : 1; + } } - return noIntlCompareFileExtensions(one, other); -} - -function noIntlCompareFileExtensions(one: string | null, other: string | null): number { - const [oneName, oneExtension] = extractNameAndExtension(one && one.toLowerCase()); - const [otherName, otherExtension] = extractNameAndExtension(other && other.toLowerCase()); - - if (oneExtension !== otherExtension) { - return oneExtension < otherExtension ? -1 : 1; - } - - if (oneName === otherName) { - return 0; - } - - return oneName < otherName ? -1 : 1; + return result; } function extractNameAndExtension(str?: string | null): [string, string] { diff --git a/src/vs/base/common/console.ts b/src/vs/base/common/console.ts index 5ea6aefe13..2ade61a6e7 100644 --- a/src/vs/base/common/console.ts +++ b/src/vs/base/common/console.ts @@ -131,7 +131,10 @@ export function log(entry: IRemoteConsoleLog, label: string): void { } // Log it - console[entry.severity].apply(console, consoleArgs); + if (typeof (console as any)[entry.severity] !== 'function') { + throw new Error('Unknown console method'); + } + (console as any)[entry.severity].apply(console, consoleArgs); } function color(color: string): string { diff --git a/src/vs/base/common/errorMessage.ts b/src/vs/base/common/errorMessage.ts index 654f1374c0..7019fee66b 100644 --- a/src/vs/base/common/errorMessage.ts +++ b/src/vs/base/common/errorMessage.ts @@ -10,7 +10,7 @@ import * as arrays from 'vs/base/common/arrays'; function exceptionToErrorMessage(exception: any, verbose: boolean): string { if (exception.message) { if (verbose && (exception.stack || exception.stacktrace)) { - return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), exception.stack || exception.stacktrace); + return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace)); } return detectSystemErrorMessage(exception); @@ -19,6 +19,14 @@ function exceptionToErrorMessage(exception: any, verbose: boolean): string { return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); } +function stackToString(stack: string[] | string | undefined): string | undefined { + if (Array.isArray(stack)) { + return stack.join('\n'); + } + + return stack; +} + function detectSystemErrorMessage(exception: any): string { // See https://nodejs.org/api/errors.html#errors_class_system_error diff --git a/src/vs/base/common/errorsWithActions.ts b/src/vs/base/common/errorsWithActions.ts index e69f58757d..86a4d5c6ac 100644 --- a/src/vs/base/common/errorsWithActions.ts +++ b/src/vs/base/common/errorsWithActions.ts @@ -6,11 +6,11 @@ import { IAction } from 'vs/base/common/actions'; export interface IErrorOptions { - actions?: IAction[]; + actions?: ReadonlyArray; } export interface IErrorWithActions { - actions?: IAction[]; + actions?: ReadonlyArray; } export function isErrorWithActions(obj: any): obj is IErrorWithActions { diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index c34b499e89..71f8b84f96 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -5,7 +5,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; -import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; /** @@ -13,12 +13,11 @@ import { LinkedList } from 'vs/base/common/linkedList'; * can be subscribed. The event is the subscriber function itself. */ export interface Event { - (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; + (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable; } export namespace Event { - const _disposable = { dispose() { } }; - export const None: Event = function () { return _disposable; }; + export const None: Event = () => Disposable.None; /** * Given an event, returns another event which only fires once. @@ -50,7 +49,7 @@ export namespace Event { /** * Given an event and a `map` function, returns another event which maps each element - * throught the mapping function. + * through the mapping function. */ export function map(event: Event, map: (i: I) => O): Event { return snapshot((listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables)); @@ -86,12 +85,12 @@ export namespace Event { * whenever any of the provided events emit. */ export function any(...events: Event[]): Event { - return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); + return (listener, thisArgs = null, disposables?) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); } /** * Given an event and a `merge` function, returns another event which maps each element - * and the cummulative result throught the `merge` function. Similar to `map`, but with memory. + * and the cumulative result through the `merge` function. Similar to `map`, but with memory. */ export function reduce(event: Event, merge: (last: O | undefined, event: I) => O, initial?: O): Event { let output: O | undefined = initial; @@ -272,7 +271,7 @@ export namespace Event { filter(fn: (e: T) => boolean): IChainableEvent; reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent; latch(): IChainableEvent; - on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; + on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable; once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; } @@ -300,7 +299,7 @@ export namespace Event { return new ChainableEvent(latch(this.event)); } - on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { + on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[] | DisposableStore) { return this.event(listener, thisArgs, disposables); } @@ -477,7 +476,7 @@ export class Emitter { */ get event(): Event { if (!this._event) { - this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]) => { + this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore) => { if (!this._listeners) { this._listeners = new LinkedList(); } @@ -522,7 +521,9 @@ export class Emitter { } } }; - if (Array.isArray(disposables)) { + if (disposables instanceof DisposableStore) { + disposables.add(result); + } else if (Array.isArray(disposables)) { disposables.push(result); } diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 19c05ac0ae..75ea7867f2 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -125,7 +125,11 @@ const wordSeparators = new Set(); .forEach(s => wordSeparators.add(s.charCodeAt(0))); function isWordSeparator(code: number): boolean { - return wordSeparators.has(code); + return isWhitespace(code) || wordSeparators.has(code); +} + +function charactersMatch(codeA: number, codeB: number): boolean { + return (codeA === codeB) || (isWordSeparator(codeA) && isWordSeparator(codeB)); } function isAlphanumeric(code: number): boolean { @@ -298,7 +302,7 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti return []; } else if (j === target.length) { return null; - } else if (word[i] !== target[j]) { + } else if (!charactersMatch(word.charCodeAt(i), target.charCodeAt(j))) { return null; } else { let result: IMatch[] | null = null; @@ -316,9 +320,8 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti function nextWord(word: string, start: number): number { for (let i = start; i < word.length; i++) { - const c = word.charCodeAt(i); - if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1))) || - isWordSeparator(c) || (i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) { + if (isWordSeparator(word.charCodeAt(i)) || + (i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) { return i; } } @@ -492,7 +495,7 @@ function isUpperCaseAtPos(pos: number, word: string, wordLow: string): boolean { return word[pos] !== wordLow[pos]; } -function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { +export function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { while (patternPos < patternLen && wordPos < wordLen) { if (patternLow[patternPos] === wordLow[wordPos]) { patternPos += 1; diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 6b23264127..1ee7e3331c 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -458,14 +458,14 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG if (parsedPattern === NULL) { return FALSE; } - const resultPattern = function (path: string, basename: string) { + const resultPattern: ParsedPattern & { allBasenames?: string[]; allPaths?: string[]; } = function (path: string, basename: string) { return !!parsedPattern(path, basename); }; if (parsedPattern.allBasenames) { - (resultPattern).allBasenames = parsedPattern.allBasenames; + resultPattern.allBasenames = parsedPattern.allBasenames; } if (parsedPattern.allPaths) { - (resultPattern).allPaths = parsedPattern.allPaths; + resultPattern.allPaths = parsedPattern.allPaths; } return resultPattern; } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index e9772091dd..fc69a11452 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -92,3 +92,25 @@ export function removeMarkdownEscapes(text: string): string { } return text.replace(/\\([\\`*_{}[\]()#+\-.!])/g, '$1'); } + +export function parseHrefAndDimensions(href: string): { href: string, dimensions: string[] } { + const dimensions: string[] = []; + const splitted = href.split('|').map(s => s.trim()); + href = splitted[0]; + const parameters = splitted[1]; + if (parameters) { + const heightFromParams = /height=(\d+)/.exec(parameters); + const widthFromParams = /width=(\d+)/.exec(parameters); + const height = heightFromParams ? heightFromParams[1] : ''; + const width = widthFromParams ? widthFromParams[1] : ''; + const widthIsFinite = isFinite(parseInt(width)); + const heightIsFinite = isFinite(parseInt(height)); + if (widthIsFinite) { + dimensions.push(`width="${width}"`); + } + if (heightIsFinite) { + dimensions.push(`height="${height}"`); + } + } + return { href, dimensions }; +} diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 178e9c0ee3..ac7b97b132 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -5,6 +5,45 @@ import { once } from 'vs/base/common/functional'; +/** + * Enables logging of potentially leaked disposables. + * + * A disposable is considered leaked if it is not disposed or not registered as the child of + * another disposable. This tracking is very simple an only works for classes that either + * extend Disposable or use a DisposableStore. This means there are a lot of false positives. + */ +const TRACK_DISPOSABLES = false; + +const __is_disposable_tracked__ = '__is_disposable_tracked__'; + +function markTracked(x: T): void { + if (!TRACK_DISPOSABLES) { + return; + } + + if (x && x !== Disposable.None) { + try { + (x as any)[__is_disposable_tracked__] = true; + } catch { + // noop + } + } +} + +function trackDisposable(x: T): T { + if (!TRACK_DISPOSABLES) { + return x; + } + + const stack = new Error('Potentially leaked disposable').stack!; + setTimeout(() => { + if (!(x as any)[__is_disposable_tracked__]) { + console.log(stack); + } + }, 3000); + return x; +} + export interface IDisposable { dispose(): void; } @@ -15,56 +54,156 @@ export function isDisposable(thing: E): thing is E & IDisposab } export function dispose(disposable: T): T; -export function dispose(...disposables: Array): T[]; -export function dispose(disposables: T[]): T[]; -export function dispose(first: T | T[], ...rest: T[]): T | T[] | undefined { - if (Array.isArray(first)) { - first.forEach(d => d && d.dispose()); +export function dispose(disposable: T | undefined): T | undefined; +export function dispose(disposables: Array): Array; +export function dispose(disposables: ReadonlyArray): ReadonlyArray; +export function dispose(disposables: T | T[] | undefined): T | T[] | undefined { + if (Array.isArray(disposables)) { + disposables.forEach(d => { + if (d) { + markTracked(d); + d.dispose(); + } + }); return []; - } else if (rest.length === 0) { - if (first) { - first.dispose(); - return first; - } - return undefined; + } else if (disposables) { + markTracked(disposables); + disposables.dispose(); + return disposables; } else { - dispose(first); - dispose(rest); - return []; + return undefined; } } -export function combinedDisposable(disposables: IDisposable[]): IDisposable { - return { dispose: () => dispose(disposables) }; +export function combinedDisposable(...disposables: IDisposable[]): IDisposable { + disposables.forEach(markTracked); + return trackDisposable({ dispose: () => dispose(disposables) }); } export function toDisposable(fn: () => void): IDisposable { - return { dispose() { fn(); } }; + const self = trackDisposable({ + dispose: () => { + markTracked(self); + fn(); + } + }); + return self; +} + +export class DisposableStore implements IDisposable { + private _toDispose = new Set(); + private _isDisposed = false; + + /** + * Dispose of all registered disposables and mark this object as disposed. + * + * Any future disposables added to this object will be disposed of on `add`. + */ + public dispose(): void { + if (this._isDisposed) { + return; + } + + markTracked(this); + this._isDisposed = true; + this.clear(); + } + + /** + * Dispose of all registered disposables but do not mark this object as disposed. + */ + public clear(): void { + this._toDispose.forEach(item => item.dispose()); + this._toDispose.clear(); + } + + public add(t: T): T { + if (!t) { + return t; + } + if ((t as any as DisposableStore) === this) { + throw new Error('Cannot register a disposable on itself!'); + } + + markTracked(t); + if (this._isDisposed) { + console.warn(new Error('Registering disposable on object that has already been disposed of').stack); + t.dispose(); + } else { + this._toDispose.add(t); + } + + return t; + } } export abstract class Disposable implements IDisposable { static None = Object.freeze({ dispose() { } }); - protected _toDispose: IDisposable[] = []; - protected get toDispose(): IDisposable[] { return this._toDispose; } + private readonly _store = new DisposableStore(); - private _lifecycle_disposable_isDisposed = false; + constructor() { + trackDisposable(this); + } public dispose(): void { - this._lifecycle_disposable_isDisposed = true; - this._toDispose = dispose(this._toDispose); + markTracked(this); + + this._store.dispose(); } protected _register(t: T): T { - if (this._lifecycle_disposable_isDisposed) { - console.warn('Registering disposable on object that has already been disposed.'); - t.dispose(); - } else { - this._toDispose.push(t); + if ((t as any as Disposable) === this) { + throw new Error('Cannot register a disposable on itself!'); + } + return this._store.add(t); + } +} + +/** + * Manages the lifecycle of a disposable value that may be changed. + * + * This ensures that when the the disposable value is changed, the previously held disposable is disposed of. You can + * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up. + */ +export class MutableDisposable implements IDisposable { + private _value?: T; + private _isDisposed = false; + + constructor() { + trackDisposable(this); + } + + get value(): T | undefined { + return this._isDisposed ? undefined : this._value; + } + + set value(value: T | undefined) { + if (this._isDisposed || value === this._value) { + return; } - return t; + if (this._value) { + this._value.dispose(); + } + if (value) { + markTracked(value); + } + this._value = value; + } + + clear() { + this.value = undefined; + } + + dispose(): void { + this._isDisposed = true; + markTracked(this); + if (this._value) { + this._value.dispose(); + } + this._value = undefined; } } @@ -74,22 +213,23 @@ export interface IReference extends IDisposable { export abstract class ReferenceCollection { - private references: { [key: string]: { readonly object: T; counter: number; } } = Object.create(null); + private references: Map = new Map(); constructor() { } acquire(key: string): IReference { - let reference = this.references[key]; + let reference = this.references.get(key); if (!reference) { - reference = this.references[key] = { counter: 0, object: this.createReferencedObject(key) }; + reference = { counter: 0, object: this.createReferencedObject(key) }; + this.references.set(key, reference); } const { object } = reference; const dispose = once(() => { - if (--reference.counter === 0) { - this.destroyReferencedObject(key, reference.object); - delete this.references[key]; + if (--reference!.counter === 0) { + this.destroyReferencedObject(key, reference!.object); + this.references.delete(key); } }); diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index af16456a13..db69800686 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -7,6 +7,9 @@ import { basename, posix, extname } from 'vs/base/common/path'; import { endsWith, startsWithUTF8BOM, startsWith } from 'vs/base/common/strings'; import { coalesce } from 'vs/base/common/arrays'; import { match } from 'vs/base/common/glob'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { DataUri } from 'vs/base/common/resources'; export const MIME_TEXT = 'text/plain'; export const MIME_BINARY = 'application/octet-stream'; @@ -106,12 +109,28 @@ export function clearTextMimes(onlyUserConfigured?: boolean): void { /** * Given a file, return the best matching mime type for it */ -export function guessMimeTypes(path: string | null, firstLine?: string): string[] { +export function guessMimeTypes(resource: URI | null, firstLine?: string): string[] { + let path: string | undefined; + if (resource) { + switch (resource.scheme) { + case Schemas.file: + path = resource.fsPath; + break; + case Schemas.data: + const metadata = DataUri.parseMetaData(resource); + path = metadata.get(DataUri.META_DATA_LABEL); + break; + default: + path = resource.path; + } + } + if (!path) { return [MIME_UNKNOWN]; } path = path.toLowerCase(); + const filename = basename(path); // 1.) User configured mappings have highest priority @@ -255,52 +274,54 @@ interface MapExtToMediaMimes { // Known media mimes that we can handle const mapExtToMediaMimes: MapExtToMediaMimes = { + '.aac': 'audio/x-aac', + '.avi': 'video/x-msvideo', '.bmp': 'image/bmp', + '.flv': 'video/x-flv', '.gif': 'image/gif', - '.jpg': 'image/jpg', - '.jpeg': 'image/jpg', - '.jpe': 'image/jpg', - '.png': 'image/png', - '.tiff': 'image/tiff', - '.tif': 'image/tiff', '.ico': 'image/x-icon', - '.tga': 'image/x-tga', - '.psd': 'image/vnd.adobe.photoshop', - '.webp': 'image/webp', + '.jpe': 'image/jpg', + '.jpeg': 'image/jpg', + '.jpg': 'image/jpg', + '.m1v': 'video/mpeg', + '.m2a': 'audio/mpeg', + '.m2v': 'video/mpeg', + '.m3a': 'audio/mpeg', '.mid': 'audio/midi', '.midi': 'audio/midi', - '.mp4a': 'audio/mp4', - '.mpga': 'audio/mpeg', + '.mk3d': 'video/x-matroska', + '.mks': 'video/x-matroska', + '.mkv': 'video/x-matroska', + '.mov': 'video/quicktime', + '.movie': 'video/x-sgi-movie', '.mp2': 'audio/mpeg', '.mp2a': 'audio/mpeg', '.mp3': 'audio/mpeg', - '.m2a': 'audio/mpeg', - '.m3a': 'audio/mpeg', - '.oga': 'audio/ogg', - '.ogg': 'audio/ogg', - '.spx': 'audio/ogg', - '.aac': 'audio/x-aac', - '.wav': 'audio/x-wav', - '.wma': 'audio/x-ms-wma', '.mp4': 'video/mp4', + '.mp4a': 'audio/mp4', '.mp4v': 'video/mp4', - '.mpg4': 'video/mp4', + '.mpe': 'video/mpeg', '.mpeg': 'video/mpeg', '.mpg': 'video/mpeg', - '.mpe': 'video/mpeg', - '.m1v': 'video/mpeg', - '.m2v': 'video/mpeg', + '.mpg4': 'video/mp4', + '.mpga': 'audio/mpeg', + '.oga': 'audio/ogg', + '.ogg': 'audio/ogg', '.ogv': 'video/ogg', + '.png': 'image/png', + '.psd': 'image/vnd.adobe.photoshop', '.qt': 'video/quicktime', - '.mov': 'video/quicktime', + '.spx': 'audio/ogg', + '.svg': 'image/svg+xml', + '.tga': 'image/x-tga', + '.tif': 'image/tiff', + '.tiff': 'image/tiff', + '.wav': 'audio/x-wav', '.webm': 'video/webm', - '.mkv': 'video/x-matroska', - '.mk3d': 'video/x-matroska', - '.mks': 'video/x-matroska', + '.webp': 'image/webp', + '.wma': 'audio/x-ms-wma', '.wmv': 'video/x-ms-wmv', - '.flv': 'video/x-flv', - '.avi': 'video/x-msvideo', - '.movie': 'video/x-sgi-movie' + '.woff': 'application/font-woff', }; export function getMediaMime(path: string): string | undefined { diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 926ad30a8d..5fc05b6a5b 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -46,4 +46,6 @@ export namespace Schemas { export const command: string = 'command'; export const vscodeRemote: string = 'vscode-remote'; + + export const userData: string = 'vscode-userdata'; } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 1b734d0364..f130ef5930 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -14,11 +14,11 @@ export function deepClone(obj: T): T { return obj as any; } const result: any = Array.isArray(obj) ? [] : {}; - Object.keys(obj as any).forEach((key: string) => { - if (obj[key] && typeof obj[key] === 'object') { - result[key] = deepClone(obj[key]); + Object.keys(obj).forEach((key: string) => { + if ((obj)[key] && typeof (obj)[key] === 'object') { + result[key] = deepClone((obj)[key]); } else { - result[key] = obj[key]; + result[key] = (obj)[key]; } }); return result; diff --git a/src/vs/base/common/parsers.ts b/src/vs/base/common/parsers.ts index 35489f1881..8505be9cfd 100644 --- a/src/vs/base/common/parsers.ts +++ b/src/vs/base/common/parsers.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Types from 'vs/base/common/types'; - export const enum ValidationState { OK = 0, Info = 1, @@ -78,25 +76,4 @@ export abstract class Parser { public fatal(message: string): void { this._problemReporter.fatal(message); } - - protected static merge(destination: T, source: T, overwrite: boolean): void { - Object.keys(source).forEach((key: string) => { - const destValue = destination[key]; - const sourceValue = source[key]; - if (Types.isUndefined(sourceValue)) { - return; - } - if (Types.isUndefined(destValue)) { - destination[key] = sourceValue; - } else { - if (overwrite) { - if (Types.isObject(destValue) && Types.isObject(sourceValue)) { - this.merge(destValue, sourceValue, overwrite); - } else { - destination[key] = sourceValue; - } - } - } - }); - } } \ No newline at end of file diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 483746bd3e..09ef68cdbb 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -212,7 +212,7 @@ export const win32: IPath = { // absolute path, get cwd for that drive, or the process cwd if // the drive cwd is not available. We're sure the device is not // a UNC path at this points, because UNC paths are always absolute. - path = process.env['=' + resolvedDevice] || process.cwd(); + path = (process.env as any)['=' + resolvedDevice] || process.cwd(); // Verify that a cwd was found and that it actually points // to our drive. If not, default to the drive's root. diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index b1fa46c8e3..d1ba3da319 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -221,7 +221,7 @@ export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep) * Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned. * The returned relative path always uses forward slashes. */ -export function relativePath(from: URI, to: URI): string | undefined { +export function relativePath(from: URI, to: URI, ignoreCase = hasToIgnoreCase(from)): string | undefined { if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { return undefined; } @@ -229,7 +229,20 @@ export function relativePath(from: URI, to: URI): string | undefined { const relativePath = paths.relative(from.path, to.path); return isWindows ? extpath.toSlashes(relativePath) : relativePath; } - return paths.posix.relative(from.path || '/', to.path || '/'); + let fromPath = from.path || '/', toPath = to.path || '/'; + if (ignoreCase) { + // make casing of fromPath match toPath + let i = 0; + for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) { + if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) { + if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) { + break; + } + } + } + fromPath = toPath.substr(0, i) + fromPath.substr(i); + } + return paths.posix.relative(fromPath, toPath); } /** diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index cd14337bce..539458e57f 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -169,6 +169,31 @@ export function getAllPropertyNames(obj: object): string[] { return res; } +export function getAllMethodNames(obj: object): string[] { + const methods: string[] = []; + for (const prop of getAllPropertyNames(obj)) { + if (typeof (obj as any)[prop] === 'function') { + methods.push(prop); + } + } + return methods; +} + +export function createProxyObject(methodNames: string[], invoke: (method: string, args: any[]) => any): T { + const createProxyMethod = (method: string): () => any => { + return function () { + const args = Array.prototype.slice.call(arguments, 0); + return invoke(method, args); + }; + }; + + let result = {} as T; + for (const methodName of methodNames) { + (result)[methodName] = createProxyMethod(methodName); + } + return result; +} + /** * Converts null to undefined, passes all other values through. */ diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index f2501bf249..e66e27e7f5 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -385,8 +385,8 @@ export class URI implements UriComponents { return data; } else { const result = new _URI(data); - result._fsPath = (data).fsPath; result._formatted = (data).external; + result._fsPath = (data)._sep === _pathSepMarker ? (data).fsPath : null; return result; } } @@ -402,10 +402,12 @@ export interface UriComponents { interface UriState extends UriComponents { $mid: number; - fsPath: string; external: string; + fsPath: string; + _sep: 1 | undefined; } +const _pathSepMarker = isWindows ? 1 : undefined; // tslint:disable-next-line:class-name class _URI extends URI { @@ -439,6 +441,7 @@ class _URI extends URI { // cached state if (this._fsPath) { res.fsPath = this._fsPath; + res._sep = _pathSepMarker; } if (this._formatted) { res.external = this._formatted; diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 96fe079105..7cfc81703b 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -10,6 +10,51 @@ export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; transformOutgoing(uri: UriComponents): UriComponents; transformOutgoingURI(uri: URI): URI; + transformOutgoingScheme(scheme: string): string; +} + +export interface UriParts { + scheme: string; + authority?: string; + path?: string; +} + +export interface IRawURITransformer { + transformIncoming(uri: UriParts): UriParts; + transformOutgoing(uri: UriParts): UriParts; + transformOutgoingScheme(scheme: string): string; +} + +function toJSON(uri: URI): UriComponents { + return uri.toJSON(); +} + +export class URITransformer implements IURITransformer { + + private readonly _uriTransformer: IRawURITransformer; + + constructor(uriTransformer: IRawURITransformer) { + this._uriTransformer = uriTransformer; + } + + public transformIncoming(uri: UriComponents): UriComponents { + const result = this._uriTransformer.transformIncoming(uri); + return (result === uri ? uri : toJSON(URI.from(result))); + } + + public transformOutgoing(uri: UriComponents): UriComponents { + const result = this._uriTransformer.transformOutgoing(uri); + return (result === uri ? uri : toJSON(URI.from(result))); + } + + public transformOutgoingURI(uri: URI): URI { + const result = this._uriTransformer.transformOutgoing(uri); + return (result === uri ? uri : URI.from(result)); + } + + public transformOutgoingScheme(scheme: string): string { + return this._uriTransformer.transformOutgoingScheme(scheme); + } } export const DefaultURITransformer: IURITransformer = new class { @@ -24,6 +69,10 @@ export const DefaultURITransformer: IURITransformer = new class { transformOutgoingURI(uri: URI): URI { return uri; } + + transformOutgoingScheme(scheme: string): string { + return scheme; + } }; function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 1655a685ed..1333090def 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -4,16 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { transformErrorForSerialization } from 'vs/base/common/errors'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { getAllPropertyNames } from 'vs/base/common/types'; +import * as types from 'vs/base/common/types'; const INITIALIZE = '$initialize'; -export interface IWorker { +export interface IWorker extends IDisposable { getId(): number; postMessage(message: string): void; - dispose(): void; } export interface IWorkerCallback { @@ -174,17 +173,22 @@ class SimpleWorkerProtocol { } } +export interface IWorkerClient { + getProxyObject(): Promise; + dispose(): void; +} + /** * Main thread side */ -export class SimpleWorkerClient extends Disposable { +export class SimpleWorkerClient extends Disposable implements IWorkerClient { - private _worker: IWorker; - private _onModuleLoaded: Promise; - private _protocol: SimpleWorkerProtocol; - private _lazyProxy: Promise; + private readonly _worker: IWorker; + private readonly _onModuleLoaded: Promise; + private readonly _protocol: SimpleWorkerProtocol; + private readonly _lazyProxy: Promise; - constructor(workerFactory: IWorkerFactory, moduleId: string) { + constructor(workerFactory: IWorkerFactory, moduleId: string, host: H) { super(); let lazyProxyReject: ((err: any) => void) | null = null; @@ -208,8 +212,15 @@ export class SimpleWorkerClient extends Disposable { this._worker.postMessage(msg); }, handleMessage: (method: string, args: any[]): Promise => { - // Intentionally not supporting worker -> main requests - return Promise.resolve(null); + if (typeof (host as any)[method] !== 'function') { + return Promise.reject(new Error('Missing method ' + method + ' on main thread host.')); + } + + try { + return Promise.resolve((host as any)[method].apply(host, args)); + } catch (e) { + return Promise.reject(e); + } } }); this._protocol.setWorkerId(this._worker.getId()); @@ -224,41 +235,33 @@ export class SimpleWorkerClient extends Disposable { loaderConfiguration = (self).requirejs.s.contexts._.config; } + const hostMethods = types.getAllMethodNames(host); + // Send initialize message this._onModuleLoaded = this._protocol.sendMessage(INITIALIZE, [ this._worker.getId(), + loaderConfiguration, moduleId, - loaderConfiguration + hostMethods, ]); - this._lazyProxy = new Promise((resolve, reject) => { - lazyProxyReject = reject; - this._onModuleLoaded.then((availableMethods: string[]) => { - let proxy = {}; - for (const methodName of availableMethods) { - (proxy as any)[methodName] = createProxyMethod(methodName, proxyMethodRequest); - } - resolve(proxy); - }, (e) => { - reject(e); - this._onError('Worker failed to load ' + moduleId, e); - }); - }); - // Create proxy to loaded code const proxyMethodRequest = (method: string, args: any[]): Promise => { return this._request(method, args); }; - const createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise): () => Promise => { - return function () { - let args = Array.prototype.slice.call(arguments, 0); - return proxyMethodRequest(method, args); - }; - }; + this._lazyProxy = new Promise((resolve, reject) => { + lazyProxyReject = reject; + this._onModuleLoaded.then((availableMethods: string[]) => { + resolve(types.createProxyObject(availableMethods, proxyMethodRequest)); + }, (e) => { + reject(e); + this._onError('Worker failed to load ' + moduleId, e); + }); + }); } - public getProxyObject(): Promise { + public getProxyObject(): Promise { return this._lazyProxy; } @@ -281,16 +284,22 @@ export interface IRequestHandler { [prop: string]: any; } +export interface IRequestHandlerFactory { + (host: H): IRequestHandler; +} + /** * Worker side */ -export class SimpleWorkerServer { +export class SimpleWorkerServer { + private _requestHandlerFactory: IRequestHandlerFactory | null; private _requestHandler: IRequestHandler | null; private _protocol: SimpleWorkerProtocol; - constructor(postSerializedMessage: (msg: string) => void, requestHandler: IRequestHandler | null) { - this._requestHandler = requestHandler; + constructor(postSerializedMessage: (msg: string) => void, requestHandlerFactory: IRequestHandlerFactory | null) { + this._requestHandlerFactory = requestHandlerFactory; + this._requestHandler = null; this._protocol = new SimpleWorkerProtocol({ sendMessage: (msg: string): void => { postSerializedMessage(msg); @@ -305,7 +314,7 @@ export class SimpleWorkerServer { private _handleMessage(method: string, args: any[]): Promise { if (method === INITIALIZE) { - return this.initialize(args[0], args[1], args[2]); + return this.initialize(args[0], args[1], args[2], args[3]); } if (!this._requestHandler || typeof this._requestHandler[method] !== 'function') { @@ -319,18 +328,19 @@ export class SimpleWorkerServer { } } - private initialize(workerId: number, moduleId: string, loaderConfig: any): Promise { + private initialize(workerId: number, loaderConfig: any, moduleId: string, hostMethods: string[]): Promise { this._protocol.setWorkerId(workerId); - if (this._requestHandler) { + const proxyMethodRequest = (method: string, args: any[]): Promise => { + return this._protocol.sendMessage(method, args); + }; + + const hostProxy = types.createProxyObject(hostMethods, proxyMethodRequest); + + if (this._requestHandlerFactory) { // static request handler - let methods: string[] = []; - for (const prop of getAllPropertyNames(this._requestHandler)) { - if (typeof this._requestHandler[prop] === 'function') { - methods.push(prop); - } - } - return Promise.resolve(methods); + this._requestHandler = this._requestHandlerFactory(hostProxy); + return Promise.resolve(types.getAllMethodNames(this._requestHandler)); } if (loaderConfig) { @@ -351,23 +361,15 @@ export class SimpleWorkerServer { return new Promise((resolve, reject) => { // Use the global require to be sure to get the global config - (self).require([moduleId], (...result: any[]) => { - let handlerModule = result[0]; - this._requestHandler = handlerModule.create(); + (self).require([moduleId], (module: { create: IRequestHandlerFactory }) => { + this._requestHandler = module.create(hostProxy); if (!this._requestHandler) { reject(new Error(`No RequestHandler!`)); return; } - let methods: string[] = []; - for (const prop of getAllPropertyNames(this._requestHandler)) { - if (typeof this._requestHandler[prop] === 'function') { - methods.push(prop); - } - } - - resolve(methods); + resolve(types.getAllMethodNames(this._requestHandler)); }, reject); }); } @@ -376,6 +378,6 @@ export class SimpleWorkerServer { /** * Called on the worker side */ -export function create(postMessage: (msg: string) => void): SimpleWorkerServer { +export function create(postMessage: (msg: string) => void): SimpleWorkerServer { return new SimpleWorkerServer(postMessage, null); } diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts index 8ad630fc4f..1f861e37de 100644 --- a/src/vs/base/node/config.ts +++ b/src/vs/base/node/config.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import { dirname } from 'vs/base/common/path'; import * as objects from 'vs/base/common/objects'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { statLink } from 'vs/base/node/pfs'; @@ -41,20 +41,17 @@ export interface IConfigOptions { * - delayed processing of changes to accomodate for lots of changes * - configurable defaults */ -export class ConfigWatcher implements IConfigWatcher, IDisposable { +export class ConfigWatcher extends Disposable implements IConfigWatcher { private cache: T; private parseErrors: json.ParseError[]; private disposed: boolean; private loaded: boolean; private timeoutHandle: NodeJS.Timer | null; - private disposables: IDisposable[]; private readonly _onDidUpdateConfiguration: Emitter>; constructor(private _path: string, private options: IConfigOptions = { defaultConfig: Object.create(null), onError: error => console.error(error) }) { - this.disposables = []; - - this._onDidUpdateConfiguration = new Emitter>(); - this.disposables.push(this._onDidUpdateConfiguration); + super(); + this._onDidUpdateConfiguration = this._register(new Emitter>()); this.registerWatcher(); this.initAsync(); @@ -111,10 +108,10 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { try { this.parseErrors = []; res = this.options.parse ? this.options.parse(raw, this.parseErrors) : json.parse(raw, this.parseErrors); + return res || this.options.defaultConfig; } catch (error) { - // Ignore parsing errors - return this.options.defaultConfig; + return this.options.defaultConfig; // Ignore parsing errors } } @@ -125,7 +122,7 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { this.watch(parentFolder, true); // Check if the path is a symlink and watch its target if so - this.handleSymbolicLink().then(undefined, error => { /* ignore error */ }); + this.handleSymbolicLink().then(undefined, () => { /* ignore error */ }); } private async handleSymbolicLink(): Promise { @@ -143,9 +140,9 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { } if (isFolder) { - this.disposables.push(watchFolder(path, (type, path) => path === this._path ? this.onConfigFileChange() : undefined, error => this.options.onError(error))); + this._register(watchFolder(path, (type, path) => path === this._path ? this.onConfigFileChange() : undefined, error => this.options.onError(error))); } else { - this.disposables.push(watchFile(path, (type, path) => this.onConfigFileChange(), error => this.options.onError(error))); + this._register(watchFile(path, () => this.onConfigFileChange(), error => this.options.onError(error))); } } @@ -187,6 +184,6 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { dispose(): void { this.disposed = true; - this.disposables = dispose(this.disposables); + super.dispose(); } } \ No newline at end of file diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index ae26c4c60c..46199670df 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -395,7 +395,7 @@ export async function resolveTerminalEncoding(verbose?: boolean): Promise { if (stdout) { - const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings); + const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings) as Array; for (const key of windowsTerminalEncodingKeys) { if (stdout.indexOf(key) >= 0) { return resolve(windowsTerminalEncodings[key]); diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index 8649aadfe8..930abbda8b 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -7,11 +7,12 @@ import * as errors from 'vs/base/common/errors'; import * as uuid from 'vs/base/common/uuid'; import { networkInterfaces } from 'os'; import { TernarySearchTree } from 'vs/base/common/map'; +import { getMac } from 'vs/base/node/macAddress'; // http://www.techrepublic.com/blog/data-center/mac-address-scorecard-for-common-virtual-machine-platforms/ // VMware ESX 3, Server, Workstation, Player 00-50-56, 00-0C-29, 00-05-69 // Microsoft Hyper-V, Virtual Server, Virtual PC 00-03-FF -// Parallells Desktop, Workstation, Server, Virtuozzo 00-1C-42 +// Parallels Desktop, Workstation, Server, Virtuozzo 00-1C-42 // Virtual Iron 4 00-0F-4B // Red Hat Xen 00-16-3E // Oracle VM 00-16-3E @@ -76,35 +77,25 @@ export const virtualMachineHint: { value(): number } = new class { }; let machineId: Promise; -export function getMachineId(): Promise { - return machineId || (machineId = getMacMachineId() - .then(id => id || uuid.generateUuid())); // fallback, generate a UUID +export async function getMachineId(): Promise { + if (!machineId) { + machineId = (async () => { + const id = await getMacMachineId(); + + return id || uuid.generateUuid(); // fallback, generate a UUID + })(); + } + + return machineId; } -function getMacMachineId(): Promise { - return new Promise(resolve => { - Promise.all([import('crypto'), import('getmac')]).then(([crypto, getmac]) => { - try { - getmac.getMac((error, macAddress) => { - if (!error) { - resolve(crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex')); - } else { - resolve(undefined); - } - }); - - // Timeout due to hang with reduced privileges #58392 - // TODO@sbatten: Remove this when getmac is patched - setTimeout(() => { - resolve(undefined); - }, 10000); - } catch (err) { - errors.onUnexpectedError(err); - resolve(undefined); - } - }, err => { - errors.onUnexpectedError(err); - resolve(undefined); - }); - }); +async function getMacMachineId(): Promise { + try { + const crypto = await import('crypto'); + const macAddress = await getMac(); + return crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex'); + } catch (err) { + errors.onUnexpectedError(err); + return undefined; + } } diff --git a/src/vs/base/node/macAddress.ts b/src/vs/base/node/macAddress.ts new file mode 100644 index 0000000000..9889a3e420 --- /dev/null +++ b/src/vs/base/node/macAddress.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 { exec } from 'child_process'; +import { isWindows } from 'vs/base/common/platform'; + +const cmdline = { + windows: 'getmac.exe', + unix: '/sbin/ifconfig -a || /sbin/ip link' +}; + +const invalidMacAddresses = [ + '00:00:00:00:00:00', + 'ff:ff:ff:ff:ff:ff', + 'ac:de:48:00:11:22' +]; + +function validateMacAddress(candidate: string): boolean { + let tempCandidate = candidate.replace(/\-/g, ':').toLowerCase(); + for (let invalidMacAddress of invalidMacAddresses) { + if (invalidMacAddress === tempCandidate) { + return false; + } + } + + return true; +} + +export function getMac(): Promise { + return new Promise(async (resolve, reject) => { + const timeout = setTimeout(() => reject('Unable to retrieve mac address (timeout after 10s)'), 10000); + + try { + resolve(await doGetMac()); + } catch (error) { + reject(error); + } finally { + clearTimeout(timeout); + } + }); +} + +function doGetMac(): Promise { + return new Promise((resolve, reject) => { + try { + exec(isWindows ? cmdline.windows : cmdline.unix, { timeout: 10000 }, (err, stdout, stdin) => { + if (err) { + return reject(`Unable to retrieve mac address (${err.toString()})`); + } else { + const regex = /(?:[a-f\d]{2}[:\-]){5}[a-f\d]{2}/gi; + + let match; + while ((match = regex.exec(stdout)) !== null) { + const macAddressCandidate = match[0]; + if (validateMacAddress(macAddressCandidate)) { + return resolve(macAddressCandidate); + } + } + + return reject('Unable to retrieve mac address (unexpected format)'); + } + }); + } catch (err) { + reject(err); + } + }); +} \ No newline at end of file diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index a4c4af6635..da1737d861 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -138,6 +138,20 @@ export async function readdir(path: string): Promise { return handleDirectoryChildren(await promisify(fs.readdir)(path)); } +export async function readdirWithFileTypes(path: string): Promise { + const children = await promisify(fs.readdir)(path, { withFileTypes: true }); + + // Mac: uses NFD unicode form on disk, but we want NFC + // See also https://github.com/nodejs/node/issues/2165 + if (platform.isMacintosh) { + for (const child of children) { + child.name = normalizeNFC(child.name); + } + } + + return children; +} + export function readdirSync(path: string): string[] { return handleDirectoryChildren(fs.readdirSync(path)); } @@ -226,7 +240,7 @@ export function readFile(path: string, encoding?: string): Promise } = Object.create(null); +const writeFilePathQueues: Map> = new Map(); export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise; export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise; @@ -249,18 +263,20 @@ function toQueueKey(path: string): string { } function ensureWriteFileQueue(queueKey: string): Queue { - let writeFileQueue = writeFilePathQueue[queueKey]; - if (!writeFileQueue) { - writeFileQueue = new Queue(); - writeFilePathQueue[queueKey] = writeFileQueue; - - const onFinish = Event.once(writeFileQueue.onFinished); - onFinish(() => { - delete writeFilePathQueue[queueKey]; - writeFileQueue.dispose(); - }); + const existingWriteFileQueue = writeFilePathQueues.get(queueKey); + if (existingWriteFileQueue) { + return existingWriteFileQueue; } + const writeFileQueue = new Queue(); + writeFilePathQueues.set(queueKey, writeFileQueue); + + const onFinish = Event.once(writeFileQueue.onFinished); + onFinish(() => { + writeFilePathQueues.delete(queueKey); + writeFileQueue.dispose(); + }); + return writeFileQueue; } diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index a98bcc4de4..fa93ee0f00 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -5,6 +5,7 @@ import * as path from 'vs/base/common/path'; import * as fs from 'fs'; +import { promisify } from 'util'; import * as cp from 'child_process'; import * as nls from 'vs/nls'; import * as Types from 'vs/base/common/types'; @@ -404,7 +405,7 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender } export namespace win32 { - export function findExecutable(command: string, cwd?: string, paths?: string[]): string { + export async function findExecutable(command: string, cwd?: string, paths?: string[]): Promise { // If we have an absolute path then we take it. if (path.isAbsolute(command)) { return command; @@ -435,15 +436,15 @@ export namespace win32 { } else { fullPath = path.join(cwd, pathEntry, command); } - if (fs.existsSync(fullPath)) { + if (await promisify(fs.exists)(fullPath)) { return fullPath; } let withExtension = fullPath + '.com'; - if (fs.existsSync(withExtension)) { + if (await promisify(fs.exists)(withExtension)) { return withExtension; } withExtension = fullPath + '.exe'; - if (fs.existsSync(withExtension)) { + if (await promisify(fs.exists)(withExtension)) { return withExtension; } } diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts deleted file mode 100644 index 5b4d173cb8..0000000000 --- a/src/vs/base/node/request.ts +++ /dev/null @@ -1,188 +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 { isBoolean, isNumber } from 'vs/base/common/types'; -import * as https from 'https'; -import * as http from 'http'; -import { Stream } from 'stream'; -import { parse as parseUrl } from 'url'; -import { createWriteStream } from 'fs'; -import { assign } from 'vs/base/common/objects'; -import { createGunzip } from 'zlib'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { canceled } from 'vs/base/common/errors'; - -export type Agent = any; - -export interface IRawRequestFunction { - (options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest; -} - -export interface IRequestOptions { - type?: string; - url?: string; - user?: string; - password?: string; - headers?: any; - timeout?: number; - data?: string | Stream; - agent?: Agent; - followRedirects?: number; - strictSSL?: boolean; - getRawRequest?(options: IRequestOptions): IRawRequestFunction; -} - -export interface IRequestContext { - // req: http.ClientRequest; - // res: http.ClientResponse; - res: { - headers: { [n: string]: string }; - statusCode?: number; - }; - stream: Stream; -} - -export interface IRequestFunction { - (options: IRequestOptions, token: CancellationToken): Promise; -} - -async function getNodeRequest(options: IRequestOptions): Promise { - const endpoint = parseUrl(options.url!); - const module = endpoint.protocol === 'https:' ? await import('https') : await import('http'); - return module.request; -} - -export function request(options: IRequestOptions, token: CancellationToken): Promise { - let req: http.ClientRequest; - - const rawRequestPromise = options.getRawRequest - ? Promise.resolve(options.getRawRequest(options)) - : Promise.resolve(getNodeRequest(options)); - - return rawRequestPromise.then(rawRequest => { - - return new Promise((c, e) => { - const endpoint = parseUrl(options.url!); - - const opts: https.RequestOptions = { - hostname: endpoint.hostname, - port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), - protocol: endpoint.protocol, - path: endpoint.path, - method: options.type || 'GET', - headers: options.headers, - agent: options.agent, - rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true - }; - - if (options.user && options.password) { - opts.auth = options.user + ':' + options.password; - } - - req = rawRequest(opts, (res: http.IncomingMessage) => { - const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; - if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { - request(assign({}, options, { - url: res.headers['location'], - followRedirects: followRedirects - 1 - }), token).then(c, e); - } else { - let stream: Stream = res; - - if (res.headers['content-encoding'] === 'gzip') { - stream = stream.pipe(createGunzip()); - } - - c({ res, stream } as IRequestContext); - } - }); - - req.on('error', e); - - if (options.timeout) { - req.setTimeout(options.timeout); - } - - if (options.data) { - if (typeof options.data === 'string') { - req.write(options.data); - } else { - options.data.pipe(req); - return; - } - } - - req.end(); - - token.onCancellationRequested(() => { - req.abort(); - e(canceled()); - }); - }); - }); -} - -function isSuccess(context: IRequestContext): boolean { - return (context.res.statusCode && context.res.statusCode >= 200 && context.res.statusCode < 300) || context.res.statusCode === 1223; -} - -function hasNoContent(context: IRequestContext): boolean { - return context.res.statusCode === 204; -} - -export function download(filePath: string, context: IRequestContext): Promise { - return new Promise((c, e) => { - const out = createWriteStream(filePath); - - out.once('finish', () => c(undefined)); - context.stream.once('error', e); - context.stream.pipe(out); - }); -} - -export function asText(context: IRequestContext): Promise { - return new Promise((c, e) => { - if (!isSuccess(context)) { - return e('Server returned ' + context.res.statusCode); - } - - if (hasNoContent(context)) { - return c(null); - } - - const buffer: string[] = []; - context.stream.on('data', (d: string) => buffer.push(d)); - context.stream.on('end', () => c(buffer.join(''))); - context.stream.on('error', e); - }); -} - -export function asJson(context: IRequestContext): Promise { - return new Promise((c, e) => { - if (!isSuccess(context)) { - return e('Server returned ' + context.res.statusCode); - } - - if (hasNoContent(context)) { - return c(null); - } - - if (!/application\/json/.test(context.res.headers['content-type'])) { - // {{SQL CARBON EDIT}} - //return e('Response doesn\'t appear to be JSON'); - } - - const buffer: string[] = []; - context.stream.on('data', (d: string) => buffer.push(d)); - context.stream.on('end', () => { - try { - c(JSON.parse(buffer.join(''))); - } catch (err) { - e(err); - } - }); - context.stream.on('error', e); - }); -} diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 4148a623b4..5ec8df2f7c 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -11,13 +11,12 @@ import * as platform from 'vs/base/common/platform'; declare var process: any; -export interface ISocket { +export interface ISocket extends IDisposable { onData(listener: (e: VSBuffer) => void): IDisposable; onClose(listener: () => void): IDisposable; onEnd(listener: () => void): IDisposable; write(buffer: VSBuffer): void; end(): void; - dispose(): void; } let emptyBuffer: VSBuffer | null = null; @@ -407,7 +406,7 @@ export class Client extends IPCClient { /** * Will ensure no messages are lost if there are no event listeners. */ -function createBufferedEvent(source: Event): Event { +export function createBufferedEvent(source: Event): Event { let emitter: Emitter; let hasListeners = false; let isDeliveringMessages = false; @@ -514,7 +513,7 @@ class Queue { * Same as Protocol, but will actually track messages and acks. * Moreover, it will ensure no messages are lost if there are no event listeners. */ -export class PersistentProtocol { +export class PersistentProtocol implements IMessagePassingProtocol { private _isReconnecting: boolean; diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index adc9cfbf31..64b9617fe1 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -246,19 +246,29 @@ function deserialize(reader: IReader): any { } } +interface PendingRequest { + request: IRawPromiseRequest | IRawEventListenRequest; + timeoutTimer: any; +} + export class ChannelServer implements IChannelServer, IDisposable { private channels = new Map>(); private activeRequests = new Map(); private protocolListener: IDisposable | null; - constructor(private protocol: IMessagePassingProtocol, private ctx: TContext) { + // Requests might come in for channels which are not yet registered. + // They will timeout after `timeoutDelay`. + private pendingRequests = new Map(); + + constructor(private protocol: IMessagePassingProtocol, private ctx: TContext, private timeoutDelay: number = 1000) { this.protocolListener = this.protocol.onMessage(msg => this.onRawMessage(msg)); this.sendResponse({ type: ResponseType.Initialize }); } registerChannel(channelName: string, channel: IServerChannel): void { this.channels.set(channelName, channel); + this.flushPendingRequests(channelName); } private sendResponse(response: IRawResponse): void { @@ -309,9 +319,12 @@ export class ChannelServer implements IChannelServer; @@ -348,8 +361,10 @@ export class ChannelServer implements IChannelServer implements IChannelServer { + console.error(`Unknown channel: ${request.channelName}`); + + if (request.type === RequestType.Promise) { + this.sendResponse({ + id: request.id, + data: { name: 'Unknown channel', message: `Channel name '${request.channelName}' timed out after ${this.timeoutDelay}ms`, stack: undefined }, + type: ResponseType.PromiseError + }); + } + }, this.timeoutDelay); + + pendingRequests.push({ request, timeoutTimer: timer }); + } + + private flushPendingRequests(channelName: string): void { + const requests = this.pendingRequests.get(channelName); + + if (requests) { + for (const request of requests) { + clearTimeout(request.timeoutTimer); + + switch (request.request.type) { + case RequestType.Promise: this.onPromise(request.request); break; + case RequestType.EventListen: this.onEventListen(request.request); break; + } + } + + this.pendingRequests.delete(channelName); + } + } + public dispose(): void { if (this.protocolListener) { this.protocolListener.dispose(); @@ -464,7 +519,7 @@ export class ChannelClient implements IChannelClient, IDisposable { }; const cancellationTokenListener = cancellationToken.onCancellationRequested(cancel); - disposable = combinedDisposable([toDisposable(cancel), cancellationTokenListener]); + disposable = combinedDisposable(toDisposable(cancel), cancellationTokenListener); this.activeRequests.add(disposable); }); @@ -587,6 +642,7 @@ export interface ClientConnectionEvent { } interface Connection extends Client { + readonly channelServer: ChannelServer; readonly channelClient: ChannelClient; } @@ -625,7 +681,7 @@ export class IPCServer implements IChannelServer, I this.channels.forEach((channel, name) => channelServer.registerChannel(name, channel)); - const connection: Connection = { channelClient, ctx }; + const connection: Connection = { channelServer, channelClient, ctx }; this._connections.add(connection); this._onDidChangeConnections.fire(connection); @@ -661,6 +717,10 @@ export class IPCServer implements IChannelServer, I registerChannel(channelName: string, channel: IServerChannel): void { this.channels.set(channelName, channel); + + this._connections.forEach(connection => { + connection.channelServer.registerChannel(channelName, channel); + }); } dispose(): void { diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 5a9b6f76d4..5a79572f41 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Socket, Server as NetServer, createConnection, createServer } from 'net'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc'; import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ISocket, Protocol, Client } from 'vs/base/parts/ipc/common/ipc.net'; +import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net'; export class NodeSocket implements ISocket { public readonly socket: Socket; @@ -65,6 +65,197 @@ export class NodeSocket implements ISocket { } } +const enum Constants { + MinHeaderByteSize = 2 +} + +const enum ReadState { + PeekHeader = 1, + ReadHeader = 2, + ReadBody = 3, + Fin = 4 +} + +/** + * See https://tools.ietf.org/html/rfc6455#section-5.2 + */ +export class WebSocketNodeSocket extends Disposable implements ISocket { + + public readonly socket: NodeSocket; + private readonly _incomingData: ChunkStream; + private readonly _onData = this._register(new Emitter()); + + private readonly _state = { + state: ReadState.PeekHeader, + readLen: Constants.MinHeaderByteSize, + mask: 0 + }; + + constructor(socket: NodeSocket) { + super(); + this.socket = socket; + this._incomingData = new ChunkStream(); + this._register(this.socket.onData(data => this._acceptChunk(data))); + } + + public dispose(): void { + this.socket.dispose(); + } + + public onData(listener: (e: VSBuffer) => void): IDisposable { + return this._onData.event(listener); + } + + public onClose(listener: () => void): IDisposable { + return this.socket.onClose(listener); + } + + public onEnd(listener: () => void): IDisposable { + return this.socket.onEnd(listener); + } + + public write(buffer: VSBuffer): void { + let headerLen = Constants.MinHeaderByteSize; + if (buffer.byteLength < 126) { + headerLen += 0; + } else if (buffer.byteLength < 2 ** 16) { + headerLen += 2; + } else { + headerLen += 8; + } + const header = VSBuffer.alloc(headerLen); + + header.writeUInt8(0b10000010, 0); + if (buffer.byteLength < 126) { + header.writeUInt8(buffer.byteLength, 1); + } else if (buffer.byteLength < 2 ** 16) { + header.writeUInt8(126, 1); + let offset = 1; + header.writeUInt8((buffer.byteLength >>> 8) & 0b11111111, ++offset); + header.writeUInt8((buffer.byteLength >>> 0) & 0b11111111, ++offset); + } else { + header.writeUInt8(127, 1); + let offset = 1; + header.writeUInt8(0, ++offset); + header.writeUInt8(0, ++offset); + header.writeUInt8(0, ++offset); + header.writeUInt8(0, ++offset); + header.writeUInt8((buffer.byteLength >>> 24) & 0b11111111, ++offset); + header.writeUInt8((buffer.byteLength >>> 16) & 0b11111111, ++offset); + header.writeUInt8((buffer.byteLength >>> 8) & 0b11111111, ++offset); + header.writeUInt8((buffer.byteLength >>> 0) & 0b11111111, ++offset); + } + + this.socket.write(VSBuffer.concat([header, buffer])); + } + + public end(): void { + this.socket.end(); + } + + private _acceptChunk(data: VSBuffer): void { + if (data.byteLength === 0) { + return; + } + + this._incomingData.acceptChunk(data); + + while (this._incomingData.byteLength >= this._state.readLen) { + + if (this._state.state === ReadState.PeekHeader) { + // peek to see if we can read the entire header + const peekHeader = this._incomingData.peek(this._state.readLen); + // const firstByte = peekHeader.readUInt8(0); + // const finBit = (firstByte & 0b10000000) >>> 7; + const secondByte = peekHeader.readUInt8(1); + const hasMask = (secondByte & 0b10000000) >>> 7; + const len = (secondByte & 0b01111111); + + this._state.state = ReadState.ReadHeader; + this._state.readLen = Constants.MinHeaderByteSize + (hasMask ? 4 : 0) + (len === 126 ? 2 : 0) + (len === 127 ? 8 : 0); + this._state.mask = 0; + + } else if (this._state.state === ReadState.ReadHeader) { + // read entire header + const header = this._incomingData.read(this._state.readLen); + const secondByte = header.readUInt8(1); + const hasMask = (secondByte & 0b10000000) >>> 7; + let len = (secondByte & 0b01111111); + + let offset = 1; + if (len === 126) { + len = ( + header.readUInt8(++offset) * 2 ** 8 + + header.readUInt8(++offset) + ); + } else if (len === 127) { + len = ( + header.readUInt8(++offset) * 0 + + header.readUInt8(++offset) * 0 + + header.readUInt8(++offset) * 0 + + header.readUInt8(++offset) * 0 + + header.readUInt8(++offset) * 2 ** 24 + + header.readUInt8(++offset) * 2 ** 16 + + header.readUInt8(++offset) * 2 ** 8 + + header.readUInt8(++offset) + ); + } + + let mask = 0; + if (hasMask) { + mask = ( + header.readUInt8(++offset) * 2 ** 24 + + header.readUInt8(++offset) * 2 ** 16 + + header.readUInt8(++offset) * 2 ** 8 + + header.readUInt8(++offset) + ); + } + + this._state.state = ReadState.ReadBody; + this._state.readLen = len; + this._state.mask = mask; + + } else if (this._state.state === ReadState.ReadBody) { + // read body + + const body = this._incomingData.read(this._state.readLen); + unmask(body, this._state.mask); + + this._state.state = ReadState.PeekHeader; + this._state.readLen = Constants.MinHeaderByteSize; + this._state.mask = 0; + + this._onData.fire(body); + } + } + } +} + +function unmask(buffer: VSBuffer, mask: number): void { + if (mask === 0) { + return; + } + let 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 m3 = (mask >>> 24) & 0b11111111; + const m2 = (mask >>> 16) & 0b11111111; + const m1 = (mask >>> 8) & 0b11111111; + if (bytesLeft >= 1) { + buffer.writeUInt8(buffer.readUInt8(offset) ^ m3, offset); + } + if (bytesLeft >= 2) { + buffer.writeUInt8(buffer.readUInt8(offset + 1) ^ m2, offset + 1); + } + if (bytesLeft >= 3) { + buffer.writeUInt8(buffer.readUInt8(offset + 2) ^ m1, offset + 2); + } +} + export function generateRandomPipeName(): string { const randomSuffix = generateUuid(); if (process.platform === 'win32') { diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts index 9df0ea6512..035abdfe77 100644 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts @@ -515,11 +515,27 @@ suite('Quick Open Scorer', () => { let query = 'vscode'; - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1)); + let res = [resourceA, resourceB].sort((r1, r2) => { + return compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => { + if (r1 as any /* TS fail */ === resourceA) { + return -1; + } + + return 1; + }); + }); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1)); + res = [resourceB, resourceA].sort((r1, r2) => { + return compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => { + if (r1 as any /* TS fail */ === resourceB) { + return -1; + } + + return 1; + }); + }); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts new file mode 100644 index 0000000000..9e4106b7d4 --- /dev/null +++ b/src/vs/base/parts/storage/common/storage.ts @@ -0,0 +1,318 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { isUndefinedOrNull } from 'vs/base/common/types'; + +export enum StorageHint { + + // A hint to the storage that the storage + // does not exist on disk yet. This allows + // the storage library to improve startup + // time by not checking the storage for data. + STORAGE_DOES_NOT_EXIST +} + +export interface IStorageOptions { + hint?: StorageHint; +} + +export interface IUpdateRequest { + insert?: Map; + delete?: Set; +} + +export interface IStorageItemsChangeEvent { + items: Map; +} + +export interface IStorageDatabase { + + readonly onDidChangeItemsExternal: Event; + + getItems(): Promise>; + updateItems(request: IUpdateRequest): Promise; + + close(recovery?: () => Map): Promise; +} + +export interface IStorage extends IDisposable { + + readonly items: Map; + readonly size: number; + readonly onDidChangeStorage: Event; + + init(): Promise; + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + + set(key: string, value: string | boolean | number | undefined | null): Promise; + delete(key: string): Promise; + + close(): Promise; +} + +enum StorageState { + None, + Initialized, + Closed +} + +export class Storage extends Disposable implements IStorage { + + private static readonly DEFAULT_FLUSH_DELAY = 100; + + private readonly _onDidChangeStorage: Emitter = this._register(new Emitter()); + readonly onDidChangeStorage: Event = this._onDidChangeStorage.event; + + private state = StorageState.None; + + private cache: Map = new Map(); + + private flushDelayer: ThrottledDelayer; + + private pendingDeletes: Set = new Set(); + private pendingInserts: Map = new Map(); + + constructor( + protected database: IStorageDatabase, + private options: IStorageOptions = Object.create(null) + ) { + super(); + + this.flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e))); + } + + private onDidChangeItemsExternal(e: IStorageItemsChangeEvent): void { + // items that change external require us to update our + // caches with the values. we just accept the value and + // emit an event if there is a change. + e.items.forEach((value, key) => this.accept(key, value)); + } + + private accept(key: string, value: string): void { + if (this.state === StorageState.Closed) { + return; // Return early if we are already closed + } + + let changed = false; + + // Item got removed, check for deletion + if (isUndefinedOrNull(value)) { + changed = this.cache.delete(key); + } + + // Item got updated, check for change + else { + const currentValue = this.cache.get(key); + if (currentValue !== value) { + this.cache.set(key, value); + changed = true; + } + } + + // Signal to outside listeners + if (changed) { + this._onDidChangeStorage.fire(key); + } + } + + get items(): Map { + return this.cache; + } + + get size(): number { + return this.cache.size; + } + + async init(): Promise { + if (this.state !== StorageState.None) { + return Promise.resolve(); // either closed or already initialized + } + + this.state = StorageState.Initialized; + + if (this.options.hint === StorageHint.STORAGE_DOES_NOT_EXIST) { + // return early if we know the storage file does not exist. this is a performance + // optimization to not load all items of the underlying storage if we know that + // there can be no items because the storage does not exist. + return Promise.resolve(); + } + + this.cache = await this.database.getItems(); + } + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { + const value = this.cache.get(key); + + if (isUndefinedOrNull(value)) { + return fallbackValue; + } + + return value; + } + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { + const value = this.get(key); + + if (isUndefinedOrNull(value)) { + return fallbackValue; + } + + return value === 'true'; + } + + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue?: number): number | undefined { + const value = this.get(key); + + if (isUndefinedOrNull(value)) { + return fallbackValue; + } + + return parseInt(value, 10); + } + + set(key: string, value: string | boolean | number | null | undefined): Promise { + if (this.state === StorageState.Closed) { + return Promise.resolve(); // Return early if we are already closed + } + + // We remove the key for undefined/null values + if (isUndefinedOrNull(value)) { + return this.delete(key); + } + + // Otherwise, convert to String and store + const valueStr = String(value); + + // Return early if value already set + const currentValue = this.cache.get(key); + if (currentValue === valueStr) { + return Promise.resolve(); + } + + // Update in cache and pending + this.cache.set(key, valueStr); + this.pendingInserts.set(key, valueStr); + this.pendingDeletes.delete(key); + + // Event + this._onDidChangeStorage.fire(key); + + // Accumulate work by scheduling after timeout + return this.flushDelayer.trigger(() => this.flushPending()); + } + + delete(key: string): Promise { + if (this.state === StorageState.Closed) { + return Promise.resolve(); // Return early if we are already closed + } + + // Remove from cache and add to pending + const wasDeleted = this.cache.delete(key); + if (!wasDeleted) { + return Promise.resolve(); // Return early if value already deleted + } + + if (!this.pendingDeletes.has(key)) { + this.pendingDeletes.add(key); + } + + this.pendingInserts.delete(key); + + // Event + this._onDidChangeStorage.fire(key); + + // Accumulate work by scheduling after timeout + return this.flushDelayer.trigger(() => this.flushPending()); + } + + async close(): Promise { + if (this.state === StorageState.Closed) { + return Promise.resolve(); // return if already closed + } + + // Update state + this.state = StorageState.Closed; + + // Trigger new flush to ensure data is persisted and then close + // even if there is an error flushing. We must always ensure + // the DB is closed to avoid corruption. + // + // Recovery: we pass our cache over as recovery option in case + // the DB is not healthy. + try { + await this.flushDelayer.trigger(() => this.flushPending(), 0 /* as soon as possible */); + } catch (error) { + // Ignore + } + + await this.database.close(() => this.cache); + } + + private flushPending(): Promise { + if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { + return Promise.resolve(); // return early if nothing to do + } + + // Get pending data + const updateRequest: IUpdateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes }; + + // Reset pending data for next run + this.pendingDeletes = new Set(); + this.pendingInserts = new Map(); + + // Update in storage + return this.database.updateItems(updateRequest); + } +} + +export class InMemoryStorageDatabase implements IStorageDatabase { + + readonly onDidChangeItemsExternal = Event.None; + + private items = new Map(); + + getItems(): Promise> { + return Promise.resolve(this.items); + } + + updateItems(request: IUpdateRequest): Promise { + if (request.insert) { + request.insert.forEach((value, key) => this.items.set(key, value)); + } + + if (request.delete) { + request.delete.forEach(key => this.items.delete(key)); + } + + return Promise.resolve(); + } + + close(): Promise { + return Promise.resolve(); + } +} \ No newline at end of file diff --git a/src/vs/base/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts similarity index 61% rename from src/vs/base/node/storage.ts rename to src/vs/base/parts/storage/node/storage.ts index 9ea376abbc..652f898f19 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -4,305 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Database, Statement } from 'vscode-sqlite3'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { Emitter, Event } from 'vs/base/common/event'; -import { ThrottledDelayer, timeout } from 'vs/base/common/async'; -import { isUndefinedOrNull } from 'vs/base/common/types'; +import { Event } from 'vs/base/common/event'; +import { timeout } from 'vs/base/common/async'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'vs/base/common/path'; import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; import { fill } from 'vs/base/common/arrays'; - -export enum StorageHint { - - // A hint to the storage that the storage - // does not exist on disk yet. This allows - // the storage library to improve startup - // time by not checking the storage for data. - STORAGE_DOES_NOT_EXIST -} - -export interface IStorageOptions { - hint?: StorageHint; -} - -export interface IUpdateRequest { - insert?: Map; - delete?: Set; -} - -export interface IStorageItemsChangeEvent { - items: Map; -} - -export interface IStorageDatabase { - - readonly onDidChangeItemsExternal: Event; - - getItems(): Promise>; - updateItems(request: IUpdateRequest): Promise; - - close(recovery?: () => Map): Promise; - - checkIntegrity(full: boolean): Promise; -} - -export interface IStorage extends IDisposable { - - readonly items: Map; - readonly size: number; - readonly onDidChangeStorage: Event; - - init(): Promise; - - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - - set(key: string, value: string | boolean | number | undefined | null): Promise; - delete(key: string): Promise; - - close(): Promise; - - checkIntegrity(full: boolean): Promise; -} - -enum StorageState { - None, - Initialized, - Closed -} - -export class Storage extends Disposable implements IStorage { - _serviceBrand: any; - - private static readonly DEFAULT_FLUSH_DELAY = 100; - - private readonly _onDidChangeStorage: Emitter = this._register(new Emitter()); - get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } - - private state = StorageState.None; - - private cache: Map = new Map(); - - private flushDelayer: ThrottledDelayer; - - private pendingDeletes: Set = new Set(); - private pendingInserts: Map = new Map(); - - constructor( - protected database: IStorageDatabase, - private options: IStorageOptions = Object.create(null) - ) { - super(); - - this.flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); - - this.registerListeners(); - } - - private registerListeners(): void { - this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e))); - } - - private onDidChangeItemsExternal(e: IStorageItemsChangeEvent): void { - // items that change external require us to update our - // caches with the values. we just accept the value and - // emit an event if there is a change. - e.items.forEach((value, key) => this.accept(key, value)); - } - - private accept(key: string, value: string): void { - if (this.state === StorageState.Closed) { - return; // Return early if we are already closed - } - - let changed = false; - - // Item got removed, check for deletion - if (isUndefinedOrNull(value)) { - changed = this.cache.delete(key); - } - - // Item got updated, check for change - else { - const currentValue = this.cache.get(key); - if (currentValue !== value) { - this.cache.set(key, value); - changed = true; - } - } - - // Signal to outside listeners - if (changed) { - this._onDidChangeStorage.fire(key); - } - } - - get items(): Map { - return this.cache; - } - - get size(): number { - return this.cache.size; - } - - async init(): Promise { - if (this.state !== StorageState.None) { - return Promise.resolve(); // either closed or already initialized - } - - this.state = StorageState.Initialized; - - if (this.options.hint === StorageHint.STORAGE_DOES_NOT_EXIST) { - // return early if we know the storage file does not exist. this is a performance - // optimization to not load all items of the underlying storage if we know that - // there can be no items because the storage does not exist. - return Promise.resolve(); - } - - this.cache = await this.database.getItems(); - } - - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - get(key: string, fallbackValue?: string): string | undefined { - const value = this.cache.get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return value; - } - - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { - const value = this.get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return value === 'true'; - } - - getNumber(key: string, fallbackValue: number): number; - getNumber(key: string, fallbackValue?: number): number | undefined; - getNumber(key: string, fallbackValue?: number): number | undefined { - const value = this.get(key); - - if (isUndefinedOrNull(value)) { - return fallbackValue; - } - - return parseInt(value, 10); - } - - set(key: string, value: string | boolean | number | null | undefined): Promise { - if (this.state === StorageState.Closed) { - return Promise.resolve(); // Return early if we are already closed - } - - // We remove the key for undefined/null values - if (isUndefinedOrNull(value)) { - return this.delete(key); - } - - // Otherwise, convert to String and store - const valueStr = String(value); - - // Return early if value already set - const currentValue = this.cache.get(key); - if (currentValue === valueStr) { - return Promise.resolve(); - } - - // Update in cache and pending - this.cache.set(key, valueStr); - this.pendingInserts.set(key, valueStr); - this.pendingDeletes.delete(key); - - // Event - this._onDidChangeStorage.fire(key); - - // Accumulate work by scheduling after timeout - return this.flushDelayer.trigger(() => this.flushPending()); - } - - delete(key: string): Promise { - if (this.state === StorageState.Closed) { - return Promise.resolve(); // Return early if we are already closed - } - - // Remove from cache and add to pending - const wasDeleted = this.cache.delete(key); - if (!wasDeleted) { - return Promise.resolve(); // Return early if value already deleted - } - - if (!this.pendingDeletes.has(key)) { - this.pendingDeletes.add(key); - } - - this.pendingInserts.delete(key); - - // Event - this._onDidChangeStorage.fire(key); - - // Accumulate work by scheduling after timeout - return this.flushDelayer.trigger(() => this.flushPending()); - } - - async close(): Promise { - if (this.state === StorageState.Closed) { - return Promise.resolve(); // return if already closed - } - - // Update state - this.state = StorageState.Closed; - - // Trigger new flush to ensure data is persisted and then close - // even if there is an error flushing. We must always ensure - // the DB is closed to avoid corruption. - // - // Recovery: we pass our cache over as recovery option in case - // the DB is not healthy. - try { - await this.flushDelayer.trigger(() => this.flushPending(), 0 /* as soon as possible */); - } catch (error) { - // Ignore - } - - await this.database.close(() => this.cache); - } - - private flushPending(): Promise { - if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { - return Promise.resolve(); // return early if nothing to do - } - - // Get pending data - const updateRequest: IUpdateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes }; - - // Reset pending data for next run - this.pendingDeletes = new Set(); - this.pendingInserts = new Map(); - - // Update in storage - return this.database.updateItems(updateRequest); - } - - checkIntegrity(full: boolean): Promise { - return this.database.checkIntegrity(full); - } -} +import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; interface IDatabaseConnection { db: Database; @@ -369,18 +77,6 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } private doUpdateItems(connection: IDatabaseConnection, request: IUpdateRequest): Promise { - let updateCount = 0; - if (request.insert) { - updateCount += request.insert.size; - } - if (request.delete) { - updateCount += request.delete.size; - } - - if (updateCount === 0) { - return Promise.resolve(); - } - if (this.logger.isTracing) { this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } @@ -541,7 +237,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { const connection = await this.whenConnected; const row = await this.get(connection, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check'); - const integrity = full ? row['integrity_check'] : row['quick_check']; + const integrity = full ? (row as any)['integrity_check'] : (row as any)['quick_check']; if (connection.isErroneous) { return `${integrity} (last error: ${connection.lastError})`; @@ -753,34 +449,3 @@ class SQLiteStorageDatabaseLogger { } } } - -export class InMemoryStorageDatabase implements IStorageDatabase { - - readonly onDidChangeItemsExternal = Event.None; - - private items = new Map(); - - getItems(): Promise> { - return Promise.resolve(this.items); - } - - updateItems(request: IUpdateRequest): Promise { - if (request.insert) { - request.insert.forEach((value, key) => this.items.set(key, value)); - } - - if (request.delete) { - request.delete.forEach(key => this.items.delete(key)); - } - - return Promise.resolve(); - } - - close(): Promise { - return Promise.resolve(); - } - - checkIntegrity(full: boolean): Promise { - return Promise.resolve('ok'); - } -} \ No newline at end of file diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts similarity index 99% rename from src/vs/base/test/node/storage/storage.test.ts rename to src/vs/base/parts/storage/test/node/storage.test.ts index ec7b8e6a4f..ebf287f0b5 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -3,7 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Storage, SQLiteStorageDatabase, IStorageDatabase, ISQLiteStorageDatabaseOptions, IStorageItemsChangeEvent } from 'vs/base/node/storage'; +import { SQLiteStorageDatabase, ISQLiteStorageDatabaseOptions } from 'vs/base/parts/storage/node/storage'; +import { Storage, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; diff --git a/src/vs/base/parts/tree/browser/CollapseAll.svg b/src/vs/base/parts/tree/browser/CollapseAll.svg deleted file mode 100644 index 7d11a30f6e..0000000000 --- a/src/vs/base/parts/tree/browser/CollapseAll.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/CollapseAll_inverse.svg b/src/vs/base/parts/tree/browser/CollapseAll_inverse.svg deleted file mode 100644 index 46a65fff71..0000000000 --- a/src/vs/base/parts/tree/browser/CollapseAll_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/collapse-all-dark.svg b/src/vs/base/parts/tree/browser/collapse-all-dark.svg new file mode 100644 index 0000000000..4862c55dbe --- /dev/null +++ b/src/vs/base/parts/tree/browser/collapse-all-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/base/parts/tree/browser/collapse-all-hc.svg b/src/vs/base/parts/tree/browser/collapse-all-hc.svg new file mode 100644 index 0000000000..05f920b29b --- /dev/null +++ b/src/vs/base/parts/tree/browser/collapse-all-hc.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/base/parts/tree/browser/collapse-all-light.svg b/src/vs/base/parts/tree/browser/collapse-all-light.svg new file mode 100644 index 0000000000..6359b42e62 --- /dev/null +++ b/src/vs/base/parts/tree/browser/collapse-all-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/base/parts/tree/browser/collapsed-dark.svg b/src/vs/base/parts/tree/browser/collapsed-dark.svg deleted file mode 100644 index cf5c3641aa..0000000000 --- a/src/vs/base/parts/tree/browser/collapsed-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/collapsed-hc.svg b/src/vs/base/parts/tree/browser/collapsed-hc.svg deleted file mode 100644 index 145c763338..0000000000 --- a/src/vs/base/parts/tree/browser/collapsed-hc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/collapsed.svg b/src/vs/base/parts/tree/browser/collapsed.svg deleted file mode 100644 index 3a63808c35..0000000000 --- a/src/vs/base/parts/tree/browser/collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/expanded-dark.svg b/src/vs/base/parts/tree/browser/expanded-dark.svg deleted file mode 100644 index 73d41e6399..0000000000 --- a/src/vs/base/parts/tree/browser/expanded-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/expanded-hc.svg b/src/vs/base/parts/tree/browser/expanded-hc.svg deleted file mode 100644 index d38d4abc89..0000000000 --- a/src/vs/base/parts/tree/browser/expanded-hc.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/expanded.svg b/src/vs/base/parts/tree/browser/expanded.svg deleted file mode 100644 index 75f73adbb0..0000000000 --- a/src/vs/base/parts/tree/browser/expanded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/tree-collapsed-dark.svg b/src/vs/base/parts/tree/browser/tree-collapsed-dark.svg new file mode 100644 index 0000000000..c2c2298dd5 --- /dev/null +++ b/src/vs/base/parts/tree/browser/tree-collapsed-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/parts/tree/browser/tree-collapsed-hc.svg b/src/vs/base/parts/tree/browser/tree-collapsed-hc.svg new file mode 100644 index 0000000000..3732cbc04b --- /dev/null +++ b/src/vs/base/parts/tree/browser/tree-collapsed-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/parts/tree/browser/tree-collapsed-light.svg b/src/vs/base/parts/tree/browser/tree-collapsed-light.svg new file mode 100644 index 0000000000..1952ad63f8 --- /dev/null +++ b/src/vs/base/parts/tree/browser/tree-collapsed-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/parts/tree/browser/tree-expanded-dark.svg b/src/vs/base/parts/tree/browser/tree-expanded-dark.svg new file mode 100644 index 0000000000..d844b383ed --- /dev/null +++ b/src/vs/base/parts/tree/browser/tree-expanded-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/parts/tree/browser/tree-expanded-hc.svg b/src/vs/base/parts/tree/browser/tree-expanded-hc.svg new file mode 100644 index 0000000000..9faa2b2a6e --- /dev/null +++ b/src/vs/base/parts/tree/browser/tree-expanded-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/parts/tree/browser/tree-expanded-light.svg b/src/vs/base/parts/tree/browser/tree-expanded-light.svg new file mode 100644 index 0000000000..f09125caf9 --- /dev/null +++ b/src/vs/base/parts/tree/browser/tree-expanded-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/base/parts/tree/browser/tree.css b/src/vs/base/parts/tree/browser/tree.css index 2b70373216..a74ae446e0 100644 --- a/src/vs/base/parts/tree/browser/tree.css +++ b/src/vs/base/parts/tree/browser/tree.css @@ -66,7 +66,7 @@ content: ' '; position: absolute; display: block; - background: url('collapsed.svg') 50% 50% no-repeat; + background: url('tree-collapsed-light.svg') 50% 50% no-repeat; width: 16px; height: 100%; top: 0; @@ -74,17 +74,17 @@ } .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded > .content:before { - background-image: url('expanded.svg'); + background-image: url('tree-expanded-light.svg'); } /* {{SQL CARBON EDIT}} -- Display a high-contrast arrow when an expandable item is selected and expanded */ .monaco-tree.focused .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children.selected.expanded:not(.loading) > .content:before { - background-image: url('expanded-hc.svg'); + background-image: url('tree-expanded-hc.svg'); } /* {{SQL CARBON EDIT}} -- Display a high-contrast arrow when an expandable item is selected and collapsed */ .monaco-tree.focused .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children.selected:not(.loading) > .content:before { - background-image: url('collapsed-hc.svg'); + background-image: url('tree-collapsed-hc.svg'); } .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children.loading > .content:before { @@ -98,11 +98,11 @@ } .vs-dark .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content:before { - background-image: url('collapsed-dark.svg'); + background-image: url('tree-collapsed-dark.svg'); } .vs-dark .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded > .content:before { - background-image: url('expanded-dark.svg'); + background-image: url('tree-expanded-dark.svg'); } .vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children.loading > .content:before { @@ -110,11 +110,11 @@ } .hc-black .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content:before { - background-image: url('collapsed-hc.svg'); + background-image: url('tree-collapsed-hc.svg'); } .hc-black .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded > .content:before { - background-image: url('expanded-hc.svg'); + background-image: url('tree-expanded-hc.svg'); } .hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children.loading > .content:before { @@ -122,10 +122,13 @@ } .monaco-tree-action.collapse-all { - background: url('CollapseAll.svg') center center no-repeat; + background: url('collapse-all-light.svg') center center no-repeat; } -.hc-black .monaco-tree-action.collapse-all, .vs-dark .monaco-tree-action.collapse-all { - background: url('CollapseAll_inverse.svg') center center no-repeat; + background: url('collapse-all-dark.svg') center center no-repeat; +} + +.hc-black .monaco-tree-action.collapse-all { + background: url('collapse-all-hc.svg') center center no-repeat; } diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 14630c8174..e13df1cdcb 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -254,7 +254,7 @@ export interface IDataSource { * * You should not attempt to "move" an element to a different * parent by keeping its ID. The idea here is to have tree location - * related IDs (eg. full file path, in the Explorer example). + * related IDs (e.g. full file path, in the Explorer example). */ getId(tree: ITree, element: any): string; @@ -616,7 +616,7 @@ export interface IActionProvider { hasActions(tree: ITree | null, element: any): boolean; /** - * Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree. + * Returns an array with the actions of the element that should show up in place right to the element in the tree. */ - getActions(tree: ITree | null, element: any): IAction[] | null; + getActions(tree: ITree | null, element: any): ReadonlyArray | null; } diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index 95b4513d13..19f7274239 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -159,7 +159,7 @@ export class ItemRegistry { public register(item: Item): void { Assert.ok(!this.isRegistered(item.id), 'item already registered: ' + item.id); - const disposable = combinedDisposable([ + const disposable = combinedDisposable( this._onDidRevealItem.add(item.onDidReveal), this._onExpandItem.add(item.onExpand), this._onDidExpandItem.add(item.onDidExpand), @@ -171,7 +171,7 @@ export class ItemRegistry { this._onRefreshItemChildren.add(item.onRefreshChildren), this._onDidRefreshItemChildren.add(item.onDidRefreshChildren), this._onDidDisposeItem.add(item.onDidDispose) - ]); + ); this.items[item.id] = { item, disposable }; } diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 4a8f1f4a4a..9c86a4c253 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -447,13 +447,13 @@ export class TreeView extends HeightMap { private onHiddenScrollTop: number | null = null; private readonly _onDOMFocus = new Emitter(); - get onDOMFocus(): Event { return this._onDOMFocus.event; } + readonly onDOMFocus: Event = this._onDOMFocus.event; private readonly _onDOMBlur = new Emitter(); - get onDOMBlur(): Event { return this._onDOMBlur.event; } + readonly onDOMBlur: Event = this._onDOMBlur.event; private readonly _onDidScroll = new Emitter(); - get onDidScroll(): Event { return this._onDidScroll.event; } + readonly onDidScroll: Event = this._onDidScroll.event; constructor(context: _.ITreeContext, container: HTMLElement) { super(); diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 82b6b9ee33..0ae54df942 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -3,23 +3,13 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareFileNames, compareFileExtensions, setFileNameComparer } from 'vs/base/common/comparers'; +import { compareFileNames, compareFileExtensions } from 'vs/base/common/comparers'; import * as assert from 'assert'; -import { IdleValue } from 'vs/base/common/async'; suite('Comparers', () => { test('compareFileNames', () => { - // Setup Intl - setFileNameComparer(new IdleValue(() => { - const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); - return { - collator: collator, - collatorIsNumeric: collator.resolvedOptions().numeric - }; - })); - assert(compareFileNames(null, null) === 0, 'null should be equal'); assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values'); assert(compareFileNames('', '') === 0, 'empty should be equal'); @@ -35,15 +25,6 @@ suite('Comparers', () => { test('compareFileExtensions', () => { - // Setup Intl - setFileNameComparer(new IdleValue(() => { - const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); - return { - collator: collator, - collatorIsNumeric: collator.resolvedOptions().numeric - }; - })); - assert(compareFileExtensions(null, null) === 0, 'null should be equal'); assert(compareFileExtensions(null, '.abc') < 0, 'null should come before real files'); assert(compareFileExtensions(null, 'abc') < 0, 'null should come before real files without extension'); @@ -66,4 +47,4 @@ suite('Comparers', () => { assert(compareFileExtensions('file2.ext2', 'file1.ext10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order'); assert(compareFileExtensions('file.ext01', 'file.ext1') < 0, 'extensions with equal numbers should be in alphabetical order'); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/browser/hash.test.ts b/src/vs/base/test/browser/hash.test.ts deleted file mode 100644 index cff6dbe9b1..0000000000 --- a/src/vs/base/test/browser/hash.test.ts +++ /dev/null @@ -1,16 +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 { createSHA1 } from 'vs/base/browser/hash'; - -suite('Hash', () => { - test('computeSHA1Hash', async () => { - assert.equal(await createSHA1(''), 'da39a3ee5e6b4b0d3255bfef95601890afd80709'); - assert.equal(await createSHA1('hello world'), '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'); - assert.equal(await createSHA1('da39a3ee5e6b4b0d3255bfef95601890afd80709'), '10a34637ad661d98ba3344717656fcc76209c2f8'); - assert.equal(await createSHA1('2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'), 'd6b0d82cea4269b51572b8fab43adcee9fc3cf9a'); - assert.equal(await createSHA1('öäü_?ß()<>ÖÄÜ'), 'b64beaeff9e317b0193c8e40a2431b210388eba9'); - }); -}); \ No newline at end of file diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/htmlContent.test.ts index 8febe04dff..707e1bd5e2 100644 --- a/src/vs/base/test/browser/htmlContent.test.ts +++ b/src/vs/base/test/browser/htmlContent.test.ts @@ -5,8 +5,19 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; import { renderMarkdown, renderText, renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('HtmlContent', () => { + const store = new DisposableStore(); + + setup(() => { + store.clear(); + }); + + teardown(() => { + store.clear(); + }); + test('render simple element', () => { let result: HTMLElement = renderText('testing'); @@ -55,7 +66,7 @@ suite('HtmlContent', () => { assert.strictEqual(content, '0'); callbackCalled = true; }, - disposeables: [] + disposeables: store } }); assert.strictEqual(result.innerHTML, '
action'); @@ -74,7 +85,7 @@ suite('HtmlContent', () => { assert.strictEqual(content, '0'); callbackCalled = true; }, - disposeables: [] + disposeables: store } }); assert.strictEqual(result.innerHTML, 'action'); diff --git a/src/vs/base/test/browser/ui/grid/gridview.test.ts b/src/vs/base/test/browser/ui/grid/gridview.test.ts index eec930b383..dfec571300 100644 --- a/src/vs/base/test/browser/ui/grid/gridview.test.ts +++ b/src/vs/base/test/browser/ui/grid/gridview.test.ts @@ -59,8 +59,8 @@ suite('Gridview', function () { ]; gridview.addView(views[0] as IView, 200, [0]); - gridview.addView(views[1][0] as IView, 200, [1]); - gridview.addView(views[1][1] as IView, 200, [1, 1]); + gridview.addView((views[1] as TestView[])[0] as IView, 200, [1]); + gridview.addView((views[1] as TestView[])[1] as IView, 200, [1, 1]); assert.deepEqual(nodesToArrays(gridview.getViews()), views); 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 1212895924..97bbcc921c 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -326,4 +326,57 @@ suite('AsyncDataTree', function () { assert(!hasClass(twistie, 'collapsed')); assert(tree.getNode(_('a')).collapsed); }); + + test('support default collapse state per element', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const getChildrenCalls: string[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + getChildrenCalls.push(element.id); + return Promise.resolve(element.children || []); + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }] + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { + collapseByDefault: el => el.id !== 'a' + }); + tree.layout(200); + + await tree.setInput(root); + assert(!tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a']); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/decorators.test.ts b/src/vs/base/test/common/decorators.test.ts index d05fb50866..1f93c2f7dd 100644 --- a/src/vs/base/test/common/decorators.test.ts +++ b/src/vs/base/test/common/decorators.test.ts @@ -119,7 +119,7 @@ suite('Decorators', () => { assert.equal(foo.answer, 42); try { - foo['$memoize$answer'] = 1337; + (foo as any)['$memoize$answer'] = 1337; assert(false); } catch (e) { assert.equal(foo.answer, 42); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 3c40821802..91e0ca7c60 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { Event, Emitter, EventBufferer, EventMultiplexer, AsyncEmitter, IWaitUntil, PauseableEmitter } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; @@ -73,6 +73,27 @@ suite('Event', function () { while (bucket.length) { bucket.pop()!.dispose(); } + doc.setText('boo'); + + // noop + subscription.dispose(); + + doc.setText('boo'); + assert.equal(counter.count, 2); + }); + + test('Emitter, store', function () { + + let bucket = new DisposableStore(); + let doc = new Samples.Document3(); + let subscription = doc.onDidChange(counter.onEvent, counter, bucket); + + doc.setText('far'); + doc.setText('boo'); + + // unhook listener + bucket.clear(); + doc.setText('boo'); // noop subscription.dispose(); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 15c8081f92..33cff513b1 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -13,8 +13,8 @@ function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, hig } } -function filterNotOk(filter: IFilter, word: string, suggestion: string) { - assert(!filter(word, suggestion)); +function filterNotOk(filter: IFilter, word: string, wordToMatchAgainst: string) { + assert(!filter(word, wordToMatchAgainst), `${word} matched ${wordToMatchAgainst}`); } suite('Filters', () => { @@ -185,23 +185,23 @@ suite('Filters', () => { assert(matchesWords('Debug Console', 'Open: Debug Console')); filterOk(matchesWords, 'gp', 'Git: Pull', [{ start: 0, end: 1 }, { start: 5, end: 6 }]); - filterOk(matchesWords, 'g p', 'Git: Pull', [{ start: 0, end: 1 }, { start: 4, end: 6 }]); + filterOk(matchesWords, 'g p', 'Git: Pull', [{ start: 0, end: 1 }, { start: 3, end: 4 }, { start: 5, end: 6 }]); filterOk(matchesWords, 'gipu', 'Git: Pull', [{ start: 0, end: 2 }, { start: 5, end: 7 }]); filterOk(matchesWords, 'gp', 'Category: Git: Pull', [{ start: 10, end: 11 }, { start: 15, end: 16 }]); - filterOk(matchesWords, 'g p', 'Category: Git: Pull', [{ start: 10, end: 11 }, { start: 14, end: 16 }]); + filterOk(matchesWords, 'g p', 'Category: Git: Pull', [{ start: 10, end: 11 }, { start: 13, end: 14 }, { start: 15, end: 16 }]); filterOk(matchesWords, 'gipu', 'Category: Git: Pull', [{ start: 10, end: 12 }, { start: 15, end: 17 }]); filterNotOk(matchesWords, 'it', 'Git: Pull'); filterNotOk(matchesWords, 'll', 'Git: Pull'); filterOk(matchesWords, 'git: プル', 'git: プル', [{ start: 0, end: 7 }]); - filterOk(matchesWords, 'git プル', 'git: プル', [{ start: 0, end: 3 }, { start: 4, end: 7 }]); + filterOk(matchesWords, 'git プル', 'git: プル', [{ start: 0, end: 4 }, { start: 5, end: 7 }]); filterOk(matchesWords, 'öäk', 'Öhm: Älles Klar', [{ start: 0, end: 1 }, { start: 5, end: 6 }, { start: 11, end: 12 }]); - assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null); - assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]); + // assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null); + // assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]); filterOk(matchesWords, 'bar', 'foo-bar'); filterOk(matchesWords, 'bar test', 'foo-bar test'); @@ -212,7 +212,12 @@ suite('Filters', () => { filterNotOk(matchesWords, 'bar est', 'foo-bar test'); filterNotOk(matchesWords, 'fo ar', 'foo-bar test'); filterNotOk(matchesWords, 'for', 'foo-bar test'); - filterNotOk(matchesWords, 'foo bar', 'foo-bar'); + + filterOk(matchesWords, 'foo bar', 'foo-bar'); + filterOk(matchesWords, 'foo bar', '123 foo-bar 456'); + filterOk(matchesWords, 'foo+bar', 'foo-bar'); + filterOk(matchesWords, 'foo-bar', 'foo bar'); + filterOk(matchesWords, 'foo:bar', 'foo:bar'); }); function assertMatches(pattern: string, word: string, decoratedWord: string | undefined, filter: FuzzyScorer, opts: { patternPos?: number, wordPos?: number, firstMatchCanBeWeak?: boolean } = {}) { diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index c083c3bdb8..b713c5912a 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -42,7 +42,8 @@ suite('Lifecycle', () => { assert(!disposable.isDisposed); assert(!disposable2.isDisposed); - dispose(disposable, disposable2); + dispose(disposable); + dispose(disposable2); assert(disposable.isDisposed); assert(disposable2.isDisposed); diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index dc41e96bb1..2c51b9226f 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -4,42 +4,43 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime'; +import { URI } from 'vs/base/common/uri'; suite('Mime', () => { test('Dynamically Register Text Mime', () => { - let guess = guessMimeTypes('foo.monaco'); + let guess = guessMimeTypes(URI.file('foo.monaco')); assert.deepEqual(guess, ['application/unknown']); registerTextMime({ id: 'monaco', extension: '.monaco', mime: 'text/monaco' }); - guess = guessMimeTypes('foo.monaco'); + guess = guessMimeTypes(URI.file('foo.monaco')); assert.deepEqual(guess, ['text/monaco', 'text/plain']); - guess = guessMimeTypes('.monaco'); + guess = guessMimeTypes(URI.file('.monaco')); assert.deepEqual(guess, ['text/monaco', 'text/plain']); registerTextMime({ id: 'codefile', filename: 'Codefile', mime: 'text/code' }); - guess = guessMimeTypes('Codefile'); + guess = guessMimeTypes(URI.file('Codefile')); assert.deepEqual(guess, ['text/code', 'text/plain']); - guess = guessMimeTypes('foo.Codefile'); + guess = guessMimeTypes(URI.file('foo.Codefile')); assert.deepEqual(guess, ['application/unknown']); registerTextMime({ id: 'docker', filepattern: 'Docker*', mime: 'text/docker' }); - guess = guessMimeTypes('Docker-debug'); + guess = guessMimeTypes(URI.file('Docker-debug')); assert.deepEqual(guess, ['text/docker', 'text/plain']); - guess = guessMimeTypes('docker-PROD'); + guess = guessMimeTypes(URI.file('docker-PROD')); assert.deepEqual(guess, ['text/docker', 'text/plain']); registerTextMime({ id: 'niceregex', mime: 'text/nice-regex', firstline: /RegexesAreNice/ }); - guess = guessMimeTypes('Randomfile.noregistration', 'RegexesAreNice'); + guess = guessMimeTypes(URI.file('Randomfile.noregistration'), 'RegexesAreNice'); assert.deepEqual(guess, ['text/nice-regex', 'text/plain']); - guess = guessMimeTypes('Randomfile.noregistration', 'RegexesAreNotNice'); + guess = guessMimeTypes(URI.file('Randomfile.noregistration'), 'RegexesAreNotNice'); assert.deepEqual(guess, ['application/unknown']); - guess = guessMimeTypes('Codefile', 'RegexesAreNice'); + guess = guessMimeTypes(URI.file('Codefile'), 'RegexesAreNice'); assert.deepEqual(guess, ['text/code', 'text/plain']); }); @@ -47,20 +48,20 @@ suite('Mime', () => { registerTextMime({ id: 'monaco', extension: '.monaco', mime: 'text/monaco' }); registerTextMime({ id: 'foobar', mime: 'text/foobar', firstline: /foobar/ }); - let guess = guessMimeTypes('foo.monaco'); + let guess = guessMimeTypes(URI.file('foo.monaco')); assert.deepEqual(guess, ['text/monaco', 'text/plain']); - guess = guessMimeTypes('foo.monaco', 'foobar'); + guess = guessMimeTypes(URI.file('foo.monaco'), 'foobar'); assert.deepEqual(guess, ['text/monaco', 'text/plain']); registerTextMime({ id: 'docker', filename: 'dockerfile', mime: 'text/winner' }); registerTextMime({ id: 'docker', filepattern: 'dockerfile*', mime: 'text/looser' }); - guess = guessMimeTypes('dockerfile'); + guess = guessMimeTypes(URI.file('dockerfile')); assert.deepEqual(guess, ['text/winner', 'text/plain']); registerTextMime({ id: 'azure-looser', mime: 'text/azure-looser', firstline: /azure/ }); registerTextMime({ id: 'azure-winner', mime: 'text/azure-winner', firstline: /azure/ }); - guess = guessMimeTypes('azure', 'azure'); + guess = guessMimeTypes(URI.file('azure'), 'azure'); assert.deepEqual(guess, ['text/azure-winner', 'text/plain']); }); @@ -68,16 +69,16 @@ suite('Mime', () => { registerTextMime({ id: 'monaco2', extension: '.monaco2', mime: 'text/monaco2' }); registerTextMime({ id: 'monaco2', filename: 'specific.monaco2', mime: 'text/specific-monaco2' }); - assert.deepEqual(guessMimeTypes('specific.monaco2'), ['text/specific-monaco2', 'text/plain']); - assert.deepEqual(guessMimeTypes('foo.monaco2'), ['text/monaco2', 'text/plain']); + assert.deepEqual(guessMimeTypes(URI.file('specific.monaco2')), ['text/specific-monaco2', 'text/plain']); + assert.deepEqual(guessMimeTypes(URI.file('foo.monaco2')), ['text/monaco2', 'text/plain']); }); test('Specificity priority 2', () => { registerTextMime({ id: 'monaco3', filename: 'specific.monaco3', mime: 'text/specific-monaco3' }); registerTextMime({ id: 'monaco3', extension: '.monaco3', mime: 'text/monaco3' }); - assert.deepEqual(guessMimeTypes('specific.monaco3'), ['text/specific-monaco3', 'text/plain']); - assert.deepEqual(guessMimeTypes('foo.monaco3'), ['text/monaco3', 'text/plain']); + assert.deepEqual(guessMimeTypes(URI.file('specific.monaco3')), ['text/specific-monaco3', 'text/plain']); + assert.deepEqual(guessMimeTypes(URI.file('foo.monaco3')), ['text/monaco3', 'text/plain']); }); test('Mimes Priority - Longest Extension wins', () => { @@ -85,13 +86,13 @@ suite('Mime', () => { registerTextMime({ id: 'monaco', extension: '.monaco.xml', mime: 'text/monaco-xml' }); registerTextMime({ id: 'monaco', extension: '.monaco.xml.build', mime: 'text/monaco-xml-build' }); - let guess = guessMimeTypes('foo.monaco'); + let guess = guessMimeTypes(URI.file('foo.monaco')); assert.deepEqual(guess, ['text/monaco', 'text/plain']); - guess = guessMimeTypes('foo.monaco.xml'); + guess = guessMimeTypes(URI.file('foo.monaco.xml')); assert.deepEqual(guess, ['text/monaco-xml', 'text/plain']); - guess = guessMimeTypes('foo.monaco.xml.build'); + guess = guessMimeTypes(URI.file('foo.monaco.xml.build')); assert.deepEqual(guess, ['text/monaco-xml-build', 'text/plain']); }); @@ -99,7 +100,7 @@ suite('Mime', () => { registerTextMime({ id: 'monaco', extension: '.monaco.xnl', mime: 'text/monaco', userConfigured: true }); registerTextMime({ id: 'monaco', extension: '.monaco.xml', mime: 'text/monaco-xml' }); - let guess = guessMimeTypes('foo.monaco.xnl'); + let guess = guessMimeTypes(URI.file('foo.monaco.xnl')); assert.deepEqual(guess, ['text/monaco', 'text/plain']); }); @@ -107,7 +108,7 @@ suite('Mime', () => { registerTextMime({ id: 'monaco', filepattern: '**/dot.monaco.xml', mime: 'text/monaco' }); registerTextMime({ id: 'other', filepattern: '*ot.other.xml', mime: 'text/other' }); - let guess = guessMimeTypes('/some/path/dot.monaco.xml'); + let guess = guessMimeTypes(URI.file('/some/path/dot.monaco.xml')); assert.deepEqual(guess, ['text/monaco', 'text/plain']); }); @@ -115,10 +116,16 @@ suite('Mime', () => { registerTextMime({ id: 'monaco', filepattern: '**/dot.monaco.xml', mime: 'text/monaco' }); registerTextMime({ id: 'other', filepattern: '**/dot.monaco.xml', mime: 'text/other' }); - let guess = guessMimeTypes('/some/path/dot.monaco.xml'); + let guess = guessMimeTypes(URI.file('/some/path/dot.monaco.xml')); assert.deepEqual(guess, ['text/other', 'text/plain']); }); + test('Data URIs', () => { + registerTextMime({ id: 'data', extension: '.data', mime: 'text/data' }); + + assert.deepEqual(guessMimeTypes(URI.parse(`data:;label:something.data;description:data,`)), ['text/data', 'text/plain']); + }); + test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => { const id = 'plumbus0'; const mime = `text/${id}`; diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 8a4150feed..9bfebd9a6e 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -237,8 +237,8 @@ suite('Resources', () => { } } - function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean) { - assert.equal(relativePath(u1, u2), expectedPath, `from ${u1.toString()} to ${u2.toString()}`); + function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean, ignoreCase?: boolean) { + assert.equal(relativePath(u1, u2, ignoreCase), expectedPath, `from ${u1.toString()} to ${u2.toString()}`); if (expectedPath !== undefined && !ignoreJoin) { assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal'); } @@ -263,6 +263,11 @@ suite('Resources', () => { assertRelativePath(URI.parse('foo://a2/b'), URI.parse('foo://a/b'), undefined); assertRelativePath(URI.parse('goo://a/b'), URI.parse('foo://a/b'), undefined); + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://A/FOO/bar/goo'), 'bar/goo', false, true); + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://A/FOO/BAR/GOO'), 'BAR/GOO', false, true); + assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://A/FOO/BAR/GOO'), '../BAR/GOO', false, true); + assertRelativePath(URI.parse('foo:///c:/a/foo'), URI.parse('foo:///C:/a/foo/xoo/'), 'xoo', false, true); + if (isWindows) { assertRelativePath(URI.file('c:\\foo\\bar'), URI.file('c:\\foo\\bar'), ''); assertRelativePath(URI.file('c:\\foo\\bar\\huu'), URI.file('c:\\foo\\bar'), '..'); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 4e9f685ecd..0f09e612a8 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -426,6 +426,19 @@ suite('URI', () => { assert.equal(uri.toString(true), input); }); + test('Unable to open \'%A0.txt\': URI malformed #76506', function () { + + let uri = URI.file('/foo/%A0.txt'); + let uri2 = URI.parse(uri.toString()); + assert.equal(uri.scheme, uri2.scheme); + assert.equal(uri.path, uri2.path); + + uri = URI.file('/foo/%2e.txt'); + uri2 = URI.parse(uri.toString()); + assert.equal(uri.scheme, uri2.scheme); + assert.equal(uri.path, uri2.path); + }); + test('URI - (de)serialize', function () { const values = [ diff --git a/src/vs/base/test/common/buffer.test.ts b/src/vs/base/test/node/buffer.test.ts similarity index 90% rename from src/vs/base/test/common/buffer.test.ts rename to src/vs/base/test/node/buffer.test.ts index 4b1d607fbc..2cc4c5ee93 100644 --- a/src/vs/base/test/common/buffer.test.ts +++ b/src/vs/base/test/node/buffer.test.ts @@ -364,4 +364,42 @@ suite('Buffer', () => { assert.equal(ended, false); assert.equal(errors.length, 0); }); + + test('Performance issue with VSBuffer#slice #76076', function () { + // Buffer#slice creates a view + { + const buff = Buffer.from([10, 20, 30, 40]); + const b2 = buff.slice(1, 3); + assert.equal(buff[1], 20); + assert.equal(b2[0], 20); + + buff[1] = 17; // modify buff AND b2 + assert.equal(buff[1], 17); + assert.equal(b2[0], 17); + } + + // TypedArray#slice creates a copy + { + const unit = new Uint8Array([10, 20, 30, 40]); + const u2 = unit.slice(1, 3); + assert.equal(unit[1], 20); + assert.equal(u2[0], 20); + + unit[1] = 17; // modify unit, NOT b2 + assert.equal(unit[1], 17); + assert.equal(u2[0], 20); + } + + // TypedArray#subarray creates a view + { + const unit = new Uint8Array([10, 20, 30, 40]); + const u2 = unit.subarray(1, 3); + assert.equal(unit[1], 20); + assert.equal(u2[0], 20); + + unit[1] = 17; // modify unit AND b2 + assert.equal(unit[1], 17); + assert.equal(u2[0], 17); + } + }); }); diff --git a/src/vs/base/test/node/id.test.ts b/src/vs/base/test/node/id.test.ts index 6bc2b1896f..df2e904aef 100644 --- a/src/vs/base/test/node/id.test.ts +++ b/src/vs/base/test/node/id.test.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as getmac from 'getmac'; import { getMachineId } from 'vs/base/node/id'; +import { getMac } from 'vs/base/node/macAddress'; suite('ID', () => { @@ -16,9 +16,7 @@ suite('ID', () => { }); test('getMac', () => { - return new Promise((resolve, reject) => { - getmac.getMac((err, macAddress) => err ? reject(err) : resolve(macAddress)); - }).then(macAddress => { + return getMac().then(macAddress => { assert.ok(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/.test(macAddress), `Expected a MAC address, got: ${macAddress}`); }); }); diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index a027989e39..ab9bf356b7 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -16,6 +16,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { canNormalize } from 'vs/base/common/normalization'; import { VSBuffer } from 'vs/base/common/buffer'; +import { join } from 'path'; const chunkSize = 64 * 1024; const readError = 'Error while reading'; @@ -386,6 +387,31 @@ suite('PFS', () => { } }); + test('readdirWithFileTypes', async () => { + if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const testDir = join(parentDir, 'pfs', id); + + const newDir = path.join(testDir, 'öäü'); + await pfs.mkdirp(newDir, 493); + + await pfs.writeFile(join(testDir, 'somefile.txt'), 'contents'); + + assert.ok(fs.existsSync(newDir)); + + const children = await pfs.readdirWithFileTypes(testDir); + + assert.equal(children.some(n => n.name === 'öäü'), true); // Mac always converts to NFD, so + assert.equal(children.some(n => n.isDirectory()), true); + + assert.equal(children.some(n => n.name === 'somefile.txt'), true); + assert.equal(children.some(n => n.isFile()), true); + + await pfs.rimraf(parentDir); + } + }); + test('writeFile (string)', async () => { const smallData = 'Hello World'; const bigData = (new Array(100 * 1024)).join('Large String\n'); diff --git a/src/vs/base/test/node/stream/fixtures/file.css b/src/vs/base/test/node/stream/fixtures/file.css deleted file mode 100644 index b7e5283202..0000000000 --- a/src/vs/base/test/node/stream/fixtures/file.css +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/*---------------------------------------------------------- -The base color for this template is #5c87b2. If you'd like -to use a different color start by replacing all instances of -#5c87b2 with your new color. -----------------------------------------------------------*/ -body -{ - background-color: #5c87b2; - font-size: .75em; - font-family: Segoe UI, Verdana, Helvetica, Sans-Serif; - margin: 8px; - padding: 0; - color: #696969; -} - -h1, h2, h3, h4, h5, h6 -{ - color: #000; - font-size: 40px; - margin: 0px; -} - -textarea -{ - font-family: Consolas -} - -#results -{ - margin-top: 2em; - margin-left: 2em; - color: black; - font-size: medium; -} - diff --git a/src/vs/base/test/node/stream/stream.test.ts b/src/vs/base/test/node/stream/stream.test.ts deleted file mode 100644 index b9e2d13a40..0000000000 --- a/src/vs/base/test/node/stream/stream.test.ts +++ /dev/null @@ -1,27 +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 stream from 'vs/base/node/stream'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; - -suite('Stream', () => { - test('readToMatchingString - ANSI', async () => { - const file = getPathFromAmdModule(require, './fixtures/file.css'); - - const result = await stream.readToMatchingString(file, '\n', 10, 100); - - // \r may be present on Windows - assert.equal(result!.replace('\r', ''), '/*---------------------------------------------------------------------------------------------'); - }); - - test('readToMatchingString - empty', async () => { - const file = getPathFromAmdModule(require, './fixtures/empty.txt'); - - const result = await stream.readToMatchingString(file, '\n', 10, 100); - assert.equal(result, null); - }); -}); diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts index 00f0fb0d9d..76e6ff3f82 100644 --- a/src/vs/base/test/node/utils.ts +++ b/src/vs/base/test/node/utils.ts @@ -16,8 +16,8 @@ export interface ITestFileResult { export function testFile(folder: string, file: string): Promise { const id = generateUuid(); const parentDir = join(tmpdir(), 'vsctests', id); - const newDir = join(parentDir, 'config', id); - const testFile = join(newDir, 'config.json'); + const newDir = join(parentDir, folder, id); + const testFile = join(newDir, file); return mkdirp(newDir, 493).then(() => { return { diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 92e4cba15f..1e74244704 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -4,17 +4,31 @@ + + - + + + + + + + + + + + + + + - + + - - + + diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js index f91b531007..a59f43e1e0 100644 --- a/src/vs/code/browser/workbench/workbench.js +++ b/src/vs/code/browser/workbench/workbench.js @@ -3,38 +3,24 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -//@ts-check 'use strict'; (function () { - function loadScript(path, callback) { - let script = document.createElement('script'); - script.onload = callback; - script.async = true; - script.type = 'text/javascript'; - script.src = path; - document.head.appendChild(script); - } + require.config({ + baseUrl: `${window.location.origin}/out`, + paths: { + 'vscode-textmate': `${window.location.origin}/node_modules/vscode-textmate/release/main`, + 'onigasm-umd': `${window.location.origin}/node_modules/onigasm-umd/release/main`, + 'xterm': `${window.location.origin}/node_modules/xterm/lib/xterm.js`, + 'xterm-addon-search': `${window.location.origin}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, + 'xterm-addon-web-links': `${window.location.origin}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, + } + }); - loadScript('./out/vs/loader.js', function () { + require(['vs/workbench/workbench.web.api'], function (api) { + const options = JSON.parse(document.getElementById('vscode-workbench-web-configuration').getAttribute('data-settings')); - // @ts-ignore - require.config({ - baseUrl: `${window.location.origin}/out` - }); - - // @ts-ignore - require([ - 'vs/workbench/workbench.web.main', - 'vs/nls!vs/workbench/workbench.web.main', - 'vs/css!vs/workbench/workbench.web.main' - ], - // @ts-ignore - function () { - - // @ts-ignore - require('vs/workbench/browser/web.main').main().then(undefined, console.error); - }); + api.create(document.body, options); }); })(); \ No newline at end of file diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index b982aadc0b..54ecf43cdf 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -33,14 +33,14 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; import { Button } from 'vs/base/browser/ui/button/button'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; const MAX_URL_LENGTH = 2045; @@ -300,7 +300,7 @@ export class IssueReporter extends Disposable { serviceCollection.set(IWindowsService, new WindowsService(mainProcessService)); this.environmentService = new EnvironmentService(configuration, configuration.execPath); - const logService = createBufferSpdLogService(`issuereporter${configuration.windowId}`, getLogLevel(this.environmentService), this.environmentService.logsPath); + const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService)); const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); this.logService = new FollowerLogService(logLevelClient, logService); @@ -336,7 +336,7 @@ export class IssueReporter extends Disposable { this.render(); }); - ['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'].forEach(elementId => { + (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => { this.addEventListener(elementId, 'click', (event: Event) => { event.stopPropagation(); this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] }); @@ -346,7 +346,7 @@ export class IssueReporter extends Disposable { const showInfoElements = document.getElementsByClassName('showInfo'); for (let i = 0; i < showInfoElements.length; i++) { const showInfo = showInfoElements.item(i); - showInfo!.addEventListener('click', (e) => { + showInfo!.addEventListener('click', (e: MouseEvent) => { e.preventDefault(); const label = (e.target); if (label) { @@ -677,12 +677,14 @@ export class IssueReporter extends Disposable { private logSearchError(error: Error) { this.logService.warn('issueReporter#search ', error.message); - /* __GDPR__ - "issueReporterSearchError" : { - "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('issueReporterSearchError', { message: error.message }); + type IssueReporterSearchErrorClassification = { + message: { classification: 'CallstackOrException', purpose: 'PerformanceAndHealth' } + }; + + type IssueReporterSearchError = { + message: string; + }; + this.telemetryService.publicLog2('issueReporterSearchError', { message: error.message }); } private setUpTypes(): void { @@ -874,13 +876,15 @@ export class IssueReporter extends Disposable { return false; } - /* __GDPR__ - "issueReporterSubmit" : { - "issueType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "numSimilarIssuesDisplayed" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed }); + type IssueReporterSubmitClassification = { + issueType: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + numSimilarIssuesDisplayed: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + }; + type IssueReporterSubmitEvent = { + issueType: any; + numSimilarIssuesDisplayed: number; + }; + this.telemetryService.publicLog2('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed }); this.hasBeenSubmitted = true; const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value); @@ -1108,11 +1112,7 @@ export class IssueReporter extends Disposable { // Exclude right click if (event.which < 3) { shell.openExternal((event.target).href); - - /* __GDPR__ - "issueReporterViewSimilarIssue" : { } - */ - this.telemetryService.publicLog('issueReporterViewSimilarIssue'); + this.telemetryService.publicLog2('issueReporterViewSimilarIssue'); } } @@ -1123,12 +1123,13 @@ export class IssueReporter extends Disposable { } else { const error = new Error(`${elementId} not found.`); this.logService.error(error); - /* __GDPR__ - "issueReporterGetElementError" : { - "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('issueReporterGetElementError', { message: error.message }); + type IssueReporterGetElementErrorClassification = { + message: { classification: 'CallstackOrException', purpose: 'PerformanceAndHealth' }; + }; + type IssueReporterGetElementErrorEvent = { + message: string; + }; + this.telemetryService.publicLog2('issueReporterGetElementError', { message: error.message }); return undefined; } diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index fa0b8c7819..121b3c9af6 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -16,7 +16,7 @@ import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu'; import { ProcessItem } from 'vs/base/common/processes'; import { addDisposableListener } from 'vs/base/browser/dom'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; @@ -24,7 +24,7 @@ let mapPidToWindowTitle = new Map(); const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/; const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/; -const listeners: IDisposable[] = []; +const listeners = new DisposableStore(); const collapsedStateCache: Map = new Map(); let lastRequestTime: number; @@ -171,7 +171,7 @@ function renderProcessGroupHeader(sectionName: string, body: HTMLElement, contai updateSectionCollapsedState(!collapsedStateCache.get(sectionName), body, twistie, sectionName); data.prepend(twistie); - listeners.push(addDisposableListener(data, 'click', (e) => { + listeners.add(addDisposableListener(data, 'click', (e) => { const isHidden = body.classList.contains('hidden'); updateSectionCollapsedState(isHidden, body, twistie, sectionName); })); @@ -222,7 +222,7 @@ function renderTableSection(sectionName: string, processList: FormattedProcessIt row.append(cpu, memory, pid, name); - listeners.push(addDisposableListener(row, 'contextmenu', (e) => { + listeners.add(addDisposableListener(row, 'contextmenu', (e) => { showContextMenu(e, p, sectionIsLocal); })); @@ -239,7 +239,7 @@ function updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessIt } container.innerHTML = ''; - listeners.forEach(l => l.dispose()); + listeners.clear(); const tableHead = document.createElement('thead'); tableHead.innerHTML = ` diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index f62e2f8b61..5c59f89bf5 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -8,7 +8,7 @@ import * as pfs from 'vs/base/node/pfs'; import { IStringDictionary } from 'vs/base/common/collections'; import product from 'vs/platform/product/node/product'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ILogService } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -30,14 +30,13 @@ interface LanguagePackFile { [locale: string]: LanguagePackEntry; } -export class LanguagePackCachedDataCleaner { - - private _disposables: IDisposable[] = []; +export class LanguagePackCachedDataCleaner extends Disposable { constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, @ILogService private readonly _logService: ILogService ) { + super(); // We have no Language pack support for dev version (run from source) // So only cleanup when we have a build version. if (this._environmentService.isBuilt) { @@ -45,10 +44,6 @@ export class LanguagePackCachedDataCleaner { } } - dispose(): void { - this._disposables = dispose(this._disposables); - } - private _manageCachedDataSoon(): void { let handle: any = setTimeout(async () => { handle = undefined; @@ -101,12 +96,10 @@ export class LanguagePackCachedDataCleaner { } }, 40 * 1000); - this._disposables.push({ - dispose() { - if (handle !== undefined) { - clearTimeout(handle); - } + this._register(toDisposable(() => { + if (handle !== undefined) { + clearTimeout(handle); } - }); + })); } } \ No newline at end of file diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts index 6c9973f789..87488502d8 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -5,7 +5,7 @@ import { basename, dirname, join } from 'vs/base/common/path'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { readdir, rimraf, stat } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/node/product'; @@ -16,7 +16,7 @@ export class NodeCachedDataCleaner { ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months - private _disposables: IDisposable[] = []; + private readonly _disposables = new DisposableStore(); constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService @@ -25,7 +25,7 @@ export class NodeCachedDataCleaner { } dispose(): void { - this._disposables = dispose(this._disposables); + this._disposables.dispose(); } private _manageCachedDataSoon(): void { @@ -76,7 +76,7 @@ export class NodeCachedDataCleaner { }, 30 * 1000); - this._disposables.push(toDisposable(() => { + this._disposables.add(toDisposable(() => { if (handle) { clearTimeout(handle); handle = undefined; diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 28b6572c9b..db2559f4fa 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -16,11 +16,11 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; -import { IRequestService } from 'vs/platform/request/node/request'; -import { RequestService } from 'vs/platform/request/electron-browser/requestService'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { RequestService } from 'vs/platform/request/browser/requestService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; @@ -30,15 +30,14 @@ import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppen import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common/windows'; import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; import { ipcRenderer } from 'electron'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LocalizationsChannel } from 'vs/platform/localizations/node/localizationsIpc'; import { DialogChannelClient } from 'vs/platform/dialogs/node/dialogIpc'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/node/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; @@ -48,6 +47,16 @@ import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contr import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; +import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; +import { Schemas } from 'vs/base/common/network'; +import { IProductService } from 'vs/platform/product/common/product'; +import { ProductService } from 'vs/platform/product/node/productService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -79,31 +88,35 @@ class MainProcessService implements IMainProcessService { } } -function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void { +async function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): Promise { const services = new ServiceCollection(); - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); - const onExit = () => dispose(disposables); + const onExit = () => disposables.dispose(); process.once('exit', onExit); ipcRenderer.once('handshake:goodbye', onExit); - disposables.push(server); + disposables.add(server); const environmentService = new EnvironmentService(initData.args, process.execPath); const mainRouter = new StaticRouter(ctx => ctx === 'main'); const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', mainRouter)); - const logService = new FollowerLogService(logLevelClient, createBufferSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath)); - disposables.push(logService); - + const logService = new FollowerLogService(logLevelClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel)); + disposables.add(logService); logService.info('main', JSON.stringify(configuration)); + const configurationService = new ConfigurationService(environmentService.settingsResource); + disposables.add(configurationService); + await configurationService.initialize(); + services.set(IEnvironmentService, environmentService); services.set(ILogService, logService); - services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath])); + services.set(IConfigurationService, configurationService); services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(IDownloadService, new SyncDescriptor(DownloadService)); + services.set(IProductService, new SyncDescriptor(ProductService)); const mainProcessService = new MainProcessService(server, mainRouter); services.set(IMainProcessService, mainProcessService); @@ -116,13 +129,23 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const dialogChannel = server.getChannel('dialog', activeWindowRouter); services.set(IDialogService, new DialogChannelClient(dialogChannel)); + // Files + const fileService = new FileService(logService); + services.set(IFileService, fileService); + disposables.add(fileService); + + const diskFileSystemProvider = new DiskFileSystemProvider(logService); + disposables.add(diskFileSystemProvider); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + const instantiationService = new InstantiationService(services); + let telemetryService: ITelemetryService; instantiationService.invokeFunction(accessor => { const services = new ServiceCollection(); const environmentService = accessor.get(IEnvironmentService); const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; - const telemetryLogService = new FollowerLogService(logLevelClient, createBufferSpdLogService('telemetry', initData.logLevel, environmentService.logsPath)); + const telemetryLogService = new FollowerLogService(logLevelClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel)); telemetryLogService.info('The below are logs for every telemetry event sent from VS Code once the log level is set to trace.'); telemetryLogService.info('==========================================================='); @@ -130,7 +153,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); - disposables.push(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data + disposables.add(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data } const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), @@ -138,8 +161,10 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I piiPaths: [appRoot, extensionsPath] }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); + telemetryService = new TelemetryService(config, configurationService); + services.set(ITelemetryService, telemetryService); } else { + telemetryService = NullTelemetryService; services.set(ITelemetryService, NullTelemetryService); } server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); @@ -147,6 +172,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); const instantiationService2 = instantiationService.createChild(services); @@ -160,18 +186,22 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const localizationsChannel = new LocalizationsChannel(localizationsService); server.registerChannel('localizations', localizationsChannel); + const diagnosticsService = accessor.get(IDiagnosticsService); + const diagnosticsChannel = new DiagnosticsChannel(diagnosticsService); + server.registerChannel('diagnostics', diagnosticsChannel); + // clean up deprecated extensions (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(); // update localizations cache (localizationsService as LocalizationsService).update(); // cache clean ups - disposables.push(combinedDisposable([ + disposables.add(combinedDisposable( instantiationService2.createInstance(NodeCachedDataCleaner), instantiationService2.createInstance(LanguagePackCachedDataCleaner), instantiationService2.createInstance(StorageDataCleaner), instantiationService2.createInstance(LogsDataCleaner) - ])); - disposables.push(extensionManagementService as ExtensionManagementService); + )); + disposables.add(extensionManagementService as ExtensionManagementService); }); }); } @@ -218,6 +248,6 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise - + diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 90ab9dd0a1..ffff29ac06 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -54,13 +54,7 @@ bootstrapWindow.load([ showPartsSplash(windowConfig); }, beforeLoaderConfig: function (windowConfig, loaderConfig) { - loaderConfig.recordStats = !!windowConfig['prof-modules']; - if (loaderConfig.nodeCachedData) { - const onNodeCachedData = window['MonacoEnvironment'].onNodeCachedData = []; - loaderConfig.nodeCachedData.onData = function () { - onNodeCachedData.push(arguments); - }; - } + loaderConfig.recordStats = true; }, beforeRequire: function () { perf.mark('willLoadWorkbenchMain'); @@ -68,7 +62,6 @@ bootstrapWindow.load([ }); /** - * // configuration: IWindowConfiguration * @param {{ * partsSplashPath?: string, * highContrast?: boolean, @@ -89,7 +82,7 @@ function showPartsSplash(configuration) { } } - // high contrast mode has been turned on from the outside, e.g OS -> ignore stored colors and layouts + // high contrast mode has been turned on from the outside, e.g. OS -> ignore stored colors and layouts if (data && configuration.highContrast && data.baseTheme !== 'hc-black') { data = undefined; } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index eae9dc72f3..f5a9de9d30 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -9,7 +9,7 @@ import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows'; import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; -import { ILifecycleService, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateChannel } from 'vs/platform/update/node/updateIpc'; @@ -36,12 +36,10 @@ import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; import product from 'vs/platform/product/node/product'; import pkg from 'vs/platform/product/node/package'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; -import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard'; +import { withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc'; import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; @@ -52,7 +50,7 @@ import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateServ import { IIssueService } from 'vs/platform/issue/common/issue'; import { IssueChannel } from 'vs/platform/issue/node/issueIpc'; import { IssueService } from 'vs/platform/issue/electron-main/issueService'; -import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; +import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; @@ -63,7 +61,6 @@ import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; import { hasArgs } from 'vs/platform/environment/node/argv'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; -import { storeBackgroundColor } from 'vs/code/electron-main/theme'; import { homedir } from 'os'; import { join, sep } from 'vs/base/common/path'; import { localize } from 'vs/nls'; @@ -83,17 +80,19 @@ import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAg import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; import { VSBuffer } from 'vs/base/common/buffer'; import { statSync } from 'fs'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; export class CodeApplication extends Disposable { private static readonly MACHINE_ID_KEY = 'telemetry.machineId'; + private static readonly TRUE_MACHINE_ID_KEY = 'telemetry.trueMachineId'; - private windowsMainService: IWindowsMainService; - - private electronIpcServer: ElectronIPCServer; - - private sharedProcess: SharedProcess; - private sharedProcessClient: Promise; + private windowsMainService: IWindowsMainService | undefined; constructor( private readonly mainIpcServer: Server, @@ -102,14 +101,12 @@ export class CodeApplication extends Disposable { @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IConfigurationService private readonly configurationService: ConfigurationService, - @IStateService private readonly stateService: IStateService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IStateService private readonly stateService: IStateService, + @ISignService private readonly signService: ISignService ) { super(); - this._register(mainIpcServer); - this._register(configurationService); - this.registerListeners(); } @@ -120,12 +117,12 @@ export class CodeApplication extends Disposable { process.on('uncaughtException', err => this.onUnexpectedError(err)); process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason)); - // Contextmenu via IPC support - registerContextMenuListener(); - // Dispose on shutdown this.lifecycleService.onWillShutdown(() => this.dispose()); + // Contextmenu via IPC support + registerContextMenuListener(); + app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => { if (this.windowsMainService) { this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled); @@ -142,8 +139,38 @@ export class CodeApplication extends Disposable { }); // Security related measures (https://electronjs.org/docs/tutorial/security) - // DO NOT CHANGE without consulting the documentation - app.on('web-contents-created', (event: Electron.Event, contents) => { + // + // !!! DO NOT CHANGE without consulting the documentation !!! + // + // app.on('remote-get-guest-web-contents', event => event.preventDefault()); // TODO@Ben TODO@Matt revisit this need for + app.on('remote-require', (event, sender, module) => { + this.logService.trace('App#on(remote-require): prevented'); + + event.preventDefault(); + }); + app.on('remote-get-global', (event, sender, module) => { + this.logService.trace(`App#on(remote-get-global): prevented on ${module}`); + + event.preventDefault(); + }); + app.on('remote-get-builtin', (event, sender, module) => { + this.logService.trace(`App#on(remote-get-builtin): prevented on ${module}`); + + if (module !== 'clipboard') { + event.preventDefault(); + } + }); + app.on('remote-get-current-window', event => { + this.logService.trace(`App#on(remote-get-current-window): prevented`); + + event.preventDefault(); + }); + app.on('remote-get-current-web-contents', event => { + this.logService.trace(`App#on(remote-get-current-web-contents): prevented`); + + event.preventDefault(); + }); + app.on('web-contents-created', (_event: Electron.Event, contents) => { contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => { const isValidWebviewSource = (source: string): boolean => { @@ -198,7 +225,7 @@ export class CodeApplication extends Disposable { event.preventDefault(); // Keep in array because more might come! - macOpenFileURIs.push(getURIToOpenFromPathSync(path)); + macOpenFileURIs.push(this.getURIToOpenFromPathSync(path)); // Clear previous handler if any if (runningTimeout !== null) { @@ -215,6 +242,7 @@ export class CodeApplication extends Disposable { urisToOpen: macOpenFileURIs, preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ }); + macOpenFileURIs = []; runningTimeout = null; } @@ -222,7 +250,9 @@ export class CodeApplication extends Disposable { }); app.on('new-window-for-tab', () => { - this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button + if (this.windowsMainService) { + this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button + } }); ipc.on('vscode:exit', (event: Event, code: number) => { @@ -232,37 +262,26 @@ export class CodeApplication extends Disposable { this.lifecycleService.kill(code); }); - ipc.on('vscode:fetchShellEnv', (event: Event) => { + ipc.on('vscode:fetchShellEnv', async (event: Event) => { const webContents = event.sender; - getShellEnvironment(this.logService).then(shellEnv => { + + try { + const shellEnv = await getShellEnvironment(this.logService, this.environmentService); if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', shellEnv); } - }, err => { + } catch (error) { if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', {}); } - this.logService.error('Error fetching shell env', err); - }); - }); - - ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: object; }) => { - if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) { - this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload); - - // Handle specific events on main side - this.onBroadcast(broadcast.channel, broadcast.payload); - - // Send to all windows (except sender window) - this.windowsMainService.sendToAll('vscode:broadcast', broadcast, [windowId]); + this.logService.error('Error fetching shell env', error); } }); ipc.on('vscode:extensionHostDebug', (_: Event, windowId: number, broadcast: any) => { if (this.windowsMainService) { - // Send to all windows (except sender window) - this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]); + this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]); // Send to all windows (except sender window) } }); @@ -271,11 +290,25 @@ export class CodeApplication extends Disposable { ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload()); - powerMonitor.on('resume', () => { // After waking up from sleep - if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:osResume', undefined); - } - }); + // Some listeners after window opened + (async () => { + await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen); + + // After waking up from sleep (after window opened) + powerMonitor.on('resume', () => { + if (this.windowsMainService) { + this.windowsMainService.sendToAll('vscode:osResume', undefined); + } + }); + + // Keyboard layout changes (after window opened) + const nativeKeymap = await import('native-keymap'); + nativeKeymap.onDidChangeKeyboardLayout(() => { + if (this.windowsMainService) { + this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); + } + }); + })(); } private onUnexpectedError(err: Error): void { @@ -299,15 +332,7 @@ export class CodeApplication extends Disposable { } } - private onBroadcast(event: string, payload: object): void { - - // Theme changes - if (event === 'vscode:changeColorTheme' && typeof payload === 'string') { - storeBackgroundColor(this.stateService, JSON.parse(payload)); - } - } - - startup(): Promise { + async startup(): Promise { this.logService.debug('Starting VS Code'); this.logService.debug(`from: ${this.environmentService.appRoot}`); this.logService.debug('args:', this.environmentService.args); @@ -335,64 +360,142 @@ export class CodeApplication extends Disposable { } // Create Electron IPC Server - this.electronIpcServer = new ElectronIPCServer(); - - const startupWithMachineId = (machineId: string) => { - this.logService.trace(`Resolved machine identifier: ${machineId}`); - - // Spawn shared process - this.sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv); - this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main')); - - // Services - return this.initServices(machineId).then(appInstantiationService => { - - // Create driver - if (this.environmentService.driverHandle) { - serveDriver(this.electronIpcServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService).then(server => { - this.logService.info('Driver started at:', this.environmentService.driverHandle); - this._register(server); - }); - } - - // Setup Auth Handler - const authHandler = appInstantiationService.createInstance(ProxyAuthHandler); - this._register(authHandler); - - // Open Windows - const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor)); - - // Post Open Windows Tasks - appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); - - // Tracing: Stop tracing after windows are ready if enabled - if (this.environmentService.args.trace) { - this.stopTracingEventually(windows); - } - }); - }; + const electronIpcServer = new ElectronIPCServer(); // Resolve unique machine ID this.logService.trace('Resolving machine identifier...'); - const resolvedMachineId = this.resolveMachineId(); - if (typeof resolvedMachineId === 'string') { - return startupWithMachineId(resolvedMachineId); - } else { - return resolvedMachineId.then(machineId => startupWithMachineId(machineId)); + const { machineId, trueMachineId } = await this.resolveMachineId(); + this.logService.trace(`Resolved machine identifier: ${machineId} (trueMachineId: ${trueMachineId})`); + + // Spawn shared process after the first window has opened and 3s have passed + const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv); + const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main')); + this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { + this._register(new RunOnceScheduler(async () => { + const userEnv = await getShellEnvironment(this.logService, this.environmentService); + + sharedProcess.spawn(userEnv); + }, 3000)).schedule(); + }); + + // Services + const appInstantiationService = await this.createServices(machineId, trueMachineId, sharedProcess, sharedProcessClient); + + // Create driver + if (this.environmentService.driverHandle) { + const server = await serveDriver(electronIpcServer, this.environmentService.driverHandle!, this.environmentService, appInstantiationService); + + this.logService.info('Driver started at:', this.environmentService.driverHandle); + this._register(server); + } + + // Setup Auth Handler + const authHandler = appInstantiationService.createInstance(ProxyAuthHandler); + this._register(authHandler); + + // Open Windows + const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient)); + + // Post Open Windows Tasks + this.afterWindowOpen(); + + // Tracing: Stop tracing after windows are ready if enabled + if (this.environmentService.args.trace) { + this.stopTracingEventually(windows); } } - private resolveMachineId(): string | Promise { - const machineId = this.stateService.getItem(CodeApplication.MACHINE_ID_KEY); - if (machineId) { - return machineId; + private async resolveMachineId(): Promise<{ machineId: string, trueMachineId?: string }> { + + // We cache the machineId for faster lookups on startup + // and resolve it only once initially if not cached + let machineId = this.stateService.getItem(CodeApplication.MACHINE_ID_KEY); + if (!machineId) { + machineId = await getMachineId(); + + this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId); } - return getMachineId().then(machineId => { - this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId); + // Check if machineId is hashed iBridge Device + let trueMachineId: string | undefined; + if (isMacintosh && machineId === '6c9d2bc8f91b89624add29c0abeae7fb42bf539fa1cdb2e3e57cd668fa9bcead') { + trueMachineId = this.stateService.getItem(CodeApplication.TRUE_MACHINE_ID_KEY); + if (!trueMachineId) { + trueMachineId = await getMachineId(); - return machineId; - }); + this.stateService.setItem(CodeApplication.TRUE_MACHINE_ID_KEY, trueMachineId); + } + } + + return { machineId, trueMachineId }; + } + + private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessClient: Promise>): Promise { + const services = new ServiceCollection(); + + // Files + const fileService = this._register(new FileService(this.logService)); + services.set(IFileService, fileService); + + const diskFileSystemProvider = this._register(new DiskFileSystemProvider(this.logService)); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + + switch (process.platform) { + case 'win32': + services.set(IUpdateService, new SyncDescriptor(Win32UpdateService)); + break; + + case 'linux': + if (process.env.SNAP && process.env.SNAP_REVISION) { + services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION])); + } else { + services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); + } + break; + + case 'darwin': + services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService)); + break; + } + + services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv])); + services.set(IWindowsService, new SyncDescriptor(WindowsService, [sharedProcess])); + services.set(ILaunchService, new SyncDescriptor(LaunchService)); + + const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics'))); + services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel])); + + services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv])); + services.set(IMenubarService, new SyncDescriptor(MenubarService)); + + const storageMainService = new StorageMainService(this.logService, this.environmentService); + services.set(IStorageMainService, storageMainService); + this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close())); + + const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService); + services.set(IBackupMainService, backupMainService); + + services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService)); + services.set(IURLService, new SyncDescriptor(URLService)); + services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); + + // Telemetry + if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { + const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender'))); + const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)); + const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath); + const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; + const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, trueMachineId }; + + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + // Init services that require it + await backupMainService.initialize(); + + return this.instantiationService.createChild(services); } private stopTracingEventually(windows: ICodeWindow[]): void { @@ -408,12 +511,14 @@ export class CodeApplication extends Disposable { contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => { if (!timeout) { - this.windowsMainService.showMessageBox({ - type: 'info', - message: localize('trace.message', "Successfully created trace."), - detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), - buttons: [localize('trace.ok', "Ok")] - }, this.windowsMainService.getLastActiveWindow()); + if (this.windowsMainService) { + this.windowsMainService.showMessageBox({ + type: 'info', + message: localize('trace.message', "Successfully created trace."), + detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), + buttons: [localize('trace.ok', "Ok")] + }, this.windowsMainService.getLastActiveWindow()); + } } else { this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); } @@ -430,72 +535,7 @@ export class CodeApplication extends Disposable { }); } - private initServices(machineId: string): Promise { - const services = new ServiceCollection(); - - if (process.platform === 'win32') { - services.set(IUpdateService, new SyncDescriptor(Win32UpdateService)); - } else if (process.platform === 'linux') { - if (process.env.SNAP && process.env.SNAP_REVISION) { - services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION])); - } else { - services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); - } - } else if (process.platform === 'darwin') { - services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService)); - } - - services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId])); - services.set(IWindowsService, new SyncDescriptor(WindowsService, [this.sharedProcess])); - services.set(ILaunchService, new SyncDescriptor(LaunchService)); - services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv])); - services.set(IMenubarService, new SyncDescriptor(MenubarService)); - services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); - services.set(IBackupMainService, new SyncDescriptor(BackupMainService)); - services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService)); - services.set(IURLService, new SyncDescriptor(URLService)); - services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); - - // Telemetry - if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); - const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)); - const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath); - const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; - const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; - - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); - } else { - services.set(ITelemetryService, NullTelemetryService); - } - - const appInstantiationService = this.instantiationService.createChild(services); - - // Init services that require it - return appInstantiationService.invokeFunction(accessor => Promise.all([ - this.initStorageService(accessor), - this.initBackupService(accessor) - ])).then(() => appInstantiationService); - } - - private initStorageService(accessor: ServicesAccessor): Promise { - const storageMainService = accessor.get(IStorageMainService) as StorageMainService; - - // Ensure to close storage on shutdown - this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close())); - - return Promise.resolve(); - - } - - private initBackupService(accessor: ServicesAccessor): Promise { - const backupMainService = accessor.get(IBackupMainService) as BackupMainService; - - return backupMainService.initialize(); - } - - private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] { - const appInstantiationService = accessor.get(IInstantiationService); + private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>): ICodeWindow[] { // Register more Main IPC services const launchService = accessor.get(ILaunchService); @@ -505,48 +545,48 @@ export class CodeApplication extends Disposable { // Register more Electron IPC services const updateService = accessor.get(IUpdateService); const updateChannel = new UpdateChannel(updateService); - this.electronIpcServer.registerChannel('update', updateChannel); + electronIpcServer.registerChannel('update', updateChannel); const issueService = accessor.get(IIssueService); const issueChannel = new IssueChannel(issueService); - this.electronIpcServer.registerChannel('issue', issueChannel); + electronIpcServer.registerChannel('issue', issueChannel); const workspacesService = accessor.get(IWorkspacesMainService); - const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService); - this.electronIpcServer.registerChannel('workspaces', workspacesChannel); + const workspacesChannel = new WorkspacesChannel(workspacesService); + electronIpcServer.registerChannel('workspaces', workspacesChannel); const windowsService = accessor.get(IWindowsService); const windowsChannel = new WindowsChannel(windowsService); - this.electronIpcServer.registerChannel('windows', windowsChannel); - this.sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel)); + electronIpcServer.registerChannel('windows', windowsChannel); + sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel)); const menubarService = accessor.get(IMenubarService); const menubarChannel = new MenubarChannel(menubarService); - this.electronIpcServer.registerChannel('menubar', menubarChannel); + electronIpcServer.registerChannel('menubar', menubarChannel); const urlService = accessor.get(IURLService); const urlChannel = new URLServiceChannel(urlService); - this.electronIpcServer.registerChannel('url', urlChannel); + electronIpcServer.registerChannel('url', urlChannel); const storageMainService = accessor.get(IStorageMainService); const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService)); - this.electronIpcServer.registerChannel('storage', storageChannel); + electronIpcServer.registerChannel('storage', storageChannel); // Log level management const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService)); - this.electronIpcServer.registerChannel('loglevel', logLevelChannel); - this.sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel)); + electronIpcServer.registerChannel('loglevel', logLevelChannel); + sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel)); - // Lifecycle - (this.lifecycleService as LifecycleService).ready(); + // Signal phase: ready (services set) + this.lifecycleService.phase = LifecycleMainPhase.Ready; // Propagate to clients - const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this + const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // Create a URL handler which forwards to the last active window const activeWindowManager = new ActiveWindowManager(windowsService); const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); - const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', activeWindowRouter); + const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', activeWindowRouter); const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel); // On Mac, Code can be running without any open windows, so we must create a window to handle urls, @@ -555,15 +595,17 @@ export class CodeApplication extends Disposable { const environmentService = accessor.get(IEnvironmentService); urlService.registerHandler({ - handleURL(uri: URI): Promise { + async handleURL(uri: URI): Promise { if (windowsMainService.getWindowCount() === 0) { const cli = { ...environmentService.args, goto: true }; const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true }); - return window.ready().then(() => urlService.open(uri)); + await window.ready(); + + return urlService.open(uri); } - return Promise.resolve(false); + return false; } }); } @@ -574,11 +616,9 @@ export class CodeApplication extends Disposable { // Watch Electron URLs and forward them to the UrlService const args = this.environmentService.args; const urls = args['open-url'] ? args._urls : []; - const urlListener = new ElectronURLListener(urls || [], urlService, this.windowsMainService); + const urlListener = new ElectronURLListener(urls || [], urlService, windowsMainService); this._register(urlListener); - this.windowsMainService.ready(this.userEnv); - // Open our first window const macOpenFiles: string[] = (global).macOpenFiles; const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; @@ -588,9 +628,9 @@ export class CodeApplication extends Disposable { const noRecentEntry = args['skip-add-to-recently-opened'] === true; const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined; + // new window if "-n" was used without paths if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { - // new window if "-n" was used without paths - return this.windowsMainService.open({ + return windowsMainService.open({ context, cli: args, forceNewWindow: true, @@ -601,12 +641,12 @@ export class CodeApplication extends Disposable { }); } + // mac: open-file event received on startup if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { - // mac: open-file event received on startup - return this.windowsMainService.open({ + return windowsMainService.open({ context: OpenContext.DOCK, cli: args, - urisToOpen: macOpenFiles.map(getURIToOpenFromPathSync), + urisToOpen: macOpenFiles.map(file => this.getURIToOpenFromPathSync(file)), noRecentEntry, waitMarkerFileURI, initialStartup: true @@ -614,7 +654,7 @@ export class CodeApplication extends Disposable { } // default: read paths from cli - return this.windowsMainService.open({ + return windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), @@ -625,61 +665,30 @@ export class CodeApplication extends Disposable { }); } - private afterWindowOpen(accessor: ServicesAccessor): void { - const windowsMainService = accessor.get(IWindowsMainService); - const historyMainService = accessor.get(IHistoryMainService); - - if (isWindows) { - - // Setup Windows mutex - try { - const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex; - const windowsMutex = new Mutex(product.win32MutexName); - this._register(toDisposable(() => windowsMutex.release())); - } catch (e) { - if (!this.environmentService.isBuilt) { - windowsMainService.showMessageBox({ - title: product.nameLong, - type: 'warning', - message: 'Failed to load windows-mutex!', - detail: e.toString(), - noLink: true - }); - } + private getURIToOpenFromPathSync(path: string): IURIToOpen { + try { + const fileStat = statSync(path); + if (fileStat.isDirectory()) { + return { folderUri: URI.file(path) }; } - // Ensure Windows foreground love module - try { - // tslint:disable-next-line:no-unused-expression - require.__$__nodeRequire('windows-foreground-love'); - } catch (e) { - if (!this.environmentService.isBuilt) { - windowsMainService.showMessageBox({ - title: product.nameLong, - type: 'warning', - message: 'Failed to load windows-foreground-love!', - detail: e.toString(), - noLink: true - }); - } + if (hasWorkspaceFileExtension(path)) { + return { workspaceUri: URI.file(path) }; } + } catch (error) { + // ignore errors } + return { fileUri: URI.file(path) }; + } + + private afterWindowOpen(): void { + + // Signal phase: after window open + this.lifecycleService.phase = LifecycleMainPhase.AfterWindowOpen; + // Remote Authorities this.handleRemoteAuthorities(); - - // Keyboard layout changes - KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); - }); - - // Jump List - historyMainService.updateWindowsJumpList(); - historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList()); - - // Start shared process after a while - const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment(this.logService).then(userEnv => this.sharedProcess.spawn(userEnv)), 3000)); - sharedProcessSpawn.schedule(); } private handleRemoteAuthorities(): void { @@ -692,18 +701,21 @@ export class CodeApplication extends Disposable { private readonly _connection: Promise; private readonly _disposeRunner: RunOnceScheduler; - constructor(authority: string, host: string, port: number) { + constructor(authority: string, host: string, port: number, signService: ISignService) { this._authority = authority; + const options: IConnectionOptions = { - isBuilt: isBuilt, + isBuilt, commit: product.commit, webSocketFactory: nodeWebSocketFactory, addressProvider: { getAddress: () => { return Promise.resolve({ host, port }); } - } + }, + signService }; + this._connection = connectRemoteAgentManagement(options, authority, `main`); this._disposeRunner = new RunOnceScheduler(() => this.dispose(), 5000); } @@ -711,14 +723,13 @@ export class CodeApplication extends Disposable { dispose(): void { this._disposeRunner.dispose(); connectionPool.delete(this._authority); - this._connection.then((connection) => { - connection.dispose(); - }); + this._connection.then(connection => connection.dispose()); } async getClient(): Promise> { this._disposeRunner.schedule(); const connection = await this._connection; + return connection.client; } } @@ -726,7 +737,9 @@ export class CodeApplication extends Disposable { const resolvedAuthorities = new Map(); ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => { this.logService.info('Received resolved authority', data.authority); + resolvedAuthorities.set(data.authority, data); + // Make sure to close and remove any existing connections if (connectionPool.has(data.authority)) { connectionPool.get(data.authority)!.dispose(); @@ -735,15 +748,19 @@ export class CodeApplication extends Disposable { const resolveAuthority = (authority: string): ResolvedAuthority | null => { this.logService.info('Resolving authority', authority); + if (authority.indexOf('+') >= 0) { if (resolvedAuthorities.has(authority)) { return withUndefinedAsNull(resolvedAuthorities.get(authority)); } + this.logService.info('Didnot find resolved authority for', authority); + return null; } else { const [host, strPort] = authority.split(':'); const port = parseInt(strPort, 10); + return { authority, host, port }; } }; @@ -752,6 +769,7 @@ export class CodeApplication extends Disposable { if (request.method !== 'GET') { return callback(undefined); } + const uri = URI.parse(request.url); let activeConnection: ActiveConnection | undefined; @@ -763,9 +781,11 @@ export class CodeApplication extends Disposable { callback(undefined); return; } - activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port); + + activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port, this.signService); connectionPool.set(uri.authority, activeConnection); } + try { const rawClient = await activeConnection!.getClient(); if (connectionPool.has(uri.authority)) { // not disposed in the meantime @@ -784,16 +804,3 @@ export class CodeApplication extends Disposable { }); } } - -function getURIToOpenFromPathSync(path: string): IURIToOpen { - try { - const fileStat = statSync(path); - if (fileStat.isDirectory()) { - return { folderUri: URI.file(path) }; - } else if (hasWorkspaceFileExtension(path)) { - return { workspaceUri: URI.file(path) }; - } - } catch (error) { - } - return { fileUri: URI.file(path) }; -} diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index 4eff58920d..214e9a7e15 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -54,7 +54,11 @@ export class ProxyAuthHandler { width: 450, height: 220, show: true, - title: 'VS Code' + title: 'VS Code', + webPreferences: { + nodeIntegration: true, + webviewTag: true + } }; const focusedWindow = this.windowsMainService.getFocusedWindow(); diff --git a/src/vs/code/electron-main/keyboard.ts b/src/vs/code/electron-main/keyboard.ts deleted file mode 100644 index aad1b04a88..0000000000 --- a/src/vs/code/electron-main/keyboard.ts +++ /dev/null @@ -1,32 +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 nativeKeymap from 'native-keymap'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { Emitter } from 'vs/base/common/event'; - -export class KeyboardLayoutMonitor { - - public static readonly INSTANCE = new KeyboardLayoutMonitor(); - - private readonly _emitter: Emitter; - private _registered: boolean; - - private constructor() { - this._emitter = new Emitter(); - this._registered = false; - } - - public onDidChangeKeyboardLayout(callback: () => void): IDisposable { - if (!this._registered) { - this._registered = true; - - nativeKeymap.onDidChangeKeyboardLayout(() => { - this._emitter.fire(); - }); - } - return this._emitter.event(callback); - } -} \ No newline at end of file diff --git a/src/vs/code/electron-main/logUploader.ts b/src/vs/code/electron-main/logUploader.ts deleted file mode 100644 index 811718d4ab..0000000000 --- a/src/vs/code/electron-main/logUploader.ts +++ /dev/null @@ -1,153 +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 os from 'os'; -import * as cp from 'child_process'; -import * as fs from 'fs'; - -import * as path from 'vs/base/common/path'; -import { localize } from 'vs/nls'; -import product from 'vs/platform/product/node/product'; -import { IRequestService } from 'vs/platform/request/node/request'; -import { IRequestContext } from 'vs/base/node/request'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { ILaunchService } from 'vs/platform/launch/electron-main/launchService'; - -interface PostResult { - readonly blob_id: string; -} - -class Endpoint { - private constructor( - public readonly url: string - ) { } - - public static getFromProduct(): Endpoint | undefined { - const logUploaderUrl = product.logUploaderUrl; - return logUploaderUrl ? new Endpoint(logUploaderUrl) : undefined; - } -} - -export async function uploadLogs( - launchService: ILaunchService, - requestService: IRequestService, - environmentService: IEnvironmentService -): Promise { - const endpoint = Endpoint.getFromProduct(); - if (!endpoint) { - console.error(localize('invalidEndpoint', 'Invalid log uploader endpoint')); - return; - } - - const logsPath = await launchService.getLogsPath(); - - if (await promptUserToConfirmLogUpload(logsPath, environmentService)) { - console.log(localize('beginUploading', 'Uploading...')); - const outZip = await zipLogs(logsPath); - const result = await postLogs(endpoint, outZip, requestService); - console.log(localize('didUploadLogs', 'Upload successful! Log file ID: {0}', result.blob_id)); - } -} - -function promptUserToConfirmLogUpload( - logsPath: string, - environmentService: IEnvironmentService -): boolean { - const confirmKey = 'iConfirmLogsUpload'; - if ((environmentService.args['upload-logs'] || '').toLowerCase() === confirmKey.toLowerCase()) { - return true; - } else { - const message = localize('logUploadPromptHeader', 'You are about to upload your session logs to a secure Microsoft endpoint that only Microsoft\'s members of the VS Code team can access.') - + '\n\n' + localize('logUploadPromptBody', 'Session logs may contain personal information such as full paths or file contents. Please review and redact your session log files here: \'{0}\'', logsPath) - + '\n\n' + localize('logUploadPromptBodyDetails', 'By continuing you confirm that you have reviewed and redacted your session log files and that you agree to Microsoft using them to debug VS Code.') - + '\n\n' + localize('logUploadPromptAcceptInstructions', 'Please run code with \'--upload-logs={0}\' to proceed with upload', confirmKey); - console.log(message); - return false; - } -} - -async function postLogs( - endpoint: Endpoint, - outZip: string, - requestService: IRequestService -): Promise { - const dotter = setInterval(() => console.log('.'), 5000); - let result: IRequestContext; - try { - result = await requestService.request({ - url: endpoint.url, - type: 'POST', - data: Buffer.from(fs.readFileSync(outZip)).toString('base64'), - headers: { - 'Content-Type': 'application/zip' - } - }, CancellationToken.None); - } catch (e) { - clearInterval(dotter); - console.log(localize('postError', 'Error posting logs: {0}', e)); - throw e; - } - - return new Promise((resolve, reject) => { - const parts: Buffer[] = []; - result.stream.on('data', data => { - parts.push(data); - }); - - result.stream.on('end', () => { - clearInterval(dotter); - try { - const response = Buffer.concat(parts).toString('utf-8'); - if (result.res.statusCode === 200) { - resolve(JSON.parse(response)); - } else { - const errorMessage = localize('responseError', 'Error posting logs. Got {0} — {1}', result.res.statusCode, response); - console.log(errorMessage); - reject(new Error(errorMessage)); - } - } catch (e) { - console.log(localize('parseError', 'Error parsing response')); - reject(e); - } - }); - }); -} - -function zipLogs( - logsPath: string -): Promise { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-log-upload')); - const outZip = path.join(tempDir, 'logs.zip'); - return new Promise((resolve, reject) => { - doZip(logsPath, outZip, tempDir, (err, stdout, stderr) => { - if (err) { - console.error(localize('zipError', 'Error zipping logs: {0}', err.message)); - reject(err); - } else { - resolve(outZip); - } - }); - }); -} - -function doZip( - logsPath: string, - outZip: string, - tempDir: string, - callback: (error: Error, stdout: string, stderr: string) => void -) { - switch (os.platform()) { - case 'win32': - // Copy directory first to avoid file locking issues - const sub = path.join(tempDir, 'sub'); - return cp.execFile('powershell', ['-Command', - `[System.IO.Directory]::CreateDirectory("${sub}"); Copy-Item -recurse "${logsPath}" "${sub}"; Compress-Archive -Path "${sub}" -DestinationPath "${outZip}"`], - { cwd: logsPath }, - callback); - default: - return cp.execFile('zip', ['-r', outZip, '.'], { cwd: logsPath }, callback); - } -} diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 111fc412cd..14ea0e476a 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/code/code.main'; +import 'vs/platform/update/node/update.config.contribution'; import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; @@ -26,82 +26,191 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/ import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; -import { IRequestService } from 'vs/platform/request/node/request'; +import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/electron-main/requestService'; import * as fs from 'fs'; import { CodeApplication } from 'vs/code/electron-main/app'; import { localize } from 'vs/nls'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; -import { IDiagnosticsService, DiagnosticsService } from 'vs/platform/diagnostics/electron-main/diagnosticsService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -import { uploadLogs } from 'vs/code/electron-main/logUploader'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; +import { Client } from 'vs/base/parts/ipc/common/ipc.net'; +import { once } from 'vs/base/common/functional'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { SignService } from 'vs/platform/sign/node/signService'; +import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; class ExpectedError extends Error { readonly isExpected = true; } -function setupIPC(accessor: ServicesAccessor): Promise { - const logService = accessor.get(ILogService); - const environmentService = accessor.get(IEnvironmentService); - const instantiationService = accessor.get(IInstantiationService); +class CodeMain { - function allowSetForegroundWindow(service: LaunchChannelClient): Promise { - let promise: Promise = Promise.resolve(); - if (platform.isWindows) { - promise = service.getMainProcessId() - .then(processId => { - logService.trace('Sending some foreground love to the running instance:', processId); + main(): void { - try { - const { allowSetForegroundWindow } = require.__$__nodeRequire('windows-foreground-love'); - allowSetForegroundWindow(processId); - } catch (e) { - // noop - } - }); + // Set the error handler early enough so that we are not getting the + // default electron error dialog popping up + setUnexpectedErrorHandler(err => console.error(err)); + + // Parse arguments + let args: ParsedArgs; + try { + args = parseMainProcessArgv(process.argv); + args = validatePaths(args); + } catch (err) { + console.error(err.message); + app.exit(1); + + return; } - return promise; + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + // + // Note: we are not doing this if the wait marker has been already + // added as argument. This can happen if Code was started from CLI. + if (args.wait && !args.waitMarkerFilePath) { + const waitMarkerFilePath = createWaitMarkerFile(args.verbose); + if (waitMarkerFilePath) { + addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); + args.waitMarkerFilePath = waitMarkerFilePath; + } + } + + // Launch + this.startup(args); } - function setup(retry: boolean): Promise { - return serve(environmentService.mainIPCHandle).then(server => { + private async startup(args: ParsedArgs): Promise { - // Print --status usage info - if (environmentService.args.status) { - logService.warn('Warning: The --status argument can only be used if Code is already running. Please run it again after Code has started.'); - throw new ExpectedError('Terminating...'); - } + // We need to buffer the spdlog logs until we are sure + // 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(); - // Log uploader usage info - if (typeof environmentService.args['upload-logs'] !== 'undefined') { - logService.warn('Warning: The --upload-logs argument can only be used if Code is already running. Please run it again after Code has started.'); - throw new ExpectedError('Terminating...'); - } + const [instantiationService, instanceEnvironment] = this.createServices(args, bufferLogService); + try { - // dock might be hidden at this case due to a retry - if (platform.isMacintosh) { - app.dock.show(); - } + // Init services + await instantiationService.invokeFunction(async accessor => { + const environmentService = accessor.get(IEnvironmentService); + const configurationService = accessor.get(IConfigurationService); + const stateService = accessor.get(IStateService); - // Set the VSCODE_PID variable here when we are sure we are the first - // instance to startup. Otherwise we would wrongly overwrite the PID - process.env['VSCODE_PID'] = String(process.pid); + try { + await this.initServices(environmentService, configurationService as ConfigurationService, stateService as StateService); + } catch (error) { - return server; - }, err => { + // Show a dialog for errors that can be resolved by the user + this.handleStartupDataDirError(environmentService, error); + + throw error; + } + }); + + // Startup + await instantiationService.invokeFunction(async accessor => { + const environmentService = accessor.get(IEnvironmentService); + const logService = accessor.get(ILogService); + const lifecycleService = accessor.get(ILifecycleService); + const configurationService = accessor.get(IConfigurationService); + + const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleService, instantiationService, true); + + bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel()); + once(lifecycleService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose()); + + return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); + }); + } catch (error) { + instantiationService.invokeFunction(this.quit, error); + } + } + + private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, typeof process.env] { + const services = new ServiceCollection(); + + const environmentService = new EnvironmentService(args, process.execPath); + const instanceEnvironment = this.patchEnvironment(environmentService); // Patch `process.env` with the instance's environment + services.set(IEnvironmentService, environmentService); + + const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]); + process.once('exit', () => logService.dispose()); + services.set(ILogService, logService); + + services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource)); + services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); + services.set(IStateService, new SyncDescriptor(StateService)); + services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IThemeMainService, new SyncDescriptor(ThemeMainService)); + services.set(ISignService, new SyncDescriptor(SignService)); + + return [new InstantiationService(services, true), instanceEnvironment]; + } + + private initServices(environmentService: IEnvironmentService, configurationService: ConfigurationService, stateService: StateService): Promise { + + // Environment service (paths) + const environmentServiceInitialization = Promise.all([ + environmentService.extensionsPath, + environmentService.nodeCachedDataDir, + environmentService.logsPath, + environmentService.globalStorageHome, + environmentService.workspaceStorageHome, + environmentService.backupHome.fsPath + ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); + + // Configuration service + const configurationServiceInitialization = configurationService.initialize(); + + // State service + const stateServiceInitialization = stateService.init(); + + return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); + } + + private patchEnvironment(environmentService: IEnvironmentService): typeof process.env { + const instanceEnvironment: typeof process.env = { + VSCODE_IPC_HOOK: environmentService.mainIPCHandle, + VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'], + VSCODE_LOGS: process.env['VSCODE_LOGS'], + // {{SQL CARBON EDIT}} We keep VSCODE_LOGS to not break functionality for merged code + ADS_LOGS: process.env['ADS_LOGS'] + }; + + if (process.env['VSCODE_PORTABLE']) { + instanceEnvironment['VSCODE_PORTABLE'] = process.env['VSCODE_PORTABLE']; + } + + assign(process.env, instanceEnvironment); + + return instanceEnvironment; + } + + private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleService: ILifecycleService, instantiationService: IInstantiationService, retry: boolean): Promise { + + // Try to setup a server for running. If that succeeds it means + // we are the first instance to startup. Otherwise it is likely + // that another instance is already running. + let server: Server; + try { + server = await serve(environmentService.mainIPCHandle); + once(lifecycleService.onWillShutdown)(() => server.dispose()); + } catch (error) { // Handle unexpected errors (the only expected error is EADDRINUSE that // indicates a second instance of Code is running) - if (err.code !== 'EADDRINUSE') { + if (error.code !== 'EADDRINUSE') { // Show a dialog for errors that can be resolved by the user - handleStartupDataDirError(environmentService, err); + this.handleStartupDataDirError(environmentService, error); // Any other runtime error is just printed to the console - return Promise.reject(err); + throw error; } // Since we are the second instance, we do not want to show the dock @@ -110,263 +219,177 @@ function setupIPC(accessor: ServicesAccessor): Promise { } // there's a running instance, let's connect to it - return connect(environmentService.mainIPCHandle, 'main').then( - client => { + let client: Client; + try { + client = await connect(environmentService.mainIPCHandle, 'main'); + } catch (error) { - // Tests from CLI require to be the only instance currently - if (environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break) { - const msg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; - logService.error(msg); - client.dispose(); - - return Promise.reject(new Error(msg)); + // Handle unexpected connection errors by showing a dialog to the user + if (!retry || platform.isWindows || error.code !== 'ECONNREFUSED') { + if (error.code === 'EPERM') { + this.showStartupWarningDialog( + localize('secondInstanceAdmin', "A second instance of {0} is already running as administrator.", product.nameShort), + localize('secondInstanceAdminDetail', "Please close the other instance and try again.") + ); } - // Show a warning dialog after some timeout if it takes long to talk to the other instance - // Skip this if we are running with --wait where it is expected that we wait for a while. - // Also skip when gathering diagnostics (--status) which can take a longer time. - let startupWarningDialogHandle: NodeJS.Timeout; - if (!environmentService.wait && !environmentService.status && !environmentService.args['upload-logs']) { - startupWarningDialogHandle = setTimeout(() => { - showStartupWarningDialog( - localize('secondInstanceNoResponse', "Another instance of {0} is running but not responding", product.nameShort), - localize('secondInstanceNoResponseDetail', "Please close all other instances and try again.") - ); - }, 10000); - } - - const channel = client.getChannel('launch'); - const service = new LaunchChannelClient(channel); - - // Process Info - if (environmentService.args.status) { - return instantiationService.invokeFunction(accessor => { - return accessor.get(IDiagnosticsService).getDiagnostics(service).then(diagnostics => { - console.log(diagnostics); - return Promise.reject(new ExpectedError()); - }); - }); - } - - // Log uploader - if (typeof environmentService.args['upload-logs'] !== 'undefined') { - return instantiationService.invokeFunction(accessor => { - return uploadLogs(service, accessor.get(IRequestService), environmentService) - .then(() => Promise.reject(new ExpectedError())); - }); - } - - logService.trace('Sending env to running instance...'); - - return allowSetForegroundWindow(service) - .then(() => service.start(environmentService.args, process.env as platform.IProcessEnvironment)) - .then(() => client.dispose()) - .then(() => { - - // Now that we started, make sure the warning dialog is prevented - if (startupWarningDialogHandle) { - clearTimeout(startupWarningDialogHandle); - } - - return Promise.reject(new ExpectedError('Sent env to running instance. Terminating...')); - }); - }, - err => { - if (!retry || platform.isWindows || err.code !== 'ECONNREFUSED') { - if (err.code === 'EPERM') { - showStartupWarningDialog( - localize('secondInstanceAdmin', "A second instance of {0} is already running as administrator.", product.nameShort), - localize('secondInstanceAdminDetail', "Please close the other instance and try again.") - ); - } - - return Promise.reject(err); - } - - // it happens on Linux and OS X that the pipe is left behind - // let's delete it, since we can't connect to it - // and then retry the whole thing - try { - fs.unlinkSync(environmentService.mainIPCHandle); - } catch (e) { - logService.warn('Could not delete obsolete instance handle', e); - return Promise.reject(e); - } - - return setup(false); + throw error; } + + // it happens on Linux and OS X that the pipe is left behind + // let's delete it, since we can't connect to it and then + // retry the whole thing + try { + fs.unlinkSync(environmentService.mainIPCHandle); + } catch (error) { + logService.warn('Could not delete obsolete instance handle', error); + + throw error; + } + + return this.doStartup(logService, environmentService, lifecycleService, instantiationService, false); + } + + // Tests from CLI require to be the only instance currently + if (environmentService.extensionTestsLocationURI && !environmentService.debugExtensionHost.break) { + const msg = 'Running extension tests from the command line is currently only supported if no other instance of Code is running.'; + logService.error(msg); + client.dispose(); + + throw new Error(msg); + } + + // Show a warning dialog after some timeout if it takes long to talk to the other instance + // Skip this if we are running with --wait where it is expected that we wait for a while. + // Also skip when gathering diagnostics (--status) which can take a longer time. + let startupWarningDialogHandle: NodeJS.Timeout | undefined = undefined; + if (!environmentService.wait && !environmentService.status) { + startupWarningDialogHandle = setTimeout(() => { + this.showStartupWarningDialog( + localize('secondInstanceNoResponse', "Another instance of {0} is running but not responding", product.nameShort), + localize('secondInstanceNoResponseDetail', "Please close all other instances and try again.") + ); + }, 10000); + } + + const channel = client.getChannel('launch'); + const launchClient = new LaunchChannelClient(channel); + + // Process Info + if (environmentService.args.status) { + return instantiationService.invokeFunction(async accessor => { + // Create a diagnostic service connected to the existing shared process + const sharedProcessClient = await connect(environmentService.sharedIPCHandle, 'main'); + const diagnosticsChannel = sharedProcessClient.getChannel('diagnostics'); + const diagnosticsService = new DiagnosticsService(diagnosticsChannel); + const mainProcessInfo = await launchClient.getMainProcessInfo(); + const remoteDiagnostics = await launchClient.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true }); + const diagnostics = await diagnosticsService.getDiagnostics(mainProcessInfo, remoteDiagnostics); + console.log(diagnostics); + + throw new ExpectedError(); + }); + } + + // Windows: allow to set foreground + if (platform.isWindows) { + await this.windowsAllowSetForegroundWindow(launchClient, logService); + } + + // Send environment over... + logService.trace('Sending env to running instance...'); + await launchClient.start(environmentService.args, process.env as platform.IProcessEnvironment); + + // Cleanup + await client.dispose(); + + // Now that we started, make sure the warning dialog is prevented + if (startupWarningDialogHandle) { + clearTimeout(startupWarningDialogHandle); + } + + throw new ExpectedError('Sent env to running instance. Terminating...'); + } + + // Print --status usage info + if (environmentService.args.status) { + logService.warn('Warning: The --status argument can only be used if Code is already running. Please run it again after Code has started.'); + + throw new ExpectedError('Terminating...'); + } + + // dock might be hidden at this case due to a retry + if (platform.isMacintosh) { + app.dock.show(); + } + + // Set the VSCODE_PID variable here when we are sure we are the first + // instance to startup. Otherwise we would wrongly overwrite the PID + process.env['VSCODE_PID'] = String(process.pid); + + return server; + } + + private handleStartupDataDirError(environmentService: IEnvironmentService, error: NodeJS.ErrnoException): void { + if (error.code === 'EACCES' || error.code === 'EPERM') { + this.showStartupWarningDialog( + localize('startupDataDirError', "Unable to write program user data."), + localize('startupDataDirErrorDetail', "Please make sure the directories {0} and {1} are writeable.", environmentService.userDataPath, environmentService.extensionsPath) ); + } + } + + private showStartupWarningDialog(message: string, detail: string): void { + dialog.showMessageBox({ + title: product.nameLong, + type: 'warning', + buttons: [mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], + message, + detail, + noLink: true }); } - return setup(true); -} + private async windowsAllowSetForegroundWindow(client: LaunchChannelClient, logService: ILogService): Promise { + if (platform.isWindows) { + const processId = await client.getMainProcessId(); -function showStartupWarningDialog(message: string, detail: string): void { - dialog.showMessageBox({ - title: product.nameLong, - type: 'warning', - buttons: [mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], - message, - detail, - noLink: true - }); -} + logService.trace('Sending some foreground love to the running instance:', processId); -function handleStartupDataDirError(environmentService: IEnvironmentService, error: NodeJS.ErrnoException): void { - if (error.code === 'EACCES' || error.code === 'EPERM') { - showStartupWarningDialog( - localize('startupDataDirError', "Unable to write program user data."), - localize('startupDataDirErrorDetail', "Please make sure the directories {0} and {1} are writeable.", environmentService.userDataPath, environmentService.extensionsPath) - ); - } -} - -function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void { - const logService = accessor.get(ILogService); - const lifecycleService = accessor.get(ILifecycleService); - - let exitCode = 0; - - if (reason) { - if ((reason as ExpectedError).isExpected) { - if (reason.message) { - logService.trace(reason.message); + try { + (await import('windows-foreground-love')).allowSetForegroundWindow(processId); + } catch (error) { + logService.error(error); } - } else { - exitCode = 1; // signal error to the outside + } + } - if (reason.stack) { - logService.error(reason.stack); + private quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void { + const logService = accessor.get(ILogService); + const lifecycleService = accessor.get(ILifecycleService); + + let exitCode = 0; + + if (reason) { + if ((reason as ExpectedError).isExpected) { + if (reason.message) { + logService.trace(reason.message); + } } else { - logService.error(`Startup error: ${reason.toString()}`); + exitCode = 1; // signal error to the outside + + if (reason.stack) { + logService.error(reason.stack); + } else { + logService.error(`Startup error: ${reason.toString()}`); + } } } + + lifecycleService.kill(exitCode); } - - lifecycleService.kill(exitCode); } -function patchEnvironment(environmentService: IEnvironmentService): typeof process.env { - const instanceEnvironment: typeof process.env = { - VSCODE_IPC_HOOK: environmentService.mainIPCHandle, - VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'], - VSCODE_LOGS: process.env['VSCODE_LOGS'], - // {{SQL CARBON EDIT}} We keep VSCODE_LOGS to not break functionality for merged code - ADS_LOGS: process.env['ADS_LOGS'] - }; - - if (process.env['VSCODE_PORTABLE']) { - instanceEnvironment['VSCODE_PORTABLE'] = process.env['VSCODE_PORTABLE']; - } - - assign(process.env, instanceEnvironment); - - return instanceEnvironment; -} - -function startup(args: ParsedArgs): void { - - // We need to buffer the spdlog logs until we are sure - // 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 instantiationService = createServices(args, bufferLogService); - instantiationService.invokeFunction(accessor => { - const environmentService = accessor.get(IEnvironmentService); - const stateService = accessor.get(IStateService); - - // Patch `process.env` with the instance's environment - const instanceEnvironment = patchEnvironment(environmentService); - - // Startup - return initServices(environmentService, stateService as StateService) - .then(() => instantiationService.invokeFunction(setupIPC), error => { - - // Show a dialog for errors that can be resolved by the user - handleStartupDataDirError(environmentService, error); - - return Promise.reject(error); - }) - .then(mainIpcServer => { - createSpdLogService('main', bufferLogService.getLevel(), environmentService.logsPath).then(logger => bufferLogService.logger = logger); - - return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); - }); - }).then(null, err => instantiationService.invokeFunction(quit, err)); -} - -function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { - const services = new ServiceCollection(); - - const environmentService = new EnvironmentService(args, process.execPath); - - const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]); - process.once('exit', () => logService.dispose()); - - services.set(IEnvironmentService, environmentService); - services.set(ILogService, logService); - services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); - services.set(IStateService, new SyncDescriptor(StateService)); - services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath])); - services.set(IRequestService, new SyncDescriptor(RequestService)); - services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); - - return new InstantiationService(services, true); -} - -function initServices(environmentService: IEnvironmentService, stateService: StateService): Promise { - - // Ensure paths for environment service exist - const environmentServiceInitialization = Promise.all([ - environmentService.extensionsPath, - environmentService.nodeCachedDataDir, - environmentService.logsPath, - environmentService.globalStorageHome, - environmentService.workspaceStorageHome, - environmentService.backupHome - ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); - - // State service - const stateServiceInitialization = stateService.init(); - - return Promise.all([environmentServiceInitialization, stateServiceInitialization]); -} - -function main(): void { - - // Set the error handler early enough so that we are not getting the - // default electron error dialog popping up - setUnexpectedErrorHandler(err => console.error(err)); - - // Parse arguments - let args: ParsedArgs; - try { - args = parseMainProcessArgv(process.argv); - args = validatePaths(args); - } catch (err) { - console.error(err.message); - app.exit(1); - - return undefined; - } - - // If we are started with --wait create a random temporary file - // and pass it over to the starting instance. We can use this file - // to wait for it to be deleted to monitor that the edited file - // is closed and then exit the waiting process. - // - // Note: we are not doing this if the wait marker has been already - // added as argument. This can happen if Code was started from CLI. - if (args.wait && !args.waitMarkerFilePath) { - const waitMarkerFilePath = createWaitMarkerFile(args.verbose); - if (waitMarkerFilePath) { - addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); - args.waitMarkerFilePath = waitMarkerFilePath; - } - } - startup(args); -} - -main(); +// Main Startup +const code = new CodeMain(); +code.main(); \ No newline at end of file diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index af04e4b519..6ef4a92a4a 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -11,9 +11,8 @@ import { ISharedProcess } from 'vs/platform/windows/electron-main/windows'; import { Barrier } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; -import { IStateService } from 'vs/platform/state/common/state'; -import { getBackgroundColor } from 'vs/code/electron-main/theme'; -import { dispose, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; +import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; export class SharedProcess implements ISharedProcess { @@ -26,19 +25,20 @@ export class SharedProcess implements ISharedProcess { private userEnv: NodeJS.ProcessEnv, @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IStateService private readonly stateService: IStateService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IThemeMainService private readonly themeMainService: IThemeMainService ) { } @memoize private get _whenReady(): Promise { this.window = new BrowserWindow({ show: false, - backgroundColor: getBackgroundColor(this.stateService), + backgroundColor: this.themeMainService.getBackgroundColor(), webPreferences: { images: false, webaudio: false, webgl: false, + nodeIntegration: true, disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer } }); @@ -67,10 +67,10 @@ export class SharedProcess implements ISharedProcess { this.window.on('close', onClose); - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); this.lifecycleService.onWillShutdown(() => { - dispose(disposables); + disposables.dispose(); // Shut the shared process down when we are quitting // @@ -104,7 +104,7 @@ export class SharedProcess implements ISharedProcess { logLevel: this.logService.getLevel() }); - disposables.push(toDisposable(() => sender.send('handshake:goodbye'))); + disposables.add(toDisposable(() => sender.send('handshake:goodbye'))); ipcMain.once('handshake:im ready', () => c(undefined)); }); }); diff --git a/src/vs/code/electron-main/theme.ts b/src/vs/code/electron-main/theme.ts deleted file mode 100644 index 4710c45392..0000000000 --- a/src/vs/code/electron-main/theme.ts +++ /dev/null @@ -1,44 +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 { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { systemPreferences } from 'electron'; -import { IStateService } from 'vs/platform/state/common/state'; - -const DEFAULT_BG_LIGHT = '#FFFFFF'; -const DEFAULT_BG_DARK = '#1E1E1E'; -const DEFAULT_BG_HC_BLACK = '#000000'; - -const THEME_STORAGE_KEY = 'theme'; -const THEME_BG_STORAGE_KEY = 'themeBackground'; - -export function storeBackgroundColor(stateService: IStateService, data: { baseTheme: string, background: string }): void { - stateService.setItem(THEME_STORAGE_KEY, data.baseTheme); - stateService.setItem(THEME_BG_STORAGE_KEY, data.background); -} - -export function getBackgroundColor(stateService: IStateService): string { - if (isWindows && systemPreferences.isInvertedColorScheme()) { - return DEFAULT_BG_HC_BLACK; - } - - let background = stateService.getItem(THEME_BG_STORAGE_KEY, null); - if (!background) { - let baseTheme: string; - if (isWindows && systemPreferences.isInvertedColorScheme()) { - baseTheme = 'hc-black'; - } else { - baseTheme = stateService.getItem(THEME_STORAGE_KEY, 'vs-dark').split(' ')[0]; - } - - background = (baseTheme === 'hc-black') ? DEFAULT_BG_HC_BLACK : (baseTheme === 'vs' ? DEFAULT_BG_LIGHT : DEFAULT_BG_DARK); - } - - if (isMacintosh && background.toUpperCase() === DEFAULT_BG_DARK) { - background = '#171717'; // https://github.com/electron/electron/issues/5150 - } - - return background; -} \ No newline at end of file diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 99d30e973c..d4b846b4da 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -7,14 +7,13 @@ import * as path from 'vs/base/common/path'; import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { IStateService } from 'vs/platform/state/common/state'; import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display } from 'electron'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { parseArgs } from 'vs/platform/environment/node/argv'; import product from 'vs/platform/product/node/product'; -import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, IRunActionInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -22,10 +21,14 @@ import { IWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/worksp import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import * as perf from 'vs/base/common/performance'; -import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/extensionGalleryService'; -import { getBackgroundColor } from 'vs/code/electron-main/theme'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService'; +import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { endsWith } from 'vs/base/common/strings'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { IFileService } from 'vs/platform/files/common/files'; +import pkg from 'vs/platform/product/node/package'; + +const RUN_TEXTMATE_IN_WORKER = false; export interface IWindowCreationOptions { state: IWindowState; @@ -41,14 +44,6 @@ export const defaultWindowState = function (mode = WindowMode.Normal): IWindowSt }; }; -interface IWorkbenchEditorConfiguration { - workbench: { - editor: { - swipeToNavigate: boolean - } - }; -} - interface ITouchBarSegment extends Electron.SegmentedControlSegment { id: string; } @@ -83,8 +78,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStateService private readonly stateService: IStateService, + @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, ) { @@ -114,7 +110,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { private createBrowserWindow(config: IWindowCreationOptions): void { // Load window state - this.windowState = this.restoreWindowState(config.state); + const [state, hasMultipleDisplays] = this.restoreWindowState(config.state); + this.windowState = state; // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); @@ -124,7 +121,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { height: this.windowState.height, x: this.windowState.x, y: this.windowState.y, - backgroundColor: getBackgroundColor(this.stateService), + backgroundColor: this.themeMainService.getBackgroundColor(), minWidth: CodeWindow.MIN_WIDTH, minHeight: CodeWindow.MIN_HEIGHT, show: !isFullscreenOrMaximized, @@ -134,7 +131,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { // want to enforce that Code stays in the foreground. This triggers a disable_hidden_ // flag that Electron provides via patch: // https://github.com/electron/libchromiumcontent/blob/master/patches/common/chromium/disable_hidden.patch - backgroundThrottling: false + backgroundThrottling: false, + nodeIntegration: true, + nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER, + webviewTag: true } }; @@ -156,7 +156,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - if (isMacintosh && windowConfig && windowConfig.nativeTabs === true) { + const useNativeTabs = isMacintosh && windowConfig && windowConfig.nativeTabs === true; + if (useNativeTabs) { options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs } @@ -177,6 +178,24 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any } + // TODO@Ben (Electron 4 regression): when running on multiple displays where the target display + // to open the window has a larger resolution than the primary display, the window will not size + // correctly unless we set the bounds again (https://github.com/microsoft/vscode/issues/74872) + // + // However, when running with native tabs with multiple windows we cannot use this workaround + // because there is a potential that the new window will be added as native tab instead of being + // a window on its own. In that case calling setBounds() would cause https://github.com/microsoft/vscode/issues/75830 + if (isMacintosh && hasMultipleDisplays && (!useNativeTabs || BrowserWindow.getAllWindows().length === 1)) { + if ([this.windowState.width, this.windowState.height, this.windowState.x, this.windowState.y].every(value => typeof value === 'number')) { + this._win.setBounds({ + width: this.windowState.width!, + height: this.windowState.height!, + x: this.windowState.x!, + y: this.windowState.y! + }); + } + } + if (isFullscreenOrMaximized) { this._win.maximize(); @@ -204,12 +223,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { return !!this.config.extensionTestsPath; } - /* - get extensionDevelopmentPaths(): string | string[] | undefined { - return this.config.extensionDevelopmentPath; - } - */ - get config(): IWindowConfiguration { return this.currentConfig; } @@ -297,13 +310,13 @@ export class CodeWindow extends Disposable implements ICodeWindow { private handleMarketplaceRequests(): void { // Resolve marketplace headers - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.environmentService); + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(pkg.version, this.environmentService, this.fileService); // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { this.marketplaceHeadersPromise.then(headers => { - const requestHeaders = objects.assign(details.requestHeaders, headers); + const requestHeaders = objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined }; if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) { requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`; } @@ -327,12 +340,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { }); this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => { - const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']); + const responseHeaders = details.responseHeaders as { [key: string]: string[] }; + + const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']); if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { return callback({ cancel: true }); } - return callback({ cancel: false, responseHeaders: details.responseHeaders }); + return callback({ cancel: false, responseHeaders }); }); // Remember that we loaded @@ -358,9 +373,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { } }); - // App commands support - this.registerNavigationListenerOn('app-command', 'browser-backward', 'browser-forward', false); - // Window Focus this._win.on('focus', () => { this._lastFocusTime = Date.now(); @@ -446,30 +458,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.currentMenuBarVisibility = newMenuBarVisibility; this.setMenuBarVisibility(newMenuBarVisibility); } - - // Swipe command support (macOS) - if (isMacintosh) { - const config = this.configurationService.getValue(); - if (config && config.workbench && config.workbench.editor && config.workbench.editor.swipeToNavigate) { - this.registerNavigationListenerOn('swipe', 'left', 'right', true); - } else { - this._win.removeAllListeners('swipe'); - } - } - } - - private registerNavigationListenerOn(command: 'swipe' | 'app-command', back: 'left' | 'browser-backward', forward: 'right' | 'browser-forward', acrossEditors: boolean) { - this._win.on(command as 'swipe' /* | 'app-command' */, (e: Electron.Event, cmd: string) => { - if (!this.isReady) { - return; // window must be ready - } - - if (cmd === back) { - this.send('vscode:runAction', { id: acrossEditors ? 'workbench.action.openPreviousRecentlyUsedEditor' : 'workbench.action.navigateBack', from: 'mouse' } as IRunActionInWindowRequest); - } else if (cmd === forward) { - this.send('vscode:runAction', { id: acrossEditors ? 'workbench.action.openNextRecentlyUsedEditor' : 'workbench.action.navigateForward', from: 'mouse' } as IRunActionInWindowRequest); - } - }); } addTabbedWindow(window: ICodeWindow): void { @@ -599,9 +587,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv); const config = objects.assign(environment, windowConfiguration); - for (let key in config) { - if (config[key] === undefined || config[key] === null || config[key] === '' || config[key] === false) { - delete config[key]; // only send over properties that have a true value + for (const key in config) { + const configValue = (config as any)[key]; + if (configValue === undefined || configValue === null || configValue === '' || configValue === false) { + delete (config as any)[key]; // only send over properties that have a true value } } @@ -675,7 +664,12 @@ export class CodeWindow extends Disposable implements ICodeWindow { // only consider non-minimized window states if (mode === WindowMode.Normal || mode === WindowMode.Maximized) { - const bounds = this.getBounds(); + let bounds: Electron.Rectangle; + if (mode === WindowMode.Normal) { + bounds = this.getBounds(); + } else { + bounds = this._win.getNormalBounds(); // make sure to persist the normal bounds when maximized to be able to restore them + } state.x = bounds.x; state.y = bounds.y; @@ -686,18 +680,23 @@ export class CodeWindow extends Disposable implements ICodeWindow { return state; } - private restoreWindowState(state?: IWindowState): IWindowState { + private restoreWindowState(state?: IWindowState): [IWindowState, boolean? /* has multiple displays */] { + let hasMultipleDisplays = false; if (state) { try { - state = this.validateWindowState(state); + const displays = screen.getAllDisplays(); + hasMultipleDisplays = displays.length > 1; + + state = this.validateWindowState(state, displays); } catch (err) { this.logService.warn(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate } } - return state || defaultWindowState(); + + return [state || defaultWindowState(), hasMultipleDisplays]; } - private validateWindowState(state: IWindowState): IWindowState | undefined { + private validateWindowState(state: IWindowState, displays: Display[]): IWindowState | undefined { if (typeof state.x !== 'number' || typeof state.y !== 'number' || typeof state.width !== 'number' @@ -710,12 +709,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { return undefined; } - const displays = screen.getAllDisplays(); - // Single Monitor: be strict about x/y positioning if (displays.length === 1) { const displayWorkingArea = this.getWorkingArea(displays[0]); - if (state.mode !== WindowMode.Maximized && displayWorkingArea) { + if (displayWorkingArea) { if (state.x < displayWorkingArea.x) { state.x = displayWorkingArea.x; // prevent window from falling out of the screen to the left } @@ -741,10 +738,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - if (state.mode === WindowMode.Maximized) { - return defaultWindowState(WindowMode.Maximized); // when maximized, make sure we have good values when the user restores the window - } - return state; } @@ -772,14 +765,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { bounds.x + bounds.width > displayWorkingArea.x && // prevent window from falling out of the screen to the left bounds.y + bounds.height > displayWorkingArea.y // prevent window from falling out of the scree nto the top ) { - if (state.mode === WindowMode.Maximized) { - const defaults = defaultWindowState(WindowMode.Maximized); // when maximized, make sure we have good values when the user restores the window - defaults.x = state.x; // carefull to keep x/y position so that the window ends up on the correct monitor - defaults.y = state.y; - - return defaults; - } - return state; } @@ -853,16 +838,17 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private useNativeFullScreen(): boolean { - const windowConfig = this.configurationService.getValue('window'); - if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') { - return true; // default - } + return true; // TODO@ben enable simple fullscreen again (https://github.com/microsoft/vscode/issues/75054) + // const windowConfig = this.configurationService.getValue('window'); + // if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') { + // return true; // default + // } - if (windowConfig.nativeTabs) { - return true; // https://github.com/electron/electron/issues/16142 - } + // if (windowConfig.nativeTabs) { + // return true; // https://github.com/electron/electron/issues/16142 + // } - return windowConfig.nativeFullScreen !== false; + // return windowConfig.nativeFullScreen !== false; } isMinimized(): boolean { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 9159e6e935..5ba81dfeea 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -15,7 +15,7 @@ import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; import { hasArgs, asArray } from 'vs/platform/environment/node/argv'; import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, FileFilter } from 'electron'; import { parseLineAndColumnAware } from 'vs/code/node/paths'; -import { ILifecycleService, UnloadReason, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleService, UnloadReason, LifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, isFileToOpen, isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; @@ -38,6 +38,8 @@ import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage'; import { getWorkspaceIdentifier } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { once } from 'vs/base/common/functional'; +import { Disposable } from 'vs/base/common/lifecycle'; const enum WindowError { UNRESPONSIVE = 1, @@ -153,15 +155,13 @@ interface IWorkspacePathToOpen { label?: string; } -export class WindowsManager implements IWindowsMainService { +export class WindowsManager extends Disposable implements IWindowsMainService { _serviceBrand: any; private static readonly windowsStateStorageKey = 'windowsState'; - private static WINDOWS: ICodeWindow[] = []; - - private initialUserEnv: IProcessEnvironment; + private static readonly WINDOWS: ICodeWindow[] = []; private readonly windowsState: IWindowsState; private lastClosedWindowState?: IWindowState; @@ -169,20 +169,21 @@ export class WindowsManager implements IWindowsMainService { private readonly dialogs: Dialogs; private readonly workspacesManager: WorkspacesManager; - private _onWindowReady = new Emitter(); - onWindowReady: CommonEvent = this._onWindowReady.event; + private readonly _onWindowReady = this._register(new Emitter()); + readonly onWindowReady: CommonEvent = this._onWindowReady.event; - private _onWindowClose = new Emitter(); - onWindowClose: CommonEvent = this._onWindowClose.event; + private readonly _onWindowClose = this._register(new Emitter()); + readonly onWindowClose: CommonEvent = this._onWindowClose.event; - private _onWindowLoad = new Emitter(); - onWindowLoad: CommonEvent = this._onWindowLoad.event; + private readonly _onWindowLoad = this._register(new Emitter()); + readonly onWindowLoad: CommonEvent = this._onWindowLoad.event; - private _onWindowsCountChanged = new Emitter(); - onWindowsCountChanged: CommonEvent = this._onWindowsCountChanged.event; + private readonly _onWindowsCountChanged = this._register(new Emitter()); + readonly onWindowsCountChanged: CommonEvent = this._onWindowsCountChanged.event; constructor( private readonly machineId: string, + private readonly initialUserEnv: IProcessEnvironment, @ILogService private readonly logService: ILogService, @IStateService private readonly stateService: IStateService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -194,6 +195,7 @@ export class WindowsManager implements IWindowsMainService { @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { + super(); const windowsStateStoreData = this.stateService.getItem(WindowsManager.windowsStateStorageKey); this.windowsState = restoreWindowsState(windowsStateStoreData); @@ -203,12 +205,21 @@ export class WindowsManager implements IWindowsMainService { this.dialogs = new Dialogs(stateService, this); this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this); + + this.lifecycleService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners()); + this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.installWindowsMutex()); } - ready(initialUserEnv: IProcessEnvironment): void { - this.initialUserEnv = initialUserEnv; - - this.registerListeners(); + private installWindowsMutex(): void { + if (isWindows) { + try { + const WindowsMutex = (require.__$__nodeRequire('windows-mutex') as typeof import('windows-mutex')).Mutex; + const mutex = new WindowsMutex(product.win32MutexName); + once(this.lifecycleService.onWillShutdown)(() => mutex.release()); + } catch (e) { + this.logService.error(e); + } + } } private registerListeners(): void { @@ -543,6 +554,7 @@ export class WindowsManager implements IWindowsMainService { // Find suitable window or folder path to open files in const fileToCheck = fileInputs.filesToOpenOrCreate[0] || fileInputs.filesToDiff[0]; + // only look at the windows with correct authority const windows = WindowsManager.WINDOWS.filter(w => w.remoteAuthority === fileInputs!.remoteAuthority); @@ -639,7 +651,6 @@ export class WindowsManager implements IWindowsMainService { // Handle folders to open (instructed and to restore) const allFoldersToOpen = arrays.distinct(foldersToOpen, folder => getComparisonKey(folder.folderUri)); // prevent duplicates - if (allFoldersToOpen.length > 0) { // Check for existing instances @@ -713,7 +724,9 @@ export class WindowsManager implements IWindowsMainService { if (fileInputs && !emptyToOpen) { emptyToOpen++; } + const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined); + for (let i = 0; i < emptyToOpen; i++) { usedWindows.push(this.openInBrowserWindow({ userEnv: openConfig.userEnv, @@ -1289,7 +1302,7 @@ export class WindowsManager implements IWindowsMainService { // For all other cases we first call into registerEmptyWindowBackupSync() to set it before // loading the window. if (options.emptyWindowBackupInfo) { - configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder); + configuration.backupPath = join(this.environmentService.backupHome.fsPath, options.emptyWindowBackupInfo.backupFolder); } let window: ICodeWindow | undefined; @@ -1536,14 +1549,13 @@ export class WindowsManager implements IWindowsMainService { return state; } - reload(win: ICodeWindow, cli?: ParsedArgs): void { + async reload(win: ICodeWindow, cli?: ParsedArgs): Promise { // Only reload when the window has not vetoed this - this.lifecycleService.unload(win, UnloadReason.RELOAD).then(veto => { - if (!veto) { - win.reload(undefined, cli); - } - }); + const veto = await this.lifecycleService.unload(win, UnloadReason.RELOAD); + if (!veto) { + win.reload(undefined, cli); + } } closeWorkspace(win: ICodeWindow): void { @@ -1554,8 +1566,10 @@ export class WindowsManager implements IWindowsMainService { }); } - enterWorkspace(win: ICodeWindow, path: URI): Promise { - return this.workspacesManager.enterWorkspace(win, path).then(result => result ? this.doEnterWorkspace(win, result) : undefined); + async enterWorkspace(win: ICodeWindow, path: URI): Promise { + const result = await this.workspacesManager.enterWorkspace(win, path); + + return result ? this.doEnterWorkspace(win, result) : undefined; } private doEnterWorkspace(win: ICodeWindow, result: IEnterWorkspaceResult): IEnterWorkspaceResult { @@ -1666,14 +1680,13 @@ export class WindowsManager implements IWindowsMainService { private onWindowError(window: ICodeWindow, error: WindowError): void { this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); - - /* __GDPR__ - "windowerror" : { - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('windowerror', { type: error }); - + type WindowErrorClassification = { + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + }; + type WindowErrorEvent = { + type: WindowError; + }; + this.telemetryService.publicLog2('windowerror', { type: error }); // Unresponsive if (error === WindowError.UNRESPONSIVE) { if (window.isExtensionDevelopmentHost || window.isExtensionTestHost || (window.win && window.win.webContents && window.win.webContents.isDevToolsOpened())) { @@ -1751,8 +1764,10 @@ export class WindowsManager implements IWindowsMainService { const paths = await this.dialogs.pick({ ...options, pickFolders: true, pickFiles: true, title }); if (paths) { this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData); - const urisToOpen = await Promise.all(paths.map(path => { - return dirExists(path).then(isDir => isDir ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) }); + const urisToOpen = await Promise.all(paths.map(async path => { + const isDir = await dirExists(path); + + return isDir ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) }; })); this.open({ context: OpenContext.DIALOG, @@ -1880,7 +1895,7 @@ class Dialogs { this.noWindowDialogQueue = new Queue(); } - pick(options: IInternalNativeOpenDialogOptions): Promise { + async pick(options: IInternalNativeOpenDialogOptions): Promise { // Ensure dialog options const dialogOptions: Electron.OpenDialogOptions = { @@ -1913,16 +1928,16 @@ class Dialogs { // Show Dialog const focusedWindow = (typeof options.windowId === 'number' ? this.windowsMainService.getWindowById(options.windowId) : undefined) || this.windowsMainService.getFocusedWindow(); - return this.showOpenDialog(dialogOptions, focusedWindow).then(paths => { - if (paths && paths.length > 0) { + const paths = await this.showOpenDialog(dialogOptions, focusedWindow); + if (paths && paths.length > 0) { - // Remember path in storage for next time - this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(paths[0])); - return paths; - } + // Remember path in storage for next time + this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(paths[0])); - return undefined; - }); + return paths; + } + + return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check } private getDialogQueue(window?: ICodeWindow): Queue { @@ -2028,28 +2043,26 @@ class WorkspacesManager { private readonly windowsMainService: IWindowsMainService, ) { } - enterWorkspace(window: ICodeWindow, path: URI): Promise { + async enterWorkspace(window: ICodeWindow, path: URI): Promise { if (!window || !window.win || !window.isReady) { - return Promise.resolve(null); // return early if the window is not ready or disposed + return null; // return early if the window is not ready or disposed } - return this.isValidTargetWorkspacePath(window, path).then(isValid => { - if (!isValid) { - return null; // return early if the workspace is not valid - } - const workspaceIdentifier = getWorkspaceIdentifier(path); - return this.doOpenWorkspace(window, workspaceIdentifier); - }); + const isValid = await this.isValidTargetWorkspacePath(window, path); + if (!isValid) { + return null; // return early if the workspace is not valid + } + return this.doOpenWorkspace(window, getWorkspaceIdentifier(path)); } - private isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise { + private async isValidTargetWorkspacePath(window: ICodeWindow, path?: URI): Promise { if (!path) { - return Promise.resolve(true); + return true; } if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) { - return Promise.resolve(false); // window is already opened on a workspace with that path + return false; // window is already opened on a workspace with that path } // Prevent overwriting a workspace that is currently opened in another window @@ -2063,10 +2076,12 @@ class WorkspacesManager { noLink: true }; - return this.windowsMainService.showMessageBox(options, this.windowsMainService.getFocusedWindow()).then(() => false); + await this.windowsMainService.showMessageBox(options, this.windowsMainService.getFocusedWindow()); + + return false; } - return Promise.resolve(true); // OK + return true; // OK } private doOpenWorkspace(window: ICodeWindow, workspace: IWorkspaceIdentifier): IEnterWorkspaceResult { @@ -2090,14 +2105,16 @@ class WorkspacesManager { return { workspace, backupPath }; } - } -function resourceFromURIToOpen(u: IURIToOpen) { +function resourceFromURIToOpen(u: IURIToOpen): URI { if (isWorkspaceToOpen(u)) { return u.workspaceUri; - } else if (isFolderToOpen(u)) { + } + + if (isFolderToOpen(u)) { return u.folderUri; } + return u.fileUri; } \ No newline at end of file diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index bf2c6afc32..d5cfa715e8 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { spawn, ChildProcess } from 'child_process'; +import { spawn, ChildProcess, SpawnOptions } from 'child_process'; import { assign } from 'vs/base/common/objects'; import { buildHelpMessage, buildVersionMessage, addArg, createWaitMarkerFile } from 'vs/platform/environment/node/argv'; import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; @@ -19,13 +19,15 @@ import { resolveTerminalEncoding } from 'vs/base/node/encoding'; import * as iconv from 'iconv-lite'; import { isWindows } from 'vs/base/common/platform'; import { ProfilingSession, Target } from 'v8-inspect-profiler'; +import { isString } from 'vs/base/common/types'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return !!argv['install-source'] || !!argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension'] - || !!argv['locate-extension']; + || !!argv['locate-extension'] + || !!argv['telemetry']; } interface IMainCli { @@ -57,6 +59,7 @@ export async function main(argv: string[]): Promise { else if (shouldSpawnCliProcess(args)) { const cli = await new Promise((c, e) => require(['vs/code/node/cliProcessMain'], c, e)); await cli.main(args); + return; } @@ -124,7 +127,7 @@ export async function main(argv: string[]): Promise { const processCallbacks: ((child: ChildProcess) => Promise)[] = []; - const verbose = args.verbose || args.status || typeof args['upload-logs'] !== 'undefined'; + const verbose = args.verbose || args.status; if (verbose) { env['ELECTRON_ENABLE_LOGGING'] = '1'; @@ -257,7 +260,7 @@ export async function main(argv: string[]): Promise { addArg(argv, `--prof-startup-prefix`, filenamePrefix); addArg(argv, `--no-cached-data`); - fs.writeFileSync(filenamePrefix, argv.slice(-6).join('|')); + writeFileSync(filenamePrefix, argv.slice(-6).join('|')); processCallbacks.push(async _child => { @@ -329,7 +332,7 @@ export async function main(argv: string[]): Promise { await extHost.stop(); // re-create the marker file to signal that profiling is done - fs.writeFileSync(filenamePrefix, ''); + writeFileSync(filenamePrefix, ''); } catch (e) { console.error('Failed to profile startup. Make sure to quit Code first.'); @@ -337,21 +340,20 @@ export async function main(argv: string[]): Promise { }); } - if (args['js-flags']) { - const match = /max_old_space_size=(\d+)/g.exec(args['js-flags']); + const jsFlags = args['js-flags']; + if (isString(jsFlags)) { + const match = /max_old_space_size=(\d+)/g.exec(jsFlags); if (match && !args['max-memory']) { addArg(argv, `--max-memory=${match[1]}`); } } - const options = { + const options: SpawnOptions = { detached: true, env }; - if (typeof args['upload-logs'] !== 'undefined') { - options['stdio'] = ['pipe', 'pipe', 'pipe']; - } else if (!verbose) { + if (!verbose) { options['stdio'] = 'ignore'; } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 04c6304e0e..b4374931e4 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -17,12 +17,12 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/ import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IExtensionManagementService, IExtensionGalleryService, IGalleryExtension, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { IRequestService } from 'vs/platform/request/node/request'; +import { IRequestService } from 'vs/platform/request/common/request'; import { RequestService } from 'vs/platform/request/node/requestService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; @@ -31,7 +31,6 @@ import { mkdirp, writeFile } from 'vs/base/node/pfs'; import { getBaseLabel } from 'vs/base/common/labels'; import { IStateService } from 'vs/platform/state/common/state'; import { StateService } from 'vs/platform/state/node/stateService'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -41,10 +40,18 @@ import { IExtensionManifest, ExtensionType, isLanguagePackExtension } from 'vs/p import { CancellationToken } from 'vs/base/common/cancellation'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { Schemas } from 'vs/base/common/network'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { buildTelemetryMessage } from 'vs/platform/telemetry/node/telemetry'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IProductService } from 'vs/platform/product/common/product'; +import { ProductService } from 'vs/platform/product/node/productService'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); -const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, eg: {0}", 'ms-vscode.csharp'); +const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-vscode.csharp'); function getId(manifest: IExtensionManifest, withVersion?: boolean): string { if (withVersion) { @@ -84,7 +91,7 @@ export class Main { } else if (argv['install-extension']) { const arg = argv['install-extension']; const args: string[] = typeof arg === 'string' ? [arg] : arg; - await this.installExtensions(args, argv['force']); + await this.installExtensions(args, !!argv['force']); } else if (argv['uninstall-extension']) { const arg = argv['uninstall-extension']; @@ -94,6 +101,8 @@ export class Main { const arg = argv['locate-extension']; const ids: string[] = typeof arg === 'string' ? [arg] : arg; await this.locateExtension(ids); + } else if (argv['telemetry']) { + console.log(buildTelemetryMessage(this.environmentService.appRoot, this.environmentService.extensionsPath)); } } @@ -275,59 +284,79 @@ export class Main { const eventPrefix = 'monacoworkbench'; -export function main(argv: ParsedArgs): Promise { +export async function main(argv: ParsedArgs): Promise { const services = new ServiceCollection(); + const disposables = new DisposableStore(); const environmentService = new EnvironmentService(argv, process.execPath); - const logService = createBufferSpdLogService('cli', getLogLevel(environmentService), environmentService.logsPath); + const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService)); process.once('exit', () => logService.dispose()); - logService.info('main', argv); + await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath].map(p => mkdirp(p))); + + const configurationService = new ConfigurationService(environmentService.settingsResource); + disposables.add(configurationService); + await configurationService.initialize(); + services.set(IEnvironmentService, environmentService); services.set(ILogService, logService); + services.set(IConfigurationService, configurationService); services.set(IStateService, new SyncDescriptor(StateService)); + services.set(IProductService, new SyncDescriptor(ProductService)); + + // Files + const fileService = new FileService(logService); + disposables.add(fileService); + services.set(IFileService, fileService); + + const diskFileSystemProvider = new DiskFileSystemProvider(logService); + disposables.add(diskFileSystemProvider); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); const instantiationService: IInstantiationService = new InstantiationService(services); - return instantiationService.invokeFunction(accessor => { + return instantiationService.invokeFunction(async accessor => { const envService = accessor.get(IEnvironmentService); const stateService = accessor.get(IStateService); - return Promise.all([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { - const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService; - const services = new ServiceCollection(); - services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.appSettingsPath])); - services.set(IRequestService, new SyncDescriptor(RequestService)); - services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); - services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + const services = new ServiceCollection(); - const appenders: AppInsightsAppender[] = []; - if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) { - if (product.aiConfig && product.aiConfig.asimovKey) { - appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService)); - } + services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); - const config: ITelemetryServiceConfig = { - appender: combinedAppender(...appenders), - commonProperties: resolveCommonProperties(product.commit, pkg.version, stateService.getItem('telemetry.machineId'), installSourcePath), - piiPaths: [appRoot, extensionsPath] - }; + const appenders: AppInsightsAppender[] = []; + if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) { - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); - } else { - services.set(ITelemetryService, NullTelemetryService); + if (product.aiConfig && product.aiConfig.asimovKey) { + appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService)); } - const instantiationService2 = instantiationService.createChild(services); - const main = instantiationService2.createInstance(Main); + const config: ITelemetryServiceConfig = { + appender: combinedAppender(...appenders), + commonProperties: resolveCommonProperties(product.commit, pkg.version, stateService.getItem('telemetry.machineId'), installSourcePath), + piiPaths: [appRoot, extensionsPath] + }; - return main.run(argv).then(() => { - // Dispose the AI adapter so that remaining data gets flushed. - return combinedAppender(...appenders).dispose(); - }); - }); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); + + // Dispose the AI adapter so that remaining data gets flushed. + disposables.add(combinedAppender(...appenders)); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + const instantiationService2 = instantiationService.createChild(services); + const main = instantiationService2.createInstance(Main); + + try { + await main.run(argv); + } finally { + disposables.dispose(); + } }); } \ No newline at end of file diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 80ab30eb96..856fd9f96e 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -8,6 +8,7 @@ import { assign } from 'vs/base/common/objects'; import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; function getUnixShellEnvironment(logService: ILogService): Promise { const promise = new Promise((resolve, reject) => { @@ -89,13 +90,16 @@ let _shellEnv: Promise; * This should only be done when Code itself is not launched * from within a shell. */ -export function getShellEnvironment(logService: ILogService): Promise { +export function getShellEnvironment(logService: ILogService, environmentService: IEnvironmentService): Promise { if (_shellEnv === undefined) { - if (isWindows) { - logService.trace('getShellEnvironment: runing on windows, skipping'); + if (environmentService.args['disable-user-env-probe']) { + logService.trace('getShellEnvironment: disable-user-env-probe set, skipping'); + _shellEnv = Promise.resolve({}); + } else if (isWindows) { + logService.trace('getShellEnvironment: running on Windows, skipping'); _shellEnv = Promise.resolve({}); } else if (process.env['VSCODE_CLI'] === '1') { - logService.trace('getShellEnvironment: runing on CLI, skipping'); + logService.trace('getShellEnvironment: running on CLI, skipping'); _shellEnv = Promise.resolve({}); } else { logService.trace('getShellEnvironment: running on Unix'); diff --git a/src/vs/code/test/electron-main/nativeHelpers.test.ts b/src/vs/code/test/electron-main/nativeHelpers.test.ts new file mode 100644 index 0000000000..8930b344c1 --- /dev/null +++ b/src/vs/code/test/electron-main/nativeHelpers.test.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 * as assert from 'assert'; +import { isWindows } from 'vs/base/common/platform'; + +suite('Windows Native Helpers', () => { + test('windows-mutex', async () => { + if (!isWindows) { + return; + } + + const mutex = await import('windows-mutex'); + assert.ok(mutex, 'Unable to load windows-mutex dependency.'); + assert.ok(typeof mutex.isActive === 'function', 'Unable to load windows-mutex dependency.'); + }); + + test('windows-foreground-love', async () => { + if (!isWindows) { + return; + } + + const foregroundLove = await import('windows-foreground-love'); + assert.ok(foregroundLove, 'Unable to load windows-foreground-love dependency.'); + }); +}); \ No newline at end of file diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/code/test/node/argv.test.ts index ea04ed5d5e..31aac0ee98 100644 --- a/src/vs/code/test/node/argv.test.ts +++ b/src/vs/code/test/node/argv.test.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { formatOptions, Option, addArg } from 'vs/platform/environment/node/argv'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; suite('formatOptions', () => { - function o(id: string, description: string): Option { + function o(id: keyof ParsedArgs, description: string): Option { return { id, description, type: 'string' }; @@ -16,30 +17,30 @@ suite('formatOptions', () => { test('Text should display small columns correctly', () => { assert.deepEqual( formatOptions([ - o('foo', 'bar') + o('add', 'bar') ], 80), - [' --foo bar'] + [' --add bar'] ); assert.deepEqual( formatOptions([ - o('f', 'bar'), - o('fo', 'ba'), - o('foo', 'b') + o('add', 'bar'), + o('wait', 'ba'), + o('trace', 'b') ], 80), [ - ' --f bar', - ' --fo ba', - ' --foo b' + ' --add bar', + ' --wait ba', + ' --trace b' ]); }); test('Text should wrap', () => { assert.deepEqual( formatOptions([ - o('foo', ('bar ').repeat(9)) + o('add', ('bar ').repeat(9)) ], 40), [ - ' --foo bar bar bar bar bar bar bar bar', + ' --add bar bar bar bar bar bar bar bar', ' bar' ]); }); @@ -47,10 +48,10 @@ suite('formatOptions', () => { test('Text should revert to the condensed view when the terminal is too narrow', () => { assert.deepEqual( formatOptions([ - o('foo', ('bar ').repeat(9)) + o('add', ('bar ').repeat(9)) ], 30), [ - ' --foo', + ' --add', ' bar bar bar bar bar bar bar bar bar ' ]); }); diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 50f46c438c..880ffc57b1 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -17,7 +17,7 @@ var _cssPluginGlobal = this; var CSSBuildLoaderPlugin; (function (CSSBuildLoaderPlugin) { - var global = _cssPluginGlobal || {}; + var global = (_cssPluginGlobal || {}); /** * Known issue: * - In IE there is no way to know if the CSS file loaded successfully or not. @@ -319,7 +319,7 @@ var CSSBuildLoaderPlugin; global.cssInlinedResources = global.cssInlinedResources || []; var normalizedFSPath = fsPath.replace(/\\/g, '/'); if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { - // console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); + console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); } global.cssInlinedResources.push(normalizedFSPath); var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 884dc7b87f..e3f2b36904 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -14,7 +14,6 @@ import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/c import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { IDimension } from 'vs/editor/common/editorCommon'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; class CSSBasedConfigurationCache { @@ -62,28 +61,17 @@ export function readFontInfo(bareFontInfo: BareFontInfo): FontInfo { return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo); } -export function restoreFontInfo(storageService: IStorageService): void { - const strStoredFontInfo = storageService.get('editorFontInfo', StorageScope.GLOBAL); - if (typeof strStoredFontInfo !== 'string') { - return; - } - let storedFontInfo: ISerializedFontInfo[] | null = null; - try { - storedFontInfo = JSON.parse(strStoredFontInfo); - } catch (err) { - return; - } - if (!Array.isArray(storedFontInfo)) { - return; - } - CSSBasedConfiguration.INSTANCE.restoreFontInfo(storedFontInfo); +export function restoreFontInfo(fontInfo: ISerializedFontInfo[]): void { + CSSBasedConfiguration.INSTANCE.restoreFontInfo(fontInfo); } -export function saveFontInfo(storageService: IStorageService): void { - const knownFontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo(); - if (knownFontInfo.length > 0) { - storageService.store('editorFontInfo', JSON.stringify(knownFontInfo), StorageScope.GLOBAL); +export function serializeFontInfo(): ISerializedFontInfo[] | null { + const fontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo(); + if (fontInfo.length > 0) { + return fontInfo; } + + return null; } export interface ISerializedFontInfo { diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index a4f73d442e..b4f9b34258 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IPointerHandlerHelper, MouseHandler } from 'vs/editor/browser/controller/mouseHandler'; import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; import { EditorMouseEvent } from 'vs/editor/browser/editorDom'; @@ -195,11 +195,6 @@ class TouchHandler extends MouseHandler { this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e))); this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e))); this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, this.viewHelper.viewDomNode), false))); - - } - - public dispose(): void { - super.dispose(); } private onTap(event: GestureEvent): void { @@ -219,26 +214,23 @@ class TouchHandler extends MouseHandler { } } -export class PointerHandler implements IDisposable { +export class PointerHandler extends Disposable { private readonly handler: MouseHandler; constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + super(); if (window.navigator.msPointerEnabled) { - this.handler = new MsPointerHandler(context, viewController, viewHelper); + this.handler = this._register(new MsPointerHandler(context, viewController, viewHelper)); } else if ((window).TouchEvent) { - this.handler = new TouchHandler(context, viewController, viewHelper); + this.handler = this._register(new TouchHandler(context, viewController, viewHelper)); } else if (window.navigator.pointerEnabled || (window).PointerEvent) { - this.handler = new StandardPointerHandler(context, viewController, viewHelper); + this.handler = this._register(new StandardPointerHandler(context, viewController, viewHelper)); } else { - this.handler = new MouseHandler(context, viewController, viewHelper); + this.handler = this._register(new MouseHandler(context, viewController, viewHelper)); } } public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null { return this.handler.getTargetAtClientPoint(clientX, clientY); } - - public dispose(): void { - this.handler.dispose(); - } } diff --git a/src/vs/editor/browser/core/editorState.ts b/src/vs/editor/browser/core/editorState.ts index 923566afae..8eb4a24723 100644 --- a/src/vs/editor/browser/core/editorState.ts +++ b/src/vs/editor/browser/core/editorState.ts @@ -8,7 +8,7 @@ import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser' import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; import { EditorKeybindingCancellationTokenSource } from 'vs/editor/browser/core/keybindingCancellation'; @@ -79,30 +79,30 @@ export class EditorState { * A cancellation token source that cancels when the editor changes as expressed * by the provided flags */ -export class EditorStateCancellationTokenSource extends EditorKeybindingCancellationTokenSource { +export class EditorStateCancellationTokenSource extends EditorKeybindingCancellationTokenSource implements IDisposable { - private readonly _listener: IDisposable[] = []; + private readonly _listener = new DisposableStore(); constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, parent?: CancellationToken) { super(editor, parent); if (flags & CodeEditorStateFlag.Position) { - this._listener.push(editor.onDidChangeCursorPosition(_ => this.cancel())); + this._listener.add(editor.onDidChangeCursorPosition(_ => this.cancel())); } if (flags & CodeEditorStateFlag.Selection) { - this._listener.push(editor.onDidChangeCursorSelection(_ => this.cancel())); + this._listener.add(editor.onDidChangeCursorSelection(_ => this.cancel())); } if (flags & CodeEditorStateFlag.Scroll) { - this._listener.push(editor.onDidScrollChange(_ => this.cancel())); + this._listener.add(editor.onDidScrollChange(_ => this.cancel())); } if (flags & CodeEditorStateFlag.Value) { - this._listener.push(editor.onDidChangeModel(_ => this.cancel())); - this._listener.push(editor.onDidChangeModelContent(_ => this.cancel())); + this._listener.add(editor.onDidChangeModel(_ => this.cancel())); + this._listener.add(editor.onDidChangeModelContent(_ => this.cancel())); } } dispose() { - dispose(this._listener); + this._listener.dispose(); super.dispose(); } } @@ -110,7 +110,7 @@ export class EditorStateCancellationTokenSource extends EditorKeybindingCancella /** * A cancellation token source that cancels when the provided model changes */ -export class TextModelCancellationTokenSource extends CancellationTokenSource { +export class TextModelCancellationTokenSource extends CancellationTokenSource implements IDisposable { private _listener: IDisposable; diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 18d758279c..00c9b07738 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -6,14 +6,14 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { WorkspaceEdit } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IProgressRunner } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; export const IBulkEditService = createDecorator('IWorkspaceEditService'); export interface IBulkEditOptions { editor?: ICodeEditor; - progress?: IProgressRunner; + progress?: IProgress; } export interface IBulkEditResult { diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 7a987f43dc..147e7bb855 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { IDisposable, dispose as disposeAll } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -17,18 +17,17 @@ import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/them export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { private readonly _styleSheet: HTMLStyleElement; - private readonly _decorationOptionProviders: { [key: string]: IModelDecorationOptionsProvider }; + private readonly _decorationOptionProviders = new Map(); private readonly _themeService: IThemeService; constructor(@IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) { super(); this._styleSheet = styleSheet; - this._decorationOptionProviders = Object.create(null); this._themeService = themeService; } public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { - let provider = this._decorationOptionProviders[key]; + let provider = this._decorationOptionProviders.get(key); if (!provider) { const providerArgs: ProviderArguments = { styleSheet: this._styleSheet, @@ -41,17 +40,17 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { } else { provider = new DecorationSubTypeOptionsProvider(this._themeService, providerArgs); } - this._decorationOptionProviders[key] = provider; + this._decorationOptionProviders.set(key, provider); } provider.refCount++; } public removeDecorationType(key: string): void { - const provider = this._decorationOptionProviders[key]; + const provider = this._decorationOptionProviders.get(key); if (provider) { provider.refCount--; if (provider.refCount <= 0) { - delete this._decorationOptionProviders[key]; + this._decorationOptionProviders.delete(key); provider.dispose(); this.listCodeEditors().forEach((ed) => ed.removeDecorations(key)); } @@ -59,7 +58,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { } public resolveDecorationOptions(decorationTypeKey: string, writable: boolean): IModelDecorationOptions { - const provider = this._decorationOptionProviders[decorationTypeKey]; + const provider = this._decorationOptionProviders.get(decorationTypeKey); if (!provider) { throw new Error('Unknown decoration type key: ' + decorationTypeKey); } @@ -124,7 +123,7 @@ interface ProviderArguments { class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { - private _disposables: IDisposable[]; + private readonly _disposables = new DisposableStore(); public refCount: number; public className: string | undefined; @@ -139,11 +138,10 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { constructor(themeService: IThemeService, providerArgs: ProviderArguments) { this.refCount = 0; - this._disposables = []; const createCSSRules = (type: ModelDecorationCSSRuleType) => { const rules = new DecorationCSSRules(type, providerArgs, themeService); - this._disposables.push(rules); + this._disposables.add(rules); if (rules.hasContent) { return rules.className; } @@ -151,7 +149,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { }; const createInlineCSSRules = (type: ModelDecorationCSSRuleType) => { const rules = new DecorationCSSRules(type, providerArgs, themeService); - this._disposables.push(rules); + this._disposables.add(rules); if (rules.hasContent) { return { className: rules.className, hasLetterSpacing: rules.hasLetterSpacing }; } @@ -203,7 +201,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { } public dispose(): void { - this._disposables = disposeAll(this._disposables); + this._disposables.dispose(); } } @@ -401,7 +399,7 @@ class DecorationCSSRules { if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); if (typeof opts.contentIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.revive(opts.contentIconPath).toString(true).replace(/'/g, '%27'))); + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asDomUri(URI.revive(opts.contentIconPath)).toString(true).replace(/'/g, '%27'))); } if (typeof opts.contentText === 'string') { const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line @@ -428,7 +426,7 @@ class DecorationCSSRules { const cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asDomUri(URI.revive(opts.gutterIconPath)).toString(true).replace(/'/g, '%27'))); if (typeof opts.gutterIconSize !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 32bc7cff95..f223361745 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -132,7 +132,7 @@ export class View extends ViewEventHandler { this._setLayout(); // Pointer handler - this.pointerHandler = new PointerHandler(this._context, viewController, this.createPointerHandlerHelper()); + this.pointerHandler = this._register(new PointerHandler(this._context, viewController, this.createPointerHandlerHelper())); this._register(model.addEventListener((events: viewEvents.ViewEvent[]) => { this.eventDispatcher.emitMany(events); @@ -342,8 +342,6 @@ export class View extends ViewEventHandler { this.eventDispatcher.removeEventHandler(this); this.outgoingEvents.dispose(); - this.pointerHandler.dispose(); - this.viewLines.dispose(); // Destroy view parts diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index a4dffc940c..05fca5ec32 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -126,7 +126,7 @@ export class RangeUtil { if (startChildIndex !== endChildIndex) { if (endChildIndex > 0 && endOffset === 0) { endChildIndex--; - endOffset = Number.MAX_VALUE; + endOffset = Constants.MAX_SAFE_SMALL_INTEGER; } } diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 98158ce394..bd498c008b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -23,9 +23,10 @@ import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/v import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; +import { ViewLineData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; function getMinimapLineHeight(renderMinimap: RenderMinimap): number { if (renderMinimap === RenderMinimap.Large) { @@ -257,7 +258,12 @@ class MinimapLayout { const computedSliderRatio = (maxMinimapSliderTop) / (scrollHeight - viewportHeight); const sliderTop = (scrollTop * computedSliderRatio); - if (minimapLinesFitting >= lineCount) { + let extraLinesAtTheBottom = 0; + if (options.scrollBeyondLastLine) { + const expectedViewportLineCount = viewportHeight / lineHeight; + extraLinesAtTheBottom = expectedViewportLineCount; + } + if (minimapLinesFitting >= lineCount + extraLinesAtTheBottom) { // All lines fit in the minimap const startLineNumber = 1; const endLineNumber = lineCount; @@ -330,10 +336,7 @@ class RenderData { * Check if the current RenderData matches accurately the new desired layout and no painting is needed. */ public linesEquals(layout: MinimapLayout): boolean { - if (this.renderedLayout.startLineNumber !== layout.startLineNumber) { - return false; - } - if (this.renderedLayout.endLineNumber !== layout.endLineNumber) { + if (!this.scrollEquals(layout)) { return false; } @@ -349,6 +352,14 @@ class RenderData { return true; } + /** + * Check if the current RenderData matches the new layout's scroll position + */ + public scrollEquals(layout: MinimapLayout): boolean { + return this.renderedLayout.startLineNumber === layout.startLineNumber + && this.renderedLayout.endLineNumber === layout.endLineNumber; + } + _get(): { imageData: ImageData; rendLineNumberStart: number; lines: MinimapLine[]; } { const tmp = this._renderedLines._get(); return { @@ -430,6 +441,7 @@ export class Minimap extends ViewPart { private readonly _domNode: FastDomNode; private readonly _shadow: FastDomNode; private readonly _canvas: FastDomNode; + private readonly _decorationsCanvas: FastDomNode; private readonly _slider: FastDomNode; private readonly _sliderHorizontal: FastDomNode; private readonly _tokensColorTracker: MinimapTokensColorTracker; @@ -439,6 +451,7 @@ export class Minimap extends ViewPart { private _options: MinimapOptions; private _lastRenderData: RenderData | null; + private _renderDecorations: boolean = false; private _buffers: MinimapBuffers | null; constructor(context: ViewContext) { @@ -464,6 +477,12 @@ export class Minimap extends ViewPart { this._canvas.setLeft(0); this._domNode.appendChild(this._canvas); + this._decorationsCanvas = createFastDomNode(document.createElement('canvas')); + this._decorationsCanvas.setPosition('absolute'); + this._decorationsCanvas.setClassName('minimap-decorations-layer'); + this._decorationsCanvas.setLeft(0); + this._domNode.appendChild(this._decorationsCanvas); + this._slider = createFastDomNode(document.createElement('div')); this._slider.setPosition('absolute'); this._slider.setClassName('minimap-slider'); @@ -479,7 +498,7 @@ export class Minimap extends ViewPart { this._applyLayout(); - this._mouseDownListener = dom.addStandardDisposableListener(this._canvas.domNode, 'mousedown', (e) => { + this._mouseDownListener = dom.addStandardDisposableListener(this._domNode.domNode, 'mousedown', (e) => { e.preventDefault(); const renderMinimap = this._options.renderMinimap; @@ -508,6 +527,7 @@ export class Minimap extends ViewPart { this._sliderMouseDownListener = dom.addStandardDisposableListener(this._slider.domNode, 'mousedown', (e) => { e.preventDefault(); + e.stopPropagation(); if (e.leftButton && this._lastRenderData) { const initialMousePosition = e.posy; @@ -564,10 +584,17 @@ export class Minimap extends ViewPart { this._domNode.setWidth(this._options.minimapWidth); this._domNode.setHeight(this._options.minimapHeight); this._shadow.setHeight(this._options.minimapHeight); + this._canvas.setWidth(this._options.canvasOuterWidth); this._canvas.setHeight(this._options.canvasOuterHeight); this._canvas.domNode.width = this._options.canvasInnerWidth; this._canvas.domNode.height = this._options.canvasInnerHeight; + + this._decorationsCanvas.setWidth(this._options.canvasOuterWidth); + this._decorationsCanvas.setHeight(this._options.canvasOuterHeight); + this._decorationsCanvas.domNode.width = this._options.canvasInnerWidth; + this._decorationsCanvas.domNode.height = this._options.canvasInnerHeight; + this._slider.setWidth(this._options.minimapWidth); } @@ -624,6 +651,7 @@ export class Minimap extends ViewPart { return true; } public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + this._renderDecorations = true; return true; } public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { @@ -642,6 +670,11 @@ export class Minimap extends ViewPart { return true; } + public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + this._renderDecorations = true; + return true; + } + // --- end event handlers public prepareRender(ctx: RenderingContext): void { @@ -684,9 +717,105 @@ export class Minimap extends ViewPart { this._sliderHorizontal.setTop(0); this._sliderHorizontal.setHeight(layout.sliderHeight); + this.renderDecorations(layout); this._lastRenderData = this.renderLines(layout); } + private renderDecorations(layout: MinimapLayout) { + if (this._renderDecorations) { + this._renderDecorations = false; + const decorations = this._context.model.getDecorationsInViewport(new Range(layout.startLineNumber, 1, layout.endLineNumber, this._context.model.getLineMaxColumn(layout.endLineNumber))); + + const { renderMinimap, canvasInnerWidth, canvasInnerHeight } = this._options; + const lineHeight = getMinimapLineHeight(renderMinimap); + const characterWidth = getMinimapCharWidth(renderMinimap); + const tabSize = this._context.model.getOptions().tabSize; + const canvasContext = this._decorationsCanvas.domNode.getContext('2d')!; + + canvasContext.clearRect(0, 0, canvasInnerWidth, canvasInnerHeight); + + // If the minimap is rendered using blocks, text takes up half the line height + const lineHeightRatio = renderMinimap === RenderMinimap.LargeBlocks || renderMinimap === RenderMinimap.SmallBlocks ? 0.5 : 1; + const height = lineHeight * lineHeightRatio; + + // Loop over decorations, ignoring those that don't have the minimap property set and rendering rectangles for each line the decoration spans + const lineOffsetMap = new Map(); + for (let i = 0; i < decorations.length; i++) { + const decoration = decorations[i]; + + if (!decoration.options.minimap) { + continue; + } + + for (let line = decoration.range.startLineNumber; line <= decoration.range.endLineNumber; line++) { + this.renderDecorationOnLine(canvasContext, lineOffsetMap, decoration, layout, line, height, lineHeight, tabSize, characterWidth); + } + } + } + } + + private renderDecorationOnLine(canvasContext: CanvasRenderingContext2D, + lineOffsetMap: Map, + decoration: ViewModelDecoration, + layout: MinimapLayout, + lineNumber: number, + height: number, + lineHeight: number, + tabSize: number, + charWidth: number): void { + const y = (lineNumber - layout.startLineNumber) * lineHeight; + + // Cache line offset data so that it is only read once per line + let lineIndexToXOffset = lineOffsetMap.get(lineNumber); + const isFirstDecorationForLine = !lineIndexToXOffset; + if (!lineIndexToXOffset) { + const lineData = this._context.model.getLineContent(lineNumber); + lineIndexToXOffset = [0]; + for (let i = 1; i < lineData.length + 1; i++) { + const charCode = lineData.charCodeAt(i - 1); + const dx = charCode === CharCode.Tab + ? tabSize * charWidth + : strings.isFullWidthCharacter(charCode) + ? 2 * charWidth + : charWidth; + + lineIndexToXOffset[i] = lineIndexToXOffset[i - 1] + dx; + } + + lineOffsetMap.set(lineNumber, lineIndexToXOffset); + } + + const { startColumn, endColumn, startLineNumber, endLineNumber } = decoration.range; + const x = startLineNumber === lineNumber ? lineIndexToXOffset[startColumn - 1] : 0; + + const endColumnForLine = endLineNumber > lineNumber ? lineIndexToXOffset.length - 1 : endColumn - 1; + + if (endColumnForLine > 0) { + // If the decoration starts at the last character of the column and spans over it, ensure it has a width + const width = lineIndexToXOffset[endColumnForLine] - x || 2; + + this.renderDecoration(canvasContext, decoration.options.minimap, x, y, width, height); + } + + if (isFirstDecorationForLine) { + this.renderLineHighlight(canvasContext, decoration.options.minimap, y, height); + } + + } + + private renderLineHighlight(canvasContext: CanvasRenderingContext2D, minimapOptions: ModelDecorationMinimapOptions, y: number, height: number): void { + const decorationColor = minimapOptions.getColor(this._context.theme); + canvasContext.fillStyle = decorationColor && decorationColor.transparent(0.5).toString() || ''; + canvasContext.fillRect(0, y, canvasContext.canvas.width, height); + } + + private renderDecoration(canvasContext: CanvasRenderingContext2D, minimapOptions: ModelDecorationMinimapOptions, x: number, y: number, width: number, height: number) { + const decorationColor = minimapOptions.getColor(this._context.theme); + + canvasContext.fillStyle = decorationColor && decorationColor.toString() || ''; + canvasContext.fillRect(x, y, width, height); + } + private renderLines(layout: MinimapLayout): RenderData { const renderMinimap = this._options.renderMinimap; const startLineNumber = layout.startLineNumber; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index f00b97775d..3e5f73e511 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -38,7 +38,8 @@ import { ClassName } from 'vs/editor/common/model/intervalTree'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as modes from 'vs/editor/common/modes'; -import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity, editorWarningBorder, editorWarningForeground } from 'vs/editor/common/view/editorColorRegistry'; +import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; +import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; diff --git a/src/vs/editor/browser/widget/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts index b08089de4c..5e956dba0f 100644 --- a/src/vs/editor/browser/widget/diffNavigator.ts +++ b/src/vs/editor/browser/widget/diffNavigator.ts @@ -5,7 +5,7 @@ import * as assert from 'vs/base/common/assert'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -33,12 +33,11 @@ const defaultOptions: Options = { /** * Create a new diff navigator for the provided diff editor. */ -export class DiffNavigator { +export class DiffNavigator extends Disposable { private readonly _editor: IDiffEditor; private readonly _options: Options; - private readonly _disposables: IDisposable[]; - private readonly _onDidUpdate = new Emitter(); + private readonly _onDidUpdate = this._register(new Emitter()); readonly onDidUpdate: Event = this._onDidUpdate.event; @@ -49,11 +48,11 @@ export class DiffNavigator { private ignoreSelectionChange: boolean; constructor(editor: IDiffEditor, options: Options = {}) { + super(); this._editor = editor; this._options = objects.mixin(options, defaultOptions, false); this.disposed = false; - this._disposables = []; this.nextIdx = -1; this.ranges = []; @@ -61,11 +60,11 @@ export class DiffNavigator { this.revealFirst = Boolean(this._options.alwaysRevealFirst); // hook up to diff editor for diff, disposal, and caret move - this._disposables.push(this._editor.onDidDispose(() => this.dispose())); - this._disposables.push(this._editor.onDidUpdateDiff(() => this._onDiffUpdated())); + this._register(this._editor.onDidDispose(() => this.dispose())); + this._register(this._editor.onDidUpdateDiff(() => this._onDiffUpdated())); if (this._options.followsCaret) { - this._disposables.push(this._editor.getModifiedEditor().onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { + this._register(this._editor.getModifiedEditor().onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { if (this.ignoreSelectionChange) { return; } @@ -73,7 +72,7 @@ export class DiffNavigator { })); } if (this._options.alwaysRevealFirst) { - this._disposables.push(this._editor.getModifiedEditor().onDidChangeModel((e) => { + this._register(this._editor.getModifiedEditor().onDidChangeModel((e) => { this.revealFirst = true; })); } @@ -216,9 +215,7 @@ export class DiffNavigator { } dispose(): void { - dispose(this._disposables); - this._disposables.length = 0; - this._onDidUpdate.dispose(); + super.dispose(); this.ranges = []; this.disposed = true; } diff --git a/src/vs/workbench/contrib/debug/browser/media/reverse-continue-inverse.svg b/src/vs/editor/browser/widget/media/addition-dark.svg similarity index 61% rename from src/vs/workbench/contrib/debug/browser/media/reverse-continue-inverse.svg rename to src/vs/editor/browser/widget/media/addition-dark.svg index 5eb56cfd7b..4d9389336b 100644 --- a/src/vs/workbench/contrib/debug/browser/media/reverse-continue-inverse.svg +++ b/src/vs/editor/browser/widget/media/addition-dark.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/editor/browser/widget/media/addition-inverse.svg b/src/vs/editor/browser/widget/media/addition-inverse.svg deleted file mode 100644 index 3475c1e196..0000000000 --- a/src/vs/editor/browser/widget/media/addition-inverse.svg +++ /dev/null @@ -1 +0,0 @@ -Layer 1 \ No newline at end of file diff --git a/src/vs/workbench/contrib/debug/browser/media/reverse-continue.svg b/src/vs/editor/browser/widget/media/addition-light.svg similarity index 61% rename from src/vs/workbench/contrib/debug/browser/media/reverse-continue.svg rename to src/vs/editor/browser/widget/media/addition-light.svg index 8bcbde7db0..01a9de7d5a 100644 --- a/src/vs/workbench/contrib/debug/browser/media/reverse-continue.svg +++ b/src/vs/editor/browser/widget/media/addition-light.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/editor/browser/widget/media/addition.svg b/src/vs/editor/browser/widget/media/addition.svg deleted file mode 100644 index bdecdb0e45..0000000000 --- a/src/vs/editor/browser/widget/media/addition.svg +++ /dev/null @@ -1 +0,0 @@ -Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/close-dark.svg b/src/vs/editor/browser/widget/media/close-dark.svg new file mode 100644 index 0000000000..6d16d212ae --- /dev/null +++ b/src/vs/editor/browser/widget/media/close-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/browser/widget/media/close-inverse.svg b/src/vs/editor/browser/widget/media/close-inverse.svg deleted file mode 100644 index 751e89b3b0..0000000000 --- a/src/vs/editor/browser/widget/media/close-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/close-light.svg b/src/vs/editor/browser/widget/media/close-light.svg new file mode 100644 index 0000000000..742fcae4ae --- /dev/null +++ b/src/vs/editor/browser/widget/media/close-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/browser/widget/media/deletion-dark.svg b/src/vs/editor/browser/widget/media/deletion-dark.svg new file mode 100644 index 0000000000..4c5a9c1e3a --- /dev/null +++ b/src/vs/editor/browser/widget/media/deletion-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/browser/widget/media/deletion-inverse.svg b/src/vs/editor/browser/widget/media/deletion-inverse.svg deleted file mode 100644 index 2de46fcf5b..0000000000 --- a/src/vs/editor/browser/widget/media/deletion-inverse.svg +++ /dev/null @@ -1 +0,0 @@ -Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/deletion-light.svg b/src/vs/editor/browser/widget/media/deletion-light.svg new file mode 100644 index 0000000000..d12a8ee313 --- /dev/null +++ b/src/vs/editor/browser/widget/media/deletion-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/browser/widget/media/deletion.svg b/src/vs/editor/browser/widget/media/deletion.svg deleted file mode 100644 index f5d128b2df..0000000000 --- a/src/vs/editor/browser/widget/media/deletion.svg +++ /dev/null @@ -1 +0,0 @@ -Layer 1 \ No newline at end of file diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css index d74b8f4154..63da0de9ad 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -40,8 +40,7 @@ background-size: 60%; opacity: 0.7; background-repeat: no-repeat; - background-position: 50% 50%; - background-position: center; + background-position: 75% center; background-size: 11px 11px; } .monaco-editor.hc-black .insert-sign, @@ -52,24 +51,24 @@ } .monaco-editor .insert-sign, .monaco-diff-editor .insert-sign { - background-image: url('addition.svg'); + background-image: url('addition-light.svg'); } .monaco-editor .delete-sign, .monaco-diff-editor .delete-sign { - background-image: url('deletion.svg'); + background-image: url('deletion-light.svg'); } .monaco-editor.vs-dark .insert-sign, .monaco-diff-editor.vs-dark .insert-sign, .monaco-editor.hc-black .insert-sign, .monaco-diff-editor.hc-black .insert-sign { - background-image: url('addition-inverse.svg'); + background-image: url('addition-dark.svg'); } .monaco-editor.vs-dark .delete-sign, .monaco-diff-editor.vs-dark .delete-sign, .monaco-editor.hc-black .delete-sign, .monaco-diff-editor.hc-black .delete-sign { - background-image: url('deletion-inverse.svg'); + background-image: url('deletion-dark.svg'); } .monaco-editor .inline-deleted-margin-view-zone { diff --git a/src/vs/editor/browser/widget/media/diffReview.css b/src/vs/editor/browser/widget/media/diffReview.css index b0eeb904da..f96044361e 100644 --- a/src/vs/editor/browser/widget/media/diffReview.css +++ b/src/vs/editor/browser/widget/media/diffReview.css @@ -62,9 +62,9 @@ margin: 2px 0; } .monaco-diff-editor .action-label.icon.close-diff-review { - background: url('close.svg') center center no-repeat; + background: url('close-light.svg') center center no-repeat; } .monaco-diff-editor.hc-black .action-label.icon.close-diff-review, .monaco-diff-editor.vs-dark .action-label.icon.close-diff-review { - background: url('close-inverse.svg') center center no-repeat; + background: url('close-dark.svg') center center no-repeat; } \ No newline at end of file diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 664f83d281..e055e81897 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -153,8 +153,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed return true; } - private static _subsetEquals(base: object, subset: object): boolean { - for (let key in subset) { + private static _subsetEquals(base: { [key: string]: any }, subset: { [key: string]: any }): boolean { + for (const key in subset) { if (hasOwnProperty.call(subset, key)) { const subsetValue = subset[key]; const baseValue = base[key]; @@ -839,7 +839,7 @@ const editorConfiguration: IConfigurationNode = { enumDescriptions: [ nls.localize('editor.gotoLocation.multiple.peek', 'Show peek view of the results (default)'), nls.localize('editor.gotoLocation.multiple.gotoAndPeek', 'Go to the primary result and show a peek view'), - nls.localize('editor.gotoLocation.multiple.goto', 'Go to the primary result and ignore others') + nls.localize('editor.gotoLocation.multiple.goto', 'Go to the primary result and enable peek-less navigation to others') ] }, 'editor.selectionHighlight': { diff --git a/src/vs/editor/common/core/position.ts b/src/vs/editor/common/core/position.ts index 5b6e25e75a..1a5e932439 100644 --- a/src/vs/editor/common/core/position.ts +++ b/src/vs/editor/common/core/position.ts @@ -36,7 +36,7 @@ export class Position { } /** - * Create a new postion from this position. + * Create a new position from this position. * * @param newLineNumber new line number * @param newColumn new column diff --git a/src/vs/editor/common/core/uint.ts b/src/vs/editor/common/core/uint.ts index a91e40a647..4532f4a22f 100644 --- a/src/vs/editor/common/core/uint.ts +++ b/src/vs/editor/common/core/uint.ts @@ -10,7 +10,7 @@ export class Uint8Matrix { public readonly cols: number; constructor(rows: number, cols: number, defaultValue: number) { - let data = new Uint8Array(rows * cols); + const data = new Uint8Array(rows * cols); for (let i = 0, len = rows * cols; i < len; i++) { data[i] = defaultValue; } @@ -85,8 +85,8 @@ export function toUint32(v: number): number { } export function toUint32Array(arr: number[]): Uint32Array { - let len = arr.length; - let r = new Uint32Array(len); + const len = arr.length; + const r = new Uint32Array(len); for (let i = 0; i < len; i++) { r[i] = toUint32(arr[i]); } diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 4679c2c2a4..bbd63ac84b 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -26,25 +26,45 @@ export enum OverviewRulerLane { } /** - * Options for rendering a model decoration in the overview ruler. + * Position in the minimap to render the decoration. */ -export interface IModelDecorationOverviewRulerOptions { +export enum MinimapPosition { + Inline = 1 +} + +export interface IDecorationOptions { /** - * CSS color to render in the overview ruler. + * CSS color to render. * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry */ color: string | ThemeColor | undefined; /** - * CSS color to render in the overview ruler. + * CSS color to render. * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry */ darkColor?: string | ThemeColor; +} + +/** + * Options for rendering a model decoration in the overview ruler. + */ +export interface IModelDecorationOverviewRulerOptions extends IDecorationOptions { /** * The position in the overview ruler. */ position: OverviewRulerLane; } +/** + * Options for rendering a model decoration in the overview ruler. + */ +export interface IModelDecorationMinimapOptions extends IDecorationOptions { + /** + * The position in the overview ruler. + */ + position: MinimapPosition; +} + /** * Options for a model decoration. */ @@ -89,6 +109,10 @@ export interface IModelDecorationOptions { * If set, render this decoration in the overview ruler. */ overviewRuler?: IModelDecorationOverviewRulerOptions | null; + /** + * If set, render this decoration in the minimap. + */ + minimap?: IModelDecorationMinimapOptions | null; /** * If set, the decoration will be rendered in the glyph margin with this CSS class name. */ @@ -759,7 +783,7 @@ export interface ITextModel { * Flush all tokenization state. * @internal */ - flushTokens(): void; + resetTokenization(): void; /** * Force tokenization information for `lineNumber` to be accurate. @@ -1092,6 +1116,12 @@ export interface ITextModel { * @internal */ onDidChangeTokens(listener: (e: IModelTokensChangedEvent) => void): IDisposable; + /** + * An event emitted when the model has been attached to the first editor or detached from the last editor. + * @event + * @internal + */ + onDidChangeAttached(listener: () => void): IDisposable; /** * An event emitted right before disposing the model. * @event diff --git a/src/vs/editor/common/model/indentationGuesser.ts b/src/vs/editor/common/model/indentationGuesser.ts index e41a7d0bd2..884d657d70 100644 --- a/src/vs/editor/common/model/indentationGuesser.ts +++ b/src/vs/editor/common/model/indentationGuesser.ts @@ -177,22 +177,26 @@ export function guessIndentation(source: ITextBuffer, defaultTabSize: number, de } let tabSize = defaultTabSize; - let tabSizeScore = (insertSpaces ? 0 : 0.1 * linesCount); - // console.log("score threshold: " + tabSizeScore); + // Guess tabSize only if inserting spaces... + if (insertSpaces) { + let tabSizeScore = (insertSpaces ? 0 : 0.1 * linesCount); - ALLOWED_TAB_SIZE_GUESSES.forEach((possibleTabSize) => { - let possibleTabSizeScore = spacesDiffCount[possibleTabSize]; - if (possibleTabSizeScore > tabSizeScore) { - tabSizeScore = possibleTabSizeScore; - tabSize = possibleTabSize; + // console.log("score threshold: " + tabSizeScore); + + ALLOWED_TAB_SIZE_GUESSES.forEach((possibleTabSize) => { + let possibleTabSizeScore = spacesDiffCount[possibleTabSize]; + if (possibleTabSizeScore > tabSizeScore) { + tabSizeScore = possibleTabSizeScore; + tabSize = possibleTabSize; + } + }); + + // Let a tabSize of 2 win even if it is not the maximum + // (only in case 4 was guessed) + if (tabSize === 4 && spacesDiffCount[4] > 0 && spacesDiffCount[2] > 0 && spacesDiffCount[2] >= spacesDiffCount[4] / 2) { + tabSize = 2; } - }); - - // Let a tabSize of 2 win even if it is not the maximum - // (only in case 4 was guessed) - if (tabSize === 4 && spacesDiffCount[4] > 0 && spacesDiffCount[2] > 0 && spacesDiffCount[2] >= spacesDiffCount[4] / 2) { - tabSize = 2; } diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 6fd267cad7..59976733d2 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -611,25 +611,25 @@ export class PieceTreeBase { let resultLen = 0; const searcher = new Searcher(searchData.wordSeparators, searchData.regex); - let startPostion = this.nodeAt2(searchRange.startLineNumber, searchRange.startColumn); - if (startPostion === null) { + let startPosition = this.nodeAt2(searchRange.startLineNumber, searchRange.startColumn); + if (startPosition === null) { return []; } let endPosition = this.nodeAt2(searchRange.endLineNumber, searchRange.endColumn); if (endPosition === null) { return []; } - let start = this.positionInBuffer(startPostion.node, startPostion.remainder); + let start = this.positionInBuffer(startPosition.node, startPosition.remainder); let end = this.positionInBuffer(endPosition.node, endPosition.remainder); - if (startPostion.node === endPosition.node) { - this.findMatchesInNode(startPostion.node, searcher, searchRange.startLineNumber, searchRange.startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result); + if (startPosition.node === endPosition.node) { + this.findMatchesInNode(startPosition.node, searcher, searchRange.startLineNumber, searchRange.startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result); return result; } let startLineNumber = searchRange.startLineNumber; - let currentNode = startPostion.node; + let currentNode = startPosition.node; while (currentNode !== endPosition.node) { let lineBreakCnt = this.getLineFeedCnt(currentNode.piece.bufferIndex, start, currentNode.piece.end); @@ -663,9 +663,9 @@ export class PieceTreeBase { } startLineNumber++; - startPostion = this.nodeAt2(startLineNumber, 1); - currentNode = startPostion.node; - start = this.positionInBuffer(startPostion.node, startPostion.remainder); + startPosition = this.nodeAt2(startLineNumber, 1); + currentNode = startPosition.node; + start = this.positionInBuffer(startPosition.node, startPosition.remainder); } if (startLineNumber === searchRange.endLineNumber) { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 835e609feb..5a8805bf3b 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -8,7 +8,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; @@ -23,9 +22,9 @@ import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd } import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents'; import { SearchData, SearchParams, TextModelSearch } from 'vs/editor/common/model/textModelSearch'; -import { ModelLinesTokens, ModelTokensChangedEventBuilder } from 'vs/editor/common/model/textModelTokens'; +import { TextModelTokenization, countEOL } from 'vs/editor/common/model/textModelTokens'; import { getWordAtText } from 'vs/editor/common/model/wordHelper'; -import { IState, LanguageId, LanguageIdentifier, TokenizationRegistry, FormattingOptions } from 'vs/editor/common/modes'; +import { LanguageId, LanguageIdentifier, FormattingOptions } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; @@ -33,8 +32,8 @@ import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/comm import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer'; - -const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048; +import { TokensStore, MultilineTokens } from 'vs/editor/common/model/tokensStore'; +import { Color } from 'vs/base/common/color'; function createTextBufferBuilder() { return new PieceTreeTextBufferBuilder(); @@ -235,6 +234,9 @@ export class TextModel extends Disposable implements model.ITextModel { private readonly _onDidChangeOptions: Emitter = this._register(new Emitter()); public readonly onDidChangeOptions: Event = this._onDidChangeOptions.event; + private readonly _onDidChangeAttached: Emitter = this._register(new Emitter()); + public readonly onDidChangeAttached: Event = this._onDidChangeAttached.event; + private readonly _eventEmitter: DidChangeContentEmitter = this._register(new DidChangeContentEmitter()); public onDidChangeRawContentFast(listener: (e: ModelRawContentChangedEvent) => void): IDisposable { return this._eventEmitter.fastEvent((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent)); @@ -242,6 +244,9 @@ export class TextModel extends Disposable implements model.ITextModel { public onDidChangeRawContent(listener: (e: ModelRawContentChangedEvent) => void): IDisposable { return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent)); } + public onDidChangeContentFast(listener: (e: IModelContentChangedEvent) => void): IDisposable { + return this._eventEmitter.fastEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent)); + } public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable { return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent)); } @@ -284,10 +289,9 @@ export class TextModel extends Disposable implements model.ITextModel { //#region Tokenization private _languageIdentifier: LanguageIdentifier; - private readonly _tokenizationListener: IDisposable; private readonly _languageRegistryListener: IDisposable; - private _revalidateTokensTimeout: any; - /*private*/_tokens: ModelLinesTokens; + private readonly _tokens: TokensStore; + private readonly _tokenization: TextModelTokenization; //#endregion constructor(source: string | model.ITextBufferFactory, creationOptions: model.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier | null, associatedResource: URI | null = null) { @@ -330,31 +334,12 @@ export class TextModel extends Disposable implements model.ITextModel { this._isDisposing = false; this._languageIdentifier = languageIdentifier || NULL_LANGUAGE_IDENTIFIER; - this._tokenizationListener = TokenizationRegistry.onDidChange((e) => { - if (e.changedLanguages.indexOf(this._languageIdentifier.language) === -1) { - return; - } - this._resetTokenizationState(); - this.emitModelTokensChangedEvent({ - tokenizationSupportChanged: true, - ranges: [{ - fromLineNumber: 1, - toLineNumber: this.getLineCount() - }] - }); - - if (this._shouldAutoTokenize()) { - this._warmUpTokens(); - } - }); - this._revalidateTokensTimeout = -1; this._languageRegistryListener = LanguageConfigurationRegistry.onDidChange((e) => { if (e.languageIdentifier.id === this._languageIdentifier.id) { this._onDidChangeLanguageConfiguration.fire({}); } }); - this._resetTokenizationState(); this._instanceId = singleLetter(MODEL_ID); this._lastDecorationId = 0; @@ -365,16 +350,17 @@ export class TextModel extends Disposable implements model.ITextModel { this._isUndoing = false; this._isRedoing = false; this._trimAutoWhitespaceLines = null; + + this._tokens = new TokensStore(); + this._tokenization = new TextModelTokenization(this); } public dispose(): void { this._isDisposing = true; this._onWillDispose.fire(); - this._tokenizationListener.dispose(); this._languageRegistryListener.dispose(); - this._clearTimers(); + this._tokenization.dispose(); this._isDisposed = true; - // Null out members, such that any use of a disposed model will throw exceptions sooner rather than later super.dispose(); this._isDisposing = false; } @@ -439,8 +425,8 @@ export class TextModel extends Disposable implements model.ITextModel { this._buffer = textBuffer; this._increaseVersionId(); - // Cancel tokenization, clear all tokens and begin tokenizing - this._resetTokenizationState(); + // Flush all tokens + this._tokens.flush(); // Destroy all my decorations this._decorations = Object.create(null); @@ -524,36 +510,18 @@ export class TextModel extends Disposable implements model.ITextModel { } } - private _resetTokenizationState(): void { - this._clearTimers(); - let tokenizationSupport = ( - this._isTooLargeForTokenization - ? null - : TokenizationRegistry.get(this._languageIdentifier.language) - ); - this._tokens = new ModelLinesTokens(this._languageIdentifier, tokenizationSupport); - this._beginBackgroundTokenization(); - } - - private _clearTimers(): void { - if (this._revalidateTokensTimeout !== -1) { - clearTimeout(this._revalidateTokensTimeout); - this._revalidateTokensTimeout = -1; - } - } - public onBeforeAttached(): void { this._attachedEditorCount++; - // Warm up tokens for the editor - this._warmUpTokens(); + if (this._attachedEditorCount === 1) { + this._onDidChangeAttached.fire(undefined); + } } public onBeforeDetached(): void { this._attachedEditorCount--; - } - - private _shouldAutoTokenize(): boolean { - return this.isAttachedToEditor(); + if (this._attachedEditorCount === 0) { + this._onDidChangeAttached.fire(undefined); + } } public isAttachedToEditor(): boolean { @@ -1292,36 +1260,6 @@ export class TextModel extends Disposable implements model.ITextModel { } } - private static _eolCount(text: string): [number, number] { - let eolCount = 0; - let firstLineLength = 0; - for (let i = 0, len = text.length; i < len; i++) { - const chr = text.charCodeAt(i); - - if (chr === CharCode.CarriageReturn) { - if (eolCount === 0) { - firstLineLength = i; - } - eolCount++; - if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) { - // \r\n... case - i++; // skip \n - } else { - // \r... case - } - } else if (chr === CharCode.LineFeed) { - if (eolCount === 0) { - firstLineLength = i; - } - eolCount++; - } - } - if (eolCount === 0) { - firstLineLength = text.length; - } - return [eolCount, firstLineLength]; - } - private _applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] { for (let i = 0, len = rawOperations.length; i < len; i++) { rawOperations[i].range = this.validateRange(rawOperations[i].range); @@ -1340,13 +1278,8 @@ export class TextModel extends Disposable implements model.ITextModel { let lineCount = oldLineCount; for (let i = 0, len = contentChanges.length; i < len; i++) { const change = contentChanges[i]; - const [eolCount, firstLineLength] = TextModel._eolCount(change.text); - try { - this._tokens.applyEdits(change.range, eolCount, firstLineLength); - } catch (err) { - // emergency recovery => reset tokens - this._tokens = new ModelLinesTokens(this._tokens.languageIdentifier, this._tokens.tokenizationSupport); - } + const [eolCount, firstLineLength] = countEOL(change.text); + this._tokens.applyEdits(change.range, eolCount, firstLineLength); this._onDidChangeDecorations.fire(); this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers); @@ -1407,10 +1340,6 @@ export class TextModel extends Disposable implements model.ITextModel { ); } - if (this._tokens.hasLinesToTokenize(this._buffer)) { - this._beginBackgroundTokenization(); - } - return result.reverseEdits; } @@ -1775,93 +1704,60 @@ export class TextModel extends Disposable implements model.ITextModel { //#region Tokenization + public setLineTokens(lineNumber: number, tokens: Uint32Array): void { + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value for lineNumber'); + } + + this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens); + } + + public setTokens(tokens: MultilineTokens[]): void { + if (tokens.length === 0) { + return; + } + + let ranges: { fromLineNumber: number; toLineNumber: number; }[] = []; + + for (let i = 0, len = tokens.length; i < len; i++) { + const element = tokens[i]; + ranges.push({ fromLineNumber: element.startLineNumber, toLineNumber: element.startLineNumber + element.tokens.length - 1 }); + for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) { + this.setLineTokens(element.startLineNumber + j, element.tokens[j]); + } + } + + this._emitModelTokensChangedEvent({ + tokenizationSupportChanged: false, + ranges: ranges + }); + } + public tokenizeViewport(startLineNumber: number, endLineNumber: number): void { - if (!this._tokens.tokenizationSupport) { - // nothing to do - return; - } - startLineNumber = Math.max(1, startLineNumber); - endLineNumber = Math.min(this.getLineCount(), endLineNumber); + endLineNumber = Math.min(this._buffer.getLineCount(), endLineNumber); + this._tokenization.tokenizeViewport(startLineNumber, endLineNumber); + } - if (endLineNumber <= this._tokens.inValidLineStartIndex) { - // nothing to do - return; - } + public clearTokens(): void { + this._tokens.flush(); + this._emitModelTokensChangedEvent({ + tokenizationSupportChanged: true, + ranges: [{ + fromLineNumber: 1, + toLineNumber: this._buffer.getLineCount() + }] + }); + } - if (startLineNumber <= this._tokens.inValidLineStartIndex) { - // tokenization has reached the viewport start... - this.forceTokenization(endLineNumber); - return; - } - - let nonWhitespaceColumn = this.getLineFirstNonWhitespaceColumn(startLineNumber); - let fakeLines: string[] = []; - let initialState: IState | null = null; - for (let i = startLineNumber - 1; nonWhitespaceColumn > 0 && i >= 1; i--) { - let newNonWhitespaceIndex = this.getLineFirstNonWhitespaceColumn(i); - - if (newNonWhitespaceIndex === 0) { - continue; - } - - if (newNonWhitespaceIndex < nonWhitespaceColumn) { - initialState = this._tokens._getState(i - 1); - if (initialState) { - break; - } - fakeLines.push(this.getLineContent(i)); - nonWhitespaceColumn = newNonWhitespaceIndex; - } - } - - if (!initialState) { - initialState = this._tokens.tokenizationSupport.getInitialState(); - } - - let state = initialState.clone(); - for (let i = fakeLines.length - 1; i >= 0; i--) { - let r = this._tokens._tokenizeText(this._buffer, fakeLines[i], state); - if (r) { - state = r.endState.clone(); - } else { - state = initialState.clone(); - } - } - - const eventBuilder = new ModelTokensChangedEventBuilder(); - for (let i = startLineNumber; i <= endLineNumber; i++) { - let text = this.getLineContent(i); - let r = this._tokens._tokenizeText(this._buffer, text, state); - if (r) { - this._tokens._setTokens(this._tokens.languageIdentifier.id, i - 1, text.length, r.tokens); - - // We cannot trust these states/tokens to be valid! - // (see https://github.com/Microsoft/vscode/issues/67607) - this._tokens._setIsInvalid(i - 1, true); - this._tokens._setState(i - 1, state); - state = r.endState.clone(); - eventBuilder.registerChangedTokens(i); - } else { - state = initialState.clone(); - } - } - - const e = eventBuilder.build(); - if (e) { + private _emitModelTokensChangedEvent(e: IModelTokensChangedEvent): void { + if (!this._isDisposing) { this._onDidChangeTokens.fire(e); } } - public flushTokens(): void { - this._resetTokenizationState(); - this.emitModelTokensChangedEvent({ - tokenizationSupportChanged: false, - ranges: [{ - fromLineNumber: 1, - toLineNumber: this.getLineCount() - }] - }); + public resetTokenization(): void { + this._tokenization.reset(); } public forceTokenization(lineNumber: number): void { @@ -1869,30 +1765,11 @@ export class TextModel extends Disposable implements model.ITextModel { throw new Error('Illegal value for lineNumber'); } - const eventBuilder = new ModelTokensChangedEventBuilder(); - - this._tokens._updateTokensUntilLine(this._buffer, eventBuilder, lineNumber); - - const e = eventBuilder.build(); - if (e) { - this._onDidChangeTokens.fire(e); - } + this._tokenization.forceTokenization(lineNumber); } public isCheapToTokenize(lineNumber: number): boolean { - if (!this._tokens.isCheapToTokenize(lineNumber)) { - return false; - } - - if (lineNumber < this._tokens.inValidLineStartIndex + 1) { - return true; - } - - if (this.getLineLength(lineNumber) < CHEAP_TOKENIZATION_LENGTH_LIMIT) { - return true; - } - - return false; + return this._tokenization.isCheapToTokenize(lineNumber); } public tokenizeIfCheap(lineNumber: number): void { @@ -1910,7 +1787,7 @@ export class TextModel extends Disposable implements model.ITextModel { } private _getLineTokens(lineNumber: number): LineTokens { - const lineText = this._buffer.getLineContent(lineNumber); + const lineText = this.getLineContent(lineNumber); return this._tokens.getTokens(this._languageIdentifier.id, lineNumber - 1, lineText); } @@ -1935,81 +1812,14 @@ export class TextModel extends Disposable implements model.ITextModel { this._languageIdentifier = languageIdentifier; - // Cancel tokenization, clear all tokens and begin tokenizing - this._resetTokenizationState(); - - this.emitModelTokensChangedEvent({ - tokenizationSupportChanged: true, - ranges: [{ - fromLineNumber: 1, - toLineNumber: this.getLineCount() - }] - }); this._onDidChangeLanguage.fire(e); this._onDidChangeLanguageConfiguration.fire({}); } - public getLanguageIdAtPosition(_lineNumber: number, _column: number): LanguageId { - if (!this._tokens.tokenizationSupport) { - return this._languageIdentifier.id; - } - let { lineNumber, column } = this.validatePosition({ lineNumber: _lineNumber, column: _column }); - - let lineTokens = this._getLineTokens(lineNumber); - return lineTokens.getLanguageId(lineTokens.findTokenIndexAtOffset(column - 1)); - } - - private _beginBackgroundTokenization(): void { - if (this._shouldAutoTokenize() && this._revalidateTokensTimeout === -1) { - this._revalidateTokensTimeout = setTimeout(() => { - this._revalidateTokensTimeout = -1; - this._revalidateTokensNow(); - }, 0); - } - } - - _warmUpTokens(): void { - // Warm up first 100 lines (if it takes less than 50ms) - const maxLineNumber = Math.min(100, this.getLineCount()); - this._revalidateTokensNow(maxLineNumber); - - if (this._tokens.hasLinesToTokenize(this._buffer)) { - this._beginBackgroundTokenization(); - } - } - - private _revalidateTokensNow(toLineNumber: number = this._buffer.getLineCount()): void { - const MAX_ALLOWED_TIME = 20; - const eventBuilder = new ModelTokensChangedEventBuilder(); - const sw = StopWatch.create(false); - - while (this._tokens.hasLinesToTokenize(this._buffer)) { - if (sw.elapsed() > MAX_ALLOWED_TIME) { - // Stop if MAX_ALLOWED_TIME is reached - break; - } - - const tokenizedLineNumber = this._tokens._tokenizeOneLine(this._buffer, eventBuilder); - - if (tokenizedLineNumber >= toLineNumber) { - break; - } - } - - if (this._tokens.hasLinesToTokenize(this._buffer)) { - this._beginBackgroundTokenization(); - } - - const e = eventBuilder.build(); - if (e) { - this._onDidChangeTokens.fire(e); - } - } - - private emitModelTokensChangedEvent(e: IModelTokensChangedEvent): void { - if (!this._isDisposing) { - this._onDidChangeTokens.fire(e); - } + public getLanguageIdAtPosition(lineNumber: number, column: number): LanguageId { + const position = this.validatePosition(new Position(lineNumber, column)); + const lineTokens = this.getLineTokens(position.lineNumber); + return lineTokens.getLanguageId(lineTokens.findTokenIndexAtOffset(position.column - 1)); } // Having tokens allows implementing additional helper methods @@ -2823,17 +2633,25 @@ function cleanClassName(className: string): string { return className.replace(/[^a-z0-9\-_]/gi, ' '); } -export class ModelDecorationOverviewRulerOptions implements model.IModelDecorationOverviewRulerOptions { +class DecorationOptions implements model.IDecorationOptions { readonly color: string | ThemeColor; readonly darkColor: string | ThemeColor; + + constructor(options: model.IDecorationOptions) { + this.color = options.color || strings.empty; + this.darkColor = options.darkColor || strings.empty; + + } +} + +export class ModelDecorationOverviewRulerOptions extends DecorationOptions { readonly position: model.OverviewRulerLane; private _resolvedColor: string | null; constructor(options: model.IModelDecorationOverviewRulerOptions) { - this.color = options.color || strings.empty; - this.darkColor = options.darkColor || strings.empty; - this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center); + super(options); this._resolvedColor = null; + this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center); } public getColor(theme: ITheme): string { @@ -2863,6 +2681,36 @@ export class ModelDecorationOverviewRulerOptions implements model.IModelDecorati } } +export class ModelDecorationMinimapOptions extends DecorationOptions { + readonly position: model.MinimapPosition; + private _resolvedColor: Color | undefined; + + + constructor(options: model.IModelDecorationMinimapOptions) { + super(options); + this.position = options.position; + } + + public getColor(theme: ITheme): Color | undefined { + if (!this._resolvedColor) { + if (theme.type !== 'light' && this.darkColor) { + this._resolvedColor = this._resolveColor(this.darkColor, theme); + } else { + this._resolvedColor = this._resolveColor(this.color, theme); + } + } + + return this._resolvedColor; + } + + private _resolveColor(color: string | ThemeColor, theme: ITheme): Color | undefined { + if (typeof color === 'string') { + return Color.fromHex(color); + } + return theme.getColor(color.id); + } +} + export class ModelDecorationOptions implements model.IModelDecorationOptions { public static EMPTY: ModelDecorationOptions; @@ -2884,6 +2732,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { readonly showIfCollapsed: boolean; readonly collapseOnReplaceEdit: boolean; readonly overviewRuler: ModelDecorationOverviewRulerOptions | null; + readonly minimap: ModelDecorationMinimapOptions | null; readonly glyphMarginClassName: string | null; readonly linesDecorationsClassName: string | null; readonly marginClassName: string | null; @@ -2902,6 +2751,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions { this.showIfCollapsed = options.showIfCollapsed || false; this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false; this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null; + this.minimap = options.minimap ? new ModelDecorationMinimapOptions(options.minimap) : null; this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null; this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null; this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : null; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index e0c80f0701..97654c77e2 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -7,501 +7,486 @@ import * as arrays from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { IRange } from 'vs/editor/common/core/range'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; -import { ITextBuffer } from 'vs/editor/common/model'; -import { IModelTokensChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { ColorId, FontStyle, IState, ITokenizationSupport, LanguageId, LanguageIdentifier, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes'; +import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; +import { IState, ITokenizationSupport, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes'; import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { CharCode } from 'vs/base/common/charCode'; +import { MultilineTokensBuilder } from 'vs/editor/common/model/tokensStore'; -function getDefaultMetadata(topLevelLanguageId: LanguageId): number { - return ( - (topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET) - | (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET) - | (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) - | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) - | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) - ) >>> 0; +export function countEOL(text: string): [number, number] { + let eolCount = 0; + let firstLineLength = 0; + for (let i = 0, len = text.length; i < len; i++) { + const chr = text.charCodeAt(i); + + if (chr === CharCode.CarriageReturn) { + if (eolCount === 0) { + firstLineLength = i; + } + eolCount++; + if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) { + // \r\n... case + i++; // skip \n + } else { + // \r... case + } + } else if (chr === CharCode.LineFeed) { + if (eolCount === 0) { + firstLineLength = i; + } + eolCount++; + } + } + if (eolCount === 0) { + firstLineLength = text.length; + } + return [eolCount, firstLineLength]; } -const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer; - -class ModelLineTokens { - _state: IState | null; - _lineTokens: ArrayBuffer | null; - _invalid: boolean; - - constructor(state: IState | null) { - this._state = state; - this._lineTokens = null; - this._invalid = true; - } - - public deleteBeginning(toChIndex: number): void { - if (this._lineTokens === null || this._lineTokens === EMPTY_LINE_TOKENS) { - return; - } - this.delete(0, toChIndex); - } - - public deleteEnding(fromChIndex: number): void { - if (this._lineTokens === null || this._lineTokens === EMPTY_LINE_TOKENS) { - return; - } - - const tokens = new Uint32Array(this._lineTokens); - const lineTextLength = tokens[tokens.length - 2]; - this.delete(fromChIndex, lineTextLength); - } - - public delete(fromChIndex: number, toChIndex: number): void { - if (this._lineTokens === null || this._lineTokens === EMPTY_LINE_TOKENS || fromChIndex === toChIndex) { - return; - } - - const tokens = new Uint32Array(this._lineTokens); - const tokensCount = (tokens.length >>> 1); - - // special case: deleting everything - if (fromChIndex === 0 && tokens[tokens.length - 2] === toChIndex) { - this._lineTokens = EMPTY_LINE_TOKENS; - return; - } - - const fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, fromChIndex); - const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0); - const fromTokenEndOffset = tokens[fromTokenIndex << 1]; - - if (toChIndex < fromTokenEndOffset) { - // the delete range is inside a single token - const delta = (toChIndex - fromChIndex); - for (let i = fromTokenIndex; i < tokensCount; i++) { - tokens[i << 1] -= delta; - } - return; - } - - let dest: number; - let lastEnd: number; - if (fromTokenStartOffset !== fromChIndex) { - tokens[fromTokenIndex << 1] = fromChIndex; - dest = ((fromTokenIndex + 1) << 1); - lastEnd = fromChIndex; - } else { - dest = (fromTokenIndex << 1); - lastEnd = fromTokenStartOffset; - } - - const delta = (toChIndex - fromChIndex); - for (let tokenIndex = fromTokenIndex + 1; tokenIndex < tokensCount; tokenIndex++) { - const tokenEndOffset = tokens[tokenIndex << 1] - delta; - if (tokenEndOffset > lastEnd) { - tokens[dest++] = tokenEndOffset; - tokens[dest++] = tokens[(tokenIndex << 1) + 1]; - lastEnd = tokenEndOffset; - } - } - - if (dest === tokens.length) { - // nothing to trim - return; - } - - let tmp = new Uint32Array(dest); - tmp.set(tokens.subarray(0, dest), 0); - this._lineTokens = tmp.buffer; - } - - public append(_otherTokens: ArrayBuffer | null): void { - if (_otherTokens === EMPTY_LINE_TOKENS) { - return; - } - if (this._lineTokens === EMPTY_LINE_TOKENS) { - this._lineTokens = _otherTokens; - return; - } - if (this._lineTokens === null) { - return; - } - if (_otherTokens === null) { - // cannot determine combined line length... - this._lineTokens = null; - return; - } - const myTokens = new Uint32Array(this._lineTokens); - const otherTokens = new Uint32Array(_otherTokens); - const otherTokensCount = (otherTokens.length >>> 1); - - let result = new Uint32Array(myTokens.length + otherTokens.length); - result.set(myTokens, 0); - let dest = myTokens.length; - const delta = myTokens[myTokens.length - 2]; - for (let i = 0; i < otherTokensCount; i++) { - result[dest++] = otherTokens[(i << 1)] + delta; - result[dest++] = otherTokens[(i << 1) + 1]; - } - this._lineTokens = result.buffer; - } - - public insert(chIndex: number, textLength: number): void { - if (!this._lineTokens) { - // nothing to do - return; - } - - const tokens = new Uint32Array(this._lineTokens); - const tokensCount = (tokens.length >>> 1); - - let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex); - if (fromTokenIndex > 0) { - const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1]; - if (fromTokenStartOffset === chIndex) { - fromTokenIndex--; - } - } - for (let tokenIndex = fromTokenIndex; tokenIndex < tokensCount; tokenIndex++) { - tokens[tokenIndex << 1] += textLength; - } - } +const enum Constants { + CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048 } -export class ModelLinesTokens { - - public readonly languageIdentifier: LanguageIdentifier; - public readonly tokenizationSupport: ITokenizationSupport | null; - private _tokens: ModelLineTokens[]; +export class TokenizationStateStore { + private _beginState: (IState | null)[]; + private _valid: boolean[]; + private _len: number; private _invalidLineStartIndex: number; - private _lastState: IState | null; - constructor(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null) { - this.languageIdentifier = languageIdentifier; - this.tokenizationSupport = tokenizationSupport; - this._tokens = []; - if (this.tokenizationSupport) { - let initialState: IState | null = null; - try { - initialState = this.tokenizationSupport.getInitialState(); - } catch (e) { - onUnexpectedError(e); - this.tokenizationSupport = null; - } - - if (initialState) { - this._tokens[0] = new ModelLineTokens(initialState); - } - } - - this._invalidLineStartIndex = 0; - this._lastState = null; + constructor() { + this._reset(null); } - public get inValidLineStartIndex() { + private _reset(initialState: IState | null): void { + this._beginState = []; + this._valid = []; + this._len = 0; + this._invalidLineStartIndex = 0; + + if (initialState) { + this._setBeginState(0, initialState); + } + } + + public flush(initialState: IState | null): void { + this._reset(initialState); + } + + public get invalidLineStartIndex() { return this._invalidLineStartIndex; } - public getTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineText: string): LineTokens { - let rawLineTokens: ArrayBuffer | null = null; - if (lineIndex < this._tokens.length && this._tokens[lineIndex]) { - rawLineTokens = this._tokens[lineIndex]._lineTokens; + private _invalidateLine(lineIndex: number): void { + if (lineIndex < this._len) { + this._valid[lineIndex] = false; } - if (rawLineTokens !== null && rawLineTokens !== EMPTY_LINE_TOKENS) { - return new LineTokens(new Uint32Array(rawLineTokens), lineText); - } - - let lineTokens = new Uint32Array(2); - lineTokens[0] = lineText.length; - lineTokens[1] = getDefaultMetadata(topLevelLanguageId); - return new LineTokens(lineTokens, lineText); - } - - public isCheapToTokenize(lineNumber: number): boolean { - const firstInvalidLineNumber = this._invalidLineStartIndex + 1; - return (firstInvalidLineNumber >= lineNumber); - } - - public hasLinesToTokenize(buffer: ITextBuffer): boolean { - return (this._invalidLineStartIndex < buffer.getLineCount()); - } - - public invalidateLine(lineIndex: number): void { - this._setIsInvalid(lineIndex, true); if (lineIndex < this._invalidLineStartIndex) { - this._setIsInvalid(this._invalidLineStartIndex, true); this._invalidLineStartIndex = lineIndex; } } - _setIsInvalid(lineIndex: number, invalid: boolean): void { - if (lineIndex < this._tokens.length && this._tokens[lineIndex]) { - this._tokens[lineIndex]._invalid = invalid; + private _isValid(lineIndex: number): boolean { + if (lineIndex < this._len) { + return this._valid[lineIndex]; } + return false; } - _isInvalid(lineIndex: number): boolean { - if (lineIndex < this._tokens.length && this._tokens[lineIndex]) { - return this._tokens[lineIndex]._invalid; - } - return true; - } - - _getState(lineIndex: number): IState | null { - if (lineIndex < this._tokens.length && this._tokens[lineIndex]) { - return this._tokens[lineIndex]._state; + public getBeginState(lineIndex: number): IState | null { + if (lineIndex < this._len) { + return this._beginState[lineIndex]; } return null; } - _setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, tokens: Uint32Array): void { - let target: ModelLineTokens; - if (lineIndex < this._tokens.length && this._tokens[lineIndex]) { - target = this._tokens[lineIndex]; - } else { - target = new ModelLineTokens(null); - this._tokens[lineIndex] = target; + private _ensureLine(lineIndex: number): void { + while (lineIndex >= this._len) { + this._beginState[this._len] = null; + this._valid[this._len] = false; + this._len++; } - - if (lineTextLength === 0) { - let hasDifferentLanguageId = false; - if (tokens && tokens.length > 1) { - hasDifferentLanguageId = (TokenMetadata.getLanguageId(tokens[1]) !== topLevelLanguageId); - } - - if (!hasDifferentLanguageId) { - target._lineTokens = EMPTY_LINE_TOKENS; - return; - } - } - - if (!tokens || tokens.length === 0) { - tokens = new Uint32Array(2); - tokens[0] = 0; - tokens[1] = getDefaultMetadata(topLevelLanguageId); - } - - LineTokens.convertToEndOffset(tokens, lineTextLength); - - target._lineTokens = tokens.buffer; } - _setState(lineIndex: number, state: IState): void { - if (lineIndex < this._tokens.length && this._tokens[lineIndex]) { - this._tokens[lineIndex]._state = state; - } else { - const tmp = new ModelLineTokens(state); - this._tokens[lineIndex] = tmp; + private _deleteLines(start: number, deleteCount: number): void { + if (deleteCount === 0) { + return; } + this._beginState.splice(start, deleteCount); + this._valid.splice(start, deleteCount); + this._len -= deleteCount; + } + + private _insertLines(insertIndex: number, insertCount: number): void { + if (insertCount === 0) { + return; + } + let beginState: (IState | null)[] = []; + let valid: boolean[] = []; + for (let i = 0; i < insertCount; i++) { + beginState[i] = null; + valid[i] = false; + } + this._beginState = arrays.arrayInsert(this._beginState, insertIndex, beginState); + this._valid = arrays.arrayInsert(this._valid, insertIndex, valid); + this._len += insertCount; + } + + private _setValid(lineIndex: number, valid: boolean): void { + this._ensureLine(lineIndex); + this._valid[lineIndex] = valid; + } + + private _setBeginState(lineIndex: number, beginState: IState | null): void { + this._ensureLine(lineIndex); + this._beginState[lineIndex] = beginState; + } + + public setEndState(linesLength: number, lineIndex: number, endState: IState): void { + this._setValid(lineIndex, true); + this._invalidLineStartIndex = lineIndex + 1; + + // Check if this was the last line + if (lineIndex === linesLength - 1) { + return; + } + + // Check if the end state has changed + const previousEndState = this.getBeginState(lineIndex + 1); + if (previousEndState === null || !endState.equals(previousEndState)) { + this._setBeginState(lineIndex + 1, endState); + this._invalidateLine(lineIndex + 1); + return; + } + + // Perhaps we can skip tokenizing some lines... + let i = lineIndex + 1; + while (i < linesLength) { + if (!this._isValid(i)) { + break; + } + i++; + } + this._invalidLineStartIndex = i; + } + + public setFakeTokens(lineIndex: number): void { + this._setValid(lineIndex, false); } //#region Editing - public applyEdits(range: Range, eolCount: number, firstLineLength: number): void { - + public applyEdits(range: IRange, eolCount: number): void { const deletingLinesCnt = range.endLineNumber - range.startLineNumber; const insertingLinesCnt = eolCount; const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt); for (let j = editingLinesCnt; j >= 0; j--) { - this.invalidateLine(range.startLineNumber + j - 1); + this._invalidateLine(range.startLineNumber + j - 1); } this._acceptDeleteRange(range); - this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength); + this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount); } - private _acceptDeleteRange(range: Range): void { + private _acceptDeleteRange(range: IRange): void { const firstLineIndex = range.startLineNumber - 1; - if (firstLineIndex >= this._tokens.length) { + if (firstLineIndex >= this._len) { return; } - if (range.startLineNumber === range.endLineNumber) { - if (range.startColumn === range.endColumn) { - // Nothing to delete - return; - } - - this._tokens[firstLineIndex].delete(range.startColumn - 1, range.endColumn - 1); - return; - } - - const firstLine = this._tokens[firstLineIndex]; - firstLine.deleteEnding(range.startColumn - 1); - - const lastLineIndex = range.endLineNumber - 1; - let lastLineTokens: ArrayBuffer | null = null; - if (lastLineIndex < this._tokens.length) { - const lastLine = this._tokens[lastLineIndex]; - lastLine.deleteBeginning(range.endColumn - 1); - lastLineTokens = lastLine._lineTokens; - } - - // Take remaining text on last line and append it to remaining text on first line - firstLine.append(lastLineTokens); - - // Delete middle lines - this._tokens.splice(range.startLineNumber, range.endLineNumber - range.startLineNumber); + this._deleteLines(range.startLineNumber, range.endLineNumber - range.startLineNumber); } - private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void { - - if (eolCount === 0 && firstLineLength === 0) { - // Nothing to insert - return; - } + private _acceptInsertText(position: Position, eolCount: number): void { const lineIndex = position.lineNumber - 1; - if (lineIndex >= this._tokens.length) { + if (lineIndex >= this._len) { return; } - if (eolCount === 0) { - // Inserting text on one line - this._tokens[lineIndex].insert(position.column - 1, firstLineLength); - return; - } - - const line = this._tokens[lineIndex]; - line.deleteEnding(position.column - 1); - line.insert(position.column - 1, firstLineLength); - - let insert: ModelLineTokens[] = new Array(eolCount); - for (let i = eolCount - 1; i >= 0; i--) { - insert[i] = new ModelLineTokens(null); - } - this._tokens = arrays.arrayInsert(this._tokens, position.lineNumber, insert); + this._insertLines(position.lineNumber, eolCount); } //#endregion +} - //#region Tokenization +export class TextModelTokenization extends Disposable { - public _tokenizeOneLine(buffer: ITextBuffer, eventBuilder: ModelTokensChangedEventBuilder): number { - if (!this.hasLinesToTokenize(buffer)) { - return buffer.getLineCount() + 1; + private readonly _textModel: TextModel; + private readonly _tokenizationStateStore: TokenizationStateStore; + private _revalidateTokensTimeout: any; + private _tokenizationSupport: ITokenizationSupport | null; + + constructor(textModel: TextModel) { + super(); + this._textModel = textModel; + this._tokenizationStateStore = new TokenizationStateStore(); + this._revalidateTokensTimeout = -1; + this._tokenizationSupport = null; + + this._register(TokenizationRegistry.onDidChange((e) => { + const languageIdentifier = this._textModel.getLanguageIdentifier(); + if (e.changedLanguages.indexOf(languageIdentifier.language) === -1) { + return; + } + + this._resetTokenizationState(); + this._textModel.clearTokens(); + })); + + this._register(this._textModel.onDidChangeRawContentFast((e) => { + if (e.containsEvent(RawContentChangedType.Flush)) { + this._resetTokenizationState(); + return; + } + })); + + this._register(this._textModel.onDidChangeContentFast((e) => { + for (let i = 0, len = e.changes.length; i < len; i++) { + const change = e.changes[i]; + const [eolCount] = countEOL(change.text); + this._tokenizationStateStore.applyEdits(change.range, eolCount); + } + + this._beginBackgroundTokenization(); + })); + + this._register(this._textModel.onDidChangeAttached(() => { + this._beginBackgroundTokenization(); + })); + + this._register(this._textModel.onDidChangeLanguage(() => { + this._resetTokenizationState(); + this._textModel.clearTokens(); + })); + + this._resetTokenizationState(); + } + + public dispose(): void { + this._clearTimers(); + super.dispose(); + } + + private _clearTimers(): void { + if (this._revalidateTokensTimeout !== -1) { + clearTimeout(this._revalidateTokensTimeout); + this._revalidateTokensTimeout = -1; } - const lineNumber = this._invalidLineStartIndex + 1; - this._updateTokensUntilLine(buffer, eventBuilder, lineNumber); + } + + private _resetTokenizationState(): void { + this._clearTimers(); + const [tokenizationSupport, initialState] = initializeTokenization(this._textModel); + this._tokenizationSupport = tokenizationSupport; + this._tokenizationStateStore.flush(initialState); + this._beginBackgroundTokenization(); + } + + private _beginBackgroundTokenization(): void { + if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize() && this._revalidateTokensTimeout === -1) { + this._revalidateTokensTimeout = setTimeout(() => { + this._revalidateTokensTimeout = -1; + this._revalidateTokensNow(); + }, 0); + } + } + + private _revalidateTokensNow(toLineNumber: number = this._textModel.getLineCount()): void { + const MAX_ALLOWED_TIME = 20; + const builder = new MultilineTokensBuilder(); + const sw = StopWatch.create(false); + + while (this._hasLinesToTokenize()) { + if (sw.elapsed() > MAX_ALLOWED_TIME) { + // Stop if MAX_ALLOWED_TIME is reached + break; + } + + const tokenizedLineNumber = this._tokenizeOneInvalidLine(builder); + + if (tokenizedLineNumber >= toLineNumber) { + break; + } + } + + this._beginBackgroundTokenization(); + this._textModel.setTokens(builder.tokens); + } + + public tokenizeViewport(startLineNumber: number, endLineNumber: number): void { + const builder = new MultilineTokensBuilder(); + this._tokenizeViewport(builder, startLineNumber, endLineNumber); + this._textModel.setTokens(builder.tokens); + } + + public reset(): void { + this._resetTokenizationState(); + this._textModel.clearTokens(); + } + + public forceTokenization(lineNumber: number): void { + const builder = new MultilineTokensBuilder(); + this._updateTokensUntilLine(builder, lineNumber); + this._textModel.setTokens(builder.tokens); + } + + public isCheapToTokenize(lineNumber: number): boolean { + if (!this._tokenizationSupport) { + return true; + } + + const firstInvalidLineNumber = this._tokenizationStateStore.invalidLineStartIndex + 1; + if (lineNumber > firstInvalidLineNumber) { + return false; + } + + if (lineNumber < firstInvalidLineNumber) { + return true; + } + + if (this._textModel.getLineLength(lineNumber) < Constants.CHEAP_TOKENIZATION_LENGTH_LIMIT) { + return true; + } + + return false; + } + + private _hasLinesToTokenize(): boolean { + if (!this._tokenizationSupport) { + return false; + } + return (this._tokenizationStateStore.invalidLineStartIndex < this._textModel.getLineCount()); + } + + private _tokenizeOneInvalidLine(builder: MultilineTokensBuilder): number { + if (!this._hasLinesToTokenize()) { + return this._textModel.getLineCount() + 1; + } + const lineNumber = this._tokenizationStateStore.invalidLineStartIndex + 1; + this._updateTokensUntilLine(builder, lineNumber); return lineNumber; } - public _tokenizeText(buffer: ITextBuffer, text: string, state: IState): TokenizationResult2 { - let r: TokenizationResult2 | null = null; - - if (this.tokenizationSupport) { - try { - r = this.tokenizationSupport.tokenize2(text, state, 0); - } catch (e) { - onUnexpectedError(e); - } - } - - if (!r) { - r = nullTokenize2(this.languageIdentifier.id, text, state, 0); - } - return r; - } - - public _updateTokensUntilLine(buffer: ITextBuffer, eventBuilder: ModelTokensChangedEventBuilder, lineNumber: number): void { - if (!this.tokenizationSupport) { - this._invalidLineStartIndex = buffer.getLineCount(); + private _updateTokensUntilLine(builder: MultilineTokensBuilder, lineNumber: number): void { + if (!this._tokenizationSupport) { return; } - - const linesLength = buffer.getLineCount(); + const languageIdentifier = this._textModel.getLanguageIdentifier(); + const linesLength = this._textModel.getLineCount(); const endLineIndex = lineNumber - 1; // Validate all states up to and including endLineIndex - for (let lineIndex = this._invalidLineStartIndex; lineIndex <= endLineIndex; lineIndex++) { - const endStateIndex = lineIndex + 1; - const text = buffer.getLineContent(lineIndex + 1); - const lineStartState = this._getState(lineIndex); + for (let lineIndex = this._tokenizationStateStore.invalidLineStartIndex; lineIndex <= endLineIndex; lineIndex++) { + const text = this._textModel.getLineContent(lineIndex + 1); + const lineStartState = this._tokenizationStateStore.getBeginState(lineIndex); - let r: TokenizationResult2 | null = null; + const r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, lineStartState!); + builder.add(lineIndex + 1, r.tokens); + this._tokenizationStateStore.setEndState(linesLength, lineIndex, r.endState); + lineIndex = this._tokenizationStateStore.invalidLineStartIndex - 1; // -1 because the outer loop increments it + } + } - try { - // Tokenize only the first X characters - let freshState = lineStartState!.clone(); - r = this.tokenizationSupport.tokenize2(text, freshState, 0); - } catch (e) { - onUnexpectedError(e); + private _tokenizeViewport(builder: MultilineTokensBuilder, startLineNumber: number, endLineNumber: number): void { + if (!this._tokenizationSupport) { + // nothing to do + return; + } + + if (endLineNumber <= this._tokenizationStateStore.invalidLineStartIndex) { + // nothing to do + return; + } + + if (startLineNumber <= this._tokenizationStateStore.invalidLineStartIndex) { + // tokenization has reached the viewport start... + this._updateTokensUntilLine(builder, endLineNumber); + return; + } + + let nonWhitespaceColumn = this._textModel.getLineFirstNonWhitespaceColumn(startLineNumber); + let fakeLines: string[] = []; + let initialState: IState | null = null; + for (let i = startLineNumber - 1; nonWhitespaceColumn > 0 && i >= 1; i--) { + let newNonWhitespaceIndex = this._textModel.getLineFirstNonWhitespaceColumn(i); + + if (newNonWhitespaceIndex === 0) { + continue; } - if (!r) { - r = nullTokenize2(this.languageIdentifier.id, text, lineStartState, 0); - } - this._setTokens(this.languageIdentifier.id, lineIndex, text.length, r.tokens); - eventBuilder.registerChangedTokens(lineIndex + 1); - this._setIsInvalid(lineIndex, false); - - if (endStateIndex < linesLength) { - const previousEndState = this._getState(endStateIndex); - if (previousEndState !== null && r.endState.equals(previousEndState)) { - // The end state of this line remains the same - let nextInvalidLineIndex = lineIndex + 1; - while (nextInvalidLineIndex < linesLength) { - if (this._isInvalid(nextInvalidLineIndex)) { - break; - } - if (nextInvalidLineIndex + 1 < linesLength) { - if (this._getState(nextInvalidLineIndex + 1) === null) { - break; - } - } else { - if (this._lastState === null) { - break; - } - } - nextInvalidLineIndex++; - } - this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, nextInvalidLineIndex); - lineIndex = nextInvalidLineIndex - 1; // -1 because the outer loop increments it - } else { - this._setState(endStateIndex, r.endState); + if (newNonWhitespaceIndex < nonWhitespaceColumn) { + initialState = this._tokenizationStateStore.getBeginState(i - 1); + if (initialState) { + break; } - } else { - this._lastState = r.endState; + fakeLines.push(this._textModel.getLineContent(i)); + nonWhitespaceColumn = newNonWhitespaceIndex; } } - this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, endLineIndex + 1); - } - // #endregion + if (!initialState) { + initialState = this._tokenizationSupport.getInitialState(); + } + + const languageIdentifier = this._textModel.getLanguageIdentifier(); + let state = initialState; + for (let i = fakeLines.length - 1; i >= 0; i--) { + let r = safeTokenize(languageIdentifier, this._tokenizationSupport, fakeLines[i], state); + state = r.endState; + } + + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + let text = this._textModel.getLineContent(lineNumber); + let r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, state); + builder.add(lineNumber, r.tokens); + this._tokenizationStateStore.setFakeTokens(lineNumber - 1); + state = r.endState; + } + } } -export class ModelTokensChangedEventBuilder { - - private readonly _ranges: { fromLineNumber: number; toLineNumber: number; }[]; - - constructor() { - this._ranges = []; +function initializeTokenization(textModel: TextModel): [ITokenizationSupport | null, IState | null] { + const languageIdentifier = textModel.getLanguageIdentifier(); + let tokenizationSupport = ( + textModel.isTooLargeForTokenization() + ? null + : TokenizationRegistry.get(languageIdentifier.language) + ); + let initialState: IState | null = null; + if (tokenizationSupport) { + try { + initialState = tokenizationSupport.getInitialState(); + } catch (e) { + onUnexpectedError(e); + tokenizationSupport = null; + } } + return [tokenizationSupport, initialState]; +} - public registerChangedTokens(lineNumber: number): void { - const ranges = this._ranges; - const rangesLength = ranges.length; - const previousRange = rangesLength > 0 ? ranges[rangesLength - 1] : null; +function safeTokenize(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null, text: string, state: IState): TokenizationResult2 { + let r: TokenizationResult2 | null = null; - if (previousRange && previousRange.toLineNumber === lineNumber - 1) { - // extend previous range - previousRange.toLineNumber++; - } else { - // insert new range - ranges[rangesLength] = { - fromLineNumber: lineNumber, - toLineNumber: lineNumber - }; + if (tokenizationSupport) { + try { + r = tokenizationSupport.tokenize2(text, state.clone(), 0); + } catch (e) { + onUnexpectedError(e); } } - public build(): IModelTokensChangedEvent | null { - if (this._ranges.length === 0) { - return null; - } - return { - tokenizationSupportChanged: false, - ranges: this._ranges - }; + if (!r) { + r = nullTokenize2(languageIdentifier.id, text, state, 0); } + + LineTokens.convertToEndOffset(r.tokens, text.length); + return r; } diff --git a/src/vs/editor/common/model/tokensStore.ts b/src/vs/editor/common/model/tokensStore.ts new file mode 100644 index 0000000000..40c39f4bb5 --- /dev/null +++ b/src/vs/editor/common/model/tokensStore.ts @@ -0,0 +1,330 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from 'vs/base/common/arrays'; +import { LineTokens } from 'vs/editor/common/core/lineTokens'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange } from 'vs/editor/common/core/range'; +import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes'; + +function getDefaultMetadata(topLevelLanguageId: LanguageId): number { + return ( + (topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET) + | (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET) + | (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; +} + +const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer; + +export class MultilineTokensBuilder { + + public readonly tokens: MultilineTokens[]; + + constructor() { + this.tokens = []; + } + + public add(lineNumber: number, lineTokens: Uint32Array): void { + if (this.tokens.length > 0) { + const last = this.tokens[this.tokens.length - 1]; + const lastLineNumber = last.startLineNumber + last.tokens.length - 1; + if (lastLineNumber + 1 === lineNumber) { + // append + last.tokens.push(lineTokens); + return; + } + } + this.tokens.push(new MultilineTokens(lineNumber, lineTokens)); + } +} + +export class MultilineTokens { + + public readonly startLineNumber: number; + public readonly tokens: Uint32Array[]; + + constructor(lineNumber: number, tokens: Uint32Array) { + this.startLineNumber = lineNumber; + this.tokens = [tokens]; + } +} + +export class TokensStore { + private _lineTokens: (ArrayBuffer | null)[]; + private _len: number; + + constructor() { + this._lineTokens = []; + this._len = 0; + } + + public flush(): void { + this._lineTokens = []; + this._len = 0; + } + + public getTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineText: string): LineTokens { + let rawLineTokens: ArrayBuffer | null = null; + if (lineIndex < this._len) { + rawLineTokens = this._lineTokens[lineIndex]; + } + + if (rawLineTokens !== null && rawLineTokens !== EMPTY_LINE_TOKENS) { + return new LineTokens(new Uint32Array(rawLineTokens), lineText); + } + + let lineTokens = new Uint32Array(2); + lineTokens[0] = lineText.length; + lineTokens[1] = getDefaultMetadata(topLevelLanguageId); + return new LineTokens(lineTokens, lineText); + } + + private static _massageTokens(topLevelLanguageId: LanguageId, lineTextLength: number, tokens: Uint32Array): ArrayBuffer { + if (lineTextLength === 0) { + let hasDifferentLanguageId = false; + if (tokens && tokens.length > 1) { + hasDifferentLanguageId = (TokenMetadata.getLanguageId(tokens[1]) !== topLevelLanguageId); + } + + if (!hasDifferentLanguageId) { + return EMPTY_LINE_TOKENS; + } + } + + if (!tokens || tokens.length === 0) { + tokens = new Uint32Array(2); + tokens[0] = lineTextLength; + tokens[1] = getDefaultMetadata(topLevelLanguageId); + } + + return tokens.buffer; + } + + private _ensureLine(lineIndex: number): void { + while (lineIndex >= this._len) { + this._lineTokens[this._len] = null; + this._len++; + } + } + + private _deleteLines(start: number, deleteCount: number): void { + if (deleteCount === 0) { + return; + } + this._lineTokens.splice(start, deleteCount); + this._len -= deleteCount; + } + + private _insertLines(insertIndex: number, insertCount: number): void { + if (insertCount === 0) { + return; + } + let lineTokens: (ArrayBuffer | null)[] = []; + for (let i = 0; i < insertCount; i++) { + lineTokens[i] = null; + } + this._lineTokens = arrays.arrayInsert(this._lineTokens, insertIndex, lineTokens); + this._len += insertCount; + } + + public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array): void { + const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens); + this._ensureLine(lineIndex); + this._lineTokens[lineIndex] = tokens; + } + + //#region Editing + + public applyEdits(range: IRange, eolCount: number, firstLineLength: number): void { + this._acceptDeleteRange(range); + this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength); + } + + private _acceptDeleteRange(range: IRange): void { + + const firstLineIndex = range.startLineNumber - 1; + if (firstLineIndex >= this._len) { + return; + } + + if (range.startLineNumber === range.endLineNumber) { + if (range.startColumn === range.endColumn) { + // Nothing to delete + return; + } + + this._lineTokens[firstLineIndex] = TokensStore._delete(this._lineTokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1); + return; + } + + this._lineTokens[firstLineIndex] = TokensStore._deleteEnding(this._lineTokens[firstLineIndex], range.startColumn - 1); + + const lastLineIndex = range.endLineNumber - 1; + let lastLineTokens: ArrayBuffer | null = null; + if (lastLineIndex < this._len) { + lastLineTokens = TokensStore._deleteBeginning(this._lineTokens[lastLineIndex], range.endColumn - 1); + } + + // Take remaining text on last line and append it to remaining text on first line + this._lineTokens[firstLineIndex] = TokensStore._append(this._lineTokens[firstLineIndex], lastLineTokens); + + // Delete middle lines + this._deleteLines(range.startLineNumber, range.endLineNumber - range.startLineNumber); + } + + private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void { + + if (eolCount === 0 && firstLineLength === 0) { + // Nothing to insert + return; + } + + const lineIndex = position.lineNumber - 1; + if (lineIndex >= this._len) { + return; + } + + if (eolCount === 0) { + // Inserting text on one line + this._lineTokens[lineIndex] = TokensStore._insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength); + return; + } + + this._lineTokens[lineIndex] = TokensStore._deleteEnding(this._lineTokens[lineIndex], position.column - 1); + this._lineTokens[lineIndex] = TokensStore._insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength); + + this._insertLines(position.lineNumber, eolCount); + } + + private static _deleteBeginning(lineTokens: ArrayBuffer | null, toChIndex: number): ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { + return lineTokens; + } + return TokensStore._delete(lineTokens, 0, toChIndex); + } + + private static _deleteEnding(lineTokens: ArrayBuffer | null, fromChIndex: number): ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { + return lineTokens; + } + + const tokens = new Uint32Array(lineTokens); + const lineTextLength = tokens[tokens.length - 2]; + return TokensStore._delete(lineTokens, fromChIndex, lineTextLength); + } + + private static _delete(lineTokens: ArrayBuffer | null, fromChIndex: number, toChIndex: number): ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS || fromChIndex === toChIndex) { + return lineTokens; + } + + const tokens = new Uint32Array(lineTokens); + const tokensCount = (tokens.length >>> 1); + + // special case: deleting everything + if (fromChIndex === 0 && tokens[tokens.length - 2] === toChIndex) { + return EMPTY_LINE_TOKENS; + } + + const fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, fromChIndex); + const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0); + const fromTokenEndOffset = tokens[fromTokenIndex << 1]; + + if (toChIndex < fromTokenEndOffset) { + // the delete range is inside a single token + const delta = (toChIndex - fromChIndex); + for (let i = fromTokenIndex; i < tokensCount; i++) { + tokens[i << 1] -= delta; + } + return lineTokens; + } + + let dest: number; + let lastEnd: number; + if (fromTokenStartOffset !== fromChIndex) { + tokens[fromTokenIndex << 1] = fromChIndex; + dest = ((fromTokenIndex + 1) << 1); + lastEnd = fromChIndex; + } else { + dest = (fromTokenIndex << 1); + lastEnd = fromTokenStartOffset; + } + + const delta = (toChIndex - fromChIndex); + for (let tokenIndex = fromTokenIndex + 1; tokenIndex < tokensCount; tokenIndex++) { + const tokenEndOffset = tokens[tokenIndex << 1] - delta; + if (tokenEndOffset > lastEnd) { + tokens[dest++] = tokenEndOffset; + tokens[dest++] = tokens[(tokenIndex << 1) + 1]; + lastEnd = tokenEndOffset; + } + } + + if (dest === tokens.length) { + // nothing to trim + return lineTokens; + } + + let tmp = new Uint32Array(dest); + tmp.set(tokens.subarray(0, dest), 0); + return tmp.buffer; + } + + private static _append(lineTokens: ArrayBuffer | null, _otherTokens: ArrayBuffer | null): ArrayBuffer | null { + if (_otherTokens === EMPTY_LINE_TOKENS) { + return lineTokens; + } + if (lineTokens === EMPTY_LINE_TOKENS) { + return _otherTokens; + } + if (lineTokens === null) { + return lineTokens; + } + if (_otherTokens === null) { + // cannot determine combined line length... + return null; + } + const myTokens = new Uint32Array(lineTokens); + const otherTokens = new Uint32Array(_otherTokens); + const otherTokensCount = (otherTokens.length >>> 1); + + let result = new Uint32Array(myTokens.length + otherTokens.length); + result.set(myTokens, 0); + let dest = myTokens.length; + const delta = myTokens[myTokens.length - 2]; + for (let i = 0; i < otherTokensCount; i++) { + result[dest++] = otherTokens[(i << 1)] + delta; + result[dest++] = otherTokens[(i << 1) + 1]; + } + return result.buffer; + } + + private static _insert(lineTokens: ArrayBuffer | null, chIndex: number, textLength: number): ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { + // nothing to do + return lineTokens; + } + + const tokens = new Uint32Array(lineTokens); + const tokensCount = (tokens.length >>> 1); + + let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex); + if (fromTokenIndex > 0) { + const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1]; + if (fromTokenStartOffset === chIndex) { + fromTokenIndex--; + } + } + for (let tokenIndex = fromTokenIndex; tokenIndex < tokensCount; tokenIndex++) { + tokens[tokenIndex << 1] += textLength; + } + return lineTokens; + } + + //#endregion +} diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index ca1acb961f..a7b1cea0cb 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -17,8 +17,8 @@ import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/t import * as model from 'vs/editor/common/model'; import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; -import { IMarkerData } from 'vs/platform/markers/common/markers'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IMarkerData } from 'vs/platform/markers/common/markers'; /** * Open ended enum at runtime @@ -510,6 +510,11 @@ export interface CompletionContext { */ export interface CompletionItemProvider { + /** + * @internal + */ + _debugDisplayName?: string; + triggerCharacters?: string[]; /** * Provide completion items for the given position and document. @@ -550,6 +555,10 @@ export interface CodeActionContext { trigger: CodeActionTrigger; } +export interface CodeActionList extends IDisposable { + readonly actions: ReadonlyArray; +} + /** * The code action interface defines the contract between extensions and * the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. @@ -559,7 +568,7 @@ export interface CodeActionProvider { /** * Provide commands for the given document and range. */ - provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult; + provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult; /** * Optional list of CodeActionKinds that this provider returns. @@ -624,6 +633,10 @@ export interface SignatureHelp { activeParameter: number; } +export interface SignatureHelpResult extends IDisposable { + value: SignatureHelp; +} + export enum SignatureHelpTriggerKind { Invoke = 1, TriggerCharacter = 2, @@ -649,7 +662,7 @@ export interface SignatureHelpProvider { /** * Provide help for the signature at the given position and document. */ - provideSignatureHelp(model: model.ITextModel, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; + provideSignatureHelp(model: model.ITextModel, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; } /** @@ -1000,6 +1013,7 @@ export interface IInplaceReplaceSupportResult { export interface ILink { range: IRange; url?: URI | string; + tooltip?: string; } export interface ILinksList { @@ -1093,7 +1107,6 @@ export interface DocumentColorProvider { } export interface SelectionRange { - kind: string; range: IRange; } @@ -1228,19 +1241,7 @@ export interface CommentThreadTemplate { export interface CommentInfo { extensionId?: string; threads: CommentThread[]; - commentingRanges?: (IRange[] | CommentingRanges); - reply?: Command; - draftMode?: DraftMode; - template?: CommentThreadTemplate; -} - -/** - * @internal - */ -export enum DraftMode { - NotSupported, - InDraft, - NotInDraft + commentingRanges: CommentingRanges; } /** @@ -1280,7 +1281,7 @@ export interface CommentInput { /** * @internal */ -export interface CommentThread2 { +export interface CommentThread { commentThreadHandle: number; controllerHandle: number; extensionId?: string; @@ -1294,11 +1295,6 @@ export interface CommentThread2 { collapsibleState?: CommentThreadCollapsibleState; input?: CommentInput; onDidChangeInput: Event; - acceptInputCommand?: Command; - additionalCommands?: Command[]; - deleteCommand?: Command; - onDidChangeAcceptInputCommand: Event; - onDidChangeAdditionalCommands: Event; onDidChangeRange: Event; onDidChangeLabel: Event; onDidChangeCollasibleState: Event; @@ -1312,30 +1308,6 @@ export interface CommentThread2 { export interface CommentingRanges { readonly resource: URI; ranges: IRange[]; - newCommentThreadCallback?: (uri: UriComponents, range: IRange) => Promise; -} - -/** - * @internal - */ -export interface CommentThread { - extensionId?: string; - threadId: string | null; - resource: string | null; - range: IRange; - comments: Comment[] | undefined; - collapsibleState?: CommentThreadCollapsibleState; - reply?: Command; - isDisposed?: boolean; - contextValue?: string; -} - -/** - * @internal - */ -export interface NewCommentAction { - ranges: IRange[]; - actions: Command[]; } /** @@ -1367,12 +1339,6 @@ export interface Comment { readonly userName: string; readonly userIconPath?: string; readonly contextValue?: string; - readonly canEdit?: boolean; - readonly canDelete?: boolean; - readonly selectCommand?: Command; - readonly editCommand?: Command; - readonly deleteCommand?: Command; - readonly isDraft?: boolean; readonly commentReactions?: CommentReaction[]; readonly label?: string; readonly mode?: CommentMode; @@ -1385,54 +1351,17 @@ export interface CommentThreadChangedEvent { /** * Added comment threads. */ - readonly added: (CommentThread | CommentThread2)[]; + readonly added: CommentThread[]; /** * Removed comment threads. */ - readonly removed: (CommentThread | CommentThread2)[]; + readonly removed: CommentThread[]; /** * Changed comment threads. */ - readonly changed: (CommentThread | CommentThread2)[]; - - /** - * changed draft mode. - */ - readonly draftMode?: DraftMode; -} - -/** - * @internal - */ -export interface DocumentCommentProvider { - provideDocumentComments(resource: URI, token: CancellationToken): Promise; - createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise; - replyToCommentThread(resource: URI, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise; - editComment(resource: URI, comment: Comment, text: string, token: CancellationToken): Promise; - deleteComment(resource: URI, comment: Comment, token: CancellationToken): Promise; - startDraft?(resource: URI, token: CancellationToken): Promise; - deleteDraft?(resource: URI, token: CancellationToken): Promise; - finishDraft?(resource: URI, token: CancellationToken): Promise; - - startDraftLabel?: string; - deleteDraftLabel?: string; - finishDraftLabel?: string; - - addReaction?(resource: URI, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise; - deleteReaction?(resource: URI, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise; - reactionGroup?: CommentReaction[]; - - onDidChangeCommentThreads?(): Event; -} - -/** - * @internal - */ -export interface WorkspaceCommentProvider { - provideWorkspaceComments(token: CancellationToken): Promise; - onDidChangeCommentThreads(): Event; + readonly changed: CommentThread[]; } /** @@ -1461,15 +1390,21 @@ export interface IWebviewPanelOptions { readonly retainContextWhenHidden?: boolean; } -export interface ICodeLensSymbol { +export interface CodeLens { range: IRange; id?: string; command?: Command; } + +export interface CodeLensList { + lenses: CodeLens[]; + dispose(): void; +} + export interface CodeLensProvider { onDidChange?: Event; - provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult; - resolveCodeLens?(model: model.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult; + provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult; + resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } // --- feature registries ------ diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 2c729d1dc6..fad3f67d89 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -22,7 +22,8 @@ import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/modes/linkCo import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; -import { getAllPropertyNames } from 'vs/base/common/types'; +import * as types from 'vs/base/common/types'; +import { EditorWorkerHost } from 'vs/editor/common/services/editorWorkerServiceImpl'; export interface IMirrorModel { readonly uri: URI; @@ -30,7 +31,11 @@ export interface IMirrorModel { getValue(): string; } -export interface IWorkerContext { +export interface IWorkerContext { + /** + * A proxy to the main thread host object. + */ + host: H; /** * Get all available mirror models in this worker. */ @@ -322,17 +327,53 @@ declare var require: any; /** * @internal */ -export abstract class BaseEditorSimpleWorker { +export class EditorSimpleWorker implements IRequestHandler, IDisposable { + _requestHandlerBrand: any; + + private readonly _host: EditorWorkerHost; + private _models: { [uri: string]: MirrorModel; }; private readonly _foreignModuleFactory: IForeignModuleFactory | null; private _foreignModule: any; - constructor(foreignModuleFactory: IForeignModuleFactory | null) { + constructor(host: EditorWorkerHost, foreignModuleFactory: IForeignModuleFactory | null) { + this._host = host; + this._models = Object.create(null); this._foreignModuleFactory = foreignModuleFactory; this._foreignModule = null; } - protected abstract _getModel(uri: string): ICommonModel; - protected abstract _getModels(): ICommonModel[]; + public dispose(): void { + this._models = Object.create(null); + } + + protected _getModel(uri: string): ICommonModel { + return this._models[uri]; + } + + private _getModels(): ICommonModel[] { + let all: MirrorModel[] = []; + Object.keys(this._models).forEach((key) => all.push(this._models[key])); + return all; + } + + public acceptNewModel(data: IRawModelData): void { + this._models[data.url] = new MirrorModel(URI.parse(data.url), data.lines, data.EOL, data.versionId); + } + + public acceptModelChanged(strURL: string, e: IModelChangedEvent): void { + if (!this._models[strURL]) { + return; + } + let model = this._models[strURL]; + model.onEvents(e); + } + + public acceptRemovedModel(strURL: string): void { + if (!this._models[strURL]) { + return; + } + delete this._models[strURL]; + } // ---- BEGIN diff -------------------------------------------------------------------------- @@ -440,7 +481,7 @@ export abstract class BaseEditorSimpleWorker { } // make sure diff won't take too long - if (Math.max(text.length, original.length) > BaseEditorSimpleWorker._diffLimit) { + if (Math.max(text.length, original.length) > EditorSimpleWorker._diffLimit) { result.push({ range, text }); continue; } @@ -503,7 +544,7 @@ export abstract class BaseEditorSimpleWorker { for ( let iter = model.createWordIterator(wordDefRegExp), e = iter.next(); - !e.done && suggestions.length <= BaseEditorSimpleWorker._suggestionsLimit; + !e.done && suggestions.length <= EditorSimpleWorker._suggestionsLimit; e = iter.next() ) { const word = e.value; @@ -591,8 +632,15 @@ export abstract class BaseEditorSimpleWorker { // ---- BEGIN foreign module support -------------------------------------------------------------------------- - public loadForeignModule(moduleId: string, createData: any): Promise { - let ctx: IWorkerContext = { + public loadForeignModule(moduleId: string, createData: any, foreignHostMethods: string[]): Promise { + const proxyMethodRequest = (method: string, args: any[]): Promise => { + return this._host.fhr(method, args); + }; + + const foreignHost = types.createProxyObject(foreignHostMethods, proxyMethodRequest); + + let ctx: IWorkerContext = { + host: foreignHost, getMirrorModels: (): IMirrorModel[] => { return this._getModels(); } @@ -601,27 +649,14 @@ export abstract class BaseEditorSimpleWorker { if (this._foreignModuleFactory) { this._foreignModule = this._foreignModuleFactory(ctx, createData); // static foreing module - let methods: string[] = []; - for (const prop of getAllPropertyNames(this._foreignModule)) { - if (typeof this._foreignModule[prop] === 'function') { - methods.push(prop); - } - } - return Promise.resolve(methods); + return Promise.resolve(types.getAllMethodNames(this._foreignModule)); } // ESM-comment-begin return new Promise((resolve, reject) => { require([moduleId], (foreignModule: { create: IForeignModuleFactory }) => { this._foreignModule = foreignModule.create(ctx, createData); - let methods: string[] = []; - for (const prop of getAllPropertyNames(this._foreignModule)) { - if (typeof this._foreignModule[prop] === 'function') { - methods.push(prop); - } - } - - resolve(methods); + resolve(types.getAllMethodNames(this._foreignModule)); }, reject); }); @@ -648,59 +683,12 @@ export abstract class BaseEditorSimpleWorker { // ---- END foreign module support -------------------------------------------------------------------------- } -/** - * @internal - */ -export class EditorSimpleWorkerImpl extends BaseEditorSimpleWorker implements IRequestHandler, IDisposable { - _requestHandlerBrand: any; - - private _models: { [uri: string]: MirrorModel; }; - - constructor(foreignModuleFactory: IForeignModuleFactory | null) { - super(foreignModuleFactory); - this._models = Object.create(null); - } - - public dispose(): void { - this._models = Object.create(null); - } - - protected _getModel(uri: string): ICommonModel { - return this._models[uri]; - } - - protected _getModels(): ICommonModel[] { - let all: MirrorModel[] = []; - Object.keys(this._models).forEach((key) => all.push(this._models[key])); - return all; - } - - public acceptNewModel(data: IRawModelData): void { - this._models[data.url] = new MirrorModel(URI.parse(data.url), data.lines, data.EOL, data.versionId); - } - - public acceptModelChanged(strURL: string, e: IModelChangedEvent): void { - if (!this._models[strURL]) { - return; - } - let model = this._models[strURL]; - model.onEvents(e); - } - - public acceptRemovedModel(strURL: string): void { - if (!this._models[strURL]) { - return; - } - delete this._models[strURL]; - } -} - /** * Called on the worker side * @internal */ -export function create(): IRequestHandler { - return new EditorSimpleWorkerImpl(null); +export function create(host: EditorWorkerHost): IRequestHandler { + return new EditorSimpleWorker(host, null); } // This is only available in a Web Worker diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index 7912ca8080..2f26f50b9d 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IntervalTimer } from 'vs/base/common/async'; -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; +import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/base/common/worker/simpleWorker'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; @@ -15,7 +15,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; +import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; @@ -133,6 +133,8 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider { private readonly _configurationService: ITextResourceConfigurationService; private readonly _modelService: IModelService; + readonly _debugDisplayName = 'wordbasedCompletions'; + constructor( workerManager: WorkerManager, configurationService: ITextResourceConfigurationService, @@ -222,12 +224,12 @@ class WorkerManager extends Disposable { class EditorModelManager extends Disposable { - private readonly _proxy: EditorSimpleWorkerImpl; + private readonly _proxy: EditorSimpleWorker; private readonly _modelService: IModelService; - private _syncedModels: { [modelUrl: string]: IDisposable[]; } = Object.create(null); + private _syncedModels: { [modelUrl: string]: IDisposable; } = Object.create(null); private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null); - constructor(proxy: EditorSimpleWorkerImpl, modelService: IModelService, keepIdleModels: boolean) { + constructor(proxy: EditorSimpleWorker, modelService: IModelService, keepIdleModels: boolean) { super(); this._proxy = proxy; this._modelService = modelService; @@ -295,14 +297,14 @@ class EditorModelManager extends Disposable { versionId: model.getVersionId() }); - let toDispose: IDisposable[] = []; - toDispose.push(model.onDidChangeContent((e) => { + const toDispose = new DisposableStore(); + toDispose.add(model.onDidChangeContent((e) => { this._proxy.acceptModelChanged(modelUrl.toString(), e); })); - toDispose.push(model.onWillDispose(() => { + toDispose.add(model.onWillDispose(() => { this._stopModelSync(modelUrl); })); - toDispose.push(toDisposable(() => { + toDispose.add(toDisposable(() => { this._proxy.acceptRemovedModel(modelUrl); })); @@ -317,11 +319,6 @@ class EditorModelManager extends Disposable { } } -interface IWorkerClient { - getProxyObject(): Promise; - dispose(): void; -} - class SynchronousWorkerClient implements IWorkerClient { private readonly _instance: T; private readonly _proxyObj: Promise; @@ -340,10 +337,24 @@ class SynchronousWorkerClient implements IWorkerClient } } +export class EditorWorkerHost { + + private readonly _workerClient: EditorWorkerClient; + + constructor(workerClient: EditorWorkerClient) { + this._workerClient = workerClient; + } + + // foreign host request + public fhr(method: string, args: any[]): Promise { + return this._workerClient.fhr(method, args); + } +} + export class EditorWorkerClient extends Disposable { private readonly _modelService: IModelService; - private _worker: IWorkerClient | null; + private _worker: IWorkerClient | null; private readonly _workerFactory: DefaultWorkerFactory; private _modelManager: EditorModelManager | null; @@ -355,37 +366,43 @@ export class EditorWorkerClient extends Disposable { this._modelManager = null; } - private _getOrCreateWorker(): IWorkerClient { + // foreign host request + public fhr(method: string, args: any[]): Promise { + throw new Error(`Not implemented!`); + } + + private _getOrCreateWorker(): IWorkerClient { if (!this._worker) { try { - this._worker = this._register(new SimpleWorkerClient( + this._worker = this._register(new SimpleWorkerClient( this._workerFactory, - 'vs/editor/common/services/editorSimpleWorker' + 'vs/editor/common/services/editorSimpleWorker', + new EditorWorkerHost(this) )); } catch (err) { logOnceWebWorkerWarning(err); - this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl(null)); + this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null)); } } return this._worker; } - protected _getProxy(): Promise { + protected _getProxy(): Promise { return this._getOrCreateWorker().getProxyObject().then(undefined, (err) => { logOnceWebWorkerWarning(err); - this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl(null)); + this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null)); return this._getOrCreateWorker().getProxyObject(); }); } - private _getOrCreateModelManager(proxy: EditorSimpleWorkerImpl): EditorModelManager { + private _getOrCreateModelManager(proxy: EditorSimpleWorker): EditorModelManager { if (!this._modelManager) { this._modelManager = this._register(new EditorModelManager(proxy, this._modelService, false)); } return this._modelManager; } - protected _withSyncedResources(resources: URI[]): Promise { + protected _withSyncedResources(resources: URI[]): Promise { return this._getProxy().then((proxy) => { this._getOrCreateModelManager(proxy).ensureSyncedResources(resources); return proxy; diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index 3a9ea2e795..6dbde97acb 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -85,19 +85,7 @@ export function detectModeId(modelService: IModelService, modeService: IModeServ } // otherwise fallback to path based detection - let path: string | undefined; - if (resource.scheme === Schemas.data) { - const metadata = DataUri.parseMetaData(resource); - path = metadata.get(DataUri.META_DATA_LABEL); - } else { - path = resource.path.toLowerCase(); - } - - if (path) { - return modeService.getModeIdByFilepathOrFirstLine(path); - } - - return null; // finally - we do not know the mode id + return modeService.getModeIdByFilepathOrFirstLine(resource); } export function cssEscape(val: string): string { diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 551edafe41..8546b8c063 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -323,11 +323,11 @@ export class LanguagesRegistry extends Disposable { return []; } - public getModeIdsFromFilepathOrFirstLine(filepath: string | null, firstLine?: string): string[] { - if (!filepath && !firstLine) { + public getModeIdsFromFilepathOrFirstLine(resource: URI | null, firstLine?: string): string[] { + if (!resource && !firstLine) { return []; } - let mimeTypes = mime.guessMimeTypes(filepath, firstLine); + let mimeTypes = mime.guessMimeTypes(resource, firstLine); return this.extractModeIds(mimeTypes.join(',')); } diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index 07978df937..4710c947f0 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -63,10 +63,10 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor _serviceBrand: any; - private readonly _onDidChangeMarker = new Emitter(); + private readonly _onDidChangeMarker = this._register(new Emitter()); readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; - private readonly _markerDecorations: Map = new Map(); + private readonly _markerDecorations = new Map(); constructor( @IModelService modelService: IModelService, @@ -79,6 +79,12 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this)); } + dispose() { + super.dispose(); + this._markerDecorations.forEach(value => value.dispose()); + this._markerDecorations.clear(); + } + getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null { const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri)); return markerDecorations ? withUndefinedAsNull(markerDecorations.getMarker(decoration)) : null; diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index f2a54a7189..3e60e3b1a1 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -41,7 +41,7 @@ export interface IModeService { getMimeForMode(modeId: string): string | null; getLanguageName(modeId: string): string | null; getModeIdForLanguageName(alias: string): string | null; - getModeIdByFilepathOrFirstLine(filepath: string, firstLine?: string): string | null; + getModeIdByFilepathOrFirstLine(resource: URI, firstLine?: string): string | null; getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string | null; getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier | null; getConfigurationFiles(modeId: string): URI[]; @@ -49,7 +49,7 @@ export interface IModeService { // --- instantiation create(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): ILanguageSelection; createByLanguageName(languageName: string): ILanguageSelection; - createByFilepathOrFirstLine(filepath: string | null, firstLine?: string): ILanguageSelection; + createByFilepathOrFirstLine(rsource: URI | null, firstLine?: string): ILanguageSelection; triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void; } diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index ef8aa2c45e..f457fd470d 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -94,8 +94,8 @@ export class ModeServiceImpl implements IModeService { return this._registry.getModeIdForLanguageNameLowercase(alias); } - public getModeIdByFilepathOrFirstLine(filepath: string | null, firstLine?: string): string | null { - const modeIds = this._registry.getModeIdsFromFilepathOrFirstLine(filepath, firstLine); + public getModeIdByFilepathOrFirstLine(resource: URI | null, firstLine?: string): string | null { + const modeIds = this._registry.getModeIdsFromFilepathOrFirstLine(resource, firstLine); if (modeIds.length > 0) { return modeIds[0]; @@ -138,9 +138,9 @@ export class ModeServiceImpl implements IModeService { }); } - public createByFilepathOrFirstLine(filepath: string | null, firstLine?: string): ILanguageSelection { + public createByFilepathOrFirstLine(resource: URI | null, firstLine?: string): ILanguageSelection { return new LanguageSelection(this.onLanguagesMaybeChanged, () => { - const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine); + const modeId = this.getModeIdByFilepathOrFirstLine(resource, firstLine); return this._createModeAndGetLanguageIdentifier(modeId); }); } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index fc8e129553..64a369660a 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; @@ -30,7 +30,7 @@ class ModelData implements IDisposable { private _languageSelection: ILanguageSelection | null; private _languageSelectionListener: IDisposable | null; - private _modelEventListeners: IDisposable[]; + private readonly _modelEventListeners = new DisposableStore(); constructor( model: ITextModel, @@ -42,9 +42,8 @@ class ModelData implements IDisposable { this._languageSelection = null; this._languageSelectionListener = null; - this._modelEventListeners = []; - this._modelEventListeners.push(model.onWillDispose(() => onWillDispose(model))); - this._modelEventListeners.push(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e))); + this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model))); + this._modelEventListeners.add(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e))); } private _disposeLanguageSelection(): void { @@ -59,7 +58,7 @@ class ModelData implements IDisposable { } public dispose(): void { - this._modelEventListeners = dispose(this._modelEventListeners); + this._modelEventListeners.dispose(); this._disposeLanguageSelection(); } diff --git a/src/vs/editor/common/services/resourceConfiguration.ts b/src/vs/editor/common/services/resourceConfiguration.ts index 7b51da53fd..7cdd6a6c7e 100644 --- a/src/vs/editor/common/services/resourceConfiguration.ts +++ b/src/vs/editor/common/services/resourceConfiguration.ts @@ -25,7 +25,7 @@ export interface ITextResourceConfigurationService { * Value can be of native type or an object keyed off the section name. * * @param resource - Resource for which the configuration has to be fetched. - * @param postion - Position in the resource for which configuration has to be fetched. + * @param position - Position in the resource for which configuration has to be fetched. * @param section - Section of the configuraion. * */ diff --git a/src/vs/editor/common/services/resourceConfigurationImpl.ts b/src/vs/editor/common/services/resourceConfigurationImpl.ts index 1140355aee..e988031d8e 100644 --- a/src/vs/editor/common/services/resourceConfigurationImpl.ts +++ b/src/vs/editor/common/services/resourceConfigurationImpl.ts @@ -50,7 +50,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex if (model) { return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language; } - return this.modeService.getModeIdByFilepathOrFirstLine(resource.path); + return this.modeService.getModeIdByFilepathOrFirstLine(resource); } } \ No newline at end of file diff --git a/src/vs/editor/common/services/webWorker.ts b/src/vs/editor/common/services/webWorker.ts index cd274432cf..983500b283 100644 --- a/src/vs/editor/common/services/webWorker.ts +++ b/src/vs/editor/common/services/webWorker.ts @@ -6,6 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { EditorWorkerClient } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; +import * as types from 'vs/base/common/types'; /** * Create a new web worker that has model syncing capabilities built in. @@ -48,11 +49,16 @@ export interface IWebWorkerOptions { * A label to be used to identify the web worker for debugging purposes. */ label?: string; + /** + * An object that can be used by the web worker to make calls back to the main thread. + */ + host?: any; } class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWorker { private readonly _foreignModuleId: string; + private readonly _foreignModuleHost: { [method: string]: Function } | null; private _foreignModuleCreateData: any | null; private _foreignProxy: Promise | null; @@ -60,13 +66,28 @@ class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWork super(modelService, opts.label); this._foreignModuleId = opts.moduleId; this._foreignModuleCreateData = opts.createData || null; + this._foreignModuleHost = opts.host || null; this._foreignProxy = null; } + // foreign host request + public fhr(method: string, args: any[]): Promise { + if (!this._foreignModuleHost || typeof this._foreignModuleHost[method] !== 'function') { + return Promise.reject(new Error('Missing method ' + method + ' or missing main thread foreign host.')); + } + + try { + return Promise.resolve(this._foreignModuleHost[method].apply(this._foreignModuleHost, args)); + } catch (e) { + return Promise.reject(e); + } + } + private _getForeignProxy(): Promise { if (!this._foreignProxy) { this._foreignProxy = this._getProxy().then((proxy) => { - return proxy.loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData).then((foreignMethods) => { + const foreignHostMethods = this._foreignModuleHost ? types.getAllMethodNames(this._foreignModuleHost) : []; + return proxy.loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData, foreignHostMethods).then((foreignMethods) => { this._foreignModuleCreateData = null; const proxyMethodRequest = (method: string, args: any[]): Promise => { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 610f9139a4..ce5fe1e1a4 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -228,6 +228,13 @@ export enum OverviewRulerLane { Full = 7 } +/** + * Position in the minimap to render the decoration. + */ +export enum MinimapPosition { + Inline = 1 +} + /** * End of line character preference. */ diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index f80df149dd..9fe4195b39 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { Color, RGBA } from 'vs/base/common/color'; -import { activeContrastBorder, editorBackground, editorForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, editorBackground, editorForeground, registerColor, editorWarningForeground, editorInfoForeground, editorWarningBorder, editorInfoBorder } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; /** @@ -37,26 +37,14 @@ export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.bord export const editorGutter = registerColor('editorGutter.background', { dark: editorBackground, light: editorBackground, hc: editorBackground }, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); -export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#ea4646', light: '#d60a0a', hc: null }, nls.localize('errorForeground', 'Foreground color of error squigglies in the editor.')); -export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error squigglies in the editor.')); - -export const editorWarningForeground = registerColor('editorWarning.foreground', { dark: '#4d9e4d', light: '#117711', hc: null }, nls.localize('warningForeground', 'Foreground color of warning squigglies in the editor.')); -export const editorWarningBorder = registerColor('editorWarning.border', { dark: null, light: null, hc: Color.fromHex('#71B771').transparent(0.8) }, nls.localize('warningBorder', 'Border color of warning squigglies in the editor.')); - -export const editorInfoForeground = registerColor('editorInfo.foreground', { dark: '#008000', light: '#008000', hc: null }, nls.localize('infoForeground', 'Foreground color of info squigglies in the editor.')); -export const editorInfoBorder = registerColor('editorInfo.border', { dark: null, light: null, hc: Color.fromHex('#71B771').transparent(0.8) }, nls.localize('infoBorder', 'Border color of info squigglies in the editor.')); - -export const editorHintForeground = registerColor('editorHint.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('hintForeground', 'Foreground color of hint squigglies in the editor.')); -export const editorHintBorder = registerColor('editorHint.border', { dark: null, light: null, hc: Color.fromHex('#eeeeee').transparent(0.8) }, nls.localize('hintBorder', 'Border color of hint squigglies in the editor.')); - export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hc: Color.fromHex('#fff').transparent(0.8) }, nls.localize('unnecessaryCodeBorder', 'Border color of unnecessary (unused) source code in the editor.')); export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hc: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.')); const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); -export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: new Color(new RGBA(18, 136, 18, 0.7)), light: new Color(new RGBA(18, 136, 18, 0.7)), hc: new Color(new RGBA(50, 255, 50, 1)) }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); -export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: new Color(new RGBA(18, 18, 136, 0.7)), light: new Color(new RGBA(18, 18, 136, 0.7)), hc: new Color(new RGBA(50, 50, 255, 1)) }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); +export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); +export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoBorder }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); // contains all color rules that used to defined in editor/browser/widget/editor.css registerThemingParticipant((theme, collector) => { diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index faf1bd239b..942a74829b 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -13,6 +13,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { ITheme } from 'vs/platform/theme/common/themeService'; +import { IDisposable } from 'vs/base/common/lifecycle'; export class OutputPosition { _outputPositionBrand: void; @@ -62,11 +63,9 @@ export interface ISplitLine { getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number; } -export interface IViewModelLinesCollection { +export interface IViewModelLinesCollection extends IDisposable { createCoordinatesConverter(): ICoordinatesConverter; - dispose(): void; - setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean; setTabSize(newTabSize: number): boolean; getHiddenAreas(): Range[]; diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 367df28c44..8a9335996d 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -15,8 +15,14 @@ import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTr import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger'; import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -export class CodeActionSet { +export interface CodeActionSet extends IDisposable { + readonly actions: readonly CodeAction[]; + readonly hasAutoFix: boolean; +} + +class ManagedCodeActionSet extends Disposable implements CodeActionSet { private static codeActionsComparator(a: CodeAction, b: CodeAction): number { if (isNonEmptyArray(a.diagnostics)) { @@ -34,8 +40,10 @@ export class CodeActionSet { public readonly actions: readonly CodeAction[]; - public constructor(actions: readonly CodeAction[]) { - this.actions = mergeSort([...actions], CodeActionSet.codeActionsComparator); + public constructor(actions: readonly CodeAction[], disposables: DisposableStore) { + super(); + this._register(disposables); + this.actions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); } public get hasAutoFix() { @@ -59,12 +67,14 @@ export function getCodeActions( const cts = new TextModelCancellationTokenSource(model, token); const providers = getCodeActionProviders(model, filter); + const disposables = new DisposableStore(); const promises = providers.map(provider => { return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token)).then(providedCodeActions => { - if (cts.token.isCancellationRequested || !Array.isArray(providedCodeActions)) { + if (cts.token.isCancellationRequested || !providedCodeActions) { return []; } - return providedCodeActions.filter(action => action && filtersAction(filter, action)); + disposables.add(providedCodeActions); + return providedCodeActions.actions.filter(action => action && filtersAction(filter, action)); }, (err): CodeAction[] => { if (isPromiseCanceledError(err)) { throw err; @@ -84,7 +94,7 @@ export function getCodeActions( return Promise.all(promises) .then(flatten) - .then(actions => new CodeActionSet(actions)) + .then(actions => new ManagedCodeActionSet(actions, disposables)) .finally(() => { listener.dispose(); cts.dispose(); @@ -106,7 +116,7 @@ function getCodeActionProviders( }); } -registerLanguageCommand('_executeCodeActionProvider', function (accessor, args): Promise> { +registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { const { resource, range, kind } = args; if (!(resource instanceof URI) || !Range.isIRange(range)) { throw illegalArgument(); @@ -117,9 +127,12 @@ registerLanguageCommand('_executeCodeActionProvider', function (accessor, args): throw illegalArgument(); } - return getCodeActions( + const codeActionSet = await getCodeActions( model, model.validateRange(range), { type: 'manual', filter: { includeSourceActions: true, kind: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, - CancellationToken.None).then(actions => actions.actions); + CancellationToken.None); + + setTimeout(() => codeActionSet.dispose(), 0); + return codeActionSet.actions; }); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 697f8979d2..5fe9a90798 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancelablePromise } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; @@ -13,21 +12,21 @@ import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CodeAction } from 'vs/editor/common/modes'; +import { CodeActionUi } from 'vs/editor/contrib/codeAction/codeActionUi'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IProgressService } from 'vs/platform/progress/common/progress'; -import { CodeActionModel, SUPPORTED_CODE_ACTIONS, CodeActionsState } from './codeActionModel'; -import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeActionTrigger'; -import { CodeActionContextMenu } from './codeActionWidget'; -import { LightBulbWidget } from './lightBulbWidget'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; +import { CodeActionAutoApply, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger'; import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +import { IPosition } from 'vs/editor/common/core/position'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -35,6 +34,7 @@ function contextKeyForSupportedActions(kind: CodeActionKind) { new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b')); } + export class QuickFixController extends Disposable implements IEditorContribution { private static readonly ID = 'editor.contrib.quickFixController'; @@ -45,104 +45,68 @@ export class QuickFixController extends Disposable implements IEditorContributio private readonly _editor: ICodeEditor; private readonly _model: CodeActionModel; - private readonly _codeActionContextMenu: CodeActionContextMenu; - private readonly _lightBulbWidget: LightBulbWidget; - - private _activeRequest: CancelablePromise | undefined; + private readonly _ui: CodeActionUi; constructor( editor: ICodeEditor, @IMarkerService markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, - @IProgressService progressService: IProgressService, + @IEditorProgressService progressService: IEditorProgressService, @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, @ICommandService private readonly _commandService: ICommandService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, ) { super(); this._editor = editor; - this._model = new CodeActionModel(this._editor, markerService, contextKeyService, progressService); - this._codeActionContextMenu = new CodeActionContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action)); - this._lightBulbWidget = this._register(new LightBulbWidget(editor)); + this._model = this._register(new CodeActionModel(this._editor, markerService, contextKeyService, progressService)); + this._register(this._model.onDidChangeState((newState) => this.update(newState))); - this._updateLightBulbTitle(); - - this._register(this._codeActionContextMenu.onDidExecuteCodeAction(_ => this._model.trigger({ type: 'auto', filter: {} }))); - this._register(this._lightBulbWidget.onClick(this._handleLightBulbSelect, this)); - this._register(this._model.onDidChangeState(e => this._onDidChangeCodeActionsState(e))); - this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)); - } - - public dispose(): void { - super.dispose(); - this._model.dispose(); - } - - private _onDidChangeCodeActionsState(newState: CodeActionsState.State): void { - if (this._activeRequest) { - this._activeRequest.cancel(); - this._activeRequest = undefined; - } - - if (newState.type === CodeActionsState.Type.Triggered) { - this._activeRequest = newState.actions; - - if (newState.trigger.filter && newState.trigger.filter.kind) { - // Triggered for specific scope - newState.actions.then(fixes => { - if (fixes.actions.length > 0) { - // Apply if we only have one action or requested autoApply - if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.actions.length === 1)) { - this._onApplyCodeAction(fixes.actions[0]); - return; - } + this._ui = this._register(new CodeActionUi(editor, QuickFixAction.Id, { + applyCodeAction: async (action, retrigger) => { + try { + await this._applyCodeAction(action); + } finally { + if (retrigger) { + this._trigger({ type: 'auto', filter: {} }); } - this._codeActionContextMenu.show(newState.actions, newState.position); - - }).catch(onUnexpectedError); - } else if (newState.trigger.type === 'manual') { - this._codeActionContextMenu.show(newState.actions, newState.position); - } else { - // auto magically triggered - // * update an existing list of code actions - // * manage light bulb - if (this._codeActionContextMenu.isVisible) { - this._codeActionContextMenu.show(newState.actions, newState.position); - } else { - this._lightBulbWidget.tryShow(newState); } } - } else { - this._lightBulbWidget.hide(); - } + }, contextMenuService, keybindingService)); + } + + private update(newState: CodeActionsState.State): void { + this._ui.update(newState); + } + + public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) { + return this._ui.showCodeActionList(actions, at); } public getId(): string { return QuickFixController.ID; } - private _handleLightBulbSelect(e: { x: number, y: number, state: CodeActionsState.Triggered }): void { - this._codeActionContextMenu.show(e.state.actions, e); - } - - public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Promise { - return this._model.trigger({ type: 'manual', filter, autoApply }); - } - - private _updateLightBulbTitle(): void { - const kb = this._keybindingService.lookupKeybinding(QuickFixAction.Id); - let title: string; - if (kb) { - title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel()); - } else { - title = nls.localize('quickFix', "Show Fixes"); + public manualTriggerAtCurrentPosition( + notAvailableMessage: string, + filter?: CodeActionFilter, + autoApply?: CodeActionAutoApply + ): void { + if (!this._editor.hasModel()) { + return; } - this._lightBulbWidget.title = title; + + MessageController.get(this._editor).closeMessage(); + const triggerPosition = this._editor.getPosition(); + this._trigger({ type: 'manual', filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); } - private _onApplyCodeAction(action: CodeAction): Promise { + private _trigger(trigger: CodeActionTrigger) { + return this._model.trigger(trigger); + } + + private _applyCodeAction(action: CodeAction): Promise { return applyCodeAction(action, this._bulkEditService, this._commandService, this._editor); } } @@ -161,28 +125,18 @@ export async function applyCodeAction( } } -function showCodeActionsForEditorSelection( +function triggerCodeActionsForEditorSelection( editor: ICodeEditor, notAvailableMessage: string, - filter?: CodeActionFilter, - autoApply?: CodeActionAutoApply -) { - if (!editor.hasModel()) { - return; - } - - const controller = QuickFixController.get(editor); - if (!controller) { - return; - } - - MessageController.get(editor).closeMessage(); - const pos = editor.getPosition(); - controller.triggerFromEditorSelection(filter, autoApply).then(codeActions => { - if (!codeActions || !codeActions.actions.length) { - MessageController.get(editor).showMessage(notAvailableMessage, pos); + filter: CodeActionFilter | undefined, + autoApply: CodeActionAutoApply | undefined +): void { + if (editor.hasModel()) { + const controller = QuickFixController.get(editor); + if (controller) { + controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply); } - }); + } } export class QuickFixAction extends EditorAction { @@ -193,7 +147,7 @@ export class QuickFixAction extends EditorAction { super({ id: QuickFixAction.Id, label: nls.localize('quickfix.trigger.label', "Quick Fix..."), - alias: 'Quick Fix', + alias: 'Quick Fix...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -204,7 +158,7 @@ export class QuickFixAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available")); + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), undefined, undefined); } } @@ -284,7 +238,7 @@ export class CodeActionCommand extends EditorCommand { kind: CodeActionKind.Empty, apply: CodeActionAutoApply.IfSingle, }); - return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), { kind: args.kind, includeSourceActions: true, @@ -303,7 +257,7 @@ export class RefactorAction extends EditorAction { super({ id: RefactorAction.Id, label: nls.localize('refactor.label', "Refactor..."), - alias: 'Refactor', + alias: 'Refactor...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -347,7 +301,7 @@ export class RefactorAction extends EditorAction { kind: CodeActionKind.Refactor, apply: CodeActionAutoApply.Never }); - return showCodeActionsForEditorSelection(editor, + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), { kind: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.Empty, @@ -366,7 +320,7 @@ export class SourceAction extends EditorAction { super({ id: SourceAction.Id, label: nls.localize('source.label', "Source Action..."), - alias: 'Source Action', + alias: 'Source Action...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), menuOpts: { group: '1_modification', @@ -402,7 +356,7 @@ export class SourceAction extends EditorAction { kind: CodeActionKind.Source, apply: CodeActionAutoApply.Never }); - return showCodeActionsForEditorSelection(editor, + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.source.noneMessage', "No source actions available"), { kind: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.Empty, @@ -434,7 +388,7 @@ export class OrganizeImportsAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - return showCodeActionsForEditorSelection(editor, + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.organize.noneMessage', "No organize imports action available"), { kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, CodeActionAutoApply.IfSingle); @@ -457,7 +411,7 @@ export class FixAllAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - return showCodeActionsForEditorSelection(editor, + return triggerCodeActionsForEditorSelection(editor, nls.localize('fixAll.noneMessage', "No fix all action available"), { kind: CodeActionKind.SourceFixAll, includeSourceActions: true }, CodeActionAutoApply.IfSingle); @@ -472,7 +426,7 @@ export class AutoFixAction extends EditorAction { super({ id: AutoFixAction.Id, label: nls.localize('autoFix.label', "Auto Fix..."), - alias: 'Auto Fix', + alias: 'Auto Fix...', precondition: ContextKeyExpr.and( EditorContextKeys.writable, contextKeyForSupportedActions(CodeActionKind.QuickFix)), @@ -488,7 +442,7 @@ export class AutoFixAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - return showCodeActionsForEditorSelection(editor, + return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"), { kind: CodeActionKind.QuickFix, diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index fff21ce6b8..a78b509942 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { CancelablePromise, createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; -import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; @@ -14,36 +14,34 @@ import { Selection } from 'vs/editor/common/core/selection'; import { CodeActionProviderRegistry } from 'vs/editor/common/modes'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { getCodeActions, CodeActionSet } from './codeAction'; import { CodeActionTrigger } from './codeActionTrigger'; export const SUPPORTED_CODE_ACTIONS = new RawContextKey('supportedCodeAction', ''); -export class CodeActionOracle { +export type TriggeredCodeAction = undefined | { + readonly selection: Selection; + readonly trigger: CodeActionTrigger; + readonly position: Position; +}; - private _disposables: IDisposable[] = []; - private readonly _autoTriggerTimer = new TimeoutTimer(); +class CodeActionOracle extends Disposable { + + private readonly _autoTriggerTimer = this._register(new TimeoutTimer()); constructor( private readonly _editor: ICodeEditor, private readonly _markerService: IMarkerService, - private readonly _signalChange: (newState: CodeActionsState.State) => void, + private readonly _signalChange: (triggered: TriggeredCodeAction) => void, private readonly _delay: number = 250, - private readonly _progressService?: IProgressService, ) { - this._disposables.push( - this._markerService.onMarkerChanged(e => this._onMarkerChanges(e)), - this._editor.onDidChangeCursorPosition(() => this._onCursorChange()), - ); + super(); + this._register(this._markerService.onMarkerChanged(e => this._onMarkerChanges(e))); + this._register(this._editor.onDidChangeCursorPosition(() => this._onCursorChange())); } - dispose(): void { - this._disposables = dispose(this._disposables); - this._autoTriggerTimer.cancel(); - } - - trigger(trigger: CodeActionTrigger) { + public trigger(trigger: CodeActionTrigger): TriggeredCodeAction { const selection = this._getRangeOfSelectionUnlessWhitespaceEnclosed(trigger); return this._createEventAndSignalChange(trigger, selection); } @@ -109,38 +107,27 @@ export class CodeActionOracle { } } } - return selection ? selection : undefined; + return selection; } - private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): Promise { - if (!selection) { + private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): TriggeredCodeAction { + const model = this._editor.getModel(); + if (!selection || !model) { // cancel - this._signalChange(CodeActionsState.Empty); - return Promise.resolve(undefined); - } else { - const model = this._editor.getModel(); - if (!model) { - // cancel - this._signalChange(CodeActionsState.Empty); - return Promise.resolve(undefined); - } - - const markerRange = this._getRangeOfMarker(selection); - const position = markerRange ? markerRange.getStartPosition() : selection.getStartPosition(); - const actions = createCancelablePromise(token => getCodeActions(model, selection, trigger, token)); - - if (this._progressService && trigger.type === 'manual') { - this._progressService.showWhile(actions, 250); - } - - this._signalChange(new CodeActionsState.Triggered( - trigger, - selection, - position, - actions - )); - return actions; + this._signalChange(undefined); + return undefined; } + + const markerRange = this._getRangeOfMarker(selection); + const position = markerRange ? markerRange.getStartPosition() : selection.getStartPosition(); + + const e: TriggeredCodeAction = { + trigger, + selection, + position + }; + this._signalChange(e); + return e; } } @@ -167,47 +154,39 @@ export namespace CodeActionsState { export type State = typeof Empty | Triggered; } -export class CodeActionModel { +export class CodeActionModel extends Disposable { - private _codeActionOracle?: CodeActionOracle; + private readonly _codeActionOracle = this._register(new MutableDisposable()); private _state: CodeActionsState.State = CodeActionsState.Empty; - private _onDidChangeState = new Emitter(); - private _disposables: IDisposable[] = []; private readonly _supportedCodeActions: IContextKey; + private readonly _onDidChangeState = this._register(new Emitter()); + public readonly onDidChangeState = this._onDidChangeState.event; + constructor( private readonly _editor: ICodeEditor, private readonly _markerService: IMarkerService, contextKeyService: IContextKeyService, - private readonly _progressService: IProgressService + private readonly _progressService?: IEditorProgressService ) { + super(); this._supportedCodeActions = SUPPORTED_CODE_ACTIONS.bindTo(contextKeyService); - this._disposables.push(this._editor.onDidChangeModel(() => this._update())); - this._disposables.push(this._editor.onDidChangeModelLanguage(() => this._update())); - this._disposables.push(CodeActionProviderRegistry.onDidChange(() => this._update())); + this._register(this._editor.onDidChangeModel(() => this._update())); + this._register(this._editor.onDidChangeModelLanguage(() => this._update())); + this._register(CodeActionProviderRegistry.onDidChange(() => this._update())); this._update(); } dispose(): void { - this._disposables = dispose(this._disposables); - dispose(this._codeActionOracle); - } - - get onDidChangeState(): Event { - return this._onDidChangeState.event; + super.dispose(); + this.setState(CodeActionsState.Empty, true); } private _update(): void { - if (this._codeActionOracle) { - this._codeActionOracle.dispose(); - this._codeActionOracle = undefined; - } + this._codeActionOracle.value = undefined; - if (this._state.type === CodeActionsState.Type.Triggered) { - this._state.actions.cancel(); - } this.setState(CodeActionsState.Empty); const model = this._editor.getModel(); @@ -224,25 +203,46 @@ export class CodeActionModel { this._supportedCodeActions.set(supportedActions.join(' ')); - this._codeActionOracle = new CodeActionOracle(this._editor, this._markerService, newState => this.setState(newState), undefined, this._progressService); - this._codeActionOracle.trigger({ type: 'auto' }); + this._codeActionOracle.value = new CodeActionOracle(this._editor, this._markerService, trigger => { + if (!trigger) { + this.setState(CodeActionsState.Empty); + return; + } + + const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token)); + if (this._progressService && trigger.trigger.type === 'manual') { + this._progressService.showWhile(actions, 250); + } + + this.setState(new CodeActionsState.Triggered(trigger.trigger, trigger.selection, trigger.position, actions)); + + }, undefined); + this._codeActionOracle.value.trigger({ type: 'auto' }); } else { this._supportedCodeActions.reset(); } } - public trigger(trigger: CodeActionTrigger): Promise { - if (this._codeActionOracle) { - return this._codeActionOracle.trigger(trigger); + public trigger(trigger: CodeActionTrigger) { + if (this._codeActionOracle.value) { + this._codeActionOracle.value.trigger(trigger); } - return Promise.resolve(undefined); } - private setState(newState: CodeActionsState.State) { + private setState(newState: CodeActionsState.State, skipNotify?: boolean) { if (newState === this._state) { return; } + + // Cancel old request + if (this._state.type === CodeActionsState.Type.Triggered) { + this._state.actions.cancel(); + } + this._state = newState; - this._onDidChangeState.fire(newState); + + if (!skipNotify) { + this._onDidChangeState.fire(newState); + } } } diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index c641c9ce13..382a4bea1a 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -5,6 +5,7 @@ import { startsWith } from 'vs/base/common/strings'; import { CodeAction } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; export class CodeActionKind { private static readonly sep = '.'; @@ -90,4 +91,8 @@ export interface CodeActionTrigger { readonly type: 'auto' | 'manual'; readonly filter?: CodeActionFilter; readonly autoApply?: CodeActionAutoApply; + readonly context?: { + readonly notAvailableMessage: string; + readonly position: Position; + }; } \ No newline at end of file diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts new file mode 100644 index 0000000000..09e2613844 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeAction } from 'vs/editor/common/modes'; +import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; +import { MessageController } from 'vs/editor/contrib/message/messageController'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { CodeActionsState } from './codeActionModel'; +import { CodeActionAutoApply } from './codeActionTrigger'; +import { CodeActionWidget } from './codeActionWidget'; +import { LightBulbWidget } from './lightBulbWidget'; +import { IPosition } from 'vs/editor/common/core/position'; +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; + +export class CodeActionUi extends Disposable { + + private readonly _codeActionWidget: CodeActionWidget; + private readonly _lightBulbWidget: LightBulbWidget; + private readonly _activeCodeActions = this._register(new MutableDisposable()); + + constructor( + private readonly _editor: ICodeEditor, + quickFixActionId: string, + private readonly delegate: { + applyCodeAction: (action: CodeAction, regtriggerAfterApply: boolean) => void + }, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + ) { + super(); + + this._codeActionWidget = this._register(new CodeActionWidget(this._editor, contextMenuService, { + onSelectCodeAction: async (action) => { + this.delegate.applyCodeAction(action, /* retrigger */ true); + } + })); + this._lightBulbWidget = this._register(new LightBulbWidget(this._editor, quickFixActionId, keybindingService)); + + this._register(this._lightBulbWidget.onClick(this._handleLightBulbSelect, this)); + } + + public async update(newState: CodeActionsState.State): Promise { + if (newState.type !== CodeActionsState.Type.Triggered) { + this._lightBulbWidget.hide(); + return; + } + + let actions: CodeActionSet; + try { + actions = await newState.actions; + } catch (e) { + onUnexpectedError(e); + return; + } + + this._lightBulbWidget.update(actions, newState.position); + + if (!actions.actions.length && newState.trigger.context) { + MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); + this._activeCodeActions.value = actions; + return; + } + + if (newState.trigger.type === 'manual') { + if (newState.trigger.filter && newState.trigger.filter.kind) { + // Triggered for specific scope + if (actions.actions.length > 0) { + // Apply if we only have one action or requested autoApply + if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.actions.length === 1)) { + try { + await this.delegate.applyCodeAction(actions.actions[0], false); + } finally { + actions.dispose(); + } + return; + } + } + } + this._activeCodeActions.value = actions; + this._codeActionWidget.show(actions, newState.position); + } else { + // auto magically triggered + if (this._codeActionWidget.isVisible) { + // TODO: Figure out if we should update the showing menu? + actions.dispose(); + } else { + this._activeCodeActions.value = actions; + } + } + } + + public async showCodeActionList(actions: CodeActionSet, at?: IAnchor | IPosition): Promise { + this._codeActionWidget.show(actions, at); + } + + private _handleLightBulbSelect(e: { x: number, y: number, actions: CodeActionSet }): void { + this._codeActionWidget.show(e.actions, e); + } +} diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index 8a7c0ea9eb..d16aac5244 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -6,35 +6,47 @@ import { getDomNodePagePosition } from 'vs/base/browser/dom'; import { Action } from 'vs/base/common/actions'; import { canceled } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Position } from 'vs/editor/common/core/position'; +import { Position, IPosition } from 'vs/editor/common/core/position'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { CodeAction } from 'vs/editor/common/modes'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -export class CodeActionContextMenu { +interface CodeActionWidgetDelegate { + onSelectCodeAction: (action: CodeAction) => Promise; +} + +export class CodeActionWidget extends Disposable { private _visible: boolean; - - private readonly _onDidExecuteCodeAction = new Emitter(); - public readonly onDidExecuteCodeAction: Event = this._onDidExecuteCodeAction.event; + private readonly _showingActions = this._register(new MutableDisposable()); constructor( private readonly _editor: ICodeEditor, private readonly _contextMenuService: IContextMenuService, - private readonly _onApplyCodeAction: (action: CodeAction) => Promise - ) { } + private readonly _delegate: CodeActionWidgetDelegate, + ) { + super(); + } - async show(actionsToShow: Promise, at?: { x: number; y: number } | Position): Promise { - const codeActions = await actionsToShow; + public async show(codeActions: CodeActionSet, at?: IAnchor | IPosition): Promise { + if (!codeActions.actions.length) { + this._visible = false; + return; + } if (!this._editor.getDomNode()) { // cancel when editor went off-dom + this._visible = false; return Promise.reject(canceled()); } + this._visible = true; const actions = codeActions.actions.map(action => this.codeActionToAction(action)); + + this._showingActions.value = codeActions; this._contextMenuService.showContextMenu({ getAnchor: () => { if (Position.isIPosition(at)) { @@ -54,16 +66,14 @@ export class CodeActionContextMenu { private codeActionToAction(action: CodeAction): Action { const id = action.command ? action.command.id : action.title; const title = action.title; - return new Action(id, title, undefined, true, () => - this._onApplyCodeAction(action) - .finally(() => this._onDidExecuteCodeAction.fire(undefined))); + return new Action(id, title, undefined, true, () => this._delegate.onSelectCodeAction(action)); } get isVisible(): boolean { return this._visible; } - private _toCoords(position: Position): { x: number, y: number } { + private _toCoords(position: IPosition): { x: number, y: number } { if (!this._editor.hasModel()) { return { x: 0, y: 0 }; } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.css b/src/vs/editor/contrib/codeAction/lightBulbWidget.css index d5bde8dacc..8aba43a852 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.css +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.css @@ -18,11 +18,11 @@ } .monaco-editor.vs .lightbulb-glyph { - background: url('lightbulb.svg') center center no-repeat; + background: url('lightbulb-light.svg') center center no-repeat; } .monaco-editor.vs .lightbulb-glyph.autofixable { - background: url('lightbulb-autofix.svg') center center no-repeat; + background: url('lightbulb-autofix-light.svg') center center no-repeat; } .monaco-editor.vs-dark .lightbulb-glyph, diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 41d45f8bb1..d8eebdad6a 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -5,67 +5,90 @@ import * as dom from 'vs/base/browser/dom'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import 'vs/css!./lightBulbWidget'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { IPosition } from 'vs/editor/common/core/position'; import { TextModel } from 'vs/editor/common/model/textModel'; import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; -import { CodeActionsState } from './codeActionModel'; +import * as nls from 'vs/nls'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + +namespace LightBulbState { + + export const enum Type { + Hidden, + Showing, + } + + export const Hidden = new class { readonly type = Type.Hidden; }; + + export class Showing { + readonly type = Type.Showing; + + constructor( + public readonly actions: CodeActionSet, + public readonly editorPosition: IPosition, + public readonly widgetPosition: IContentWidgetPosition, + ) { } + } + + export type State = typeof Hidden | Showing; +} + export class LightBulbWidget extends Disposable implements IContentWidget { private static readonly _posPref = [ContentWidgetPositionPreference.EXACT]; private readonly _domNode: HTMLDivElement; - private readonly _editor: ICodeEditor; - private readonly _onClick = this._register(new Emitter<{ x: number; y: number; state: CodeActionsState.Triggered }>()); + private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet }>()); public readonly onClick = this._onClick.event; - private _position: IContentWidgetPosition | null; - private _state: CodeActionsState.State = CodeActionsState.Empty; - private _futureFixes = new CancellationTokenSource(); + private _state: LightBulbState.State = LightBulbState.Hidden; - constructor(editor: ICodeEditor) { + constructor( + private readonly _editor: ICodeEditor, + private readonly _quickFixActionId: string, + @IKeybindingService private readonly _keybindingService: IKeybindingService + ) { super(); this._domNode = document.createElement('div'); this._domNode.className = 'lightbulb-glyph'; - this._editor = editor; this._editor.addContentWidget(this); - this._register(this._editor.onDidChangeModel(_ => this._futureFixes.cancel())); - this._register(this._editor.onDidChangeModelLanguage(_ => this._futureFixes.cancel())); this._register(this._editor.onDidChangeModelContent(_ => { // cancel when the line in question has been removed const editorModel = this._editor.getModel(); - if (this._state.type !== CodeActionsState.Type.Triggered || !editorModel || this._state.position.lineNumber >= editorModel.getLineCount()) { - this._futureFixes.cancel(); + if (this._state.type !== LightBulbState.Type.Showing || !editorModel || this._state.editorPosition.lineNumber >= editorModel.getLineCount()) { + this.hide(); } })); - this._register(dom.addStandardDisposableListener(this._domNode, 'click', e => { - if (this._state.type !== CodeActionsState.Type.Triggered) { + this._register(dom.addStandardDisposableListener(this._domNode, 'mousedown', e => { + if (this._state.type !== LightBulbState.Type.Showing) { return; } // Make sure that focus / cursor location is not lost when clicking widget icon this._editor.focus(); + dom.EventHelper.stop(e, true); // a bit of extra work to make sure the menu // doesn't cover the line-text const { top, height } = dom.getDomNodePagePosition(this._domNode); const { lineHeight } = this._editor.getConfiguration(); let pad = Math.floor(lineHeight / 3); - if (this._position && this._position.position !== null && this._position.position.lineNumber < this._state.position.lineNumber) { + if (this._state.widgetPosition.position !== null && this._state.widgetPosition.position.lineNumber < this._state.editorPosition.lineNumber) { pad += lineHeight; } this._onClick.fire({ x: e.posx, y: top + height + pad, - state: this._state + actions: this._state.actions }); })); this._register(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => { @@ -87,6 +110,9 @@ export class LightBulbWidget extends Disposable implements IContentWidget { this.hide(); } })); + + this._updateLightBulbTitle(); + this._register(this._keybindingService.onDidUpdateKeybindings(this._updateLightBulbTitle, this)); } dispose(): void { @@ -103,60 +129,23 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } getPosition(): IContentWidgetPosition | null { - return this._position; + return this._state.type === LightBulbState.Type.Showing ? this._state.widgetPosition : null; } - tryShow(newState: CodeActionsState.State) { - - if (newState.type !== CodeActionsState.Type.Triggered || this._position && (!newState.position || this._position.position && this._position.position.lineNumber !== newState.position.lineNumber)) { - // hide when getting a 'hide'-request or when currently - // showing on another line - this.hide(); - } else if (this._futureFixes) { - // cancel pending show request in any case - this._futureFixes.cancel(); + public update(actions: CodeActionSet, atPosition: IPosition) { + if (actions.actions.length <= 0) { + return this.hide(); } - this._futureFixes = new CancellationTokenSource(); - const { token } = this._futureFixes; - this._state = newState; - - if (this._state.type === CodeActionsState.Empty.type) { - return; - } - - const selection = this._state.rangeOrSelection; - this._state.actions.then(fixes => { - if (!token.isCancellationRequested && fixes.actions.length > 0 && selection) { - this._show(fixes); - } else { - this.hide(); - } - }).catch(() => { - this.hide(); - }); - } - - set title(value: string) { - this._domNode.title = value; - } - - get title(): string { - return this._domNode.title; - } - - private _show(codeActions: CodeActionSet): void { const config = this._editor.getConfiguration(); if (!config.contribInfo.lightbulbEnabled) { - return; + return this.hide(); } - if (this._state.type !== CodeActionsState.Type.Triggered) { - return; - } - const { lineNumber, column } = this._state.position; + + const { lineNumber, column } = atPosition; const model = this._editor.getModel(); if (!model) { - return; + return this.hide(); } const tabSize = model.getOptions().tabSize; @@ -176,23 +165,35 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } else if (column * config.fontInfo.spaceWidth < 22) { // cannot show lightbulb above/below and showing // it inline would overlay the cursor... - this.hide(); - return; + return this.hide(); } } - this._position = { + this._state = new LightBulbState.Showing(actions, atPosition, { position: { lineNumber: effectiveLineNumber, column: 1 }, preference: LightBulbWidget._posPref - }; - dom.toggleClass(this._domNode, 'autofixable', codeActions.hasAutoFix); + }); + dom.toggleClass(this._domNode, 'autofixable', actions.hasAutoFix); this._editor.layoutContentWidget(this); } - hide(): void { - this._position = null; - this._state = CodeActionsState.Empty; - this._futureFixes.cancel(); + private set title(value: string) { + this._domNode.title = value; + } + + public hide(): void { + this._state = LightBulbState.Hidden; this._editor.layoutContentWidget(this); } + + private _updateLightBulbTitle(): void { + const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId); + let title: string; + if (kb) { + title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel()); + } else { + title = nls.localize('quickFix', "Show Fixes"); + } + this.title = title; + } } diff --git a/src/vs/editor/contrib/codeAction/lightbulb-autofix-dark.svg b/src/vs/editor/contrib/codeAction/lightbulb-autofix-dark.svg index 40678e79d7..34d4f3aedf 100644 --- a/src/vs/editor/contrib/codeAction/lightbulb-autofix-dark.svg +++ b/src/vs/editor/contrib/codeAction/lightbulb-autofix-dark.svg @@ -1,10 +1,4 @@ - - - - - - - - + + diff --git a/src/vs/editor/contrib/codeAction/lightbulb-autofix-light.svg b/src/vs/editor/contrib/codeAction/lightbulb-autofix-light.svg new file mode 100644 index 0000000000..c34a0c2805 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/lightbulb-autofix-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/editor/contrib/codeAction/lightbulb-autofix.svg b/src/vs/editor/contrib/codeAction/lightbulb-autofix.svg deleted file mode 100644 index a4b4858e4d..0000000000 --- a/src/vs/editor/contrib/codeAction/lightbulb-autofix.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/codeAction/lightbulb-dark.svg b/src/vs/editor/contrib/codeAction/lightbulb-dark.svg index 520f78f3e5..d2b6e1287a 100644 --- a/src/vs/editor/contrib/codeAction/lightbulb-dark.svg +++ b/src/vs/editor/contrib/codeAction/lightbulb-dark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/editor/contrib/codeAction/lightbulb-light.svg b/src/vs/editor/contrib/codeAction/lightbulb-light.svg new file mode 100644 index 0000000000..8572effd08 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/lightbulb-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/editor/contrib/codeAction/lightbulb.svg b/src/vs/editor/contrib/codeAction/lightbulb.svg deleted file mode 100644 index b359604661..0000000000 --- a/src/vs/editor/contrib/codeAction/lightbulb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index aaaca49740..3ed337306f 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -3,22 +3,34 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CodeAction, CodeActionContext, CodeActionProvider, CodeActionProviderRegistry, Command, LanguageIdentifier, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes'; +import * as modes from 'vs/editor/common/modes'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; +function staticCodeActionProvider(...actions: modes.CodeAction[]): modes.CodeActionProvider { + return new class implements modes.CodeActionProvider { + provideCodeActions(): modes.CodeActionList { + return { + actions: actions, + dispose: () => { } + }; + } + }; +} + + suite('CodeAction', () => { - let langId = new LanguageIdentifier('fooLang', 17); + let langId = new modes.LanguageIdentifier('fooLang', 17); let uri = URI.parse('untitled:path'); let model: TextModel; - let disposables: IDisposable[] = []; + const disposables = new DisposableStore(); let testData = { diagnostics: { abc: { @@ -46,7 +58,7 @@ suite('CodeAction', () => { }, command: { abc: { - command: new class implements Command { + command: new class implements modes.Command { id: '1'; title: 'abc'; }, @@ -56,8 +68,8 @@ suite('CodeAction', () => { spelling: { bcd: { diagnostics: [], - edit: new class implements WorkspaceEdit { - edits: ResourceTextEdit[]; + edit: new class implements modes.WorkspaceEdit { + edits: modes.ResourceTextEdit[]; }, title: 'abc' } @@ -79,30 +91,27 @@ suite('CodeAction', () => { }; setup(function () { + disposables.clear(); model = TextModel.createFromString('test1\ntest2\ntest3', undefined, langId, uri); - disposables = [model]; + disposables.add(model); }); teardown(function () { - dispose(disposables); + disposables.clear(); }); test('CodeActions are sorted by type, #38623', async function () { - const provider = new class implements CodeActionProvider { - provideCodeActions() { - return [ - testData.command.abc, - testData.diagnostics.bcd, - testData.spelling.bcd, - testData.tsLint.bcd, - testData.tsLint.abc, - testData.diagnostics.abc - ]; - } - }; + const provider = staticCodeActionProvider( + testData.command.abc, + testData.diagnostics.bcd, + testData.spelling.bcd, + testData.tsLint.bcd, + testData.tsLint.abc, + testData.diagnostics.abc + ); - disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); const expected = [ // CodeActions with a diagnostics array are shown first ordered by diagnostics.message @@ -122,17 +131,13 @@ suite('CodeAction', () => { }); test('getCodeActions should filter by scope', async function () { - const provider = new class implements CodeActionProvider { - provideCodeActions(): CodeAction[] { - return [ - { title: 'a', kind: 'a' }, - { title: 'b', kind: 'b' }, - { title: 'a.b', kind: 'a.b' } - ]; - } - }; + const provider = staticCodeActionProvider( + { title: 'a', kind: 'a' }, + { title: 'b', kind: 'b' }, + { title: 'a.b', kind: 'a.b' } + ); - disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None); @@ -154,15 +159,18 @@ suite('CodeAction', () => { }); test('getCodeActions should forward requested scope to providers', async function () { - const provider = new class implements CodeActionProvider { - provideCodeActions(_model: any, _range: Range, context: CodeActionContext, _token: any): CodeAction[] { - return [ - { title: context.only || '', kind: context.only } - ]; + const provider = new class implements modes.CodeActionProvider { + provideCodeActions(_model: any, _range: Range, context: modes.CodeActionContext, _token: any): modes.CodeActionList { + return { + actions: [ + { title: context.only || '', kind: context.only } + ], + dispose: () => { } + }; } }; - disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 1); @@ -170,16 +178,12 @@ suite('CodeAction', () => { }); test('getCodeActions should not return source code action by default', async function () { - const provider = new class implements CodeActionProvider { - provideCodeActions(): CodeAction[] { - return [ - { title: 'a', kind: CodeActionKind.Source.value }, - { title: 'b', kind: 'b' } - ]; - } - }; + const provider = staticCodeActionProvider( + { title: 'a', kind: CodeActionKind.Source.value }, + { title: 'b', kind: 'b' } + ); - disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None); @@ -196,16 +200,16 @@ suite('CodeAction', () => { test('getCodeActions should not invoke code action providers filtered out by providedCodeActionKinds', async function () { let wasInvoked = false; - const provider = new class implements CodeActionProvider { - provideCodeActions() { + const provider = new class implements modes.CodeActionProvider { + provideCodeActions(): modes.CodeActionList { wasInvoked = true; - return []; + return { actions: [], dispose: () => { } }; } providedCodeActionKinds = [CodeActionKind.Refactor.value]; }; - disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index a5bef22904..59b98899d9 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -4,32 +4,38 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CodeActionProviderRegistry, LanguageIdentifier } from 'vs/editor/common/modes'; -import { CodeActionOracle, CodeActionsState } from 'vs/editor/contrib/codeAction/codeActionModel'; +import * as modes from 'vs/editor/common/modes'; +import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/codeActionModel'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { MarkerService } from 'vs/platform/markers/common/markerService'; const testProvider = { - provideCodeActions() { - return [{ id: 'test-command', title: 'test', arguments: [] }]; + provideCodeActions(): modes.CodeActionList { + return { + actions: [ + { title: 'test', command: { id: 'test-command', title: 'test', arguments: [] } } + ], + dispose() { /* noop*/ } + }; } }; -suite('CodeAction', () => { +suite('CodeActionModel', () => { - const languageIdentifier = new LanguageIdentifier('foo-lang', 3); + const languageIdentifier = new modes.LanguageIdentifier('foo-lang', 3); let uri = URI.parse('untitled:path'); let model: TextModel; let markerService: MarkerService; let editor: ICodeEditor; - let disposables: IDisposable[]; + const disposables = new DisposableStore(); setup(() => { - disposables = []; + disposables.clear(); markerService = new MarkerService(); model = TextModel.createFromString('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri); editor = createTestCodeEditor({ model: model }); @@ -37,26 +43,28 @@ suite('CodeAction', () => { }); teardown(() => { - dispose(disposables); + disposables.clear(); editor.dispose(); model.dispose(); markerService.dispose(); }); test('Orcale -> marker added', done => { - const reg = CodeActionProviderRegistry.register(languageIdentifier.language, testProvider); - disposables.push(reg); + const reg = modes.CodeActionProviderRegistry.register(languageIdentifier.language, testProvider); + disposables.add(reg); - const oracle = new CodeActionOracle(editor, markerService, (e: CodeActionsState.Triggered) => { + const contextKeys = new MockContextKeyService(); + const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); + disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { assert.equal(e.trigger.type, 'auto'); assert.ok(e.actions); e.actions.then(fixes => { - oracle.dispose(); + model.dispose(); assert.equal(fixes.actions.length, 1); done(); }, done); - }); + })); // start here markerService.changeOne('fake', uri, [{ @@ -70,8 +78,8 @@ suite('CodeAction', () => { }); test('Orcale -> position changed', () => { - const reg = CodeActionProviderRegistry.register(languageIdentifier.language, testProvider); - disposables.push(reg); + const reg = modes.CodeActionProviderRegistry.register(languageIdentifier.language, testProvider); + disposables.add(reg); markerService.changeOne('fake', uri, [{ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, @@ -84,28 +92,29 @@ suite('CodeAction', () => { editor.setPosition({ lineNumber: 2, column: 1 }); return new Promise((resolve, reject) => { - - const oracle = new CodeActionOracle(editor, markerService, (e: CodeActionsState.Triggered) => { + const contextKeys = new MockContextKeyService(); + const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); + disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { assert.equal(e.trigger.type, 'auto'); assert.ok(e.actions); e.actions.then(fixes => { - oracle.dispose(); + model.dispose(); assert.equal(fixes.actions.length, 1); resolve(undefined); }, reject); - }); + })); // start here editor.setPosition({ lineNumber: 1, column: 1 }); }); }); test('Lightbulb is in the wrong place, #29933', async function () { - const reg = CodeActionProviderRegistry.register(languageIdentifier.language, { - provideCodeActions(_doc, _range) { - return []; + const reg = modes.CodeActionProviderRegistry.register(languageIdentifier.language, { + provideCodeActions(_doc, _range): modes.CodeActionList { + return { actions: [], dispose() { /* noop*/ } }; } }); - disposables.push(reg); + disposables.add(reg); editor.getModel()!.setValue('// @ts-check\n2\ncon\n'); @@ -119,8 +128,9 @@ suite('CodeAction', () => { // case 1 - drag selection over multiple lines -> range of enclosed marker, position or marker await new Promise(resolve => { - - let oracle = new CodeActionOracle(editor, markerService, (e: CodeActionsState.Triggered) => { + const contextKeys = new MockContextKeyService(); + const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); + disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { assert.equal(e.trigger.type, 'auto'); const selection = e.rangeOrSelection; assert.deepEqual(selection.selectionStartLineNumber, 1); @@ -128,31 +138,32 @@ suite('CodeAction', () => { assert.deepEqual(selection.endLineNumber, 4); assert.deepEqual(selection.endColumn, 1); assert.deepEqual(e.position, { lineNumber: 3, column: 1 }); - - oracle.dispose(); + model.dispose(); resolve(undefined); - }, 5); + }, 5)); editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }); }); }); test('Orcale -> should only auto trigger once for cursor and marker update right after each other', done => { - const reg = CodeActionProviderRegistry.register(languageIdentifier.language, testProvider); - disposables.push(reg); + const reg = modes.CodeActionProviderRegistry.register(languageIdentifier.language, testProvider); + disposables.add(reg); let triggerCount = 0; - const oracle = new CodeActionOracle(editor, markerService, (e: CodeActionsState.Triggered) => { + const contextKeys = new MockContextKeyService(); + const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); + disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { assert.equal(e.trigger.type, 'auto'); ++triggerCount; // give time for second trigger before completing test setTimeout(() => { - oracle.dispose(); + model.dispose(); assert.strictEqual(triggerCount, 1); done(); }, 50); - }, 5 /*delay*/); + }, 5 /*delay*/)); markerService.changeOne('fake', uri, [{ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6, diff --git a/src/vs/editor/contrib/codelens/codeLensCache.ts b/src/vs/editor/contrib/codelens/codeLensCache.ts index b23eb35037..fc32c7d06f 100644 --- a/src/vs/editor/contrib/codelens/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/codeLensCache.ts @@ -6,18 +6,20 @@ import { ITextModel } from 'vs/editor/common/model'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens'; +import { CodeLensModel } from 'vs/editor/contrib/codelens/codelens'; import { LRUCache, values } from 'vs/base/common/map'; -import { ICodeLensSymbol, CodeLensProvider } from 'vs/editor/common/modes'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { CodeLensProvider, CodeLensList, CodeLens } from 'vs/editor/common/modes'; +import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { Range } from 'vs/editor/common/core/range'; +import { runWhenIdle } from 'vs/base/common/async'; +import { once } from 'vs/base/common/functional'; export const ICodeLensCache = createDecorator('ICodeLensCache'); export interface ICodeLensCache { _serviceBrand: any; - put(model: ITextModel, data: ICodeLensData[]): void; - get(model: ITextModel): ICodeLensData[] | undefined; + put(model: ITextModel, data: CodeLensModel): void; + get(model: ITextModel): CodeLensModel | undefined; delete(model: ITextModel): void; } @@ -30,7 +32,7 @@ class CacheItem { constructor( readonly lineCount: number, - readonly data: ICodeLensData[] + readonly data: CodeLensModel ) { } } @@ -39,7 +41,7 @@ export class CodeLensCache implements ICodeLensCache { _serviceBrand: any; private readonly _fakeProvider = new class implements CodeLensProvider { - provideCodeLenses(): ICodeLensSymbol[] { + provideCodeLenses(): CodeLensList { throw new Error('not supported'); } }; @@ -48,26 +50,29 @@ export class CodeLensCache implements ICodeLensCache { constructor(@IStorageService storageService: IStorageService) { - const key = 'codelens/cache'; + // remove old data + const oldkey = 'codelens/cache'; + runWhenIdle(() => storageService.remove(oldkey, StorageScope.WORKSPACE)); // restore lens data on start + const key = 'codelens/cache2'; const raw = storageService.get(key, StorageScope.WORKSPACE, '{}'); this._deserialize(raw); // store lens data on shutdown - const listener = storageService.onWillSaveState(() => { - storageService.store(key, this._serialize(), StorageScope.WORKSPACE); - listener.dispose(); + once(storageService.onWillSaveState)(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + storageService.store(key, this._serialize(), StorageScope.WORKSPACE); + } }); } - put(model: ITextModel, data: ICodeLensData[]): void { - const item = new CacheItem(model.getLineCount(), data.map(item => { - return { - symbol: item.symbol, - provider: this._fakeProvider - }; - })); + put(model: ITextModel, data: CodeLensModel): void { + + const lensModel = new CodeLensModel(); + lensModel.add({ lenses: data.lenses.map(v => v.symbol), dispose() { } }, this._fakeProvider); + + const item = new CacheItem(model.getLineCount(), lensModel); this._cache.set(model.uri.toString(), item); } @@ -86,7 +91,7 @@ export class CodeLensCache implements ICodeLensCache { const data: Record = Object.create(null); this._cache.forEach((value, key) => { const lines = new Set(); - for (const d of value.data) { + for (const d of value.data.lenses) { lines.add(d.symbol.range.startLineNumber); } data[key] = { @@ -102,14 +107,14 @@ export class CodeLensCache implements ICodeLensCache { const data: Record = JSON.parse(raw); for (const key in data) { const element = data[key]; - const symbols: ICodeLensData[] = []; + const lenses: CodeLens[] = []; for (const line of element.lines) { - symbols.push({ - provider: this._fakeProvider, - symbol: { range: new Range(line, 1, line, 11) } - }); + lenses.push({ range: new Range(line, 1, line, 11) }); } - this._cache.set(key, new CacheItem(element.lineCount, symbols)); + + const model = new CodeLensModel(); + model.add({ lenses, dispose() { } }, this._fakeProvider); + this._cache.set(key, new CacheItem(element.lineCount, model)); } } catch { // ignore... diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index 2d3fd1ec6e..8bb0cdd987 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -9,38 +9,59 @@ import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/error import { URI } from 'vs/base/common/uri'; import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ITextModel } from 'vs/editor/common/model'; -import { CodeLensProvider, CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { CodeLensProvider, CodeLensProviderRegistry, CodeLens, CodeLensList } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; -export interface ICodeLensData { - symbol: ICodeLensSymbol; +export interface CodeLensItem { + symbol: CodeLens; provider: CodeLensProvider; } -export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise { +export class CodeLensModel { - const symbols: ICodeLensData[] = []; - const provider = CodeLensProviderRegistry.ordered(model); + lenses: CodeLensItem[] = []; - const promises = provider.map(provider => Promise.resolve(provider.provideCodeLenses(model, token)).then(result => { - if (Array.isArray(result)) { - for (let symbol of result) { - symbols.push({ symbol, provider }); - } + private readonly _dispoables = new DisposableStore(); + + dispose(): void { + this._dispoables.dispose(); + } + + add(list: CodeLensList, provider: CodeLensProvider): void { + this._dispoables.add(list); + for (const symbol of list.lenses) { + this.lenses.push({ symbol, provider }); } - }).catch(onUnexpectedExternalError)); + } +} + +export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise { + + const provider = CodeLensProviderRegistry.ordered(model); + const providerRanks = new Map(); + const result = new CodeLensModel(); + + const promises = provider.map((provider, i) => { + + providerRanks.set(provider, i); + + return Promise.resolve(provider.provideCodeLenses(model, token)) + .then(list => list && result.add(list, provider)) + .catch(onUnexpectedExternalError); + }); return Promise.all(promises).then(() => { - return mergeSort(symbols, (a, b) => { + result.lenses = mergeSort(result.lenses, (a, b) => { // sort by lineNumber, provider-rank, and column if (a.symbol.range.startLineNumber < b.symbol.range.startLineNumber) { return -1; } else if (a.symbol.range.startLineNumber > b.symbol.range.startLineNumber) { return 1; - } else if (provider.indexOf(a.provider) < provider.indexOf(b.provider)) { + } else if (providerRanks.get(a.provider)! < providerRanks.get(b.provider)!) { return -1; - } else if (provider.indexOf(a.provider) > provider.indexOf(b.provider)) { + } else if (providerRanks.get(a.provider)! > providerRanks.get(b.provider)!) { return 1; } else if (a.symbol.range.startColumn < b.symbol.range.startColumn) { return -1; @@ -50,6 +71,8 @@ export function getCodeLensData(model: ITextModel, token: CancellationToken): Pr return 0; } }); + + return result; }); } @@ -65,12 +88,12 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { throw illegalArgument(); } - const result: ICodeLensSymbol[] = []; + const result: CodeLens[] = []; return getCodeLensData(model, CancellationToken.None).then(value => { let resolve: Promise[] = []; - for (const item of value) { + for (const item of value.lenses) { if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) { result.push(item.symbol); } else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) { @@ -78,7 +101,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { } } - return Promise.all(resolve); + return Promise.all(resolve).finally(() => setTimeout(() => value.dispose(), 0)); }).then(() => { return result; diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index b2b61a2f40..551619ec0d 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -5,16 +5,15 @@ import { CancelablePromise, RunOnceScheduler, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; -import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; -import { ICodeLensData, getCodeLensData } from 'vs/editor/contrib/codelens/codelens'; -import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; +import { CodeLensProviderRegistry, CodeLens } from 'vs/editor/common/modes'; +import { CodeLensModel, getCodeLensData, CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; +import { CodeLensWidget, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache'; @@ -25,12 +24,14 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { private _isEnabled: boolean; - private _globalToDispose: IDisposable[]; - private _localToDispose: IDisposable[]; - private _lenses: CodeLens[]; - private _currentFindCodeLensSymbolsPromise: CancelablePromise | null; - private _modelChangeCounter: number; - private _currentResolveCodeLensSymbolsPromise: CancelablePromise | null; + private readonly _globalToDispose = new DisposableStore(); + private readonly _localToDispose = new DisposableStore(); + private _lenses: CodeLensWidget[] = []; + private _currentFindCodeLensSymbolsPromise: CancelablePromise | undefined; + private _oldCodeLensModels = new DisposableStore(); + private _currentCodeLensModel: CodeLensModel | undefined; + private _modelChangeCounter: number = 0; + private _currentResolveCodeLensSymbolsPromise: CancelablePromise | undefined; private _detectVisibleLenses: RunOnceScheduler; constructor( @@ -41,41 +42,39 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { ) { this._isEnabled = this._editor.getConfiguration().contribInfo.codeLens; - this._globalToDispose = []; - this._localToDispose = []; - this._lenses = []; - this._currentFindCodeLensSymbolsPromise = null; - this._modelChangeCounter = 0; - - this._globalToDispose.push(this._editor.onDidChangeModel(() => this._onModelChange())); - this._globalToDispose.push(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); - this._globalToDispose.push(this._editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { - let prevIsEnabled = this._isEnabled; + this._globalToDispose.add(this._editor.onDidChangeModel(() => this._onModelChange())); + this._globalToDispose.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange())); + this._globalToDispose.add(this._editor.onDidChangeConfiguration(() => { + const prevIsEnabled = this._isEnabled; this._isEnabled = this._editor.getConfiguration().contribInfo.codeLens; if (prevIsEnabled !== this._isEnabled) { this._onModelChange(); } })); - this._globalToDispose.push(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); + this._globalToDispose.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); this._onModelChange(); } dispose(): void { this._localDispose(); - this._globalToDispose = dispose(this._globalToDispose); + this._globalToDispose.dispose(); + this._oldCodeLensModels.dispose(); + dispose(this._currentCodeLensModel); } private _localDispose(): void { if (this._currentFindCodeLensSymbolsPromise) { this._currentFindCodeLensSymbolsPromise.cancel(); - this._currentFindCodeLensSymbolsPromise = null; + this._currentFindCodeLensSymbolsPromise = undefined; this._modelChangeCounter++; } if (this._currentResolveCodeLensSymbolsPromise) { this._currentResolveCodeLensSymbolsPromise.cancel(); - this._currentResolveCodeLensSymbolsPromise = null; + this._currentResolveCodeLensSymbolsPromise = undefined; } - this._localToDispose = dispose(this._localToDispose); + this._localToDispose.clear(); + this._oldCodeLensModels.clear(); + dispose(this._currentCodeLensModel); } getId(): string { @@ -104,7 +103,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { // no provider -> return but check with // cached lenses. they expire after 30 seconds if (cachedLenses) { - this._localToDispose.push(disposableTimeout(() => { + this._localToDispose.add(disposableTimeout(() => { const cachedLensesNow = this._codeLensCache.get(model); if (cachedLenses === cachedLensesNow) { this._codeLensCache.delete(model); @@ -118,7 +117,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { for (const provider of CodeLensProviderRegistry.all(model)) { if (typeof provider.onDidChange === 'function') { let registration = provider.onDidChange(() => scheduler.schedule()); - this._localToDispose.push(registration); + this._localToDispose.add(registration); } } @@ -136,18 +135,26 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._currentFindCodeLensSymbolsPromise.then(result => { if (counterValue === this._modelChangeCounter) { // only the last one wins + if (this._currentCodeLensModel) { + this._oldCodeLensModels.add(this._currentCodeLensModel); + } + this._currentCodeLensModel = result; + + // cache model to reduce flicker this._codeLensCache.put(model, result); + + // render lenses this._renderCodeLensSymbols(result); this._detectVisibleLenses.schedule(); } }, onUnexpectedError); }, 250); - this._localToDispose.push(scheduler); - this._localToDispose.push(this._detectVisibleLenses); - this._localToDispose.push(this._editor.onDidChangeModelContent((e) => { - this._editor.changeDecorations((changeAccessor) => { - this._editor.changeViewZones((viewAccessor) => { - let toDispose: CodeLens[] = []; + this._localToDispose.add(scheduler); + this._localToDispose.add(this._detectVisibleLenses); + this._localToDispose.add(this._editor.onDidChangeModelContent(() => { + this._editor.changeDecorations(decorationsAccessor => { + this._editor.changeViewZones(viewZonesAccessor => { + let toDispose: CodeLensWidget[] = []; let lastLensLineNumber: number = -1; this._lenses.forEach((lens) => { @@ -157,17 +164,17 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { toDispose.push(lens); } else { - lens.update(viewAccessor); + lens.update(viewZonesAccessor); lastLensLineNumber = lens.getLineNumber(); } }); let helper = new CodeLensHelper(); toDispose.forEach((l) => { - l.dispose(helper, viewAccessor); + l.dispose(helper, viewZonesAccessor); this._lenses.splice(this._lenses.indexOf(l), 1); }); - helper.commit(changeAccessor); + helper.commit(decorationsAccessor); }); }); @@ -176,20 +183,20 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { // Ask for all references again scheduler.schedule(); })); - this._localToDispose.push(this._editor.onDidScrollChange(e => { + this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { this._detectVisibleLenses.schedule(); } })); - this._localToDispose.push(this._editor.onDidLayoutChange(e => { + this._localToDispose.add(this._editor.onDidLayoutChange(() => { this._detectVisibleLenses.schedule(); })); - this._localToDispose.push(toDisposable(() => { + this._localToDispose.add(toDisposable(() => { if (this._editor.getModel()) { const scrollState = StableEditorScrollState.capture(this._editor); - this._editor.changeDecorations((changeAccessor) => { - this._editor.changeViewZones((accessor) => { - this._disposeAllLenses(changeAccessor, accessor); + this._editor.changeDecorations(decorationsAccessor => { + this._editor.changeViewZones(viewZonesAccessor => { + this._disposeAllLenses(decorationsAccessor, viewZonesAccessor); }); }); scrollState.restore(this._editor); @@ -198,14 +205,14 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._disposeAllLenses(undefined, undefined); } })); - this._localToDispose.push(this._editor.onDidChangeConfiguration(e => { + this._localToDispose.add(this._editor.onDidChangeConfiguration(e => { if (e.fontInfo) { for (const lens of this._lenses) { lens.updateHeight(); } } })); - this._localToDispose.push(this._editor.onMouseUp(e => { + this._localToDispose.add(this._editor.onMouseUp(e => { if (e.target.type === editorBrowser.MouseTargetType.CONTENT_WIDGET && e.target.element && e.target.element.tagName === 'A') { for (const lens of this._lenses) { let command = lens.getCommand(e.target.element as HTMLLinkElement); @@ -228,16 +235,16 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._lenses = []; } - private _renderCodeLensSymbols(symbols: ICodeLensData[]): void { + private _renderCodeLensSymbols(symbols: CodeLensModel): void { if (!this._editor.hasModel()) { return; } let maxLineNumber = this._editor.getModel().getLineCount(); - let groups: ICodeLensData[][] = []; - let lastGroup: ICodeLensData[] | undefined; + let groups: CodeLensItem[][] = []; + let lastGroup: CodeLensItem[] | undefined; - for (let symbol of symbols) { + for (let symbol of symbols.lenses) { let line = symbol.symbol.range.startLineNumber; if (line < 1 || line > maxLineNumber) { // invalid code lens @@ -254,10 +261,12 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { const scrollState = StableEditorScrollState.capture(this._editor); - this._editor.changeDecorations((changeAccessor) => { - this._editor.changeViewZones((accessor) => { + this._editor.changeDecorations(decorationsAccessor => { + this._editor.changeViewZones(viewZoneAccessor => { - let codeLensIndex = 0, groupsIndex = 0, helper = new CodeLensHelper(); + const helper = new CodeLensHelper(); + let codeLensIndex = 0; + let groupsIndex = 0; while (groupsIndex < groups.length && codeLensIndex < this._lenses.length) { @@ -265,14 +274,14 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { let codeLensLineNumber = this._lenses[codeLensIndex].getLineNumber(); if (codeLensLineNumber < symbolsLineNumber) { - this._lenses[codeLensIndex].dispose(helper, accessor); + this._lenses[codeLensIndex].dispose(helper, viewZoneAccessor); this._lenses.splice(codeLensIndex, 1); } else if (codeLensLineNumber === symbolsLineNumber) { this._lenses[codeLensIndex].updateCodeLensSymbols(groups[groupsIndex], helper); groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLens(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses.schedule())); codeLensIndex++; groupsIndex++; } @@ -280,17 +289,17 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { // Delete extra code lenses while (codeLensIndex < this._lenses.length) { - this._lenses[codeLensIndex].dispose(helper, accessor); + this._lenses[codeLensIndex].dispose(helper, viewZoneAccessor); this._lenses.splice(codeLensIndex, 1); } // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLens(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses.schedule())); groupsIndex++; } - helper.commit(changeAccessor); + helper.commit(decorationsAccessor); }); }); @@ -300,7 +309,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { private _onViewportChanged(): void { if (this._currentResolveCodeLensSymbolsPromise) { this._currentResolveCodeLensSymbolsPromise.cancel(); - this._currentResolveCodeLensSymbolsPromise = null; + this._currentResolveCodeLensSymbolsPromise = undefined; } const model = this._editor.getModel(); @@ -308,8 +317,8 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { return; } - const toResolve: ICodeLensData[][] = []; - const lenses: CodeLens[] = []; + const toResolve: CodeLensItem[][] = []; + const lenses: CodeLensWidget[] = []; this._lenses.forEach((lens) => { const request = lens.computeIfNecessary(model); if (request) { @@ -326,7 +335,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { const promises = toResolve.map((request, i) => { - const resolvedSymbols = new Array(request.length); + const resolvedSymbols = new Array(request.length); const promises = request.map((request, i) => { if (!request.symbol.command && typeof request.provider.resolveCodeLens === 'function') { return Promise.resolve(request.provider.resolveCodeLens(model, request.symbol, token)).then(symbol => { @@ -339,7 +348,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { }); return Promise.all(promises).then(() => { - lenses[i].updateCommands(resolvedSymbols); + if (!token.isCancellationRequested) { + lenses[i].updateCommands(resolvedSymbols); + } }); }); @@ -347,10 +358,11 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { }); this._currentResolveCodeLensSymbolsPromise.then(() => { - this._currentResolveCodeLensSymbolsPromise = null; - }).catch(err => { - this._currentResolveCodeLensSymbolsPromise = null; - onUnexpectedError(err); + this._oldCodeLensModels.clear(); // dispose old models once we have updated the UI with the current model + this._currentResolveCodeLensSymbolsPromise = undefined; + }, err => { + onUnexpectedError(err); // can also be cancellation! + this._currentResolveCodeLensSymbolsPromise = undefined; }); } } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 9701a319f4..2f85d841a0 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -11,9 +11,9 @@ import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { Command, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { Command, CodeLens } from 'vs/editor/common/modes'; import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegistry'; -import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens'; +import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -65,7 +65,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { constructor( editor: editorBrowser.ICodeEditor, symbolRange: Range, - data: ICodeLensData[] + data: CodeLensItem[] ) { this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool); this._editor = editor; @@ -88,7 +88,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { this._domNode.innerHTML = ' '; } - withCommands(inSymbols: Array, animate: boolean): void { + withCommands(inSymbols: Array, animate: boolean): void { this._commands.clear(); const symbols = coalesce(inSymbols); @@ -189,17 +189,17 @@ export class CodeLensHelper { } } -export class CodeLens { +export class CodeLensWidget { private readonly _editor: editorBrowser.ICodeEditor; private readonly _viewZone: CodeLensViewZone; private readonly _viewZoneId: number; private readonly _contentWidget: CodeLensContentWidget; private _decorationIds: string[]; - private _data: ICodeLensData[]; + private _data: CodeLensItem[]; constructor( - data: ICodeLensData[], + data: CodeLensItem[], editor: editorBrowser.ICodeEditor, helper: CodeLensHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor, @@ -256,7 +256,7 @@ export class CodeLens { }); } - updateCodeLensSymbols(data: ICodeLensData[], helper: CodeLensHelper): void { + updateCodeLensSymbols(data: CodeLensItem[], helper: CodeLensHelper): void { while (this._decorationIds.length) { helper.removeDecoration(this._decorationIds.pop()!); } @@ -270,7 +270,7 @@ export class CodeLens { }); } - computeIfNecessary(model: ITextModel): ICodeLensData[] | null { + computeIfNecessary(model: ITextModel): CodeLensItem[] | null { if (!this._contentWidget.isVisible()) { return null; } @@ -285,7 +285,7 @@ export class CodeLens { return this._data; } - updateCommands(symbols: Array): void { + updateCommands(symbols: Array): void { this._contentWidget.withCommands(symbols, true); for (let i = 0; i < this._data.length; i++) { const resolved = symbols[i]; diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 40f51d379c..b34ed1b937 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -7,7 +7,7 @@ import { CancelablePromise, TimeoutTimer, createCancelablePromise } from 'vs/bas import { RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { hash } from 'vs/base/common/hash'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -22,14 +22,13 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur const MAX_DECORATORS = 500; -export class ColorDetector implements IEditorContribution { +export class ColorDetector extends Disposable implements IEditorContribution { private static readonly ID: string = 'editor.contrib.colorDetector'; static RECOMPUTE_TIME = 1000; // ms - private _globalToDispose: IDisposable[] = []; - private _localToDispose: IDisposable[] = []; + private readonly _localToDispose = this._register(new DisposableStore()); private _computePromise: CancelablePromise | null; private _timeoutTimer: TimeoutTimer | null; @@ -37,7 +36,7 @@ export class ColorDetector implements IEditorContribution { private _colorDatas = new Map(); private _colorDecoratorIds: string[] = []; - private readonly _decorationsTypes: { [key: string]: boolean } = {}; + private readonly _decorationsTypes = new Set(); private _isEnabled: boolean; @@ -45,13 +44,14 @@ export class ColorDetector implements IEditorContribution { @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService ) { - this._globalToDispose.push(_editor.onDidChangeModel((e) => { + super(); + this._register(_editor.onDidChangeModel((e) => { this._isEnabled = this.isEnabled(); this.onModelChanged(); })); - this._globalToDispose.push(_editor.onDidChangeModelLanguage((e) => this.onModelChanged())); - this._globalToDispose.push(ColorProviderRegistry.onDidChange((e) => this.onModelChanged())); - this._globalToDispose.push(_editor.onDidChangeConfiguration((e) => { + this._register(_editor.onDidChangeModelLanguage((e) => this.onModelChanged())); + this._register(ColorProviderRegistry.onDidChange((e) => this.onModelChanged())); + this._register(_editor.onDidChangeConfiguration((e) => { let prevIsEnabled = this._isEnabled; this._isEnabled = this.isEnabled(); if (prevIsEnabled !== this._isEnabled) { @@ -78,7 +78,7 @@ export class ColorDetector implements IEditorContribution { // handle deprecated settings. [languageId].colorDecorators.enable const deprecatedConfig = this._configurationService.getValue<{}>(languageId.language); if (deprecatedConfig) { - const colorDecorators = deprecatedConfig['colorDecorators']; // deprecatedConfig.valueOf('.colorDecorators.enable'); + const colorDecorators = (deprecatedConfig as any)['colorDecorators']; // deprecatedConfig.valueOf('.colorDecorators.enable'); if (colorDecorators && colorDecorators['enable'] !== undefined && !colorDecorators['enable']) { return colorDecorators['enable']; } @@ -98,7 +98,7 @@ export class ColorDetector implements IEditorContribution { dispose(): void { this.stop(); this.removeAllDecorations(); - this._globalToDispose = dispose(this._globalToDispose); + super.dispose(); } private onModelChanged(): void { @@ -113,7 +113,7 @@ export class ColorDetector implements IEditorContribution { return; } - this._localToDispose.push(this._editor.onDidChangeModelContent((e) => { + this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { if (!this._timeoutTimer) { this._timeoutTimer = new TimeoutTimer(); this._timeoutTimer.cancelAndSet(() => { @@ -149,7 +149,7 @@ export class ColorDetector implements IEditorContribution { this._computePromise.cancel(); this._computePromise = null; } - this._localToDispose = dispose(this._localToDispose); + this._localToDispose.clear(); } private updateDecorations(colorDatas: IColorData[]): void { @@ -180,7 +180,7 @@ export class ColorDetector implements IEditorContribution { let color = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`; let key = 'colorBox-' + subKey; - if (!this._decorationsTypes[key] && !newDecorationsTypes[key]) { + if (!this._decorationsTypes.has(key) && !newDecorationsTypes[key]) { this._codeEditorService.registerDecorationType(key, { before: { contentText: ' ', @@ -210,11 +210,11 @@ export class ColorDetector implements IEditorContribution { }); } - for (let subType in this._decorationsTypes) { + this._decorationsTypes.forEach(subType => { if (!newDecorationsTypes[subType]) { this._codeEditorService.removeDecorationType(subType); } - } + }); this._colorDecoratorIds = this._editor.deltaDecorations(this._colorDecoratorIds, decorations); } @@ -223,9 +223,9 @@ export class ColorDetector implements IEditorContribution { this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); this._colorDecoratorIds = this._editor.deltaDecorations(this._colorDecoratorIds, []); - for (let subType in this._decorationsTypes) { + this._decorationsTypes.forEach(subType => { this._codeEditorService.removeDecorationType(subType); - } + }); } getColorData(position: Position): IColorData | null { diff --git a/src/vs/editor/contrib/colorPicker/colorPicker.css b/src/vs/editor/contrib/colorPicker/colorPicker.css index 85653b5481..d929b4dd47 100644 --- a/src/vs/editor/contrib/colorPicker/colorPicker.css +++ b/src/vs/editor/contrib/colorPicker/colorPicker.css @@ -84,21 +84,21 @@ .colorpicker-body .hue-strip { position: relative; margin-left: 8px; - cursor: -webkit-grab; + cursor: grab; background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); } .colorpicker-body .opacity-strip { position: relative; margin-left: 8px; - cursor: -webkit-grab; + cursor: grab; background: url('images/opacity-background.png'); background-size: 9px 9px; image-rendering: pixelated; } .colorpicker-body .strip.grabbing { - cursor: -webkit-grabbing; + cursor: grabbing; } .colorpicker-body .slider { diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index af63d1d4af..0444170f8e 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -986,7 +986,8 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, - expectedSelection + expectedSelection, + true ); innerMode.dispose(); outerMode.dispose(); diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index a77302fc2e..74f55dd764 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -10,7 +10,7 @@ import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionba import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAction } from 'vs/base/common/actions'; import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; @@ -31,7 +31,7 @@ export class ContextMenuController implements IEditorContribution { return editor.getContribution(ContextMenuController.ID); } - private _toDispose: IDisposable[] = []; + private readonly _toDispose = new DisposableStore(); private _contextMenuIsBeingShownCount: number = 0; private readonly _editor: ICodeEditor; @@ -45,13 +45,13 @@ export class ContextMenuController implements IEditorContribution { ) { this._editor = editor; - this._toDispose.push(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e))); - this._toDispose.push(this._editor.onMouseWheel((e: IMouseWheelEvent) => { + this._toDispose.add(this._editor.onContextMenu((e: IEditorMouseEvent) => this._onContextMenu(e))); + this._toDispose.add(this._editor.onMouseWheel((e: IMouseWheelEvent) => { if (this._contextMenuIsBeingShownCount > 0) { this._contextViewService.hideContextView(); } })); - this._toDispose.push(this._editor.onKeyDown((e: IKeyboardEvent) => { + this._toDispose.add(this._editor.onKeyDown((e: IKeyboardEvent) => { if (e.keyCode === KeyCode.ContextMenu) { // Chrome is funny like that e.preventDefault(); @@ -125,7 +125,7 @@ export class ContextMenuController implements IEditorContribution { } } - private _getMenuActions(model: ITextModel): IAction[] { + private _getMenuActions(model: ITextModel): ReadonlyArray { const result: IAction[] = []; let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService); @@ -141,7 +141,7 @@ export class ContextMenuController implements IEditorContribution { return result; } - private _doShowContextMenu(actions: IAction[], anchor: IAnchor | null = null): void { + private _doShowContextMenu(actions: ReadonlyArray, anchor: IAnchor | null = null): void { if (!this._editor.hasModel()) { return; } @@ -217,7 +217,7 @@ export class ContextMenuController implements IEditorContribution { this._contextViewService.hideContextView(); } - this._toDispose = dispose(this._toDispose); + this._toDispose.dispose(); } } diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index e2957bab58..bb50bac3b8 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -5,7 +5,7 @@ import 'vs/css!./dnd'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; @@ -28,12 +28,11 @@ function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean { } } -export class DragAndDropController implements editorCommon.IEditorContribution { +export class DragAndDropController extends Disposable implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.dragAndDrop'; private readonly _editor: ICodeEditor; - private _toUnhook: IDisposable[]; private _dragSelection: Selection | null; private _dndDecorationIds: string[]; private _mouseDown: boolean; @@ -45,15 +44,15 @@ export class DragAndDropController implements editorCommon.IEditorContribution { } constructor(editor: ICodeEditor) { + super(); this._editor = editor; - this._toUnhook = []; - this._toUnhook.push(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); - this._toUnhook.push(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); - this._toUnhook.push(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); - this._toUnhook.push(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e))); - this._toUnhook.push(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); - this._toUnhook.push(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); - this._toUnhook.push(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); + this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); + this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); + this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); + this._register(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e))); + this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); + this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); + this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); this._dndDecorationIds = []; this._mouseDown = false; this._modifierPressed = false; @@ -228,7 +227,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution { this._dragSelection = null; this._mouseDown = false; this._modifierPressed = false; - this._toUnhook = dispose(this._toUnhook); + super.dispose(); } } diff --git a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts index c78e4c4565..b18f5bf0cc 100644 --- a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts +++ b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts @@ -91,7 +91,7 @@ export class DragAndDropCommand implements editorCommon.ICommand { this.selection.endColumn ); } else { - // The target position is before the selection's end postion. Since the selection doesn't contain the target position, the selection is one-line and target position is before this selection. + // The target position is before the selection's end position. Since the selection doesn't contain the target position, the selection is one-line and target position is before this selection. this.targetSelection = new Selection( this.targetPosition.lineNumber - this.selection.endLineNumber + this.selection.startLineNumber, this.targetPosition.column, diff --git a/src/vs/editor/contrib/documentSymbols/media/BooleanData_16x.svg b/src/vs/editor/contrib/documentSymbols/media/BooleanData_16x.svg deleted file mode 100644 index d9fd295d0b..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/BooleanData_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/BooleanData_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/BooleanData_16x_darkp.svg deleted file mode 100644 index 48e8c5a383..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/BooleanData_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Class_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Class_16x.svg deleted file mode 100644 index e553c3633e..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Class_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg deleted file mode 100644 index c43aad29ef..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x.svg b/src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x.svg deleted file mode 100644 index 2af5cc6fae..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x_darkp.svg deleted file mode 100644 index a2df3032cb..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/ColorPalette_ColorPalette_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Constant_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Constant_16x.svg deleted file mode 100644 index ed2a175100..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Constant_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Constant_16x_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/Constant_16x_inverse.svg deleted file mode 100644 index 173e427f96..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Constant_16x_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Document_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Document_16x.svg deleted file mode 100644 index 7b36178ab4..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Document_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Document_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Document_16x_darkp.svg deleted file mode 100644 index bced3a467e..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Document_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/EnumItem_16x.svg b/src/vs/editor/contrib/documentSymbols/media/EnumItem_16x.svg deleted file mode 100644 index aa901ec193..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/EnumItem_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/EnumItem_inverse_16x.svg b/src/vs/editor/contrib/documentSymbols/media/EnumItem_inverse_16x.svg deleted file mode 100644 index 791759092f..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/EnumItem_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Enumerator_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Enumerator_16x.svg deleted file mode 100755 index e4a9551fd5..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Enumerator_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Enumerator_inverse_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Enumerator_inverse_16x.svg deleted file mode 100755 index d8e9f4f107..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Enumerator_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode.svg b/src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode.svg deleted file mode 100644 index 0e202ec10b..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode_inverse.svg deleted file mode 100644 index a508edcd3d..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Event_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Field_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Field_16x.svg deleted file mode 100644 index e1b5aa5e31..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Field_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg deleted file mode 100644 index 5fc48ceff0..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Indexer_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Indexer_16x.svg deleted file mode 100644 index ff55f31ffa..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Indexer_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Indexer_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Indexer_16x_darkp.svg deleted file mode 100644 index 2f3788e773..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Indexer_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x.svg b/src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x.svg deleted file mode 100644 index 7a80c7fe26..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x_darkp.svg deleted file mode 100644 index ef98b5133f..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/IntelliSenseKeyword_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Interface_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Interface_16x.svg deleted file mode 100644 index 0c08c8d50a..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Interface_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg deleted file mode 100644 index f7c2934a55..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg b/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg deleted file mode 100644 index e78894b6c6..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg deleted file mode 100644 index 44a44b489d..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Method_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Method_16x.svg deleted file mode 100644 index e1b587f9cc..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Method_16x.svg +++ /dev/null @@ -1 +0,0 @@ -Method_16x \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Method_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Method_16x_darkp.svg deleted file mode 100644 index 0b7dd26efd..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Method_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ -Method_16x \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Namespace_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Namespace_16x.svg deleted file mode 100644 index 772b9152cb..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Namespace_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Namespace_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Namespace_16x_darkp.svg deleted file mode 100644 index dc052a068c..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Namespace_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Numeric_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Numeric_16x.svg deleted file mode 100644 index ac848f89b8..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Numeric_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Numeric_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Numeric_16x_darkp.svg deleted file mode 100644 index 4144eea0c0..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Numeric_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode.svg b/src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode.svg deleted file mode 100644 index ba2f2d091c..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode_inverse.svg deleted file mode 100644 index 21e1e814b2..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Operator_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Property_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Property_16x.svg deleted file mode 100644 index cac629e113..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Property_16x.svg +++ /dev/null @@ -1 +0,0 @@ -Property_16x \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Property_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Property_16x_darkp.svg deleted file mode 100644 index bad83c9a32..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Property_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ -Property_16x \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Snippet_16x.svg b/src/vs/editor/contrib/documentSymbols/media/Snippet_16x.svg deleted file mode 100644 index 640c247786..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Snippet_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Snippet_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Snippet_16x_darkp.svg deleted file mode 100644 index 0fb4b8bc9e..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Snippet_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/String_16x.svg b/src/vs/editor/contrib/documentSymbols/media/String_16x.svg deleted file mode 100644 index 880d50dd03..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/String_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/String_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/String_16x_darkp.svg deleted file mode 100644 index de3ea3b37e..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/String_16x_darkp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode.svg b/src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode.svg deleted file mode 100644 index e776cbc565..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode_inverse.svg deleted file mode 100644 index 1b76b62be9..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Structure_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode.svg b/src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode.svg deleted file mode 100644 index 788cc8d645..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode_inverse.svg deleted file mode 100644 index 6cec71cb03..0000000000 --- a/src/vs/editor/contrib/documentSymbols/media/Template_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/boolean-dark.svg b/src/vs/editor/contrib/documentSymbols/media/boolean-dark.svg new file mode 100644 index 0000000000..e009568b13 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/boolean-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/boolean-light.svg b/src/vs/editor/contrib/documentSymbols/media/boolean-light.svg new file mode 100644 index 0000000000..06613f8bed --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/boolean-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/class-dark.svg b/src/vs/editor/contrib/documentSymbols/media/class-dark.svg new file mode 100644 index 0000000000..a71e221f6b --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/class-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/class-light.svg b/src/vs/editor/contrib/documentSymbols/media/class-light.svg new file mode 100644 index 0000000000..aa106f18f8 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/class-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/constant-dark.svg b/src/vs/editor/contrib/documentSymbols/media/constant-dark.svg new file mode 100644 index 0000000000..0e90ecafcd --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/constant-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/constant-light.svg b/src/vs/editor/contrib/documentSymbols/media/constant-light.svg new file mode 100644 index 0000000000..1a369c1d8a --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/constant-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/enumerator-dark.svg b/src/vs/editor/contrib/documentSymbols/media/enumerator-dark.svg new file mode 100644 index 0000000000..82d4ff29c4 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/enumerator-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/enumerator-item-dark.svg b/src/vs/editor/contrib/documentSymbols/media/enumerator-item-dark.svg new file mode 100644 index 0000000000..23c697fdf1 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/enumerator-item-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/enumerator-item-light.svg b/src/vs/editor/contrib/documentSymbols/media/enumerator-item-light.svg new file mode 100644 index 0000000000..a99045d335 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/enumerator-item-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/enumerator-light.svg b/src/vs/editor/contrib/documentSymbols/media/enumerator-light.svg new file mode 100644 index 0000000000..e2441a0dc1 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/enumerator-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/event-dark.svg b/src/vs/editor/contrib/documentSymbols/media/event-dark.svg new file mode 100644 index 0000000000..051bef316e --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/event-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/event-light.svg b/src/vs/editor/contrib/documentSymbols/media/event-light.svg new file mode 100644 index 0000000000..712344d1f9 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/event-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/field-dark.svg b/src/vs/editor/contrib/documentSymbols/media/field-dark.svg new file mode 100644 index 0000000000..15623061c5 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/field-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/field-light.svg b/src/vs/editor/contrib/documentSymbols/media/field-light.svg new file mode 100644 index 0000000000..72dd79504f --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/field-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/file-dark.svg b/src/vs/editor/contrib/documentSymbols/media/file-dark.svg new file mode 100644 index 0000000000..5ed5762a1f --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/file-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/file-light.svg b/src/vs/editor/contrib/documentSymbols/media/file-light.svg new file mode 100644 index 0000000000..ad54e13b1b --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/file-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/indexer-dark.svg b/src/vs/editor/contrib/documentSymbols/media/indexer-dark.svg new file mode 100644 index 0000000000..e92131d3d0 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/indexer-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/indexer-light.svg b/src/vs/editor/contrib/documentSymbols/media/indexer-light.svg new file mode 100644 index 0000000000..207899642c --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/indexer-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/interface-dark.svg b/src/vs/editor/contrib/documentSymbols/media/interface-dark.svg new file mode 100644 index 0000000000..6d482b2abd --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/interface-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/interface-light.svg b/src/vs/editor/contrib/documentSymbols/media/interface-light.svg new file mode 100644 index 0000000000..a397dd00b0 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/interface-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/keyword-dark.svg b/src/vs/editor/contrib/documentSymbols/media/keyword-dark.svg new file mode 100644 index 0000000000..70ba6ea933 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/keyword-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/keyword-light.svg b/src/vs/editor/contrib/documentSymbols/media/keyword-light.svg new file mode 100644 index 0000000000..fc57528a3e --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/keyword-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/method-dark.svg b/src/vs/editor/contrib/documentSymbols/media/method-dark.svg new file mode 100644 index 0000000000..970d7b6148 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/method-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/method-light.svg b/src/vs/editor/contrib/documentSymbols/media/method-light.svg new file mode 100644 index 0000000000..403a9b90dd --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/method-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/namespace-dark.svg b/src/vs/editor/contrib/documentSymbols/media/namespace-dark.svg new file mode 100644 index 0000000000..9a725bb41f --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/namespace-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/namespace-light.svg b/src/vs/editor/contrib/documentSymbols/media/namespace-light.svg new file mode 100644 index 0000000000..1339da7ce2 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/namespace-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/numeric-dark.svg b/src/vs/editor/contrib/documentSymbols/media/numeric-dark.svg new file mode 100644 index 0000000000..a1573df010 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/numeric-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/numeric-light.svg b/src/vs/editor/contrib/documentSymbols/media/numeric-light.svg new file mode 100644 index 0000000000..ea0e56e022 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/numeric-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/operator-dark.svg b/src/vs/editor/contrib/documentSymbols/media/operator-dark.svg new file mode 100644 index 0000000000..957f5f44f1 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/operator-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/operator-light.svg b/src/vs/editor/contrib/documentSymbols/media/operator-light.svg new file mode 100644 index 0000000000..bf6ed57996 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/operator-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/property-dark.svg b/src/vs/editor/contrib/documentSymbols/media/property-dark.svg new file mode 100644 index 0000000000..23e07ffa19 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/property-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/property-light.svg b/src/vs/editor/contrib/documentSymbols/media/property-light.svg new file mode 100644 index 0000000000..be642dd152 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/property-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/snippet-dark.svg b/src/vs/editor/contrib/documentSymbols/media/snippet-dark.svg new file mode 100644 index 0000000000..79799f98c2 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/snippet-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/snippet-light.svg b/src/vs/editor/contrib/documentSymbols/media/snippet-light.svg new file mode 100644 index 0000000000..45fa3a001e --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/snippet-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/string-dark.svg b/src/vs/editor/contrib/documentSymbols/media/string-dark.svg new file mode 100644 index 0000000000..80fb9d6567 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/string-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/string-light.svg b/src/vs/editor/contrib/documentSymbols/media/string-light.svg new file mode 100644 index 0000000000..02a0282e90 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/string-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/structure-dark.svg b/src/vs/editor/contrib/documentSymbols/media/structure-dark.svg new file mode 100644 index 0000000000..13766a5dce --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/structure-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/structure-light.svg b/src/vs/editor/contrib/documentSymbols/media/structure-light.svg new file mode 100644 index 0000000000..c96bcfa61b --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/structure-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css index 9d582c102a..6b61c64681 100644 --- a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css +++ b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css @@ -15,268 +15,269 @@ width: 16px; min-height: 14px; min-width: 16px; + background-position: center; } /* default icons */ .monaco-workbench .symbol-icon { - background-image: url('Field_16x.svg'); + background-image: url('field-light.svg'); background-repeat: no-repeat; } .vs-dark .monaco-workbench .symbol-icon, .hc-black .monaco-workbench .symbol-icon { - background-image: url('Field_16x_darkp.svg'); + background-image: url('field-dark.svg'); } /* constant */ .monaco-workbench .symbol-icon.constant { - background-image: url('Constant_16x.svg'); + background-image: url('constant-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.constant, .hc-black .monaco-workbench .symbol-icon.constant { - background-image: url('Constant_16x_inverse.svg'); + background-image: url('constant-dark.svg'); } /* enum */ .monaco-workbench .symbol-icon.enum { - background-image: url('Enumerator_16x.svg'); + background-image: url('enumerator-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.enum, .hc-black .monaco-workbench .symbol-icon.enum { - background-image: url('Enumerator_inverse_16x.svg'); + background-image: url('enumerator-dark.svg'); } /* enum-member */ .monaco-workbench .symbol-icon.enum-member { - background-image: url('EnumItem_16x.svg'); + background-image: url('enumerator-item-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.enum-member, .hc-black .monaco-workbench .symbol-icon.enum-member { - background-image: url('EnumItem_inverse_16x.svg'); + background-image: url('enumerator-item-dark.svg'); } /* struct */ .monaco-workbench .symbol-icon.struct { - background-image: url('Structure_16x_vscode.svg'); + background-image: url('structure-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.struct, .hc-black .monaco-workbench .symbol-icon.struct { - background-image: url('Structure_16x_vscode_inverse.svg'); + background-image: url('structure-dark.svg'); } /* event */ .monaco-workbench .symbol-icon.event { - background-image: url('Event_16x_vscode.svg'); + background-image: url('event-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.event, .hc-black .monaco-workbench .symbol-icon.event { - background-image: url('Event_16x_vscode_inverse.svg'); + background-image: url('event-dark.svg'); } /* operator */ .monaco-workbench .symbol-icon.operator { - background-image: url('Operator_16x_vscode.svg'); + background-image: url('operator-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.operator, .hc-black .monaco-workbench .symbol-icon.operator { - background-image: url('Operator_16x_vscode_inverse.svg'); + background-image: url('operator-dark.svg'); } /* type paramter */ .monaco-workbench .symbol-icon.type-parameter { - background-image: url('Template_16x_vscode.svg'); + background-image: url('template-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.type-parameter, .hc-black .monaco-workbench .symbol-icon.type-parameter { - background-image: url('Template_16x_vscode_inverse.svg'); + background-image: url('template-dark.svg'); } /* boolean, null */ .monaco-workbench .symbol-icon.boolean { - background-image: url('BooleanData_16x.svg'); + background-image: url('boolean-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.boolean, .hc-black .monaco-workbench .symbol-icon.boolean { - background-image: url('BooleanData_16x_darkp.svg'); + background-image: url('boolean-dark.svg'); } /* null */ .monaco-workbench .symbol-icon.null { - background-image: url('BooleanData_16x.svg'); + background-image: url('boolean-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.null, .hc-black .monaco-workbench .symbol-icon.null { - background-image: url('BooleanData_16x_darkp.svg'); + background-image: url('boolean-dark.svg'); } /* class */ .monaco-workbench .symbol-icon.class { - background-image: url('Class_16x.svg'); + background-image: url('class-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.class, .hc-black .monaco-workbench .symbol-icon.class { - background-image: url('Class_16x_darkp.svg'); + background-image: url('class-dark.svg'); } /* constructor */ .monaco-workbench .symbol-icon.constructor { - background-image: url('Method_16x.svg'); + background-image: url('method-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.constructor, .hc-black .monaco-workbench .symbol-icon.constructor { - background-image: url('Method_16x_darkp.svg'); + background-image: url('method-dark.svg'); } /* file */ .monaco-workbench .symbol-icon.file { - background-image: url('Document_16x.svg'); + background-image: url('file-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.file, .hc-black .monaco-workbench .symbol-icon.file { - background-image: url('Document_16x_darkp.svg'); + background-image: url('file-dark.svg'); } /* field */ .monaco-workbench .symbol-icon.field { - background-image: url('Field_16x.svg'); + background-image: url('field-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.field, .hc-black .monaco-workbench .symbol-icon.field { - background-image: url('Field_16x_darkp.svg'); + background-image: url('field-dark.svg'); } /* variable */ .monaco-workbench .symbol-icon.variable { - background-image: url('LocalVariable_16x_vscode.svg'); + background-image: url('variable-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.variable, .hc-black .monaco-workbench .symbol-icon.variable { - background-image: url('LocalVariable_16x_vscode_inverse.svg'); + background-image: url('variable-dark.svg'); } /* array */ .monaco-workbench .symbol-icon.array { - background-image: url('Indexer_16x.svg'); + background-image: url('indexer-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.array, .hc-black .monaco-workbench .symbol-icon.array { - background-image: url('Indexer_16x_darkp.svg'); + background-image: url('indexer-dark.svg'); } /* keyword */ /* todo@joh not used? */ .monaco-workbench .symbol-icon.keyword { - background-image: url('IntelliSenseKeyword_16x.svg'); + background-image: url('keyword-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.keyword, .hc-black .monaco-workbench .symbol-icon.keyword { - background-image: url('IntelliSenseKeyword_16x_darkp.svg'); + background-image: url('keyword-dark.svg'); } /* interface */ .monaco-workbench .symbol-icon.interface { - background-image: url('Interface_16x.svg'); + background-image: url('interface-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.interface, .hc-black .monaco-workbench .symbol-icon.interface { - background-image: url('Interface_16x_darkp.svg'); + background-image: url('interface-dark.svg'); } /* method */ .monaco-workbench .symbol-icon.method { - background-image: url('Method_16x.svg'); + background-image: url('method-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.method, .hc-black .monaco-workbench .symbol-icon.method { - background-image: url('Method_16x_darkp.svg'); + background-image: url('method-dark.svg'); } /* function */ .monaco-workbench .symbol-icon.function { - background-image: url('Method_16x.svg'); + background-image: url('method-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.function, .hc-black .monaco-workbench .symbol-icon.function { - background-image: url('Method_16x_darkp.svg'); + background-image: url('method-dark.svg'); } /* object */ .monaco-workbench .symbol-icon.object { - background-image: url('Namespace_16x.svg'); + background-image: url('namespace-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.object, .hc-black .monaco-workbench .symbol-icon.object { - background-image: url('Namespace_16x_darkp.svg'); + background-image: url('namespace-dark.svg'); } /* namespace */ .monaco-workbench .symbol-icon.namespace { - background-image: url('Namespace_16x.svg'); + background-image: url('namespace-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.namespace, .hc-black .monaco-workbench .symbol-icon.namespace { - background-image: url('Namespace_16x_darkp.svg'); + background-image: url('namespace-dark.svg'); } /* package */ .monaco-workbench .symbol-icon.package { - background-image: url('Namespace_16x.svg'); + background-image: url('namespace-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.package, .hc-black .monaco-workbench .symbol-icon.package { - background-image: url('Namespace_16x_darkp.svg'); + background-image: url('namespace-dark.svg'); } /* module */ .monaco-workbench .symbol-icon.module { - background-image: url('Namespace_16x.svg'); + background-image: url('namespace-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.module, .hc-black .monaco-workbench .symbol-icon.module { - background-image: url('Namespace_16x_darkp.svg'); + background-image: url('namespace-dark.svg'); } /* number */ .monaco-workbench .symbol-icon.number { - background-image: url('Numeric_16x.svg'); + background-image: url('numeric-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.number, .hc-black .monaco-workbench .symbol-icon.number { - background-image: url('Numeric_16x_darkp.svg'); + background-image: url('numeric-dark.svg'); } /* property */ .monaco-workbench .symbol-icon.property { - background-image: url('Property_16x.svg'); + background-image: url('property-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.property, .hc-black .monaco-workbench .symbol-icon.property { - background-image: url('Property_16x_darkp.svg'); + background-image: url('property-dark.svg'); } /* snippet */ /* todo@joh unused? */ .monaco-workbench .symbol-icon.snippet { - background-image: url('Snippet_16x.svg'); + background-image: url('snippet-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.snippet, .hc-black .monaco-workbench .symbol-icon.snippet { - background-image: url('Snippet_16x_darkp.svg'); + background-image: url('snippet-dark.svg'); } /* string */ .monaco-workbench .symbol-icon.string { - background-image: url('String_16x.svg'); + background-image: url('string-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.string, .hc-black .monaco-workbench .symbol-icon.string { - background-image: url('String_16x_darkp.svg'); + background-image: url('string-dark.svg'); } /* key */ .monaco-workbench .symbol-icon.key { - background-image: url('String_16x.svg'); + background-image: url('string-light.svg'); } .vs-dark .monaco-workbench .symbol-icon.key, .hc-black .monaco-workbench .symbol-icon.key { - background-image: url('String_16x_darkp.svg'); + background-image: url('string-dark.svg'); } diff --git a/src/vs/editor/contrib/documentSymbols/media/template-dark.svg b/src/vs/editor/contrib/documentSymbols/media/template-dark.svg new file mode 100644 index 0000000000..425ced36f0 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/template-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/template-light.svg b/src/vs/editor/contrib/documentSymbols/media/template-light.svg new file mode 100644 index 0000000000..496d8f7c85 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/template-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/variable-dark.svg b/src/vs/editor/contrib/documentSymbols/media/variable-dark.svg new file mode 100644 index 0000000000..687fcabfff --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/variable-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/media/variable-light.svg b/src/vs/editor/contrib/documentSymbols/media/variable-light.svg new file mode 100644 index 0000000000..ede7e9434d --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/variable-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 2c17fe9a5f..7faebde985 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -15,20 +15,18 @@ import { Range } from 'vs/editor/common/core/range'; import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; -import { IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IdleValue } from 'vs/base/common/async'; export type OutlineItem = OutlineGroup | OutlineElement; export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider { - constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { } - getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } { if (element instanceof OutlineGroup) { return element.provider.displayName || element.id; @@ -36,10 +34,6 @@ export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelP return element.symbol.name; } } - - mightProducePrintableCharacter(event: IKeyboardEvent): boolean { - return this._keybindingService.mightProducePrintableCharacter(event); - } } @@ -215,6 +209,8 @@ export const enum OutlineSortOrder { export class OutlineItemComparator implements ITreeSorter { + private readonly _collator = new IdleValue(() => new Intl.Collator(undefined, { numeric: true })); + constructor( public type: OutlineSortOrder = OutlineSortOrder.ByPosition ) { } @@ -225,11 +221,11 @@ export class OutlineItemComparator implements ITreeSorter { } else if (a instanceof OutlineElement && b instanceof OutlineElement) { if (this.type === OutlineSortOrder.ByKind) { - return a.symbol.kind - b.symbol.kind || a.symbol.name.localeCompare(b.symbol.name); + return a.symbol.kind - b.symbol.kind || this._collator.getValue().compare(a.symbol.name, b.symbol.name); } else if (this.type === OutlineSortOrder.ByName) { - return a.symbol.name.localeCompare(b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); + return this._collator.getValue().compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range); } else if (this.type === OutlineSortOrder.ByPosition) { - return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || a.symbol.name.localeCompare(b.symbol.name); + return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.getValue().compare(a.symbol.name, b.symbol.name); } } return 0; diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index 0a443a038b..8915397931 100644 --- a/src/vs/editor/contrib/find/findDecorations.ts +++ b/src/vs/editor/contrib/find/findDecorations.ts @@ -7,9 +7,9 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { FindMatch, IModelDecorationsChangeAccessor, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { FindMatch, IModelDecorationsChangeAccessor, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness, MinimapPosition } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; +import { overviewRulerFindMatchForeground, minimapFindMatch } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; export class FindDecorations implements IDisposable { @@ -269,6 +269,10 @@ export class FindDecorations implements IDisposable { overviewRuler: { color: themeColorFromId(overviewRulerFindMatchForeground), position: OverviewRulerLane.Center + }, + minimap: { + color: themeColorFromId(minimapFindMatch), + position: MinimapPosition.Inline } }); @@ -279,6 +283,10 @@ export class FindDecorations implements IDisposable { overviewRuler: { color: themeColorFromId(overviewRulerFindMatchForeground), position: OverviewRulerLane.Center + }, + minimap: { + color: themeColorFromId(minimapFindMatch), + position: MinimapPosition.Inline } }); diff --git a/src/vs/editor/contrib/find/findState.ts b/src/vs/editor/contrib/find/findState.ts index 643f5fc1fb..339f248079 100644 --- a/src/vs/editor/contrib/find/findState.ts +++ b/src/vs/editor/contrib/find/findState.ts @@ -69,7 +69,7 @@ export class FindReplaceState implements IDisposable { private _matchesPosition: number; private _matchesCount: number; private _currentMatch: Range | null; - private readonly _onFindReplaceStateChange: Emitter; + private readonly _onFindReplaceStateChange = new Emitter(); public get searchString(): string { return this._searchString; } public get replaceString(): string { return this._replaceString; } @@ -87,7 +87,7 @@ export class FindReplaceState implements IDisposable { public get matchesPosition(): number { return this._matchesPosition; } public get matchesCount(): number { return this._matchesCount; } public get currentMatch(): Range | null { return this._currentMatch; } - public get onFindReplaceStateChange(): Event { return this._onFindReplaceStateChange.event; } + public readonly onFindReplaceStateChange: Event = this._onFindReplaceStateChange.event; constructor() { this._searchString = ''; @@ -104,7 +104,6 @@ export class FindReplaceState implements IDisposable { this._matchesPosition = 0; this._matchesCount = 0; this._currentMatch = null; - this._onFindReplaceStateChange = new Emitter(); } public dispose(): void { diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 6d6b526424..5644623d0c 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -96,8 +96,8 @@ display: flex; display: -webkit-flex; flex: initial; - margin: 0 1px 0 3px; - padding: 2px 2px 0 2px; + margin: 0 0 0 3px; + padding: 2px 0 0 2px; height: 25px; vertical-align: middle; box-sizing: border-box; @@ -151,11 +151,11 @@ } .monaco-editor .find-widget .previous { - background-image: url('images/previous.svg'); + background-image: url('images/chevron-previous-light.svg'); } .monaco-editor .find-widget .next { - background-image: url('images/next.svg'); + background-image: url('images/chevron-next-light.svg'); } .monaco-editor .find-widget .disabled { @@ -175,8 +175,8 @@ content: ''; display: inline-block; background-repeat: no-repeat; - background-position: 0 0; - background-image: url('images/cancelSelectionFind.svg'); + background-position: center; + background-image: url('images/find-selection-light.svg'); width: 20px; height: 20px; border: none; @@ -200,23 +200,23 @@ } .monaco-editor .find-widget .close-fw { - background-image: url('images/close.svg'); + background-image: url('images/close-light.svg'); } .monaco-editor .find-widget .expand { - background-image: url('images/expando-expanded.svg'); + background-image: url('images/tree-expanded-light.svg'); } .monaco-editor .find-widget .collapse { - background-image: url('images/expando-collapsed.svg'); + background-image: url('images/tree-collapsed-light.svg'); } .monaco-editor .find-widget .replace { - background-image: url('images/replace.svg'); + background-image: url('images/replace-light.svg'); } .monaco-editor .find-widget .replace-all { - background-image: url('images/replace-all.svg'); + background-image: url('images/replace-all-light.svg'); } .monaco-editor .find-widget > .replace-part { @@ -272,17 +272,17 @@ .monaco-editor.hc-black .find-widget .previous, .monaco-editor.vs-dark .find-widget .previous { - background-image: url('images/previous-inverse.svg'); + background-image: url('images/chevron-previous-dark.svg'); } .monaco-editor.hc-black .find-widget .next, .monaco-editor.vs-dark .find-widget .next { - background-image: url('images/next-inverse.svg'); + background-image: url('images/chevron-next-dark.svg'); } .monaco-editor.hc-black .find-widget .monaco-checkbox .label, .monaco-editor.vs-dark .find-widget .monaco-checkbox .label { - background-image: url('images/cancelSelectionFind-inverse.svg'); + background-image: url('images/find-selection-dark.svg'); } .monaco-editor.vs-dark .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before + .label { @@ -300,22 +300,22 @@ .monaco-editor.hc-black .find-widget .replace, .monaco-editor.vs-dark .find-widget .replace { - background-image: url('images/replace-inverse.svg'); + background-image: url('images/replace-dark.svg'); } .monaco-editor.hc-black .find-widget .replace-all, .monaco-editor.vs-dark .find-widget .replace-all { - background-image: url('images/replace-all-inverse.svg'); + background-image: url('images/replace-all-dark.svg'); } .monaco-editor.hc-black .find-widget .expand, .monaco-editor.vs-dark .find-widget .expand { - background-image: url('images/expando-expanded-dark.svg'); + background-image: url('images/tree-expanded-dark.svg'); } .monaco-editor.hc-black .find-widget .collapse, .monaco-editor.vs-dark .find-widget .collapse { - background-image: url('images/expando-collapsed-dark.svg'); + background-image: url('images/tree-collapsed-dark.svg'); } .monaco-editor.hc-black .find-widget .button:not(.disabled):hover, diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index bbee8c4ae7..f0d8174565 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -27,7 +27,7 @@ import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MA import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -1193,6 +1193,11 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .find-widget { border: 2px solid ${hcBorder}; }`); } + const foreground = theme.getColor(editorWidgetForeground); + if (foreground) { + collector.addRule(`.monaco-editor .find-widget { color: ${foreground}; }`); + } + const error = theme.getColor(errorForeground); if (error) { collector.addRule(`.monaco-editor .find-widget.no-results .matchesCount { color: ${error}; }`); diff --git a/src/vs/editor/contrib/find/images/cancelSelectionFind-inverse.svg b/src/vs/editor/contrib/find/images/cancelSelectionFind-inverse.svg deleted file mode 100644 index d776fcde98..0000000000 --- a/src/vs/editor/contrib/find/images/cancelSelectionFind-inverse.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/cancelSelectionFind.svg b/src/vs/editor/contrib/find/images/cancelSelectionFind.svg deleted file mode 100644 index cdff5731a8..0000000000 --- a/src/vs/editor/contrib/find/images/cancelSelectionFind.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/chevron-next-dark.svg b/src/vs/editor/contrib/find/images/chevron-next-dark.svg new file mode 100644 index 0000000000..dbe70d742d --- /dev/null +++ b/src/vs/editor/contrib/find/images/chevron-next-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/chevron-next-light.svg b/src/vs/editor/contrib/find/images/chevron-next-light.svg new file mode 100644 index 0000000000..ec824f41cc --- /dev/null +++ b/src/vs/editor/contrib/find/images/chevron-next-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/chevron-previous-dark.svg b/src/vs/editor/contrib/find/images/chevron-previous-dark.svg new file mode 100644 index 0000000000..5db4f79da8 --- /dev/null +++ b/src/vs/editor/contrib/find/images/chevron-previous-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/chevron-previous-light.svg b/src/vs/editor/contrib/find/images/chevron-previous-light.svg new file mode 100644 index 0000000000..aac3a5020c --- /dev/null +++ b/src/vs/editor/contrib/find/images/chevron-previous-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/close-dark.svg b/src/vs/editor/contrib/find/images/close-dark.svg index 751e89b3b0..75644595d1 100644 --- a/src/vs/editor/contrib/find/images/close-dark.svg +++ b/src/vs/editor/contrib/find/images/close-dark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/editor/contrib/find/images/close-light.svg b/src/vs/editor/contrib/find/images/close-light.svg new file mode 100644 index 0000000000..cf5f28ca35 --- /dev/null +++ b/src/vs/editor/contrib/find/images/close-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/close.svg b/src/vs/editor/contrib/find/images/close.svg deleted file mode 100644 index fde34404d4..0000000000 --- a/src/vs/editor/contrib/find/images/close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/expando-collapsed-dark.svg b/src/vs/editor/contrib/find/images/expando-collapsed-dark.svg deleted file mode 100644 index 6f3abfce78..0000000000 --- a/src/vs/editor/contrib/find/images/expando-collapsed-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/expando-collapsed.svg b/src/vs/editor/contrib/find/images/expando-collapsed.svg deleted file mode 100644 index 5dcb87c772..0000000000 --- a/src/vs/editor/contrib/find/images/expando-collapsed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/expando-expanded-dark.svg b/src/vs/editor/contrib/find/images/expando-expanded-dark.svg deleted file mode 100644 index 22dfac04f1..0000000000 --- a/src/vs/editor/contrib/find/images/expando-expanded-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/expando-expanded.svg b/src/vs/editor/contrib/find/images/expando-expanded.svg deleted file mode 100644 index e55ccd923e..0000000000 --- a/src/vs/editor/contrib/find/images/expando-expanded.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/find/images/find-selection-dark.svg b/src/vs/editor/contrib/find/images/find-selection-dark.svg new file mode 100644 index 0000000000..6fc07d81a5 --- /dev/null +++ b/src/vs/editor/contrib/find/images/find-selection-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/find-selection-light.svg b/src/vs/editor/contrib/find/images/find-selection-light.svg new file mode 100644 index 0000000000..3608b15d29 --- /dev/null +++ b/src/vs/editor/contrib/find/images/find-selection-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/next-inverse.svg b/src/vs/editor/contrib/find/images/next-inverse.svg deleted file mode 100644 index 50482917af..0000000000 --- a/src/vs/editor/contrib/find/images/next-inverse.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/next.svg b/src/vs/editor/contrib/find/images/next.svg deleted file mode 100644 index a2a011453a..0000000000 --- a/src/vs/editor/contrib/find/images/next.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/previous-inverse.svg b/src/vs/editor/contrib/find/images/previous-inverse.svg deleted file mode 100644 index 8ff41da5da..0000000000 --- a/src/vs/editor/contrib/find/images/previous-inverse.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/previous.svg b/src/vs/editor/contrib/find/images/previous.svg deleted file mode 100644 index 3c8b367a93..0000000000 --- a/src/vs/editor/contrib/find/images/previous.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/images/replace-all-dark.svg b/src/vs/editor/contrib/find/images/replace-all-dark.svg new file mode 100644 index 0000000000..07bd41a789 --- /dev/null +++ b/src/vs/editor/contrib/find/images/replace-all-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/replace-all-inverse.svg b/src/vs/editor/contrib/find/images/replace-all-inverse.svg deleted file mode 100644 index 45312b6089..0000000000 --- a/src/vs/editor/contrib/find/images/replace-all-inverse.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/src/vs/editor/contrib/find/images/replace-all-light.svg b/src/vs/editor/contrib/find/images/replace-all-light.svg new file mode 100644 index 0000000000..cd3974fae7 --- /dev/null +++ b/src/vs/editor/contrib/find/images/replace-all-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/replace-all.svg b/src/vs/editor/contrib/find/images/replace-all.svg deleted file mode 100644 index 4254f7c6d1..0000000000 --- a/src/vs/editor/contrib/find/images/replace-all.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/src/vs/editor/contrib/find/images/replace-dark.svg b/src/vs/editor/contrib/find/images/replace-dark.svg new file mode 100644 index 0000000000..5882b22c58 --- /dev/null +++ b/src/vs/editor/contrib/find/images/replace-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/replace-inverse.svg b/src/vs/editor/contrib/find/images/replace-inverse.svg deleted file mode 100644 index 9a59e26378..0000000000 --- a/src/vs/editor/contrib/find/images/replace-inverse.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/src/vs/editor/contrib/find/images/replace-light.svg b/src/vs/editor/contrib/find/images/replace-light.svg new file mode 100644 index 0000000000..220f2aba40 --- /dev/null +++ b/src/vs/editor/contrib/find/images/replace-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/replace.svg b/src/vs/editor/contrib/find/images/replace.svg deleted file mode 100644 index 8b1eb0de23..0000000000 --- a/src/vs/editor/contrib/find/images/replace.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/src/vs/editor/contrib/find/images/tree-collapsed-dark.svg b/src/vs/editor/contrib/find/images/tree-collapsed-dark.svg new file mode 100644 index 0000000000..17de497f33 --- /dev/null +++ b/src/vs/editor/contrib/find/images/tree-collapsed-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/tree-collapsed-light.svg b/src/vs/editor/contrib/find/images/tree-collapsed-light.svg new file mode 100644 index 0000000000..296499b8e5 --- /dev/null +++ b/src/vs/editor/contrib/find/images/tree-collapsed-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/tree-expanded-dark.svg b/src/vs/editor/contrib/find/images/tree-expanded-dark.svg new file mode 100644 index 0000000000..a1df6a8d44 --- /dev/null +++ b/src/vs/editor/contrib/find/images/tree-expanded-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/images/tree-expanded-light.svg b/src/vs/editor/contrib/find/images/tree-expanded-light.svg new file mode 100644 index 0000000000..e60e357f57 --- /dev/null +++ b/src/vs/editor/contrib/find/images/tree-expanded-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/find/simpleFindWidget.css b/src/vs/editor/contrib/find/simpleFindWidget.css index 9ae6b32803..22a5dd741e 100644 --- a/src/vs/editor/contrib/find/simpleFindWidget.css +++ b/src/vs/editor/contrib/find/simpleFindWidget.css @@ -52,25 +52,25 @@ } .monaco-workbench .simple-find-part .button.previous { - background-image: url('images/previous.svg'); + background-image: url('images/chevron-previous-light.svg'); } .monaco-workbench .simple-find-part .button.next { - background-image: url('images/next.svg'); + background-image: url('images/chevron-next-light.svg'); } .monaco-workbench .simple-find-part .button.close-fw { - background-image: url('images/close.svg'); + background-image: url('images/close-light.svg'); } .hc-black .monaco-workbench .simple-find-part .button.previous, .vs-dark .monaco-workbench .simple-find-part .button.previous { - background-image: url('images/previous-inverse.svg'); + background-image: url('images/chevron-previous-dark.svg'); } .hc-black .monaco-workbench .simple-find-part .button.next, .vs-dark .monaco-workbench .simple-find-part .button.next { - background-image: url('images/next-inverse.svg'); + background-image: url('images/chevron-next-dark.svg'); } .hc-black .monaco-workbench .simple-find-part .button.close-fw, @@ -78,7 +78,7 @@ background-image: url('images/close-dark.svg'); } -monaco-workbench .simple-find-part .button.disabled { +.monaco-workbench .simple-find-part .button.disabled { opacity: 0.3; cursor: default; } \ No newline at end of file diff --git a/src/vs/editor/contrib/find/simpleFindWidget.ts b/src/vs/editor/contrib/find/simpleFindWidget.ts index 61cbcdc127..954cdf81b2 100644 --- a/src/vs/editor/contrib/find/simpleFindWidget.ts +++ b/src/vs/editor/contrib/find/simpleFindWidget.ts @@ -33,6 +33,9 @@ export abstract class SimpleFindWidget extends Widget { private readonly _focusTracker: dom.IFocusTracker; private readonly _findInputFocusTracker: dom.IFocusTracker; private readonly _updateHistoryDelayer: Delayer; + private prevBtn: SimpleButton; + private nextBtn: SimpleButton; + private foundMatch: boolean; constructor( @IContextViewService private readonly _contextViewService: IContextViewService, @@ -54,6 +57,8 @@ export abstract class SimpleFindWidget extends Widget { new RegExp(value); return null; } catch (e) { + this.foundMatch = false; + this._updateButtons(); return { content: e.message }; } } @@ -63,7 +68,8 @@ export abstract class SimpleFindWidget extends Widget { this._updateHistoryDelayer = new Delayer(500); this.oninput(this._findInput.domNode, (e) => { - this.onInputChanged(); + this.foundMatch = this.onInputChanged(); + this._updateButtons(); this._delayedUpdateHistory(); }); @@ -99,35 +105,35 @@ export abstract class SimpleFindWidget extends Widget { } })); - const prevBtn = new SimpleButton({ + this.prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL, className: 'previous', onTrigger: () => { this.find(true); } - }); + })); - const nextBtn = new SimpleButton({ + this.nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL, className: 'next', onTrigger: () => { this.find(false); } - }); + })); - const closeBtn = new SimpleButton({ + const closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL, className: 'close-fw', onTrigger: () => { this.hide(); } - }); + })); this._innerDomNode = document.createElement('div'); this._innerDomNode.classList.add('simple-find-part'); this._innerDomNode.appendChild(this._findInput.domNode); - this._innerDomNode.appendChild(prevBtn.domNode); - this._innerDomNode.appendChild(nextBtn.domNode); + this._innerDomNode.appendChild(this.prevBtn.domNode); + this._innerDomNode.appendChild(this.nextBtn.domNode); this._innerDomNode.appendChild(closeBtn.domNode); // _domNode wraps _innerDomNode, ensuring that @@ -156,7 +162,7 @@ export abstract class SimpleFindWidget extends Widget { })); } - protected abstract onInputChanged(): void; + protected abstract onInputChanged(): boolean; protected abstract find(previous: boolean): void; protected abstract onFocusTrackerFocus(): void; protected abstract onFocusTrackerBlur(): void; @@ -213,6 +219,7 @@ export abstract class SimpleFindWidget extends Widget { } this._isVisible = true; + this._updateButtons(); setTimeout(() => { dom.addClass(this._innerDomNode, 'visible'); @@ -243,6 +250,7 @@ export abstract class SimpleFindWidget extends Widget { // Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list setTimeout(() => { this._isVisible = false; + this._updateButtons(); dom.removeClass(this._innerDomNode, 'visible'); }, 200); } @@ -267,6 +275,12 @@ export abstract class SimpleFindWidget extends Widget { protected _getCaseSensitiveValue(): boolean { return this._findInput.getCaseSensitive(); } + + private _updateButtons() { + let hasInput = this.inputValue.length > 0; + this.prevBtn.setEnabled(this._isVisible && hasInput && this.foundMatch); + this.nextBtn.setEnabled(this._isVisible && hasInput && this.foundMatch); + } } // theming diff --git a/src/vs/editor/contrib/folding/arrow-collapse-dark.svg b/src/vs/editor/contrib/folding/arrow-collapse-dark.svg deleted file mode 100644 index 1d7ce3b6bc..0000000000 --- a/src/vs/editor/contrib/folding/arrow-collapse-dark.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/arrow-collapse.svg b/src/vs/editor/contrib/folding/arrow-collapse.svg deleted file mode 100644 index 9e6896640c..0000000000 --- a/src/vs/editor/contrib/folding/arrow-collapse.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/arrow-expand-dark.svg b/src/vs/editor/contrib/folding/arrow-expand-dark.svg deleted file mode 100644 index 4d1a5ca84d..0000000000 --- a/src/vs/editor/contrib/folding/arrow-expand-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/arrow-expand.svg b/src/vs/editor/contrib/folding/arrow-expand.svg deleted file mode 100644 index f1472e2751..0000000000 --- a/src/vs/editor/contrib/folding/arrow-expand.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/folding.css b/src/vs/editor/contrib/folding/folding.css index e65f94b70e..c6ac58bf05 100644 --- a/src/vs/editor/contrib/folding/folding.css +++ b/src/vs/editor/contrib/folding/folding.css @@ -14,12 +14,16 @@ } .monaco-editor .margin-view-overlays .folding { - background-image: url('arrow-expand.svg'); + background-image: url('tree-expanded-light.svg'); } .monaco-editor.hc-black .margin-view-overlays .folding, .monaco-editor.vs-dark .margin-view-overlays .folding { - background-image: url('arrow-expand-dark.svg'); + background-image: url('tree-expanded-dark.svg'); +} + +.monaco-editor.hc-black .margin-view-overlays .folding { + background-image: url('tree-expanded-hc.svg'); } .monaco-editor .margin-view-overlays:hover .folding, @@ -28,13 +32,16 @@ } .monaco-editor .margin-view-overlays .folding.collapsed { - background-image: url('arrow-collapse.svg'); + background-image: url('tree-collapsed-light.svg'); opacity: 1; } -.monaco-editor.hc-black .margin-view-overlays .folding.collapsed, .monaco-editor.vs-dark .margin-view-overlays .folding.collapsed { - background-image: url('arrow-collapse-dark.svg'); + background-image: url('tree-collapsed-dark.svg'); +} + +.monaco-editor.hc-black .margin-view-overlays .folding.collapsed { + background-image: url('tree-collapsed-hc.svg'); } .monaco-editor .inline-folded:after { diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 9ec22649c4..db1b60fe85 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -9,7 +9,7 @@ import * as types from 'vs/base/common/types'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { RunOnceScheduler, Delayer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions'; @@ -31,7 +31,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { InitializingRangeProvider, ID_INIT_PROVIDER } from 'vs/editor/contrib/folding/intializingRangeProvider'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +const CONTEXT_FOLDING_ENABLED = new RawContextKey('foldingEnabled', false); export const ID = 'editor.contrib.folding'; export interface RangeProvider { @@ -46,7 +48,7 @@ interface FoldingStateMemento { provider?: string; } -export class FoldingController implements IEditorContribution { +export class FoldingController extends Disposable implements IEditorContribution { static MAX_FOLDING_REGIONS = 5000; @@ -73,30 +75,34 @@ export class FoldingController implements IEditorContribution { private foldingModelPromise: Promise | null; private updateScheduler: Delayer | null; - private globalToDispose: IDisposable[]; - + private foldingEnabled: IContextKey; private cursorChangedScheduler: RunOnceScheduler | null; - private localToDispose: IDisposable[]; + private readonly localToDispose = this._register(new DisposableStore()); - constructor(editor: ICodeEditor) { + constructor( + editor: ICodeEditor, + @IContextKeyService private readonly contextKeyService: IContextKeyService + ) { + super(); this.editor = editor; this._isEnabled = this.editor.getConfiguration().contribInfo.folding; this._autoHideFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls === 'mouseover'; this._useFoldingProviders = this.editor.getConfiguration().contribInfo.foldingStrategy !== 'indentation'; - this.globalToDispose = []; - this.localToDispose = []; this.foldingDecorationProvider = new FoldingDecorationProvider(editor); this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls; + this.foldingEnabled = CONTEXT_FOLDING_ENABLED.bindTo(this.contextKeyService); + this.foldingEnabled.set(this._isEnabled); - this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged())); + this._register(this.editor.onDidChangeModel(() => this.onModelChanged())); - this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { + this._register(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { if (e.contribInfo) { let oldIsEnabled = this._isEnabled; this._isEnabled = this.editor.getConfiguration().contribInfo.folding; + this.foldingEnabled.set(this._isEnabled); if (oldIsEnabled !== this._isEnabled) { this.onModelChanged(); } @@ -113,7 +119,6 @@ export class FoldingController implements IEditorContribution { } } })); - this.globalToDispose.push({ dispose: () => dispose(this.localToDispose) }); this.onModelChanged(); } @@ -121,10 +126,6 @@ export class FoldingController implements IEditorContribution { return ID; } - public dispose(): void { - this.globalToDispose = dispose(this.globalToDispose); - } - /** * Store view state. */ @@ -173,7 +174,7 @@ export class FoldingController implements IEditorContribution { } private onModelChanged(): void { - this.localToDispose = dispose(this.localToDispose); + this.localToDispose.clear(); let model = this.editor.getModel(); if (!this._isEnabled || !model || model.isTooLargeForTokenization()) { @@ -182,23 +183,23 @@ export class FoldingController implements IEditorContribution { } this.foldingModel = new FoldingModel(model, this.foldingDecorationProvider); - this.localToDispose.push(this.foldingModel); + this.localToDispose.add(this.foldingModel); this.hiddenRangeModel = new HiddenRangeModel(this.foldingModel); - this.localToDispose.push(this.hiddenRangeModel); - this.localToDispose.push(this.hiddenRangeModel.onDidChange(hr => this.onHiddenRangesChanges(hr))); + this.localToDispose.add(this.hiddenRangeModel); + this.localToDispose.add(this.hiddenRangeModel.onDidChange(hr => this.onHiddenRangesChanges(hr))); this.updateScheduler = new Delayer(200); this.cursorChangedScheduler = new RunOnceScheduler(() => this.revealCursor(), 200); - this.localToDispose.push(this.cursorChangedScheduler); - this.localToDispose.push(FoldingRangeProviderRegistry.onDidChange(() => this.onFoldingStrategyChanged())); - this.localToDispose.push(this.editor.onDidChangeModelLanguageConfiguration(() => this.onFoldingStrategyChanged())); // covers model language changes as well - this.localToDispose.push(this.editor.onDidChangeModelContent(() => this.onModelContentChanged())); - this.localToDispose.push(this.editor.onDidChangeCursorPosition(() => this.onCursorPositionChanged())); - this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); - this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); - this.localToDispose.push({ + this.localToDispose.add(this.cursorChangedScheduler); + this.localToDispose.add(FoldingRangeProviderRegistry.onDidChange(() => this.onFoldingStrategyChanged())); + this.localToDispose.add(this.editor.onDidChangeModelLanguageConfiguration(() => this.onFoldingStrategyChanged())); // covers model language changes as well + this.localToDispose.add(this.editor.onDidChangeModelContent(() => this.onModelContentChanged())); + this.localToDispose.add(this.editor.onDidChangeCursorPosition(() => this.onCursorPositionChanged())); + this.localToDispose.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); + this.localToDispose.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); + this.localToDispose.add({ dispose: () => { if (this.foldingRegionPromise) { this.foldingRegionPromise.cancel(); @@ -503,7 +504,7 @@ class UnfoldAction extends FoldingAction { id: 'editor.unfold', label: nls.localize('unfoldAction.label', "Unfold"), alias: 'Unfold', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, @@ -567,7 +568,7 @@ class UnFoldRecursivelyAction extends FoldingAction { id: 'editor.unfoldRecursively', label: nls.localize('unFoldRecursivelyAction.label', "Unfold Recursively"), alias: 'Unfold Recursively', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET), @@ -588,7 +589,7 @@ class FoldAction extends FoldingAction { id: 'editor.fold', label: nls.localize('foldAction.label', "Fold"), alias: 'Fold', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, @@ -652,7 +653,7 @@ class FoldRecursivelyAction extends FoldingAction { id: 'editor.foldRecursively', label: nls.localize('foldRecursivelyAction.label', "Fold Recursively"), alias: 'Fold Recursively', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET), @@ -674,7 +675,7 @@ class FoldAllBlockCommentsAction extends FoldingAction { id: 'editor.foldAllBlockComments', label: nls.localize('foldAllBlockComments.label', "Fold All Block Comments"), alias: 'Fold All Block Comments', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH), @@ -707,7 +708,7 @@ class FoldAllRegionsAction extends FoldingAction { id: 'editor.foldAllMarkerRegions', label: nls.localize('foldAllMarkerRegions.label', "Fold All Regions"), alias: 'Fold All Regions', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8), @@ -740,7 +741,7 @@ class UnfoldAllRegionsAction extends FoldingAction { id: 'editor.unfoldAllMarkerRegions', label: nls.localize('unfoldAllMarkerRegions.label', "Unfold All Regions"), alias: 'Unfold All Regions', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9), @@ -773,7 +774,7 @@ class FoldAllAction extends FoldingAction { id: 'editor.foldAll', label: nls.localize('foldAllAction.label', "Fold All"), alias: 'Fold All', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0), @@ -794,7 +795,7 @@ class UnfoldAllAction extends FoldingAction { id: 'editor.unfoldAll', label: nls.localize('unfoldAllAction.label', "Unfold All"), alias: 'Unfold All', - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J), @@ -838,7 +839,7 @@ for (let i = 1; i <= 7; i++) { id: FoldLevelAction.ID(i), label: nls.localize('foldLevelAction.label', "Fold Level {0}", i), alias: `Fold Level ${i}`, - precondition: undefined, + precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i)), diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index 024b311b91..77cbcfd0ec 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -29,9 +29,9 @@ export class FoldingModel { private _isInitialized: boolean; private _updateEventEmitter = new Emitter(); + public readonly onDidChange: Event = this._updateEventEmitter.event; public get regions(): FoldingRegions { return this._regions; } - public get onDidChange(): Event { return this._updateEventEmitter.event; } public get textModel() { return this._textModel; } public get isInitialized() { return this._isInitialized; } @@ -47,7 +47,7 @@ export class FoldingModel { if (!regions.length) { return; } - let processed = {}; + let processed: { [key: string]: boolean | undefined } = {}; this._decorationProvider.changeDecorations(accessor => { for (let region of regions) { let index = region.regionIndex; diff --git a/src/vs/editor/contrib/folding/tree-collapsed-dark.svg b/src/vs/editor/contrib/folding/tree-collapsed-dark.svg new file mode 100644 index 0000000000..243be1451c --- /dev/null +++ b/src/vs/editor/contrib/folding/tree-collapsed-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/folding/tree-collapsed-hc.svg b/src/vs/editor/contrib/folding/tree-collapsed-hc.svg new file mode 100644 index 0000000000..40ba72b708 --- /dev/null +++ b/src/vs/editor/contrib/folding/tree-collapsed-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/folding/tree-collapsed-light.svg b/src/vs/editor/contrib/folding/tree-collapsed-light.svg new file mode 100644 index 0000000000..0d746558a4 --- /dev/null +++ b/src/vs/editor/contrib/folding/tree-collapsed-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/folding/tree-expanded-dark.svg b/src/vs/editor/contrib/folding/tree-expanded-dark.svg new file mode 100644 index 0000000000..5570923e17 --- /dev/null +++ b/src/vs/editor/contrib/folding/tree-expanded-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/folding/tree-expanded-hc.svg b/src/vs/editor/contrib/folding/tree-expanded-hc.svg new file mode 100644 index 0000000000..b370009330 --- /dev/null +++ b/src/vs/editor/contrib/folding/tree-expanded-hc.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/folding/tree-expanded-light.svg b/src/vs/editor/contrib/folding/tree-expanded-light.svg new file mode 100644 index 0000000000..939ebc8b96 --- /dev/null +++ b/src/vs/editor/contrib/folding/tree-expanded-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index b363cba00f..27787975d4 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -6,7 +6,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -30,18 +30,18 @@ class FormatOnType implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.autoFormat'; private readonly _editor: ICodeEditor; - private _callOnDispose: IDisposable[] = []; - private _callOnModel: IDisposable[] = []; + private readonly _callOnDispose = new DisposableStore(); + private readonly _callOnModel = new DisposableStore(); constructor( editor: ICodeEditor, @IEditorWorkerService private readonly _workerService: IEditorWorkerService ) { this._editor = editor; - this._callOnDispose.push(editor.onDidChangeConfiguration(() => this._update())); - this._callOnDispose.push(editor.onDidChangeModel(() => this._update())); - this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this._update())); - this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this._update, this)); + this._callOnDispose.add(editor.onDidChangeConfiguration(() => this._update())); + this._callOnDispose.add(editor.onDidChangeModel(() => this._update())); + this._callOnDispose.add(editor.onDidChangeModelLanguage(() => this._update())); + this._callOnDispose.add(OnTypeFormattingEditProviderRegistry.onDidChange(this._update, this)); } getId(): string { @@ -49,14 +49,14 @@ class FormatOnType implements editorCommon.IEditorContribution { } dispose(): void { - this._callOnDispose = dispose(this._callOnDispose); - this._callOnModel = dispose(this._callOnModel); + this._callOnDispose.dispose(); + this._callOnModel.dispose(); } private _update(): void { // clean up - this._callOnModel = dispose(this._callOnModel); + this._callOnModel.clear(); // we are disabled if (!this._editor.getConfiguration().contribInfo.formatOnType) { @@ -81,7 +81,7 @@ class FormatOnType implements editorCommon.IEditorContribution { for (let ch of support.autoFormatTriggerCharacters) { triggerChars.add(ch.charCodeAt(0)); } - this._callOnModel.push(this._editor.onDidType((text: string) => { + this._callOnModel.add(this._editor.onDidType((text: string) => { let lastCharCode = text.charCodeAt(text.length - 1); if (triggerChars.has(lastCharCode)) { this._trigger(String.fromCharCode(lastCharCode)); @@ -156,20 +156,17 @@ class FormatOnPaste implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.formatOnPaste'; - private _callOnDispose: IDisposable[]; - private _callOnModel: IDisposable[]; + private readonly _callOnDispose = new DisposableStore(); + private readonly _callOnModel = new DisposableStore(); constructor( private readonly editor: ICodeEditor, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { - this._callOnDispose = []; - this._callOnModel = []; - - this._callOnDispose.push(editor.onDidChangeConfiguration(() => this._update())); - this._callOnDispose.push(editor.onDidChangeModel(() => this._update())); - this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this._update())); - this._callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this._update, this)); + this._callOnDispose.add(editor.onDidChangeConfiguration(() => this._update())); + this._callOnDispose.add(editor.onDidChangeModel(() => this._update())); + this._callOnDispose.add(editor.onDidChangeModelLanguage(() => this._update())); + this._callOnDispose.add(DocumentRangeFormattingEditProviderRegistry.onDidChange(this._update, this)); } getId(): string { @@ -177,14 +174,14 @@ class FormatOnPaste implements editorCommon.IEditorContribution { } dispose(): void { - this._callOnDispose = dispose(this._callOnDispose); - this._callOnModel = dispose(this._callOnModel); + this._callOnDispose.dispose(); + this._callOnModel.dispose(); } private _update(): void { // clean up - this._callOnModel = dispose(this._callOnModel); + this._callOnModel.clear(); // we are disabled if (!this.editor.getConfiguration().contribInfo.formatOnPaste) { @@ -201,7 +198,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { return; } - this._callOnModel.push(this.editor.onDidPaste(range => this._trigger(range))); + this._callOnModel.add(this.editor.onDidPaste(range => this._trigger(range))); } private _trigger(range: Range): void { @@ -251,7 +248,7 @@ class FormatSelectionAction extends EditorAction { super({ id: 'editor.action.formatSelection', label: nls.localize('formatSelection.label', "Format Selection"), - alias: 'Format Code', + alias: 'Format Selection', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider), kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentSelectionFormattingProvider), diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 68f2358f4f..3ea69fc591 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -25,7 +25,7 @@ import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition } from './goToDefinition'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { EditorStateCancellationTokenSource, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; @@ -58,7 +58,7 @@ export class DefinitionAction extends EditorAction { } const notificationService = accessor.get(INotificationService); const editorService = accessor.get(ICodeEditorService); - const progressService = accessor.get(IProgressService); + const progressService = accessor.get(IEditorProgressService); const symbolNavService = accessor.get(ISymbolNavigationService); const model = editor.getModel(); @@ -176,7 +176,6 @@ export class DefinitionAction extends EditorAction { resource: reference.uri, options: { selection: Range.collapseToStart(range), - revealIfOpened: true, revealInCenterIfOutsideViewport: true } }, editor, sideBySide); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index f8f3ea32d3..2d522a05a6 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -10,13 +10,13 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { DefinitionProviderRegistry, LocationLink } from 'vs/editor/common/modes'; import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { getDefinitionsAtPosition } from './goToDefinition'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -33,7 +33,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC static MAX_SOURCE_PREVIEW_LINES = 8; private readonly editor: ICodeEditor; - private toUnhook: IDisposable[]; + private readonly toUnhook = new DisposableStore(); private decorations: string[]; private currentWordUnderMouse: IWordAtPosition | null; private previousPromise: CancelablePromise | null; @@ -43,19 +43,18 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC @ITextModelService private readonly textModelResolverService: ITextModelService, @IModeService private readonly modeService: IModeService ) { - this.toUnhook = []; this.decorations = []; this.editor = editor; this.previousPromise = null; let linkGesture = new ClickLinkGesture(editor); - this.toUnhook.push(linkGesture); + this.toUnhook.add(linkGesture); - this.toUnhook.push(linkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { + this.toUnhook.add(linkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { this.startFindDefinition(mouseEvent, withNullAsUndefined(keyboardEvent)); })); - this.toUnhook.push(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => { + this.toUnhook.add(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => { if (this.isEnabled(mouseEvent)) { this.gotoDefinition(mouseEvent.target, mouseEvent.hasSideBySideModifier).then(() => { this.removeDecorations(); @@ -66,7 +65,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC } })); - this.toUnhook.push(linkGesture.onCancel(() => { + this.toUnhook.add(linkGesture.onCancel(() => { this.removeDecorations(); this.currentWordUnderMouse = null; })); @@ -150,7 +149,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return; } - const previewValue = this.getPreviewValue(textEditorModel, startLineNumber); + const previewValue = this.getPreviewValue(textEditorModel, startLineNumber, result); let wordRange: Range; if (result.originSelectionRange) { @@ -159,7 +158,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC wordRange = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); } - const modeId = this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri.fsPath); + const modeId = this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri); this.addDecoration( wordRange, new MarkdownString().appendCodeblock(modeId ? modeId : '', previewValue) @@ -170,8 +169,8 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC }).then(undefined, onUnexpectedError); } - private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number) { - let rangeToUse = this.getPreviewRangeBasedOnBrackets(textEditorModel, startLineNumber); + private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number, result: LocationLink) { + let rangeToUse = result.targetSelectionRange ? result.range : this.getPreviewRangeBasedOnBrackets(textEditorModel, startLineNumber); const numberOfLinesInRange = rangeToUse.endLineNumber - rangeToUse.startLineNumber; if (numberOfLinesInRange >= GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES) { rangeToUse = this.getPreviewRangeBasedOnIndentation(textEditorModel, startLineNumber); @@ -181,7 +180,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return previewValue; } - private stripIndentationFromPreviewRange(textEditorModel: ITextModel, startLineNumber: number, previewRange: Range) { + private stripIndentationFromPreviewRange(textEditorModel: ITextModel, startLineNumber: number, previewRange: IRange) { const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber); let minIndent = startIndent; @@ -303,7 +302,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC } public dispose(): void { - this.toUnhook = dispose(this.toUnhook); + this.toUnhook.dispose(); } } diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts index b3d90178f0..ef5057bd51 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionResultsNavigation.ts @@ -13,11 +13,11 @@ import { registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorEx import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; -import { Disposable, dispose, combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export const ctxHasSymbols = new RawContextKey('hasSymbols', false); @@ -38,14 +38,14 @@ class SymbolNavigationService implements ISymbolNavigationService { private _currentModel?: ReferencesModel = undefined; private _currentIdx: number = -1; - private _currentDisposables: IDisposable[] = []; - private _currentMessage?: IDisposable = undefined; + private _currentState?: IDisposable; + private _currentMessage?: IDisposable; private _ignoreEditorChange: boolean = false; constructor( @IContextKeyService contextKeyService: IContextKeyService, @ICodeEditorService private readonly _editorService: ICodeEditorService, - @IStatusbarService private readonly _statusbarService: IStatusbarService, + @INotificationService private readonly _notificationService: INotificationService, @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { this._ctxHasSymbols = ctxHasSymbols.bindTo(contextKeyService); @@ -53,7 +53,7 @@ class SymbolNavigationService implements ISymbolNavigationService { reset(): void { this._ctxHasSymbols.reset(); - dispose(this._currentDisposables); + dispose(this._currentState); dispose(this._currentMessage); this._currentModel = undefined; this._currentIdx = -1; @@ -72,8 +72,8 @@ class SymbolNavigationService implements ISymbolNavigationService { this._ctxHasSymbols.set(true); this._showMessage(); - const editorStatus = new EditorStatus(this._editorService); - const listener = editorStatus.onDidChange(_ => { + const editorState = new EditorState(this._editorService); + const listener = editorState.onDidChange(_ => { if (this._ignoreEditorChange) { return; @@ -104,7 +104,7 @@ class SymbolNavigationService implements ISymbolNavigationService { } }); - this._currentDisposables = [editorStatus, listener]; + this._currentState = combinedDisposable(editorState, listener); } revealNext(source: ICodeEditor): Promise { @@ -126,8 +126,7 @@ class SymbolNavigationService implements ISymbolNavigationService { resource: reference.uri, options: { selection: Range.collapseToStart(reference.range), - revealInCenterIfOutsideViewport: true, - revealIfOpened: true + revealInCenterIfOutsideViewport: true } }, source).finally(() => { this._ignoreEditorChange = false; @@ -141,10 +140,10 @@ class SymbolNavigationService implements ISymbolNavigationService { const kb = this._keybindingService.lookupKeybinding('editor.gotoNextSymbolFromResult'); const message = kb - ? localize('location.kb', "Symbol {0} of {1}, press {2} to reveal next", this._currentIdx + 1, this._currentModel!.references.length, kb.getLabel()) + ? localize('location.kb', "Symbol {0} of {1}, {2} for next", this._currentIdx + 1, this._currentModel!.references.length, kb.getLabel()) : localize('location', "Symbol {0} of {1}", this._currentIdx + 1, this._currentModel!.references.length); - this._currentMessage = this._statusbarService.setStatusMessage(message); + this._currentMessage = this._notificationService.status(message); } } @@ -183,26 +182,31 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // -class EditorStatus extends Disposable { +class EditorState { private readonly _listener = new Map(); + private readonly _disposables = new DisposableStore(); private readonly _onDidChange = new Emitter<{ editor: ICodeEditor }>(); readonly onDidChange: Event<{ editor: ICodeEditor }> = this._onDidChange.event; constructor(@ICodeEditorService editorService: ICodeEditorService) { - super(); - this._register(this._onDidChange); - this._register(editorService.onCodeEditorRemove(this._onDidRemoveEditor, this)); - this._register(editorService.onCodeEditorAdd(this._onDidAddEditor, this)); + this._disposables.add(editorService.onCodeEditorRemove(this._onDidRemoveEditor, this)); + this._disposables.add(editorService.onCodeEditorAdd(this._onDidAddEditor, this)); editorService.listCodeEditors().forEach(this._onDidAddEditor, this); } + dispose(): void { + this._disposables.dispose(); + this._onDidChange.dispose(); + this._listener.forEach(dispose); + } + private _onDidAddEditor(editor: ICodeEditor): void { - this._listener.set(editor, combinedDisposable([ + this._listener.set(editor, combinedDisposable( editor.onDidChangeCursorPosition(_ => this._onDidChange.fire({ editor })), editor.onDidChangeModelContent(_ => this._onDidChange.fire({ editor })), - ])); + )); } private _onDidRemoveEditor(editor: ICodeEditor): void { diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 23a3f009c1..ee221e2732 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; @@ -206,7 +206,7 @@ export class MarkerController implements editorCommon.IEditorContribution { private _model: MarkerModel | null = null; private _widget: MarkerNavigationWidget | null = null; private readonly _widgetVisible: IContextKey; - private _disposeOnClose: IDisposable[] = []; + private readonly _disposeOnClose = new DisposableStore(); constructor( editor: ICodeEditor, @@ -226,11 +226,12 @@ export class MarkerController implements editorCommon.IEditorContribution { public dispose(): void { this._cleanUp(); + this._disposeOnClose.dispose(); } private _cleanUp(): void { this._widgetVisible.reset(); - this._disposeOnClose = dispose(this._disposeOnClose); + this._disposeOnClose.clear(); this._widget = null; this._model = null; } @@ -255,19 +256,21 @@ export class MarkerController implements editorCommon.IEditorContribution { this._widgetVisible.set(true); this._widget.onDidClose(() => this._cleanUp(), this, this._disposeOnClose); - this._disposeOnClose.push(this._model); - this._disposeOnClose.push(this._widget); - this._disposeOnClose.push(...actions); - this._disposeOnClose.push(this._widget.onDidSelectRelatedInformation(related => { + this._disposeOnClose.add(this._model); + this._disposeOnClose.add(this._widget); + for (const action of actions) { + this._disposeOnClose.add(action); + } + this._disposeOnClose.add(this._widget.onDidSelectRelatedInformation(related => { this._editorService.openCodeEditor({ resource: related.resource, options: { pinned: true, revealIfOpened: true, selection: Range.lift(related).collapseToStart() } }, this._editor).then(undefined, onUnexpectedError); this.closeMarkersNavigation(false); })); - this._disposeOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp())); + this._disposeOnClose.add(this._editor.onDidChangeModel(() => this._cleanUp())); - this._disposeOnClose.push(this._model.onCurrentMarkerChanged(marker => { + this._disposeOnClose.add(this._model.onCurrentMarkerChanged(marker => { if (!marker || !this._model) { this._cleanUp(); } else { @@ -279,7 +282,7 @@ export class MarkerController implements editorCommon.IEditorContribution { }); } })); - this._disposeOnClose.push(this._model.onMarkerSetChanged(() => { + this._disposeOnClose.add(this._model.onMarkerSetChanged(() => { if (!this._widget || !this._widget.position || !this._model) { return; } @@ -428,7 +431,7 @@ export class NextMarkerAction extends MarkerNavigationAction { super(true, false, { id: NextMarkerAction.ID, label: NextMarkerAction.LABEL, - alias: 'Go to Next Error or Warning', + alias: 'Go to Next Problem (Error, Warning, Info)', precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib } }); @@ -442,7 +445,7 @@ class PrevMarkerAction extends MarkerNavigationAction { super(false, false, { id: PrevMarkerAction.ID, label: PrevMarkerAction.LABEL, - alias: 'Go to Previous Error or Warning', + alias: 'Go to Previous Problem (Error, Warning, Info)', precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib } }); @@ -454,7 +457,7 @@ class NextMarkerInFilesAction extends MarkerNavigationAction { super(true, true, { id: 'editor.action.marker.nextInFiles', label: nls.localize('markerAction.nextInFiles.label', "Go to Next Problem in Files (Error, Warning, Info)"), - alias: 'Go to Next Error or Warning in Files', + alias: 'Go to Next Problem in Files (Error, Warning, Info)', precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.focus, @@ -470,7 +473,7 @@ class PrevMarkerInFilesAction extends MarkerNavigationAction { super(false, true, { id: 'editor.action.marker.prevInFiles', label: nls.localize('markerAction.previousInFiles.label', "Go to Previous Problem in Files (Error, Warning, Info)"), - alias: 'Go to Previous Error or Warning in Files', + alias: 'Go to Previous Problem in Files (Error, Warning, Info)', precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.focus, diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 9ed7491b50..897e5cfd4c 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -6,15 +6,14 @@ import 'vs/css!./media/gotoErrorWidget'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerColor, oneOf, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, oneOf, textLinkForeground, editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; -import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/editor/common/view/editorColorRegistry'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ScrollType } from 'vs/editor/common/editorCommon'; @@ -26,7 +25,7 @@ import { basename } from 'vs/base/common/resources'; import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; -import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; class MessageWidget { @@ -168,8 +167,9 @@ export class MarkerNavigationWidget extends PeekViewWidget { private _parentContainer: HTMLElement; private _container: HTMLElement; + private _icon: HTMLElement; private _message: MessageWidget; - private _callOnDispose: IDisposable[] = []; + private readonly _callOnDispose = new DisposableStore(); private _severity: MarkerSeverity; private _backgroundColor?: Color; private _onDidSelectRelatedInformation = new Emitter(); @@ -179,7 +179,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { constructor( editor: ICodeEditor, - private readonly actions: IAction[], + private readonly actions: ReadonlyArray, private readonly _themeService: IThemeService ) { super(editor, { showArrow: true, showFrame: true, isAccessible: true }); @@ -187,7 +187,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { this._backgroundColor = Color.white; this._applyTheme(_themeService.getTheme()); - this._callOnDispose.push(_themeService.onThemeChange(this._applyTheme.bind(this))); + this._callOnDispose.add(_themeService.onThemeChange(this._applyTheme.bind(this))); this.create(); } @@ -218,7 +218,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { } dispose(): void { - this._callOnDispose = dispose(this._callOnDispose); + this._callOnDispose.dispose(); super.dispose(); } @@ -231,6 +231,10 @@ export class MarkerNavigationWidget extends PeekViewWidget { this._actionbarWidget.push(this.actions, { label: false, icon: true }); } + protected _fillTitleIcon(container: HTMLElement): void { + this._icon = dom.append(container, dom.$('')); + } + protected _getActionBarOptions(): IActionBarOptions { return { orientation: ActionsOrientation.HORIZONTAL_REVERSE @@ -247,7 +251,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { container.appendChild(this._container); this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related)); - this._disposables.push(this._message); + this._disposables.add(this._message); } show(where: Position, heightInLines: number): void { @@ -278,19 +282,9 @@ export class MarkerNavigationWidget extends PeekViewWidget { : nls.localize('change', "{0} of {1} problem", markerIdx, markerCount); this.setTitle(basename(model.uri), detail); } - let headingIconClassName = 'error'; - if (this._severity === MarkerSeverity.Warning) { - headingIconClassName = 'warning'; - } else if (this._severity === MarkerSeverity.Info) { - headingIconClassName = 'info'; - } - this.setTitleIcon(headingIconClassName); + this._icon.className = SeverityIcon.className(MarkerSeverity.toSeverity(this._severity)); this.editor.revealPositionInCenter(position, ScrollType.Smooth); - - if (this.editor.getConfiguration().accessibilitySupport !== AccessibilitySupport.Disabled) { - this.focus(); - } } updateMarker(marker: IMarker): void { diff --git a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css index d24b98149f..1a0be0be1a 100644 --- a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css +++ b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css @@ -5,28 +5,10 @@ /* marker zone */ -.monaco-editor .peekview-widget .head .peekview-title .icon.warning { - background: url('status-warning.svg') center center no-repeat; -} - -.monaco-editor .peekview-widget .head .peekview-title .icon.error { - background: url('status-error.svg') center center no-repeat; -} - -.monaco-editor .peekview-widget .head .peekview-title .icon.info { - background: url('status-info.svg') center center no-repeat; -} - -.vs-dark .monaco-editor .peekview-widget .head .peekview-title .icon.warning { - background: url('status-warning-inverse.svg') center center no-repeat; -} - -.vs-dark .monaco-editor .peekview-widget .head .peekview-title .icon.error { - background: url('status-error-inverse.svg') center center no-repeat; -} - -.vs-dark .monaco-editor .peekview-widget .head .peekview-title .icon.info { - background: url('status-info-inverse.svg') center center no-repeat; +.monaco-editor .peekview-widget .head .peekview-title .severity-icon { + display: inline-block; + vertical-align: text-top; + margin-right: 4px; } .monaco-editor .marker-widget { diff --git a/src/vs/editor/contrib/gotoError/media/status-error-inverse.svg b/src/vs/editor/contrib/gotoError/media/status-error-inverse.svg deleted file mode 100644 index 3c852a7ffd..0000000000 --- a/src/vs/editor/contrib/gotoError/media/status-error-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/gotoError/media/status-error.svg b/src/vs/editor/contrib/gotoError/media/status-error.svg deleted file mode 100644 index a1ddb39fed..0000000000 --- a/src/vs/editor/contrib/gotoError/media/status-error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/gotoError/media/status-info-inverse.svg b/src/vs/editor/contrib/gotoError/media/status-info-inverse.svg deleted file mode 100644 index d38c363e0e..0000000000 --- a/src/vs/editor/contrib/gotoError/media/status-info-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/gotoError/media/status-info.svg b/src/vs/editor/contrib/gotoError/media/status-info.svg deleted file mode 100644 index 6e2e22f67b..0000000000 --- a/src/vs/editor/contrib/gotoError/media/status-info.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/gotoError/media/status-warning-inverse.svg b/src/vs/editor/contrib/gotoError/media/status-warning-inverse.svg deleted file mode 100644 index df44e61b32..0000000000 --- a/src/vs/editor/contrib/gotoError/media/status-warning-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/gotoError/media/status-warning.svg b/src/vs/editor/contrib/gotoError/media/status-warning.svg deleted file mode 100644 index f4e2a84b0a..0000000000 --- a/src/vs/editor/contrib/gotoError/media/status-warning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css index ceb23ae42c..a11ede8bf1 100644 --- a/src/vs/editor/contrib/hover/hover.css +++ b/src/vs/editor/contrib/hover/hover.css @@ -29,6 +29,7 @@ .monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) { max-width: 500px; + word-wrap: break-word; } .monaco-editor-hover p, diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index bca65f0fa7..30865a0884 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -7,7 +7,7 @@ import 'vs/css!./hover'; import * as nls from 'vs/nls'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -25,16 +25,13 @@ import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCod import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; export class ModesHoverController implements IEditorContribution { private static readonly ID = 'editor.contrib.hover'; - private _toUnhook: IDisposable[]; + private readonly _toUnhook = new DisposableStore(); private readonly _didChangeConfigurationHandler: IDisposable; private _contentWidget: ModesContentHoverWidget; @@ -68,13 +65,8 @@ export class ModesHoverController implements IEditorContribution { @IModeService private readonly _modeService: IModeService, @IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IBulkEditService private readonly _bulkEditService: IBulkEditService, - @ICommandService private readonly _commandService: ICommandService, @IThemeService private readonly _themeService: IThemeService ) { - this._toUnhook = []; - this._isMouseDown = false; this._hoverClicked = false; @@ -96,22 +88,22 @@ export class ModesHoverController implements IEditorContribution { this._isHoverEnabled = hoverOpts.enabled; this._isHoverSticky = hoverOpts.sticky; if (this._isHoverEnabled) { - this._toUnhook.push(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); - this._toUnhook.push(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); - this._toUnhook.push(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); - this._toUnhook.push(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); - this._toUnhook.push(this._editor.onDidChangeModelDecorations(() => this._onModelDecorationsChanged())); + this._toUnhook.add(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); + this._toUnhook.add(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); + this._toUnhook.add(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); + this._toUnhook.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); + this._toUnhook.add(this._editor.onDidChangeModelDecorations(() => this._onModelDecorationsChanged())); } else { - this._toUnhook.push(this._editor.onMouseMove(hideWidgetsEventHandler)); + this._toUnhook.add(this._editor.onMouseMove(hideWidgetsEventHandler)); } - this._toUnhook.push(this._editor.onMouseLeave(hideWidgetsEventHandler)); - this._toUnhook.push(this._editor.onDidChangeModel(hideWidgetsEventHandler)); - this._toUnhook.push(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e))); + this._toUnhook.add(this._editor.onMouseLeave(hideWidgetsEventHandler)); + this._toUnhook.add(this._editor.onDidChangeModel(hideWidgetsEventHandler)); + this._toUnhook.add(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e))); } private _unhookEvents(): void { - this._toUnhook = dispose(this._toUnhook); + this._toUnhook.clear(); } private _onModelDecorationsChanged(): void { @@ -213,7 +205,7 @@ export class ModesHoverController implements IEditorContribution { } private _createHoverWidget() { - this._contentWidget = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._contextMenuService, this._bulkEditService, this._commandService, this._modeService, this._openerService); + this._contentWidget = new ModesContentHoverWidget(this._editor, this._markerDecorationsService, this._themeService, this._keybindingService, this._modeService, this._openerService); this._glyphWidget = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService); } @@ -227,6 +219,7 @@ export class ModesHoverController implements IEditorContribution { public dispose(): void { this._unhookEvents(); + this._toUnhook.dispose(); this._didChangeConfigurationHandler.dispose(); if (this._glyphWidget) { diff --git a/src/vs/editor/contrib/hover/hoverWidgets.ts b/src/vs/editor/contrib/hover/hoverWidgets.ts index ece230a25c..7ff6243bce 100644 --- a/src/vs/editor/contrib/hover/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/hoverWidgets.ts @@ -8,7 +8,6 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; @@ -25,7 +24,6 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent protected _showAtRange: Range | null; private _stoleFocus: boolean; private readonly scrollbar: DomScrollableElement; - private disposables: IDisposable[] = []; // Editor.IContentWidget.allowEditorOverflow public allowEditorOverflow = true; @@ -53,7 +51,7 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent this._domNode.className = 'monaco-editor-hover-content'; this.scrollbar = new DomScrollableElement(this._domNode, {}); - this.disposables.push(this.scrollbar); + this._register(this.scrollbar); this._containerDomNode.appendChild(this.scrollbar.getDomNode()); this.onkeydown(this._containerDomNode, (e: IKeyboardEvent) => { @@ -129,7 +127,6 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent public dispose(): void { this._editor.removeContentWidget(this); - this.disposables = dispose(this.disposables); super.dispose(); } diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 966de575cc..08412d5b90 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -8,7 +8,7 @@ import * as dom from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent'; -import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, DisposableStore, combinedDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -31,13 +31,9 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/gotoError'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; -import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; -import { applyCodeAction, QuickFixAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { Action } from 'vs/base/common/actions'; +import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; +import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; @@ -200,16 +196,13 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private _shouldFocus: boolean; private _colorPicker: ColorPickerWidget | null; - private renderDisposable: IDisposable = Disposable.None; + private readonly renderDisposable = this._register(new MutableDisposable()); constructor( editor: ICodeEditor, markerDecorationsService: IMarkerDecorationsService, private readonly _themeService: IThemeService, private readonly _keybindingService: IKeybindingService, - private readonly _contextMenuService: IContextMenuService, - private readonly _bulkEditService: IBulkEditService, - private readonly _commandService: ICommandService, private readonly _modeService: IModeService, private readonly _openerService: IOpenerService | null = NullOpenerService, ) { @@ -243,8 +236,6 @@ export class ModesContentHoverWidget extends ContentHoverWidget { } dispose(): void { - this.renderDisposable.dispose(); - this.renderDisposable = Disposable.None; this._hoverOperation.cancel(); super.dispose(); } @@ -312,8 +303,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._isChangingDecorations = true; this._highlightDecorations = this._editor.deltaDecorations(this._highlightDecorations, []); this._isChangingDecorations = false; - this.renderDisposable.dispose(); - this.renderDisposable = Disposable.None; + this.renderDisposable.clear(); this._colorPicker = null; } @@ -345,7 +335,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { let isEmptyHoverContent = true; let containColorPicker = false; - let markdownDisposeables: IDisposable[] = []; + const markdownDisposeables = new DisposableStore(); const markerMessages: MarkerHover[] = []; messages.forEach((msg) => { if (!msg.range) { @@ -436,7 +426,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this.updateContents(fragment); this._colorPicker.layout(); - this.renderDisposable = combinedDisposable([colorListener, colorChangeListener, widget, ...markdownDisposeables]); + this.renderDisposable.value = combinedDisposable(colorListener, colorChangeListener, widget, markdownDisposeables); }); } else { if (msg instanceof MarkerHover) { @@ -448,15 +438,14 @@ export class ModesContentHoverWidget extends ContentHoverWidget { .forEach(contents => { const markdownHoverElement = $('div.hover-row.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); - const renderer = new MarkdownRenderer(this._editor, this._modeService, this._openerService); - markdownDisposeables.push(renderer.onDidRenderCodeBlock(() => { + const renderer = markdownDisposeables.add(new MarkdownRenderer(this._editor, this._modeService, this._openerService)); + markdownDisposeables.add(renderer.onDidRenderCodeBlock(() => { hoverContentsElement.className = 'hover-contents code-hover-contents'; this.onContentsChange(); })); - const renderedContents = renderer.render(contents); + const renderedContents = markdownDisposeables.add(renderer.render(contents)); hoverContentsElement.appendChild(renderedContents.element); fragment.appendChild(markdownHoverElement); - markdownDisposeables.push(renderedContents); isEmptyHoverContent = false; }); } @@ -526,24 +515,10 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private renderMarkerStatusbar(markerHover: MarkerHover): HTMLElement { const hoverElement = $('div.hover-row.status-bar'); - const disposables: IDisposable[] = []; + const disposables = new DisposableStore(); const actionsElement = dom.append(hoverElement, $('div.actions')); - disposables.push(this.renderAction(actionsElement, { - label: nls.localize('quick fixes', "Quick Fix..."), - commandId: QuickFixAction.Id, - run: async (target) => { - const codeActionsPromise = this.getCodeActions(markerHover.marker); - disposables.push(toDisposable(() => codeActionsPromise.cancel())); - const actions = await codeActionsPromise; - const elementPosition = dom.getDomNodePagePosition(target); - this._contextMenuService.showContextMenu({ - getAnchor: () => ({ x: elementPosition.left + 6, y: elementPosition.top + elementPosition.height + 6 }), - getActions: () => actions - }); - } - })); if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) { - disposables.push(this.renderAction(actionsElement, { + disposables.add(this.renderAction(actionsElement, { label: nls.localize('peek problem', "Peek Problem"), commandId: NextMarkerAction.ID, run: () => { @@ -553,24 +528,61 @@ export class ModesContentHoverWidget extends ContentHoverWidget { } })); } - this.renderDisposable = combinedDisposable(disposables); + + const quickfixPlaceholderElement = dom.append(actionsElement, $('div')); + quickfixPlaceholderElement.style.opacity = '0'; + quickfixPlaceholderElement.style.transition = 'opacity 0.2s'; + setTimeout(() => quickfixPlaceholderElement.style.opacity = '1', 200); + quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."); + disposables.add(toDisposable(() => quickfixPlaceholderElement.remove())); + + + const codeActionsPromise = this.getCodeActions(markerHover.marker); + disposables.add(toDisposable(() => codeActionsPromise.cancel())); + codeActionsPromise.then(actions => { + quickfixPlaceholderElement.style.transition = ''; + quickfixPlaceholderElement.style.opacity = '1'; + + if (!actions.actions.length) { + actions.dispose(); + quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); + return; + } + quickfixPlaceholderElement.remove(); + + let showing = false; + disposables.add(toDisposable(() => { + if (!showing) { + actions.dispose(); + } + })); + + disposables.add(this.renderAction(actionsElement, { + label: nls.localize('quick fixes', "Quick Fix..."), + commandId: QuickFixAction.Id, + run: (target) => { + showing = true; + const controller = QuickFixController.get(this._editor); + const elementPosition = dom.getDomNodePagePosition(target); + controller.showCodeActions(actions, { + x: elementPosition.left + 6, + y: elementPosition.top + elementPosition.height + 6 + }); + } + })); + }); + + this.renderDisposable.value = disposables; return hoverElement; } - private getCodeActions(marker: IMarker): CancelablePromise { - return createCancelablePromise(async cancellationToken => { - const codeActions = await getCodeActions(this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, cancellationToken); - if (codeActions.actions.length) { - return codeActions.actions.map(codeAction => new Action( - codeAction.command ? codeAction.command.id : codeAction.title, - codeAction.title, - undefined, - true, - () => applyCodeAction(codeAction, this._bulkEditService, this._commandService))); - } - return [ - new Action('', nls.localize('editor.action.quickFix.noneMessage', "No code actions available")) - ]; + private getCodeActions(marker: IMarker): CancelablePromise { + return createCancelablePromise(cancellationToken => { + return getCodeActions( + this._editor.getModel()!, + new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), + { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, + cancellationToken); }); } diff --git a/src/vs/editor/contrib/hover/modesGlyphHover.ts b/src/vs/editor/contrib/hover/modesGlyphHover.ts index 1ffd7db37e..f2d8968281 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -5,7 +5,7 @@ import { $ } from 'vs/base/browser/dom'; import { IMarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation'; import { GlyphHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; @@ -91,7 +91,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { private readonly _markdownRenderer: MarkdownRenderer; private readonly _computer: MarginComputer; private readonly _hoverOperation: HoverOperation; - private _renderDisposeables: IDisposable[]; + private readonly _renderDisposeables = this._register(new DisposableStore()); constructor( editor: ICodeEditor, @@ -102,7 +102,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { this._lastLineNumber = -1; - this._markdownRenderer = new MarkdownRenderer(this._editor, modeService, openerService); + this._markdownRenderer = this._register(new MarkdownRenderer(this._editor, modeService, openerService)); this._computer = new MarginComputer(this._editor); this._hoverOperation = new HoverOperation( @@ -116,7 +116,6 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { } public dispose(): void { - this._renderDisposeables = dispose(this._renderDisposeables); this._hoverOperation.cancel(); super.dispose(); } @@ -163,16 +162,15 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { } private _renderMessages(lineNumber: number, messages: IHoverMessage[]): void { - dispose(this._renderDisposeables); - this._renderDisposeables = []; + this._renderDisposeables.clear(); const fragment = document.createDocumentFragment(); - messages.forEach((msg) => { + for (const msg of messages) { const renderedContents = this._markdownRenderer.render(msg.value); - this._renderDisposeables.push(renderedContents); + this._renderDisposeables.add(renderedContents); fragment.appendChild($('div.hover-row', undefined, renderedContents.element)); - }); + } this.updateContents(fragment); this.showAt(lineNumber); diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index b8d0ea3b42..883b00ba04 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -27,7 +27,8 @@ export class Link implements ILink { toJSON(): ILink { return { range: this.range, - url: this.url + url: this.url, + tooltip: this.tooltip }; } @@ -39,6 +40,10 @@ export class Link implements ILink { return this._link.url; } + get tooltip(): string | undefined { + return this._link.tooltip; + } + resolve(token: CancellationToken): Promise { if (this._link.url) { try { @@ -143,7 +148,14 @@ export function getLinks(model: ITextModel, token: CancellationToken): Promise new LinksList(coalesce(lists))); + return Promise.all(promises).then(() => { + const result = new LinksList(coalesce(lists)); + if (!token.isCancellationRequested) { + return result; + } + result.dispose(); + return new LinksList([]); + }); } diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index f4aee3992f..803614d59a 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -9,7 +9,7 @@ import * as async from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -27,26 +27,26 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic const HOVER_MESSAGE_GENERAL_META = new MarkdownString().appendText( platform.isMacintosh - ? nls.localize('links.navigate.mac', "Cmd + click to follow link") - : nls.localize('links.navigate', "Ctrl + click to follow link") + ? nls.localize('links.navigate.mac', "Follow link (cmd + click)") + : nls.localize('links.navigate', "Follow link (ctrl + click)") ); const HOVER_MESSAGE_COMMAND_META = new MarkdownString().appendText( platform.isMacintosh - ? nls.localize('links.command.mac', "Cmd + click to execute command") - : nls.localize('links.command', "Ctrl + click to execute command") + ? nls.localize('links.command.mac', "Execute command (cmd + click)") + : nls.localize('links.command', "Execute command (ctrl + click)") ); const HOVER_MESSAGE_GENERAL_ALT = new MarkdownString().appendText( platform.isMacintosh - ? nls.localize('links.navigate.al.mac', "Option + click to follow link") - : nls.localize('links.navigate.al', "Alt + click to follow link") + ? nls.localize('links.navigate.al.mac', "Follow link (option + click)") + : nls.localize('links.navigate.al', "Follow link (alt + click)") ); const HOVER_MESSAGE_COMMAND_ALT = new MarkdownString().appendText( platform.isMacintosh - ? nls.localize('links.command.al.mac', "Option + click to execute command") - : nls.localize('links.command.al', "Alt + click to execute command") + ? nls.localize('links.command.al.mac', "Execute command (option + click)") + : nls.localize('links.command.al', "Execute command (alt + click)") ); const decoration = { @@ -111,6 +111,23 @@ class LinkOccurrence { } private static _getOptions(link: Link, useMetaKey: boolean, isActive: boolean): ModelDecorationOptions { + const options = { ...this._getBaseOptions(link, useMetaKey, isActive) }; + if (typeof link.tooltip === 'string') { + const message = new MarkdownString().appendText( + platform.isMacintosh + ? useMetaKey + ? nls.localize('links.custom.mac', "{0} (cmd + click)", link.tooltip) + : nls.localize('links.custom.mac.al', "{0} (option + click)", link.tooltip) + : useMetaKey + ? nls.localize('links.custom', "{0} (ctrl + click)", link.tooltip) + : nls.localize('links.custom.al', "{0} (alt + click)", link.tooltip) + ); + options.hoverMessage = message; + } + return options; + } + + private static _getBaseOptions(link: Link, useMetaKey: boolean, isActive: boolean): ModelDecorationOptions { if (link.url && /^command:/i.test(link.url.toString())) { if (useMetaKey) { return (isActive ? decoration.metaCommandActive : decoration.metaCommand); @@ -155,7 +172,7 @@ class LinkDetector implements editorCommon.IEditorContribution { private readonly editor: ICodeEditor; private enabled: boolean; - private listenersToRemove: IDisposable[]; + private readonly listenersToRemove = new DisposableStore(); private readonly timeout: async.TimeoutTimer; private computePromise: async.CancelablePromise | null; private activeLinksList: LinksList | null; @@ -172,22 +189,21 @@ class LinkDetector implements editorCommon.IEditorContribution { this.editor = editor; this.openerService = openerService; this.notificationService = notificationService; - this.listenersToRemove = []; let clickLinkGesture = new ClickLinkGesture(editor); - this.listenersToRemove.push(clickLinkGesture); - this.listenersToRemove.push(clickLinkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { + this.listenersToRemove.add(clickLinkGesture); + this.listenersToRemove.add(clickLinkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { this._onEditorMouseMove(mouseEvent, keyboardEvent); })); - this.listenersToRemove.push(clickLinkGesture.onExecute((e) => { + this.listenersToRemove.add(clickLinkGesture.onExecute((e) => { this.onEditorMouseUp(e); })); - this.listenersToRemove.push(clickLinkGesture.onCancel((e) => { + this.listenersToRemove.add(clickLinkGesture.onCancel((e) => { this.cleanUpActiveLinkDecoration(); })); this.enabled = editor.getConfiguration().contribInfo.links; - this.listenersToRemove.push(editor.onDidChangeConfiguration((e) => { + this.listenersToRemove.add(editor.onDidChangeConfiguration((e) => { let enabled = editor.getConfiguration().contribInfo.links; if (this.enabled === enabled) { // No change in our configuration option @@ -204,10 +220,10 @@ class LinkDetector implements editorCommon.IEditorContribution { // Start computing (for the getting enabled case) this.beginCompute(); })); - this.listenersToRemove.push(editor.onDidChangeModelContent((e) => this.onChange())); - this.listenersToRemove.push(editor.onDidChangeModel((e) => this.onModelChanged())); - this.listenersToRemove.push(editor.onDidChangeModelLanguage((e) => this.onModelModeChanged())); - this.listenersToRemove.push(LinkProviderRegistry.onDidChange((e) => this.onModelModeChanged())); + this.listenersToRemove.add(editor.onDidChangeModelContent((e) => this.onChange())); + this.listenersToRemove.add(editor.onDidChangeModel((e) => this.onModelChanged())); + this.listenersToRemove.add(editor.onDidChangeModelLanguage((e) => this.onModelModeChanged())); + this.listenersToRemove.add(LinkProviderRegistry.onDidChange((e) => this.onModelModeChanged())); this.timeout = new async.TimeoutTimer(); this.computePromise = null; @@ -397,7 +413,7 @@ class LinkDetector implements editorCommon.IEditorContribution { } public dispose(): void { - this.listenersToRemove = dispose(this.listenersToRemove); + this.listenersToRemove.dispose(); this.stop(); this.timeout.dispose(); } diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index 405eb1c8c1..a2d4690521 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -13,16 +13,16 @@ import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { TokenizationRegistry } from 'vs/editor/common/modes'; export interface IMarkdownRenderResult extends IDisposable { element: HTMLElement; } -export class MarkdownRenderer { +export class MarkdownRenderer extends Disposable { - private _onDidRenderCodeBlock = new Emitter(); + private _onDidRenderCodeBlock = this._register(new Emitter()); readonly onDidRenderCodeBlock: Event = this._onDidRenderCodeBlock.event; constructor( @@ -30,9 +30,10 @@ export class MarkdownRenderer { @IModeService private readonly _modeService: IModeService, @optional(IOpenerService) private readonly _openerService: IOpenerService | null = NullOpenerService, ) { + super(); } - private getOptions(disposeables: IDisposable[]): RenderOptions { + private getOptions(disposeables: DisposableStore): RenderOptions { return { codeBlockRenderer: (languageAlias, value) => { // In markdown, @@ -78,7 +79,7 @@ export class MarkdownRenderer { } render(markdown: IMarkdownString | undefined): IMarkdownRenderResult { - let disposeables: IDisposable[] = []; + const disposeables = new DisposableStore(); let element: HTMLElement; if (!markdown) { @@ -89,7 +90,7 @@ export class MarkdownRenderer { return { element, - dispose: () => dispose(disposeables) + dispose: () => disposeables.dispose() }; } } diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 1a5fd8d53a..65a6f5bef2 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -7,7 +7,7 @@ import 'vs/css!./messageController'; import * as nls from 'vs/nls'; import { TimeoutTimer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; @@ -29,14 +29,16 @@ export class MessageController extends Disposable implements editorCommon.IEdito return editor.getContribution(MessageController._id); } + private readonly closeTimeout = 3000; // close after 3s + getId(): string { return MessageController._id; } private readonly _editor: ICodeEditor; private readonly _visible: IContextKey; - private _messageWidget?: MessageWidget; - private _messageListeners: IDisposable[] = []; + private readonly _messageWidget = this._register(new MutableDisposable()); + private readonly _messageListeners = this._register(new DisposableStore()); constructor( editor: ICodeEditor, @@ -62,22 +64,21 @@ export class MessageController extends Disposable implements editorCommon.IEdito alert(message); this._visible.set(true); - dispose(this._messageWidget); - this._messageListeners = dispose(this._messageListeners); - this._messageWidget = new MessageWidget(this._editor, position, message); + this._messageWidget.clear(); + this._messageListeners.clear(); + this._messageWidget.value = new MessageWidget(this._editor, position, message); // close on blur, cursor, model change, dispose - this._messageListeners.push(this._editor.onDidBlurEditorText(() => this.closeMessage())); - this._messageListeners.push(this._editor.onDidChangeCursorPosition(() => this.closeMessage())); - this._messageListeners.push(this._editor.onDidDispose(() => this.closeMessage())); - this._messageListeners.push(this._editor.onDidChangeModel(() => this.closeMessage())); + this._messageListeners.add(this._editor.onDidBlurEditorText(() => this.closeMessage())); + this._messageListeners.add(this._editor.onDidChangeCursorPosition(() => this.closeMessage())); + this._messageListeners.add(this._editor.onDidDispose(() => this.closeMessage())); + this._messageListeners.add(this._editor.onDidChangeModel(() => this.closeMessage())); - // close after 3s - this._messageListeners.push(new TimeoutTimer(() => this.closeMessage(), 3000)); + this._messageListeners.add(new TimeoutTimer(() => this.closeMessage(), this.closeTimeout)); // close on mouse move let bounds: Range; - this._messageListeners.push(this._editor.onMouseMove(e => { + this._messageListeners.add(this._editor.onMouseMove(e => { // outside the text area if (!e.target.position) { return; @@ -95,9 +96,9 @@ export class MessageController extends Disposable implements editorCommon.IEdito closeMessage(): void { this._visible.reset(); - this._messageListeners = dispose(this._messageListeners); - if (this._messageWidget) { - this._messageListeners.push(MessageWidget.fadeOut(this._messageWidget)); + this._messageListeners.clear(); + if (this._messageWidget.value) { + this._messageListeners.add(MessageWidget.fadeOut(this._messageWidget.value)); } } diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index b500bd94d1..7ce401349f 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { RevealTarget } from 'vs/editor/common/controller/cursorCommon'; @@ -427,7 +427,7 @@ export class MultiCursorSelectionController extends Disposable implements IEdito private readonly _editor: ICodeEditor; private _ignoreSelectionChange: boolean; private _session: MultiCursorSession | null; - private _sessionDispose: IDisposable[]; + private readonly _sessionDispose = this._register(new DisposableStore()); public static get(editor: ICodeEditor): MultiCursorSelectionController { return editor.getContribution(MultiCursorSelectionController.ID); @@ -438,7 +438,6 @@ export class MultiCursorSelectionController extends Disposable implements IEdito this._editor = editor; this._ignoreSelectionChange = false; this._session = null; - this._sessionDispose = []; } public dispose(): void { @@ -468,27 +467,25 @@ export class MultiCursorSelectionController extends Disposable implements IEdito } findController.getState().change(newState, false); - this._sessionDispose = [ - this._editor.onDidChangeCursorSelection((e) => { - if (this._ignoreSelectionChange) { - return; - } + this._sessionDispose.add(this._editor.onDidChangeCursorSelection((e) => { + if (this._ignoreSelectionChange) { + return; + } + this._endSession(); + })); + this._sessionDispose.add(this._editor.onDidBlurEditorText(() => { + this._endSession(); + })); + this._sessionDispose.add(findController.getState().onFindReplaceStateChange((e) => { + if (e.matchCase || e.wholeWord) { this._endSession(); - }), - this._editor.onDidBlurEditorText(() => { - this._endSession(); - }), - findController.getState().onFindReplaceStateChange((e) => { - if (e.matchCase || e.wholeWord) { - this._endSession(); - } - }) - ]; + } + })); } } private _endSession(): void { - this._sessionDispose = dispose(this._sessionDispose); + this._sessionDispose.clear(); if (this._session && this._session.isDisconnectedFromFindController) { const newState: INewFindReplaceState = { wholeWordOverride: FindOptionOverride.NotSet, diff --git a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts index 25c8d96c4f..97cb8da5d1 100644 --- a/src/vs/editor/contrib/multicursor/test/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/multicursor.test.ts @@ -60,14 +60,15 @@ suite('Multicursor selection', () => { let queryState: { [key: string]: any; } = {}; let serviceCollection = new ServiceCollection(); serviceCollection.set(IStorageService, { - _serviceBrand: undefined, + _serviceBrand: undefined as any, onDidChangeStorage: Event.None, onWillSaveState: Event.None, get: (key: string) => queryState[key], getBoolean: (key: string) => !!queryState[key], getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - remove: (key) => undefined + remove: (key) => undefined, + logStorage: () => undefined } as IStorageService); test('issue #8817: Cursor position changes when you cancel multicursor', () => { diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts index 7086b5ec82..257ed3836c 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts @@ -6,7 +6,7 @@ import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; @@ -30,7 +30,7 @@ namespace ParameterHintState { export class Pending { readonly type = Type.Pending; constructor( - readonly request: CancelablePromise + readonly request: CancelablePromise ) { } } @@ -54,6 +54,7 @@ export class ParameterHintsModel extends Disposable { private readonly editor: ICodeEditor; private enabled: boolean; private _state: ParameterHintState.State = ParameterHintState.Default; + private readonly _lastSignatureHelpResult = this._register(new MutableDisposable()); private triggerChars = new CharacterSet(); private retriggerChars = new CharacterSet(); @@ -92,7 +93,6 @@ export class ParameterHintsModel extends Disposable { } cancel(silent: boolean = false): void { - this.state = ParameterHintState.Default; this.throttledDelayer.cancel(); @@ -181,14 +181,22 @@ export class ParameterHintsModel extends Disposable { return this.state.request.then(result => { // Check that we are still resolving the correct signature help if (triggerId !== this.triggerId) { + if (result) { + result.dispose(); + } return false; } - if (!result || !result.signatures || result.signatures.length === 0) { + if (!result || !result.value.signatures || result.value.signatures.length === 0) { + if (result) { + result.dispose(); + } + this._lastSignatureHelpResult.clear(); this.cancel(); return false; } else { - this.state = new ParameterHintState.Active(result); + this.state = new ParameterHintState.Active(result.value); + this._lastSignatureHelpResult.value = result; this._onChangedHints.fire(this.state.hints); return true; } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index b105b43019..e35702ea25 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -8,7 +8,7 @@ import { domEvent, stop } from 'vs/base/browser/event'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./parameterHints'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; @@ -25,13 +25,13 @@ import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameter const $ = dom.$; -export class ParameterHintsWidget implements IContentWidget, IDisposable { +export class ParameterHintsWidget extends Disposable implements IContentWidget, IDisposable { private static readonly ID = 'editor.widget.parameterHintsWidget'; private readonly markdownRenderer: MarkdownRenderer; - private renderDisposeables: IDisposable[]; - private model: ParameterHintsModel | null; + private readonly renderDisposeables = this._register(new DisposableStore()); + private readonly model = this._register(new MutableDisposable()); private readonly keyVisible: IContextKey; private readonly keyMultipleSignatures: IContextKey; private element: HTMLElement; @@ -41,7 +41,6 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { private visible: boolean; private announcedLabel: string | null; private scrollbar: DomScrollableElement; - private disposables: IDisposable[]; // Editor.IContentWidget.allowEditorOverflow allowEditorOverflow = true; @@ -52,14 +51,14 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { @IOpenerService openerService: IOpenerService, @IModeService modeService: IModeService, ) { - this.markdownRenderer = new MarkdownRenderer(editor, modeService, openerService); - this.model = new ParameterHintsModel(editor); + super(); + this.markdownRenderer = this._register(new MarkdownRenderer(editor, modeService, openerService)); + this.model.value = new ParameterHintsModel(editor); this.keyVisible = Context.Visible.bindTo(contextKeyService); this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService); this.visible = false; - this.disposables = []; - this.disposables.push(this.model.onChangedHints(newParameterHints => { + this._register(this.model.value.onChangedHints(newParameterHints => { if (newParameterHints) { this.show(); this.render(newParameterHints); @@ -79,16 +78,16 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { const next = dom.append(buttons, $('.button.next')); const onPreviousClick = stop(domEvent(previous, 'click')); - onPreviousClick(this.previous, this, this.disposables); + this._register(onPreviousClick(this.previous, this)); const onNextClick = stop(domEvent(next, 'click')); - onNextClick(this.next, this, this.disposables); + this._register(onNextClick(this.next, this)); this.overloads = dom.append(wrapper, $('.overloads')); const body = $('.body'); this.scrollbar = new DomScrollableElement(body, {}); - this.disposables.push(this.scrollbar); + this._register(this.scrollbar); wrapper.appendChild(this.scrollbar.getDomNode()); this.signature = dom.append(body, $('.signature')); @@ -99,7 +98,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.hide(); this.element.style.userSelect = 'text'; - this.disposables.push(this.editor.onDidChangeCursorSelection(e => { + this._register(this.editor.onDidChangeCursorSelection(e => { if (this.visible) { this.editor.layoutContentWidget(this); } @@ -112,11 +111,11 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { updateFont(); - Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) + this._register(Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) .filter(e => e.fontInfo) - .on(updateFont, null, this.disposables); + .on(updateFont, null)); - this.disposables.push(this.editor.onDidLayoutChange(e => this.updateMaxHeight())); + this._register(this.editor.onDidLayoutChange(e => this.updateMaxHeight())); this.updateMaxHeight(); } @@ -190,8 +189,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.renderParameters(code, signature, hints.activeParameter); } - dispose(this.renderDisposeables); - this.renderDisposeables = []; + this.renderDisposeables.clear(); const activeParameter = signature.parameters[hints.activeParameter]; @@ -202,7 +200,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } else { const renderedContents = this.markdownRenderer.render(activeParameter.documentation); dom.addClass(renderedContents.element, 'markdown-docs'); - this.renderDisposeables.push(renderedContents); + this.renderDisposeables.add(renderedContents); documentation.appendChild(renderedContents.element); } dom.append(this.docs, $('p', {}, documentation)); @@ -216,7 +214,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } else { const renderedContents = this.markdownRenderer.render(signature.documentation); dom.addClass(renderedContents.element, 'markdown-docs'); - this.renderDisposeables.push(renderedContents); + this.renderDisposeables.add(renderedContents); dom.append(this.docs, renderedContents.element); } @@ -284,22 +282,22 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } next(): void { - if (this.model) { + if (this.model.value) { this.editor.focus(); - this.model.next(); + this.model.value.next(); } } previous(): void { - if (this.model) { + if (this.model.value) { this.editor.focus(); - this.model.previous(); + this.model.value.previous(); } } cancel(): void { - if (this.model) { - this.model.cancel(); + if (this.model.value) { + this.model.value.cancel(); } } @@ -312,8 +310,8 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } trigger(context: TriggerContext): void { - if (this.model) { - this.model.trigger(context, 0); + if (this.model.value) { + this.model.value.trigger(context, 0); } } @@ -321,16 +319,6 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { const height = Math.max(this.editor.getLayoutInfo().height / 4, 250); this.element.style.maxHeight = `${height}px`; } - - dispose(): void { - this.disposables = dispose(this.disposables); - this.renderDisposeables = dispose(this.renderDisposeables); - - if (this.model) { - this.model.dispose(); - this.model = null; - } - } } registerThemingParticipant((theme, collector) => { diff --git a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts index 65c3987e04..098d759c09 100644 --- a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts +++ b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts @@ -17,18 +17,32 @@ export const Context = { MultipleSignatures: new RawContextKey('parameterHintsMultipleSignatures', false), }; -export function provideSignatureHelp(model: ITextModel, position: Position, context: modes.SignatureHelpContext, token: CancellationToken): Promise { +export function provideSignatureHelp( + model: ITextModel, + position: Position, + context: modes.SignatureHelpContext, + token: CancellationToken +): Promise { const supports = modes.SignatureHelpProviderRegistry.ordered(model); return first(supports.map(support => () => { - return Promise.resolve(support.provideSignatureHelp(model, position, token, context)).catch(onUnexpectedExternalError); + return Promise.resolve(support.provideSignatureHelp(model, position, token, context)) + .catch(e => onUnexpectedExternalError(e)); })); } -registerDefaultLanguageCommand('_executeSignatureHelpProvider', (model, position, args) => - provideSignatureHelp(model, position, { +registerDefaultLanguageCommand('_executeSignatureHelpProvider', async (model, position, args) => { + const result = await provideSignatureHelp(model, position, { triggerKind: modes.SignatureHelpTriggerKind.Invoke, isRetrigger: false, triggerCharacter: args['triggerCharacter'] - }, CancellationToken.None)); + }, CancellationToken.None); + + if (!result) { + return undefined; + } + + setTimeout(() => result.dispose(), 0); + return result.value; +}); diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 3748063f98..ca501ed02b 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { Handler } from 'vs/editor/common/editorCommon'; @@ -23,7 +23,7 @@ const mockFile = URI.parse('test:somefile.ttt'); const mockFileSelector = { scheme: 'test' }; -const emptySigHelpResult = { +const emptySigHelp: modes.SignatureHelp = { signatures: [{ label: 'none', parameters: [] @@ -31,11 +31,21 @@ const emptySigHelpResult = { activeParameter: 0, activeSignature: 0 }; -suite('ParameterHintsModel', () => { - let disposables: IDisposable[] = []; - setup(function () { - disposables = dispose(disposables); +const emptySigHelpResult: modes.SignatureHelpResult = { + value: emptySigHelp, + dispose: () => { } +}; + +suite('ParameterHintsModel', () => { + const disposables = new DisposableStore(); + + setup(() => { + disposables.clear(); + }); + + teardown(() => { + disposables.clear(); }); function createMockEditor(fileContents: string) { @@ -47,8 +57,8 @@ suite('ParameterHintsModel', () => { [IStorageService, new InMemoryStorageService()] ) }); - disposables.push(textModel); - disposables.push(editor); + disposables.add(textModel); + disposables.add(editor); return editor; } @@ -56,9 +66,9 @@ suite('ParameterHintsModel', () => { const triggerChar = '('; const editor = createMockEditor(''); - disposables.push(new ParameterHintsModel(editor)); + disposables.add(new ParameterHintsModel(editor)); - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; signatureHelpRetriggerCharacters = []; @@ -77,14 +87,14 @@ suite('ParameterHintsModel', () => { const triggerChar = '('; const editor = createMockEditor(''); - disposables.push(new ParameterHintsModel(editor)); + disposables.add(new ParameterHintsModel(editor)); let invokeCount = 0; - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; signatureHelpRetriggerCharacters = []; - provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelp | Promise { + provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise { ++invokeCount; if (invokeCount === 1) { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); @@ -99,7 +109,7 @@ suite('ParameterHintsModel', () => { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); assert.strictEqual(context.isRetrigger, true); assert.strictEqual(context.triggerCharacter, triggerChar); - assert.strictEqual(context.activeSignatureHelp, emptySigHelpResult); + assert.strictEqual(context.activeSignatureHelp, emptySigHelp); done(); } @@ -115,14 +125,14 @@ suite('ParameterHintsModel', () => { const editor = createMockEditor(''); const hintModel = new ParameterHintsModel(editor); - disposables.push(hintModel); + disposables.add(hintModel); let invokeCount = 0; - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; signatureHelpRetriggerCharacters = []; - provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelp | Promise { + provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise { ++invokeCount; if (invokeCount === 1) { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); @@ -150,10 +160,10 @@ suite('ParameterHintsModel', () => { test('Provider should get last trigger character when triggered multiple times and only be invoked once', (done) => { const editor = createMockEditor(''); - disposables.push(new ParameterHintsModel(editor, 5)); + disposables.add(new ParameterHintsModel(editor, 5)); let invokeCount = 0; - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = ['a', 'b', 'c']; signatureHelpRetriggerCharacters = []; @@ -181,14 +191,14 @@ suite('ParameterHintsModel', () => { test('Provider should be retriggered if already active', (done) => { const editor = createMockEditor(''); - disposables.push(new ParameterHintsModel(editor, 5)); + disposables.add(new ParameterHintsModel(editor, 5)); let invokeCount = 0; - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = ['a', 'b']; signatureHelpRetriggerCharacters = []; - provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelp | Promise { + provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise { ++invokeCount; if (invokeCount === 1) { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); @@ -223,7 +233,7 @@ suite('ParameterHintsModel', () => { signatureHelpRetriggerCharacters = []; - provideSignatureHelp(_model: ITextModel, _position: Position, token: CancellationToken): modes.SignatureHelp | Promise { + provideSignatureHelp(_model: ITextModel, _position: Position, token: CancellationToken): modes.SignatureHelpResult | Promise { const count = invokeCount++; token.onCancellationRequested(() => { didRequestCancellationOf = count; }); @@ -232,22 +242,25 @@ suite('ParameterHintsModel', () => { hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0); } - return new Promise(resolve => { + return new Promise(resolve => { setTimeout(() => { resolve({ - signatures: [{ - label: '' + count, - parameters: [] - }], - activeParameter: 0, - activeSignature: 0 + value: { + signatures: [{ + label: '' + count, + parameters: [] + }], + activeParameter: 0, + activeSignature: 0 + }, + dispose: () => { } }); }, 100); }); } }; - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, longRunningProvider)); + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, longRunningProvider)); hintsModel.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke }, 0); assert.strictEqual(-1, didRequestCancellationOf); @@ -269,14 +282,14 @@ suite('ParameterHintsModel', () => { const retriggerChar = 'b'; const editor = createMockEditor(''); - disposables.push(new ParameterHintsModel(editor, 5)); + disposables.add(new ParameterHintsModel(editor, 5)); let invokeCount = 0; - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; signatureHelpRetriggerCharacters = [retriggerChar]; - provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelp | Promise { + provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelpResult | Promise { ++invokeCount; if (invokeCount === 1) { assert.strictEqual(context.triggerKind, modes.SignatureHelpTriggerKind.TriggerCharacter); @@ -312,26 +325,29 @@ suite('ParameterHintsModel', () => { const editor = createMockEditor(''); const model = new ParameterHintsModel(editor, 5); - disposables.push(model); + disposables.add(model); - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; signatureHelpRetriggerCharacters = []; - async provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): Promise { + async provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): Promise { if (!context.isRetrigger) { // retrigger after delay for widget to show up setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: triggerChar }), 50); return { - activeParameter: 0, - activeSignature: 0, - signatures: [{ - label: firstProviderId, - parameters: [ - { label: paramterLabel } - ] - }] + value: { + activeParameter: 0, + activeSignature: 0, + signatures: [{ + label: firstProviderId, + parameters: [ + { label: paramterLabel } + ] + }] + }, + dispose: () => { } }; } @@ -339,19 +355,22 @@ suite('ParameterHintsModel', () => { } })); - disposables.push(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { + disposables.add(modes.SignatureHelpProviderRegistry.register(mockFileSelector, new class implements modes.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; signatureHelpRetriggerCharacters = []; - async provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): Promise { + async provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): Promise { if (context.isRetrigger) { return { - activeParameter: 0, - activeSignature: context.activeSignatureHelp ? context.activeSignatureHelp.activeSignature + 1 : 0, - signatures: [{ - label: secondProviderId, - parameters: context.activeSignatureHelp ? context.activeSignatureHelp.signatures[0].parameters : [] - }] + value: { + activeParameter: 0, + activeSignature: context.activeSignatureHelp ? context.activeSignatureHelp.activeSignature + 1 : 0, + signatures: [{ + label: secondProviderId, + parameters: context.activeSignatureHelp ? context.activeSignatureHelp.signatures[0].parameters : [] + }] + }, + dispose: () => { } }; } @@ -361,23 +380,23 @@ suite('ParameterHintsModel', () => { editor.trigger('keyboard', Handler.Type, { text: triggerChar }); - const firstHint = await getNextHint(model); - assert.strictEqual(firstHint!.signatures[0].label, firstProviderId); - assert.strictEqual(firstHint!.activeSignature, 0); - assert.strictEqual(firstHint!.signatures[0].parameters[0].label, paramterLabel); + const firstHint = (await getNextHint(model))!.value; + assert.strictEqual(firstHint.signatures[0].label, firstProviderId); + assert.strictEqual(firstHint.activeSignature, 0); + assert.strictEqual(firstHint.signatures[0].parameters[0].label, paramterLabel); - const secondHint = await getNextHint(model); - assert.strictEqual(secondHint!.signatures[0].label, secondProviderId); - assert.strictEqual(secondHint!.activeSignature, 1); - assert.strictEqual(secondHint!.signatures[0].parameters[0].label, paramterLabel); + const secondHint = (await getNextHint(model))!.value; + assert.strictEqual(secondHint.signatures[0].label, secondProviderId); + assert.strictEqual(secondHint.activeSignature, 1); + assert.strictEqual(secondHint.signatures[0].parameters[0].label, paramterLabel); }); }); function getNextHint(model: ParameterHintsModel) { - return new Promise(resolve => { + return new Promise(resolve => { const sub = model.onChangedHints(e => { sub.dispose(); - return resolve(e); + return resolve(e ? { value: e, dispose: () => { } } : undefined); }); }); } diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-down-hc.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-down-hc.svg deleted file mode 100644 index eca0994a74..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-down-hc.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-down-inverse.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-down-inverse.svg deleted file mode 100644 index 0671cba2ff..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-down-inverse.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-down.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-down.svg deleted file mode 100644 index 9514d8f317..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-down.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-next-dark.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-next-dark.svg new file mode 100644 index 0000000000..455532ddb7 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/media/chevron-next-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-next-light.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-next-light.svg new file mode 100644 index 0000000000..a443086f35 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/media/chevron-next-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-previous-dark.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-previous-dark.svg new file mode 100644 index 0000000000..5ca3526019 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/media/chevron-previous-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-previous-light.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-previous-light.svg new file mode 100644 index 0000000000..87e179a7f3 --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/media/chevron-previous-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-up-hc.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-up-hc.svg deleted file mode 100644 index 1c668f52b4..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-up-hc.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse-hc.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse-hc.svg deleted file mode 100644 index 1c668f52b4..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse-hc.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse.svg deleted file mode 100644 index 31bdf3deeb..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-up-inverse.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/chevron-up.svg b/src/vs/editor/contrib/referenceSearch/media/chevron-up.svg deleted file mode 100644 index 7e38887f57..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/chevron-up.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/vs/editor/contrib/referenceSearch/media/close-dark.svg b/src/vs/editor/contrib/referenceSearch/media/close-dark.svg new file mode 100644 index 0000000000..bffa4e9dab --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/media/close-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/referenceSearch/media/close-inverse.svg b/src/vs/editor/contrib/referenceSearch/media/close-inverse.svg deleted file mode 100644 index 751e89b3b0..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/close-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/referenceSearch/media/close-light.svg b/src/vs/editor/contrib/referenceSearch/media/close-light.svg new file mode 100644 index 0000000000..b44dee661a --- /dev/null +++ b/src/vs/editor/contrib/referenceSearch/media/close-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/referenceSearch/media/close.svg b/src/vs/editor/contrib/referenceSearch/media/close.svg deleted file mode 100644 index fde34404d4..0000000000 --- a/src/vs/editor/contrib/referenceSearch/media/close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css b/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css index 6d46559fe4..cdc264c32e 100644 --- a/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css +++ b/src/vs/editor/contrib/referenceSearch/media/peekViewWidget.css @@ -19,14 +19,6 @@ cursor: pointer; } -.monaco-editor .peekview-widget .head .peekview-title .icon { - display: inline-block; - height: 16px; - width: 16px; - vertical-align: text-bottom; - margin-right: 4px; -} - .monaco-editor .peekview-widget .head .peekview-title .dirname:not(:empty) { font-size: 0.9em; margin-left: 0.5em; @@ -65,7 +57,7 @@ } .monaco-editor .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action { - background: url('close.svg') center center no-repeat; + background: url('close-light.svg') center center no-repeat; } .monaco-editor .peekview-widget > .body { @@ -78,30 +70,23 @@ .monaco-editor.hc-black .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action, .monaco-editor.vs-dark .peekview-widget .head .peekview-actions .action-label.icon.close-peekview-action { - background: url('close-inverse.svg') center center no-repeat; + background: url('close-dark.svg') center center no-repeat; } .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up { - background: url('chevron-up-inverse.svg') center center no-repeat; -} - -.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up { - background: url('chevron-up.svg') center center no-repeat; + background: url('chevron-previous-light.svg') center center no-repeat; } +.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up, .hc-black .monaco-editor .peekview-widget .peekview-actions .icon.chevron-up { - background: url('chevron-up-inverse-hc.svg') center center no-repeat; + background: url('chevron-previous-dark.svg') center center no-repeat; } .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down { - background: url('chevron-down-inverse.svg') center center no-repeat; -} - -.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down { - background: url('chevron-down.svg') center center no-repeat; + background: url('chevron-next-light.svg') center center no-repeat; } +.vs-dark .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down, .hc-black .monaco-editor .peekview-widget .peekview-actions .icon.chevron-down { - background: url('chevron-down-hc.svg') center center no-repeat; -} - + background: url('chevron-next-dark.svg') center center no-repeat; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/referenceSearch/peekViewWidget.ts b/src/vs/editor/contrib/referenceSearch/peekViewWidget.ts index 935a9c68f1..c93dfab479 100644 --- a/src/vs/editor/contrib/referenceSearch/peekViewWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/peekViewWidget.ts @@ -85,7 +85,6 @@ export abstract class PeekViewWidget extends ZoneWidget { private _onDidClose = new Emitter(); protected _headElement: HTMLDivElement; - protected _headingIcon: HTMLElement; protected _primaryHeading: HTMLElement; protected _secondaryHeading: HTMLElement; protected _metaHeading: HTMLElement; @@ -155,18 +154,18 @@ export abstract class PeekViewWidget extends ZoneWidget { dom.append(this._headElement, titleElement); dom.addStandardDisposableListener(titleElement, 'click', event => this._onTitleClick(event)); - this._headingIcon = dom.$('span'); + this._fillTitleIcon(titleElement); this._primaryHeading = dom.$('span.filename'); this._secondaryHeading = dom.$('span.dirname'); this._metaHeading = dom.$('span.meta'); - dom.append(titleElement, this._headingIcon, this._primaryHeading, this._secondaryHeading, this._metaHeading); + dom.append(titleElement, this._primaryHeading, this._secondaryHeading, this._metaHeading); const actionsContainer = dom.$('.peekview-actions'); dom.append(this._headElement, actionsContainer); const actionBarOptions = this._getActionBarOptions(); this._actionbarWidget = new ActionBar(actionsContainer, actionBarOptions); - this._disposables.push(this._actionbarWidget); + this._disposables.add(this._actionbarWidget); this._actionbarWidget.push(new Action('peekview.close', nls.localize('label.close', "Close"), 'close-peekview-action', true, () => { this.dispose(); @@ -174,6 +173,9 @@ export abstract class PeekViewWidget extends ZoneWidget { }), { label: false, icon: true }); } + protected _fillTitleIcon(container: HTMLElement): void { + } + protected _getActionBarOptions(): IActionBarOptions { return {}; } @@ -182,10 +184,6 @@ export abstract class PeekViewWidget extends ZoneWidget { // implement me } - public setTitleIcon(iconClassName: string): void { - this._headingIcon.className = iconClassName ? `icon ${iconClassName}` : ''; - } - public setTitle(primaryHeading: string, secondaryHeading?: string): void { this._primaryHeading.innerHTML = strings.escape(primaryHeading); this._primaryHeading.setAttribute('aria-label', primaryHeading); diff --git a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts index bce70cb5ba..7bdfc81a2d 100644 --- a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts +++ b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts @@ -61,7 +61,7 @@ export class ReferenceAction extends EditorAction { super({ id: 'editor.action.referenceSearch.trigger', label: nls.localize('references.action.label', "Peek References"), - alias: 'Find All References', // leave the alias? + alias: 'Peek References', precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, diff --git a/src/vs/editor/contrib/referenceSearch/referencesController.ts b/src/vs/editor/contrib/referenceSearch/referencesController.ts index 2f714b86bb..5e230bac05 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesController.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesController.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -32,11 +32,11 @@ export abstract class ReferencesController implements editorCommon.IEditorContri private static readonly ID = 'editor.contrib.referencesController'; + private readonly _disposables = new DisposableStore(); private readonly _editor: ICodeEditor; private _widget: ReferenceWidget | null; private _model: ReferencesModel | null; private _requestIdPool = 0; - private _disposables: IDisposable[] = []; private _ignoreModelChangeEvent = false; private readonly _referenceSearchVisible: IContextKey; @@ -91,8 +91,8 @@ export abstract class ReferencesController implements editorCommon.IEditorContri this._referenceSearchVisible.set(true); // close the widget on model/mode changes - this._disposables.push(this._editor.onDidChangeModelLanguage(() => { this.closeWidget(); })); - this._disposables.push(this._editor.onDidChangeModel(() => { + this._disposables.add(this._editor.onDidChangeModelLanguage(() => { this.closeWidget(); })); + this._disposables.add(this._editor.onDidChangeModel(() => { if (!this._ignoreModelChangeEvent) { this.closeWidget(); } @@ -103,7 +103,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri this._widget.setTitle(nls.localize('labelLoading', "Loading...")); this._widget.show(range); - this._disposables.push(this._widget.onDidClose(() => { + this._disposables.add(this._widget.onDidClose(() => { modelPromise.cancel(); if (this._widget) { this._storageService.store(storageKey, JSON.stringify(this._widget.layoutData), StorageScope.GLOBAL); @@ -112,7 +112,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri this.closeWidget(); })); - this._disposables.push(this._widget.onDidSelectReference(event => { + this._disposables.add(this._widget.onDidSelectReference(event => { let { element, kind } = event; switch (kind) { case 'open': @@ -205,7 +205,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri this._widget = null; } this._referenceSearchVisible.reset(); - this._disposables = dispose(this._disposables); + this._disposables.clear(); if (this._model) { dispose(this._model); this._model = null; diff --git a/src/vs/editor/contrib/referenceSearch/referencesModel.ts b/src/vs/editor/contrib/referenceSearch/referencesModel.ts index f53a84c88a..f6a651ecc4 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesModel.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesModel.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { basename } from 'vs/base/common/resources'; -import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { defaultGenerator } from 'vs/base/common/idGenerator'; @@ -14,6 +14,7 @@ import { Range, IRange } from 'vs/editor/common/core/range'; import { Location, LocationLink } from 'vs/editor/common/modes'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { Position } from 'vs/editor/common/core/position'; +import { IMatch } from 'vs/base/common/filters'; export class OneReference { readonly id: string; @@ -61,7 +62,7 @@ export class FilePreview implements IDisposable { dispose(this._modelReference); } - preview(range: IRange, n: number = 8): { before: string; inside: string; after: string } | undefined { + preview(range: IRange, n: number = 8): { value: string; highlight: IMatch } | undefined { const model = this._modelReference.object.textEditorModel; if (!model) { @@ -73,13 +74,14 @@ export class FilePreview implements IDisposable { const beforeRange = new Range(startLineNumber, word.startColumn, startLineNumber, startColumn); const afterRange = new Range(endLineNumber, endColumn, endLineNumber, Number.MAX_VALUE); - const ret = { - before: model.getValueInRange(beforeRange).replace(/^\s+/, strings.empty), - inside: model.getValueInRange(range), - after: model.getValueInRange(afterRange).replace(/\s+$/, strings.empty) - }; + const before = model.getValueInRange(beforeRange).replace(/^\s+/, strings.empty); + const inside = model.getValueInRange(range); + const after = model.getValueInRange(afterRange).replace(/\s+$/, strings.empty); - return ret; + return { + value: before + inside + after, + highlight: { start: before.length, end: before.length + inside.length } + }; } } @@ -164,7 +166,7 @@ export class FileReferences implements IDisposable { export class ReferencesModel implements IDisposable { - private readonly _disposables: IDisposable[]; + private readonly _disposables = new DisposableStore(); readonly groups: FileReferences[] = []; readonly references: OneReference[] = []; @@ -172,7 +174,7 @@ export class ReferencesModel implements IDisposable { readonly onDidChangeReferenceRange: Event = this._onDidChangeReferenceRange.event; constructor(references: LocationLink[]) { - this._disposables = []; + // grouping and sorting const [providersFirst] = references; references.sort(ReferencesModel._compareReferences); @@ -190,7 +192,7 @@ export class ReferencesModel implements IDisposable { || !Range.equalsRange(ref.range, current.children[current.children.length - 1].range)) { let oneRef = new OneReference(current, ref.targetSelectionRange || ref.range, providersFirst === ref); - this._disposables.push(oneRef.onRefChanged((e) => this._onDidChangeReferenceRange.fire(e))); + this._disposables.add(oneRef.onRefChanged((e) => this._onDidChangeReferenceRange.fire(e))); this.references.push(oneRef); current.children.push(oneRef); } @@ -280,9 +282,8 @@ export class ReferencesModel implements IDisposable { dispose(): void { dispose(this.groups); - dispose(this._disposables); + this._disposables.dispose(); this.groups.length = 0; - this._disposables.length = 0; } private static _compareReferences(a: Location, b: Location): number { diff --git a/src/vs/editor/contrib/referenceSearch/referencesTree.ts b/src/vs/editor/contrib/referenceSearch/referencesTree.ts index 40778caf2d..d10cbd6430 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesTree.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesTree.ts @@ -15,7 +15,6 @@ import * as dom from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { getBaseLabel } from 'vs/base/common/labels'; import { dirname, basename } from 'vs/base/common/resources'; -import { escape } from 'vs/base/common/strings'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -23,6 +22,7 @@ import { IListVirtualDelegate, IKeyboardNavigationLabelProvider, IIdentityProvid import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; //#region data source @@ -82,8 +82,14 @@ export class StringRepresentationProvider implements IKeyboardNavigationLabelPro constructor(@IKeybindingService private readonly _keybindingService: IKeybindingService) { } getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } { - // todo@joao `OneReference` elements are lazy and their "real" label - // isn't known yet + if (element instanceof OneReference) { + const { preview } = element.parent; + const parts = preview && preview.preview(element.range); + if (parts) { + return parts.value; + } + } + // FileReferences or unresolved OneReference return basename(element.uri); } @@ -161,31 +167,29 @@ export class FileReferencesRenderer implements ITreeRenderer, index: number, templateData: OneReferenceTemplate): void { - templateData.set(element.element); + renderElement(node: ITreeNode, index: number, templateData: OneReferenceTemplate): void { + templateData.set(node.element, node.filterData); } disposeTemplate(): void { - // } } diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index ee15970a60..5f15ead33f 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -8,7 +8,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, IReference, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import 'vs/css!./media/referencesWidget'; @@ -47,22 +47,22 @@ class DecorationsManager implements IDisposable { private _decorations = new Map(); private _decorationIgnoreSet = new Set(); - private _callOnDispose: IDisposable[] = []; - private _callOnModelChange: IDisposable[] = []; + private readonly _callOnDispose = new DisposableStore(); + private readonly _callOnModelChange = new DisposableStore(); constructor(private _editor: ICodeEditor, private _model: ReferencesModel) { - this._callOnDispose.push(this._editor.onDidChangeModel(() => this._onModelChanged())); + this._callOnDispose.add(this._editor.onDidChangeModel(() => this._onModelChanged())); this._onModelChanged(); } public dispose(): void { - this._callOnModelChange = dispose(this._callOnModelChange); - this._callOnDispose = dispose(this._callOnDispose); + this._callOnModelChange.dispose(); + this._callOnDispose.dispose(); this.removeDecorations(); } private _onModelChanged(): void { - this._callOnModelChange = dispose(this._callOnModelChange); + this._callOnModelChange.clear(); const model = this._editor.getModel(); if (model) { for (const ref of this._model.groups) { @@ -78,7 +78,7 @@ class DecorationsManager implements IDisposable { if (!this._editor.hasModel()) { return; } - this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged())); + this._callOnModelChange.add(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged())); const newDecorations: IModelDeltaDecoration[] = []; const newDecorationsActualIndex: number[] = []; @@ -194,8 +194,8 @@ export class ReferenceWidget extends PeekViewWidget { private _model: ReferencesModel | undefined; private _decorationsManager: DecorationsManager; - private _disposeOnNewModel: IDisposable[] = []; - private _callOnDispose: IDisposable[] = []; + private readonly _disposeOnNewModel = new DisposableStore(); + private readonly _callOnDispose = new DisposableStore(); private _onDidSelectReference = new Emitter(); private _tree: WorkbenchAsyncDataTree; @@ -222,15 +222,19 @@ export class ReferenceWidget extends PeekViewWidget { super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }); this._applyTheme(themeService.getTheme()); - this._callOnDispose.push(themeService.onThemeChange(this._applyTheme.bind(this))); + this._callOnDispose.add(themeService.onThemeChange(this._applyTheme.bind(this))); this._peekViewService.addExclusiveWidget(editor, this); this.create(); } dispose(): void { this.setModel(undefined); - this._callOnDispose = dispose(this._callOnDispose); - dispose(this._preview, this._previewNotAvailableMessage, this._tree, this._previewModelReference); + this._callOnDispose.dispose(); + this._disposeOnNewModel.dispose(); + dispose(this._preview); + dispose(this._previewNotAvailableMessage); + dispose(this._tree); + dispose(this._previewModelReference); this._splitView.dispose(); super.dispose(); } @@ -344,11 +348,11 @@ export class ReferenceWidget extends PeekViewWidget { } }, Sizing.Distribute); - this._splitView.onDidSashChange(() => { + this._disposables.add(this._splitView.onDidSashChange(() => { if (this._dim.width) { this.layoutData.ratio = this._splitView.getViewSize(0) / this._dim.width; } - }, undefined, this._disposables); + }, undefined)); // listen on selection and focus let onEvent = (element: any, kind: 'show' | 'goto' | 'side') => { @@ -421,7 +425,7 @@ export class ReferenceWidget extends PeekViewWidget { public setModel(newModel: ReferencesModel | undefined): Promise { // clean up - this._disposeOnNewModel = dispose(this._disposeOnNewModel); + this._disposeOnNewModel.clear(); this._model = newModel; if (this._model) { return this._onNewModel(); @@ -443,13 +447,13 @@ export class ReferenceWidget extends PeekViewWidget { dom.hide(this._messageContainer); this._decorationsManager = new DecorationsManager(this._preview, this._model); - this._disposeOnNewModel.push(this._decorationsManager); + this._disposeOnNewModel.add(this._decorationsManager); // listen on model changes - this._disposeOnNewModel.push(this._model.onDidChangeReferenceRange(reference => this._tree.rerender(reference))); + this._disposeOnNewModel.add(this._model.onDidChangeReferenceRange(reference => this._tree.rerender(reference))); // listen on editor - this._disposeOnNewModel.push(this._preview.onMouseDown(e => { + this._disposeOnNewModel.add(this._preview.onMouseDown(e => { const { event, target } = e; if (event.detail !== 2) { return; @@ -566,7 +570,7 @@ export const peekViewEditorMatchHighlightBorder = registerColor('peekViewEditor. registerThemingParticipant((theme, collector) => { const findMatchHighlightColor = theme.getColor(peekViewResultsMatchHighlight); if (findMatchHighlightColor) { - collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .referenceMatch { background-color: ${findMatchHighlightColor}; }`); + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .referenceMatch .highlight { background-color: ${findMatchHighlightColor}; }`); } const referenceHighlightColor = theme.getColor(peekViewEditorMatchHighlight); if (referenceHighlightColor) { @@ -578,7 +582,7 @@ registerThemingParticipant((theme, collector) => { } const hcOutline = theme.getColor(activeContrastBorder); if (hcOutline) { - collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .referenceMatch { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`); + collector.addRule(`.monaco-editor .reference-zone-widget .ref-tree .referenceMatch .highlight { border: 1px dotted ${hcOutline}; box-sizing: border-box; }`); } const resultsBackground = theme.getColor(peekViewResultsBackground); if (resultsBackground) { diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index e1d6bed0c4..dfac4b9106 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { illegalArgument, onUnexpectedError } from 'vs/base/common/errors'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -114,7 +114,7 @@ class RenameController extends Disposable implements IEditorContribution { private readonly editor: ICodeEditor, @INotificationService private readonly _notificationService: INotificationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, - @IProgressService private readonly _progressService: IProgressService, + @IEditorProgressService private readonly _progressService: IEditorProgressService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IThemeService private readonly _themeService: IThemeService, ) { diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index 79961cb674..3b6042c5f9 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./renameInputField'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -24,10 +24,10 @@ export class RenameInputField implements IContentWidget, IDisposable { private _inputField: HTMLInputElement; private _visible: boolean; private readonly _visibleContextKey: IContextKey; - private _disposables: IDisposable[] = []; + private readonly _disposables = new DisposableStore(); // Editor.IContentWidget.allowEditorOverflow - public allowEditorOverflow: boolean = true; + allowEditorOverflow: boolean = true; constructor( editor: ICodeEditor, @@ -39,13 +39,13 @@ export class RenameInputField implements IContentWidget, IDisposable { this._editor = editor; this._editor.addContentWidget(this); - this._disposables.push(editor.onDidChangeConfiguration(e => { + this._disposables.add(editor.onDidChangeConfiguration(e => { if (e.fontInfo) { this.updateFont(); } })); - this._disposables.push(themeService.onThemeChange(theme => this.onThemeChange(theme))); + this._disposables.add(themeService.onThemeChange(theme => this.onThemeChange(theme))); } private onThemeChange(theme: ITheme): void { @@ -53,7 +53,7 @@ export class RenameInputField implements IContentWidget, IDisposable { } public dispose(): void { - this._disposables = dispose(this._disposables); + this._disposables.dispose(); this._editor.removeContentWidget(this); } @@ -138,9 +138,9 @@ export class RenameInputField implements IContentWidget, IDisposable { this._inputField.setAttribute('selectionEnd', selectionEnd.toString()); this._inputField.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); - const disposeOnDone: IDisposable[] = []; + const disposeOnDone = new DisposableStore(); const always = () => { - dispose(disposeOnDone); + disposeOnDone.dispose(); this._hide(); }; @@ -172,8 +172,8 @@ export class RenameInputField implements IContentWidget, IDisposable { } }; - disposeOnDone.push(this._editor.onDidChangeCursorSelection(onCursorChanged)); - disposeOnDone.push(this._editor.onDidBlurEditorWidget(() => this.cancelInput(false))); + disposeOnDone.add(this._editor.onDidChangeCursorSelection(onCursorChanged)); + disposeOnDone.add(this._editor.onDidBlurEditorWidget(() => this.cancelInput(false))); this._show(); diff --git a/src/vs/editor/contrib/smartSelect/bracketSelections.ts b/src/vs/editor/contrib/smartSelect/bracketSelections.ts index 6fd74cf046..cabec83178 100644 --- a/src/vs/editor/contrib/smartSelect/bracketSelections.ts +++ b/src/vs/editor/contrib/smartSelect/bracketSelections.ts @@ -115,8 +115,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider { } const innerBracket = Range.fromPositions(bracket.range.getEndPosition(), closing!.getStartPosition()); const outerBracket = Range.fromPositions(bracket.range.getStartPosition(), closing!.getEndPosition()); - bucket.push({ range: innerBracket, kind: 'statement.brackets' }); - bucket.push({ range: outerBracket, kind: 'statement.brackets.full' }); + bucket.push({ range: innerBracket }); + bucket.push({ range: outerBracket }); BracketSelectionRangeProvider._addBracketLeading(model, outerBracket, bucket); } } @@ -135,8 +135,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider { const startLine = bracket.startLineNumber; const column = model.getLineFirstNonWhitespaceColumn(startLine); if (column !== 0 && column !== bracket.startColumn) { - bucket.push({ range: Range.fromPositions(new Position(startLine, column), bracket.getEndPosition()), kind: 'statement.brackets.leading' }); - bucket.push({ range: Range.fromPositions(new Position(startLine, 1), bracket.getEndPosition()), kind: 'statement.brackets.leading.full' }); + bucket.push({ range: Range.fromPositions(new Position(startLine, column), bracket.getEndPosition()) }); + bucket.push({ range: Range.fromPositions(new Position(startLine, 1), bracket.getEndPosition()) }); } // xxxxxxxx @@ -147,8 +147,8 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider { if (aboveLine > 0) { const column = model.getLineFirstNonWhitespaceColumn(aboveLine); if (column === bracket.startColumn && column !== model.getLineLastNonWhitespaceColumn(aboveLine)) { - bucket.push({ range: Range.fromPositions(new Position(aboveLine, column), bracket.getEndPosition()), kind: 'statement.brackets.leading' }); - bucket.push({ range: Range.fromPositions(new Position(aboveLine, 1), bracket.getEndPosition()), kind: 'statement.brackets.leading.full' }); + bucket.push({ range: Range.fromPositions(new Position(aboveLine, column), bracket.getEndPosition()) }); + bucket.push({ range: Range.fromPositions(new Position(aboveLine, 1), bracket.getEndPosition()) }); } } } diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 2d2bf8140e..ab36647627 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -284,12 +284,12 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { // add line/block range without leading/failing whitespace const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); - if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev)) { + if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev) && cur.containsRange(rangeNoWhitespace) && !cur.equalsRange(rangeNoWhitespace)) { oneRangesWithTrivia.push(rangeNoWhitespace); } // add line/block range const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); - if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace)) { + if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace) && cur.containsRange(rangeFull) && !cur.equalsRange(rangeFull)) { oneRangesWithTrivia.push(rangeFull); } } diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 240209c453..4cbba32e1a 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { Range, IRange } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; -import { LanguageIdentifier, SelectionRangeProvider } from 'vs/editor/common/modes'; +import { LanguageIdentifier, SelectionRangeProvider, SelectionRangeRegistry } from 'vs/editor/common/modes'; import { MockMode, StaticLanguageSelector } from 'vs/editor/test/common/mocks/mockMode'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; @@ -320,4 +320,25 @@ suite('SmartSelect', () => { new Range(1, 1, 1, 21), ); }); + + test('Smart select: only add line ranges if they’re contained by the next range #73850', async function () { + + const reg = SelectionRangeRegistry.register('*', { + provideSelectionRanges() { + return [[ + { range: { startLineNumber: 1, startColumn: 10, endLineNumber: 1, endColumn: 11 } }, + { range: { startLineNumber: 1, startColumn: 10, endLineNumber: 3, endColumn: 2 } }, + { range: { startLineNumber: 1, startColumn: 1, endLineNumber: 3, endColumn: 2 } }, + ]]; + } + }); + + await assertGetRangesToPosition(['type T = {', '\tx: number', '}'], 1, 10, [ + new Range(1, 1, 3, 2), // all + new Range(1, 10, 3, 2), // { ... } + new Range(1, 10, 1, 11), // { + ]); + + reg.dispose(); + }); }); diff --git a/src/vs/editor/contrib/smartSelect/wordSelections.ts b/src/vs/editor/contrib/smartSelect/wordSelections.ts index 12ff057ef4..78c7a7252e 100644 --- a/src/vs/editor/contrib/smartSelect/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/wordSelections.ts @@ -20,7 +20,7 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { this._addInWordRanges(bucket, model, position); this._addWordRanges(bucket, model, position); this._addWhitespaceLine(bucket, model, position); - bucket.push({ range: model.getFullModelRange(), kind: 'statement.all' }); + bucket.push({ range: model.getFullModelRange() }); } return result; } @@ -65,14 +65,14 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { } if (start < end) { - bucket.push({ range: new Range(pos.lineNumber, startColumn + start, pos.lineNumber, startColumn + end), kind: 'statement.word.part' }); + bucket.push({ range: new Range(pos.lineNumber, startColumn + start, pos.lineNumber, startColumn + end) }); } } private _addWordRanges(bucket: SelectionRange[], model: ITextModel, pos: Position): void { const word = model.getWordAtPosition(pos); if (word) { - bucket.push({ range: new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn), kind: 'statement.word' }); + bucket.push({ range: new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn) }); } } @@ -81,7 +81,7 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { && model.getLineFirstNonWhitespaceColumn(pos.lineNumber) === 0 && model.getLineLastNonWhitespaceColumn(pos.lineNumber) === 0 ) { - bucket.push({ range: new Range(pos.lineNumber, 1, pos.lineNumber, model.getLineMaxColumn(pos.lineNumber)), kind: 'statement.line' }); + bucket.push({ range: new Range(pos.lineNumber, 1, pos.lineNumber, model.getLineMaxColumn(pos.lineNumber)) }); } } } diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 69bb012890..7fd0f4dd80 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { repeat } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -20,6 +20,24 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; +export interface ISnippetInsertOptions { + overwriteBefore: number; + overwriteAfter: number; + adjustWhitespace: boolean; + undoStopBefore: boolean; + undoStopAfter: boolean; + clipboardText: string | undefined; +} + +const _defaultOptions: ISnippetInsertOptions = { + overwriteBefore: 0, + overwriteAfter: 0, + undoStopBefore: true, + undoStopAfter: true, + adjustWhitespace: true, + clipboardText: undefined +}; + export class SnippetController2 implements IEditorContribution { static get(editor: ICodeEditor): SnippetController2 { @@ -35,7 +53,7 @@ export class SnippetController2 implements IEditorContribution { private readonly _hasPrevTabstop: IContextKey; private _session?: SnippetSession; - private _snippetListener: IDisposable[] = []; + private _snippetListener = new DisposableStore(); private _modelVersionId: number; private _currentChoice?: Choice; @@ -54,6 +72,7 @@ export class SnippetController2 implements IEditorContribution { this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); dispose(this._session); + this._snippetListener.dispose(); } getId(): string { @@ -62,15 +81,13 @@ export class SnippetController2 implements IEditorContribution { insert( template: string, - overwriteBefore: number = 0, overwriteAfter: number = 0, - undoStopBefore: boolean = true, undoStopAfter: boolean = true, - adjustWhitespace: boolean = true, + opts?: Partial ): void { // this is here to find out more about the yet-not-understood // error that sometimes happens when we fail to inserted a nested // snippet try { - this._doInsert(template, overwriteBefore, overwriteAfter, undoStopBefore, undoStopAfter, adjustWhitespace); + this._doInsert(template, typeof opts === 'undefined' ? _defaultOptions : { ..._defaultOptions, ...opts }); } catch (e) { this.cancel(); @@ -83,9 +100,7 @@ export class SnippetController2 implements IEditorContribution { private _doInsert( template: string, - overwriteBefore: number = 0, overwriteAfter: number = 0, - undoStopBefore: boolean = true, undoStopAfter: boolean = true, - adjustWhitespace: boolean = true, + opts: ISnippetInsertOptions ): void { if (!this._editor.hasModel()) { return; @@ -93,31 +108,29 @@ export class SnippetController2 implements IEditorContribution { // don't listen while inserting the snippet // as that is the inflight state causing cancelation - this._snippetListener = dispose(this._snippetListener); + this._snippetListener.clear(); - if (undoStopBefore) { + if (opts.undoStopBefore) { this._editor.getModel().pushStackElement(); } if (!this._session) { this._modelVersionId = this._editor.getModel().getAlternativeVersionId(); - this._session = new SnippetSession(this._editor, template, overwriteBefore, overwriteAfter, adjustWhitespace); + this._session = new SnippetSession(this._editor, template, opts); this._session.insert(); } else { - this._session.merge(template, overwriteBefore, overwriteAfter, adjustWhitespace); + this._session.merge(template, opts); } - if (undoStopAfter) { + if (opts.undoStopAfter) { this._editor.getModel().pushStackElement(); } this._updateState(); - this._snippetListener = [ - this._editor.onDidChangeModelContent(e => e.isFlush && this.cancel()), - this._editor.onDidChangeModel(() => this.cancel()), - this._editor.onDidChangeCursorSelection(() => this._updateState()) - ]; + this._snippetListener.add(this._editor.onDidChangeModelContent(e => e.isFlush && this.cancel())); + this._snippetListener.add(this._editor.onDidChangeModel(() => this.cancel())); + this._snippetListener.add(this._editor.onDidChangeCursorSelection(() => this._updateState())); } private _updateState(): void { @@ -197,7 +210,7 @@ export class SnippetController2 implements IEditorContribution { this._inSnippet.reset(); this._hasPrevTabstop.reset(); this._hasNextTabstop.reset(); - dispose(this._snippetListener); + this._snippetListener.clear(); dispose(this._session); this._session = undefined; this._modelVersionId = -1; diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index e8745ff99e..0292ba7422 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -17,11 +17,12 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { optional } from 'vs/platform/instantiation/common/instantiation'; -import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet } from './snippetParser'; +import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet, Marker } from './snippetParser'; import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver, WorkspaceBasedVariableResolver } from './snippetVariables'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import * as colors from 'vs/platform/theme/common/colorRegistry'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { ILabelService } from 'vs/platform/label/common/label'; registerThemingParticipant((theme, collector) => { @@ -122,14 +123,14 @@ export class OneSnippet { } } - let skipThisPlaceholder = false; + let couldSkipThisPlaceholder = false; if (fwd === true && this._placeholderGroupsIdx < this._placeholderGroups.length - 1) { this._placeholderGroupsIdx += 1; - skipThisPlaceholder = true; + couldSkipThisPlaceholder = true; } else if (fwd === false && this._placeholderGroupsIdx > 0) { this._placeholderGroupsIdx -= 1; - skipThisPlaceholder = true; + couldSkipThisPlaceholder = true; } else { // the selection of the current placeholder might @@ -154,7 +155,7 @@ export class OneSnippet { // consider to skip this placeholder index when the decoration // range is empty but when the placeholder wasn't. that's a strong // hint that the placeholder has been deleted. (all placeholder must match this) - skipThisPlaceholder = skipThisPlaceholder && (range.isEmpty() && placeholder.toString().length > 0); + couldSkipThisPlaceholder = couldSkipThisPlaceholder && this._hasPlaceholderBeenCollapsed(placeholder); accessor.changeDecorationOptions(id, placeholder.isFinalTabstop ? OneSnippet._decor.activeFinal : OneSnippet._decor.active); activePlaceholders.add(placeholder); @@ -177,7 +178,25 @@ export class OneSnippet { return selections; })!; - return !skipThisPlaceholder ? newSelections : this.move(fwd); + return !couldSkipThisPlaceholder ? newSelections : this.move(fwd); + } + + private _hasPlaceholderBeenCollapsed(placeholder: Placeholder): boolean { + // A placeholder is empty when it wasn't empty when authored but + // when its tracking decoration is empty. This also applies to all + // potential parent placeholders + let marker: Marker | undefined = placeholder; + while (marker) { + if (marker instanceof Placeholder) { + const id = this._placeholderDecorations.get(marker)!; + const range = this._editor.getModel().getDecorationRange(id)!; + if (range.isEmpty() && marker.toString().length > 0) { + return true; + } + } + marker = marker.parent; + } + return false; } get isAtFirstPlaceholder() { @@ -297,6 +316,20 @@ export class OneSnippet { } } +export interface ISnippetSessionInsertOptions { + overwriteBefore: number; + overwriteAfter: number; + adjustWhitespace: boolean; + clipboardText: string | undefined; +} + +const _defaultOptions: ISnippetSessionInsertOptions = { + overwriteBefore: 0, + overwriteAfter: 0, + adjustWhitespace: true, + clipboardText: undefined +}; + export class SnippetSession { static adjustWhitespace(model: ITextModel, position: IPosition, snippet: TextmateSnippet): void { @@ -345,7 +378,7 @@ export class SnippetSession { return selection; } - static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean, clipboardText: string | undefined): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { const edits: IIdentifiedSingleEditOperation[] = []; const snippets: OneSnippet[] = []; @@ -354,9 +387,11 @@ export class SnippetSession { } const model = editor.getModel(); - const modelBasedVariableResolver = new ModelBasedVariableResolver(model); - const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional)); const workspaceService = editor.invokeWithinContext(accessor => accessor.get(IWorkspaceContextService, optional)); + const modelBasedVariableResolver = editor.invokeWithinContext(accessor => new ModelBasedVariableResolver(accessor.get(ILabelService, optional), model)); + + const clipboardService = editor.invokeWithinContext(accessor => accessor.get(IClipboardService, optional)); + clipboardText = clipboardText || clipboardService && clipboardService.readTextSync(); let delta = 0; @@ -409,7 +444,7 @@ export class SnippetSession { snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, - new ClipboardBasedVariableResolver(clipboardService, idx, indexedSelections.length), + new ClipboardBasedVariableResolver(clipboardText, idx, indexedSelections.length), new SelectionBasedVariableResolver(model, selection), new CommentBasedVariableResolver(model), new TimeBasedVariableResolver, @@ -432,17 +467,13 @@ export class SnippetSession { private readonly _editor: IActiveCodeEditor; private readonly _template: string; private readonly _templateMerges: [number, number, string][] = []; - private readonly _overwriteBefore: number; - private readonly _overwriteAfter: number; - private readonly _adjustWhitespace: boolean; + private readonly _options: ISnippetSessionInsertOptions; private _snippets: OneSnippet[] = []; - constructor(editor: IActiveCodeEditor, template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true) { + constructor(editor: IActiveCodeEditor, template: string, options: ISnippetSessionInsertOptions = _defaultOptions) { this._editor = editor; this._template = template; - this._overwriteBefore = overwriteBefore; - this._overwriteAfter = overwriteAfter; - this._adjustWhitespace = adjustWhitespace; + this._options = options; } dispose(): void { @@ -461,7 +492,7 @@ export class SnippetSession { const model = this._editor.getModel(); // make insert edit and start with first selections - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._overwriteBefore, this._overwriteAfter, false, this._adjustWhitespace); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText); this._snippets = snippets; const selections = model.pushEditOperations(this._editor.getSelections(), edits, undoEdits => { @@ -475,12 +506,12 @@ export class SnippetSession { this._editor.revealRange(selections[0]); } - merge(template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true): void { + merge(template: string, options: ISnippetSessionInsertOptions = _defaultOptions): void { if (!this._editor.hasModel()) { return; } this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]); - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, overwriteBefore, overwriteAfter, true, adjustWhitespace); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText); this._editor.setSelections(this._editor.getModel().pushEditOperations(this._editor.getSelections(), edits, undoEdits => { diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index d2a364c37c..7ef44d80ad 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -4,17 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { basename, dirname } from 'vs/base/common/path'; +import * as path from 'vs/base/common/path'; +import { dirname } from 'vs/base/common/resources'; import { ITextModel } from 'vs/editor/common/model'; import { Selection } from 'vs/editor/common/core/selection'; import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad, endsWith } from 'vs/base/common/strings'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; +import { ILabelService } from 'vs/platform/label/common/label'; -export const KnownSnippetVariableNames = Object.freeze({ +export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({ 'CURRENT_YEAR': true, 'CURRENT_YEAR_SHORT': true, 'CURRENT_MONTH': true, @@ -126,6 +127,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { export class ModelBasedVariableResolver implements VariableResolver { constructor( + private readonly _labelService: ILabelService | undefined, private readonly _model: ITextModel ) { // @@ -136,10 +138,10 @@ export class ModelBasedVariableResolver implements VariableResolver { const { name } = variable; if (name === 'TM_FILENAME') { - return basename(this._model.uri.fsPath); + return path.basename(this._model.uri.fsPath); } else if (name === 'TM_FILENAME_BASE') { - const name = basename(this._model.uri.fsPath); + const name = path.basename(this._model.uri.fsPath); const idx = name.lastIndexOf('.'); if (idx <= 0) { return name; @@ -147,12 +149,14 @@ export class ModelBasedVariableResolver implements VariableResolver { return name.slice(0, idx); } - } else if (name === 'TM_DIRECTORY') { - const dir = dirname(this._model.uri.fsPath); - return dir !== '.' ? dir : ''; + } else if (name === 'TM_DIRECTORY' && this._labelService) { + if (path.dirname(this._model.uri.fsPath) === '.') { + return ''; + } + return this._labelService.getUriLabel(dirname(this._model.uri)); - } else if (name === 'TM_FILEPATH') { - return this._model.uri.fsPath; + } else if (name === 'TM_FILEPATH' && this._labelService) { + return this._labelService.getUriLabel(this._model.uri); } return undefined; @@ -162,7 +166,7 @@ export class ModelBasedVariableResolver implements VariableResolver { export class ClipboardBasedVariableResolver implements VariableResolver { constructor( - private readonly _clipboardService: IClipboardService, + private readonly _clipboardText: string | undefined, private readonly _selectionIdx: number, private readonly _selectionCount: number ) { @@ -170,20 +174,19 @@ export class ClipboardBasedVariableResolver implements VariableResolver { } resolve(variable: Variable): string | undefined { - if (variable.name !== 'CLIPBOARD' || !this._clipboardService) { + if (variable.name !== 'CLIPBOARD') { return undefined; } - const text = this._clipboardService.readText(); - if (!text) { + if (!this._clipboardText) { return undefined; } - const lines = text.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s)); + const lines = this._clipboardText.split(/\r\n|\n|\r/).filter(s => !isFalsyOrWhitespace(s)); if (lines.length === this._selectionCount) { return lines[this._selectionIdx]; } else { - return text; + return this._clipboardText; } } } @@ -250,7 +253,7 @@ export class TimeBasedVariableResolver implements VariableResolver { export class WorkspaceBasedVariableResolver implements VariableResolver { constructor( - private readonly _workspaceService: IWorkspaceContextService, + private readonly _workspaceService: IWorkspaceContextService | undefined, ) { // } @@ -266,13 +269,13 @@ export class WorkspaceBasedVariableResolver implements VariableResolver { } if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) { - return basename(workspaceIdentifier.path); + return path.basename(workspaceIdentifier.path); } - let filename = basename(workspaceIdentifier.configPath.path); + let filename = path.basename(workspaceIdentifier.configPath.path); if (endsWith(filename, WORKSPACE_EXTENSION)) { filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); } return filename; } -} \ No newline at end of file +} diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts index 14148e1776..08fdd58d9d 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.old.test.ts @@ -62,7 +62,7 @@ suite('SnippetController', () => { snippetTest((editor, cursor, template, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(template, 0, 0); + snippetController.insert(template); assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {'); assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];'); assert.equal(editor.getModel()!.getLineContent(6), '\t\t'); @@ -98,7 +98,7 @@ suite('SnippetController', () => { snippetTest((editor, cursor, template, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(template, 0, 0); + snippetController.insert(template); assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {'); assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];'); assert.equal(editor.getModel()!.getLineContent(6), '\t\t'); @@ -180,7 +180,7 @@ suite('SnippetController', () => { test('Stops when calling model.setValue()', () => { snippetTest((editor, cursor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); editor.getModel()!.setValue('goodbye'); @@ -191,7 +191,7 @@ suite('SnippetController', () => { test('Stops when undoing', () => { snippetTest((editor, cursor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); editor.getModel()!.undo(); @@ -202,7 +202,7 @@ suite('SnippetController', () => { test('Stops when moving cursor outside', () => { snippetTest((editor, cursor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); editor.setPosition({ lineNumber: 1, column: 1 }); @@ -213,7 +213,7 @@ suite('SnippetController', () => { test('Stops when disconnecting editor model', () => { snippetTest((editor, cursor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); editor.setModel(null); @@ -224,7 +224,7 @@ suite('SnippetController', () => { test('Stops when disposing editor', () => { snippetTest((editor, cursor, codeSnippet, snippetController) => { editor.setPosition({ lineNumber: 4, column: 2 }); - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); snippetController.dispose(); @@ -240,7 +240,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'foo$0'; - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); assert.equal(editor.getSelections()!.length, 2); const [first, second] = editor.getSelections()!; @@ -255,7 +255,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'foo$0bar'; - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); assert.equal(editor.getSelections()!.length, 2); const [first, second] = editor.getSelections()!; @@ -270,7 +270,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'foo$0bar'; - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); assert.equal(editor.getSelections()!.length, 2); const [first, second] = editor.getSelections()!; @@ -285,7 +285,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'foo\n$0\nbar'; - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); assert.equal(editor.getSelections()!.length, 2); const [first, second] = editor.getSelections()!; @@ -300,7 +300,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'foo\n$0\nbar'; - snippetController.insert(codeSnippet, 0, 0); + snippetController.insert(codeSnippet); assert.equal(editor.getSelections()!.length, 2); const [first, second] = editor.getSelections()!; @@ -314,7 +314,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'xo$0r'; - snippetController.insert(codeSnippet, 1, 0); + snippetController.insert(codeSnippet, { overwriteBefore: 1 }); assert.equal(editor.getSelections()!.length, 1); assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 8, endColumn: 8, endLineNumber: 2 })); @@ -327,7 +327,7 @@ suite('SnippetController', () => { editor.setSelection(new Selection(1, 19, 1, 19)); codeSnippet = '{{% url_**$1** %}}'; - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getSelections()!.length, 1); assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 1, startColumn: 27, endLineNumber: 1, endColumn: 27 })); @@ -345,7 +345,7 @@ suite('SnippetController', () => { '});' ].join('\n'); - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getSelections()!.length, 1); assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 2 }), editor.getSelection()!.toString()); @@ -363,7 +363,7 @@ suite('SnippetController', () => { '});' ].join('\n'); - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getSelections()!.length, 1); assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), editor.getSelection()!.toString()); @@ -379,7 +379,7 @@ suite('SnippetController', () => { 'aft${1}er' ].join('\n'); - controller.insert(codeSnippet, 8, 0); + controller.insert(codeSnippet, { overwriteBefore: 8 }); assert.equal(editor.getModel()!.getValue(), 'after'); assert.equal(editor.getSelections()!.length, 1); @@ -403,7 +403,7 @@ suite('SnippetController', () => { '});' ].join('\n'); - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getSelections()!.length, 2); const [first, second] = editor.getSelections()!; @@ -428,7 +428,7 @@ suite('SnippetController', () => { '});' ].join('\n'); - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getSelections()!.length, 1); const [first] = editor.getSelections()!; @@ -448,7 +448,7 @@ suite('SnippetController', () => { codeSnippet = 'afterEach'; - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 1, startColumn: 10, endLineNumber: 1, endColumn: 10 })); @@ -465,7 +465,7 @@ suite('SnippetController', () => { ]); codeSnippet = '_foo'; - controller.insert(codeSnippet, 1, 0); + controller.insert(codeSnippet, { overwriteBefore: 1 }); assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc_foo'); }, ['this._', 'abc']); @@ -478,7 +478,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'XX'; - controller.insert(codeSnippet, 1, 0); + controller.insert(codeSnippet, { overwriteBefore: 1 }); assert.equal(editor.getModel()!.getValue(), 'this.XX\nabcXX'); }, ['this._', 'abc']); @@ -492,7 +492,7 @@ suite('SnippetController', () => { ]); codeSnippet = '_foo'; - controller.insert(codeSnippet, 1, 0); + controller.insert(codeSnippet, { overwriteBefore: 1 }); assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc_foo\ndef_foo'); }, ['this._', 'abc', 'def_']); @@ -506,7 +506,7 @@ suite('SnippetController', () => { ]); codeSnippet = '._foo'; - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo'); }, ['this._', 'abc', 'def._']); @@ -520,7 +520,7 @@ suite('SnippetController', () => { ]); codeSnippet = '._foo'; - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo'); }, ['this._', 'abc', 'def._']); @@ -534,7 +534,7 @@ suite('SnippetController', () => { ]); codeSnippet = '._foo'; - controller.insert(codeSnippet, 2, 0); + controller.insert(codeSnippet, { overwriteBefore: 2 }); assert.equal(editor.getModel()!.getValue(), 'this._._foo\na._foo\ndef._._foo'); }, ['this._', 'abc', 'def._']); @@ -550,7 +550,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'document'; - controller.insert(codeSnippet, 3, 0); + controller.insert(codeSnippet, { overwriteBefore: 3 }); assert.equal(editor.getModel()!.getValue(), '{document}\n{document && true}'); }, ['{foo}', '{foo && true}']); @@ -565,7 +565,7 @@ suite('SnippetController', () => { ]); codeSnippet = 'for (var ${1:i}=0; ${1:i} { ]); codeSnippet = 'for (let ${1:i}=0; ${1:i} { editor.setSelection(new Selection(2, 5, 2, 5)); - const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', 0, 0, false); + const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined }); session.insert(); assert.equal(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}'); }); @@ -648,7 +648,7 @@ suite('SnippetSession', function () { assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 12))); editor.setSelections([new Selection(1, 9, 1, 12)]); - new SnippetSession(editor, 'far', 3, 0).insert(); + new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined }).insert(); assert.equal(model.getValue(), 'console.far'); }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 542a965348..c67a35d98a 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -9,11 +9,18 @@ import { Selection } from 'vs/editor/common/core/selection'; import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, ClipboardBasedVariableResolver, TimeBasedVariableResolver, WorkspaceBasedVariableResolver } from 'vs/editor/contrib/snippet/snippetVariables'; import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/snippetParser'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test'; suite('Snippet Variables Resolver', function () { + const labelService = new class extends mock() { + getUriLabel(uri: URI) { + return uri.fsPath; + } + }; + let model: TextModel; let resolver: VariableResolver; @@ -25,7 +32,7 @@ suite('Snippet Variables Resolver', function () { ].join('\n'), undefined, undefined, URI.parse('file:///foo/files/text.txt')); resolver = new CompositeSnippetVariableResolver([ - new ModelBasedVariableResolver(model), + new ModelBasedVariableResolver(labelService, model), new SelectionBasedVariableResolver(model, new Selection(1, 1, 1, 1)), ]); }); @@ -59,6 +66,7 @@ suite('Snippet Variables Resolver', function () { } resolver = new ModelBasedVariableResolver( + labelService, TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) ); assertVariableResolve(resolver, 'TM_FILENAME', 'ghi'); @@ -68,6 +76,7 @@ suite('Snippet Variables Resolver', function () { } resolver = new ModelBasedVariableResolver( + labelService, TextModel.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')) ); assertVariableResolve(resolver, 'TM_DIRECTORY', ''); @@ -75,6 +84,21 @@ suite('Snippet Variables Resolver', function () { }); + test('Path delimiters in code snippet variables aren\'t specific to remote OS #76840', function () { + + const labelService = new class extends mock() { + getUriLabel(uri: URI) { + return uri.fsPath.replace(/\/|\\/g, '|'); + } + }; + + const model = TextModel.createFromString([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt')); + + const resolver = new CompositeSnippetVariableResolver([new ModelBasedVariableResolver(labelService, model)]); + + assertVariableResolve(resolver, 'TM_FILEPATH', '|foo|files|text.txt'); + }); + test('editor variables, selection', function () { resolver = new SelectionBasedVariableResolver(model, new Selection(1, 2, 2, 3)); @@ -119,16 +143,19 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'text'); resolver = new ModelBasedVariableResolver( + labelService, TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'ghi'); resolver = new ModelBasedVariableResolver( + labelService, TextModel.createFromString('', undefined, undefined, URI.parse('mem:.git')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', '.git'); resolver = new ModelBasedVariableResolver( + labelService, TextModel.createFromString('', undefined, undefined, URI.parse('mem:foo.')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'foo'); @@ -209,63 +236,26 @@ suite('Snippet Variables Resolver', function () { test('Add variable to insert value from clipboard to a snippet #40153', function () { - let readTextResult: string | null | undefined; - const clipboardService = new class implements IClipboardService { - _serviceBrand: any; - readText(): any { return readTextResult; } - _throw = () => { throw new Error(); }; - writeText = this._throw; - readFindText = this._throw; - writeFindText = this._throw; - writeResources = this._throw; - readResources = this._throw; - hasResources = this._throw; - }; - let resolver = new ClipboardBasedVariableResolver(clipboardService, 1, 0); + assertVariableResolve(new ClipboardBasedVariableResolver(undefined, 1, 0), 'CLIPBOARD', undefined); - readTextResult = undefined; - assertVariableResolve(resolver, 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver(null!, 1, 0), 'CLIPBOARD', undefined); - readTextResult = null; - assertVariableResolve(resolver, 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('', 1, 0), 'CLIPBOARD', undefined); - readTextResult = ''; - assertVariableResolve(resolver, 'CLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0), 'CLIPBOARD', 'foo'); - readTextResult = 'foo'; - assertVariableResolve(resolver, 'CLIPBOARD', 'foo'); - - assertVariableResolve(resolver, 'foo', undefined); - assertVariableResolve(resolver, 'cLIPBOARD', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0), 'foo', undefined); + assertVariableResolve(new ClipboardBasedVariableResolver('foo', 1, 0), 'cLIPBOARD', undefined); }); test('Add variable to insert value from clipboard to a snippet #40153', function () { - let readTextResult: string; - let resolver: VariableResolver; - const clipboardService = new class implements IClipboardService { - _serviceBrand: any; - readText(): string { return readTextResult; } - _throw = () => { throw new Error(); }; - writeText = this._throw; - readFindText = this._throw; - writeFindText = this._throw; - writeResources = this._throw; - readResources = this._throw; - hasResources = this._throw; - }; + assertVariableResolve(new ClipboardBasedVariableResolver('line1', 1, 2), 'CLIPBOARD', 'line1'); + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2\nline3', 1, 2), 'CLIPBOARD', 'line1\nline2\nline3'); - resolver = new ClipboardBasedVariableResolver(clipboardService, 1, 2); - readTextResult = 'line1'; - assertVariableResolve(resolver, 'CLIPBOARD', 'line1'); - readTextResult = 'line1\nline2\nline3'; - assertVariableResolve(resolver, 'CLIPBOARD', 'line1\nline2\nline3'); - - readTextResult = 'line1\nline2'; - assertVariableResolve(resolver, 'CLIPBOARD', 'line2'); - readTextResult = 'line1\nline2'; - resolver = new ClipboardBasedVariableResolver(clipboardService, 0, 2); - assertVariableResolve(resolver, 'CLIPBOARD', 'line1'); + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 1, 2), 'CLIPBOARD', 'line2'); + resolver = new ClipboardBasedVariableResolver('line1\nline2', 0, 2); + assertVariableResolve(new ClipboardBasedVariableResolver('line1\nline2', 0, 2), 'CLIPBOARD', 'line1'); }); @@ -336,4 +326,4 @@ suite('Snippet Variables Resolver', function () { workspace = new Workspace('', toWorkspaceFolders([{ path: 'folderName' }], workspaceConfigPath), workspaceConfigPath); assertVariableResolve(resolver, 'WORKSPACE_NAME', 'testWorkspace'); }); -}); \ No newline at end of file +}); diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index 1e33eb7261..bc5c38cf20 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { fuzzyScore, fuzzyScoreGracefulAggressive, FuzzyScorer, FuzzyScore, anyScore } from 'vs/base/common/filters'; -import { isDisposable } from 'vs/base/common/lifecycle'; -import { CompletionList, CompletionItemProvider, CompletionItemKind } from 'vs/editor/common/modes'; +import { CompletionItemProvider, CompletionItemKind } from 'vs/editor/common/modes'; import { CompletionItem } from './suggest'; import { InternalSuggestOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; @@ -75,18 +74,6 @@ export class CompletionModel { } } - dispose(): void { - const seen = new Set(); - for (const { container } of this._items) { - if (!seen.has(container)) { - seen.add(container); - if (isDisposable(container)) { - container.dispose(); - } - } - } - } - get lineContext(): LineContext { return this._lineContext; } diff --git a/src/vs/editor/contrib/suggest/media/Class_16x.svg b/src/vs/editor/contrib/suggest/media/Class_16x.svg deleted file mode 100644 index 5ef1c6f80b..0000000000 --- a/src/vs/editor/contrib/suggest/media/Class_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Class_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Class_inverse_16x.svg deleted file mode 100644 index c43aad29ef..0000000000 --- a/src/vs/editor/contrib/suggest/media/Class_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/ColorPalette_16x.svg b/src/vs/editor/contrib/suggest/media/ColorPalette_16x.svg deleted file mode 100644 index 2af5cc6fae..0000000000 --- a/src/vs/editor/contrib/suggest/media/ColorPalette_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/ColorPalette_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/ColorPalette_inverse_16x.svg deleted file mode 100644 index 7afb32b895..0000000000 --- a/src/vs/editor/contrib/suggest/media/ColorPalette_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Constant_16x.svg b/src/vs/editor/contrib/suggest/media/Constant_16x.svg deleted file mode 100644 index ed2a175100..0000000000 --- a/src/vs/editor/contrib/suggest/media/Constant_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Constant_16x_inverse.svg b/src/vs/editor/contrib/suggest/media/Constant_16x_inverse.svg deleted file mode 100644 index 173e427f96..0000000000 --- a/src/vs/editor/contrib/suggest/media/Constant_16x_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Document_16x.svg b/src/vs/editor/contrib/suggest/media/Document_16x.svg deleted file mode 100644 index 13ded2953e..0000000000 --- a/src/vs/editor/contrib/suggest/media/Document_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Document_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Document_inverse_16x.svg deleted file mode 100644 index 949a376216..0000000000 --- a/src/vs/editor/contrib/suggest/media/Document_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/EnumItem_16x.svg b/src/vs/editor/contrib/suggest/media/EnumItem_16x.svg deleted file mode 100644 index aa901ec193..0000000000 --- a/src/vs/editor/contrib/suggest/media/EnumItem_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/EnumItem_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/EnumItem_inverse_16x.svg deleted file mode 100644 index 791759092f..0000000000 --- a/src/vs/editor/contrib/suggest/media/EnumItem_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Enumerator_16x.svg b/src/vs/editor/contrib/suggest/media/Enumerator_16x.svg deleted file mode 100644 index e4a9551fd5..0000000000 --- a/src/vs/editor/contrib/suggest/media/Enumerator_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Enumerator_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Enumerator_inverse_16x.svg deleted file mode 100644 index d8e9f4f107..0000000000 --- a/src/vs/editor/contrib/suggest/media/Enumerator_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Event_16x_vscode.svg b/src/vs/editor/contrib/suggest/media/Event_16x_vscode.svg deleted file mode 100644 index 0e202ec10b..0000000000 --- a/src/vs/editor/contrib/suggest/media/Event_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Event_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/media/Event_16x_vscode_inverse.svg deleted file mode 100644 index a508edcd3d..0000000000 --- a/src/vs/editor/contrib/suggest/media/Event_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Field_16x.svg b/src/vs/editor/contrib/suggest/media/Field_16x.svg deleted file mode 100644 index c6cb5362b3..0000000000 --- a/src/vs/editor/contrib/suggest/media/Field_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Field_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Field_inverse_16x.svg deleted file mode 100644 index 5fc48ceff0..0000000000 --- a/src/vs/editor/contrib/suggest/media/Field_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Folder_16x.svg b/src/vs/editor/contrib/suggest/media/Folder_16x.svg deleted file mode 100644 index 3d64ae71db..0000000000 --- a/src/vs/editor/contrib/suggest/media/Folder_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Folder_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Folder_inverse_16x.svg deleted file mode 100644 index 13b18d1801..0000000000 --- a/src/vs/editor/contrib/suggest/media/Folder_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode.svg b/src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode.svg deleted file mode 100644 index 5511fc9e23..0000000000 --- a/src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode_inverse.svg deleted file mode 100644 index 604d994cbd..0000000000 --- a/src/vs/editor/contrib/suggest/media/ImportFile_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_16x.svg b/src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_16x.svg deleted file mode 100644 index 4a69c4a038..0000000000 --- a/src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_inverse_16x.svg deleted file mode 100644 index decbf2c403..0000000000 --- a/src/vs/editor/contrib/suggest/media/IntelliSenseKeyword_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Interface_16x.svg b/src/vs/editor/contrib/suggest/media/Interface_16x.svg deleted file mode 100644 index 958a792742..0000000000 --- a/src/vs/editor/contrib/suggest/media/Interface_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Interface_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Interface_inverse_16x.svg deleted file mode 100644 index f7c2934a55..0000000000 --- a/src/vs/editor/contrib/suggest/media/Interface_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode.svg b/src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode.svg deleted file mode 100644 index e78894b6c6..0000000000 --- a/src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode_inverse.svg deleted file mode 100644 index 44a44b489d..0000000000 --- a/src/vs/editor/contrib/suggest/media/LocalVariable_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Method_16x.svg b/src/vs/editor/contrib/suggest/media/Method_16x.svg deleted file mode 100644 index 2be9daa5f5..0000000000 --- a/src/vs/editor/contrib/suggest/media/Method_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Method_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Method_inverse_16x.svg deleted file mode 100644 index d3c2c571d9..0000000000 --- a/src/vs/editor/contrib/suggest/media/Method_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Misc_16x.svg b/src/vs/editor/contrib/suggest/media/Misc_16x.svg deleted file mode 100644 index 13ff00b234..0000000000 --- a/src/vs/editor/contrib/suggest/media/Misc_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Misc_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Misc_inverse_16x.svg deleted file mode 100644 index 50a038657b..0000000000 --- a/src/vs/editor/contrib/suggest/media/Misc_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Namespace_16x.svg b/src/vs/editor/contrib/suggest/media/Namespace_16x.svg deleted file mode 100644 index dab07dd5ad..0000000000 --- a/src/vs/editor/contrib/suggest/media/Namespace_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Namespace_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Namespace_inverse_16x.svg deleted file mode 100644 index 9b9a44c52d..0000000000 --- a/src/vs/editor/contrib/suggest/media/Namespace_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Operator_16x_vscode.svg b/src/vs/editor/contrib/suggest/media/Operator_16x_vscode.svg deleted file mode 100644 index ba2f2d091c..0000000000 --- a/src/vs/editor/contrib/suggest/media/Operator_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Operator_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/media/Operator_16x_vscode_inverse.svg deleted file mode 100644 index 21e1e814b2..0000000000 --- a/src/vs/editor/contrib/suggest/media/Operator_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Property_16x.svg b/src/vs/editor/contrib/suggest/media/Property_16x.svg deleted file mode 100644 index fb1c74cf77..0000000000 --- a/src/vs/editor/contrib/suggest/media/Property_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Property_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Property_inverse_16x.svg deleted file mode 100644 index f90781897a..0000000000 --- a/src/vs/editor/contrib/suggest/media/Property_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Ruler_16x.svg b/src/vs/editor/contrib/suggest/media/Ruler_16x.svg deleted file mode 100644 index 2e8e88fef0..0000000000 --- a/src/vs/editor/contrib/suggest/media/Ruler_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Ruler_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Ruler_inverse_16x.svg deleted file mode 100644 index 373ab812f9..0000000000 --- a/src/vs/editor/contrib/suggest/media/Ruler_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Snippet_16x.svg b/src/vs/editor/contrib/suggest/media/Snippet_16x.svg deleted file mode 100644 index 8bf3b9f67d..0000000000 --- a/src/vs/editor/contrib/suggest/media/Snippet_16x.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - diff --git a/src/vs/editor/contrib/suggest/media/Snippet_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/Snippet_inverse_16x.svg deleted file mode 100644 index 501ff9c617..0000000000 --- a/src/vs/editor/contrib/suggest/media/Snippet_inverse_16x.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/src/vs/editor/contrib/suggest/media/String_16x.svg b/src/vs/editor/contrib/suggest/media/String_16x.svg deleted file mode 100644 index 35e744ce90..0000000000 --- a/src/vs/editor/contrib/suggest/media/String_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/String_inverse_16x.svg b/src/vs/editor/contrib/suggest/media/String_inverse_16x.svg deleted file mode 100644 index 1ac0cf99ac..0000000000 --- a/src/vs/editor/contrib/suggest/media/String_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Structure_16x_vscode.svg b/src/vs/editor/contrib/suggest/media/Structure_16x_vscode.svg deleted file mode 100644 index e776cbc565..0000000000 --- a/src/vs/editor/contrib/suggest/media/Structure_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Structure_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/media/Structure_16x_vscode_inverse.svg deleted file mode 100644 index 1b76b62be9..0000000000 --- a/src/vs/editor/contrib/suggest/media/Structure_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Template_16x_vscode.svg b/src/vs/editor/contrib/suggest/media/Template_16x_vscode.svg deleted file mode 100644 index 788cc8d645..0000000000 --- a/src/vs/editor/contrib/suggest/media/Template_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/Template_16x_vscode_inverse.svg b/src/vs/editor/contrib/suggest/media/Template_16x_vscode_inverse.svg deleted file mode 100644 index 6cec71cb03..0000000000 --- a/src/vs/editor/contrib/suggest/media/Template_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/class-dark.svg b/src/vs/editor/contrib/suggest/media/class-dark.svg new file mode 100644 index 0000000000..a71e221f6b --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/class-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/class-light.svg b/src/vs/editor/contrib/suggest/media/class-light.svg new file mode 100644 index 0000000000..aa106f18f8 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/class-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/close-dark.svg b/src/vs/editor/contrib/suggest/media/close-dark.svg index 751e89b3b0..556e2e2099 100644 --- a/src/vs/editor/contrib/suggest/media/close-dark.svg +++ b/src/vs/editor/contrib/suggest/media/close-dark.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/src/vs/editor/contrib/suggest/media/close-light.svg b/src/vs/editor/contrib/suggest/media/close-light.svg new file mode 100644 index 0000000000..c84816a032 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/close-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/close.svg b/src/vs/editor/contrib/suggest/media/close.svg deleted file mode 100644 index fde34404d4..0000000000 --- a/src/vs/editor/contrib/suggest/media/close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/suggest/media/color-dark.svg b/src/vs/editor/contrib/suggest/media/color-dark.svg new file mode 100644 index 0000000000..0914abcdbd --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/color-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/color-light.svg b/src/vs/editor/contrib/suggest/media/color-light.svg new file mode 100644 index 0000000000..ca089a1bf2 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/color-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/constant-dark.svg b/src/vs/editor/contrib/suggest/media/constant-dark.svg new file mode 100644 index 0000000000..0e90ecafcd --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/constant-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/editor/contrib/suggest/media/constant-light.svg b/src/vs/editor/contrib/suggest/media/constant-light.svg new file mode 100644 index 0000000000..1a369c1d8a --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/constant-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/vs/editor/contrib/suggest/media/enumerator-dark.svg b/src/vs/editor/contrib/suggest/media/enumerator-dark.svg new file mode 100644 index 0000000000..82d4ff29c4 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/enumerator-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/enumerator-item-dark.svg b/src/vs/editor/contrib/suggest/media/enumerator-item-dark.svg new file mode 100644 index 0000000000..23c697fdf1 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/enumerator-item-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/enumerator-item-light.svg b/src/vs/editor/contrib/suggest/media/enumerator-item-light.svg new file mode 100644 index 0000000000..a99045d335 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/enumerator-item-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/enumerator-light.svg b/src/vs/editor/contrib/suggest/media/enumerator-light.svg new file mode 100644 index 0000000000..e2441a0dc1 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/enumerator-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/event-dark.svg b/src/vs/editor/contrib/suggest/media/event-dark.svg new file mode 100644 index 0000000000..712344d1f9 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/event-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/event-light.svg b/src/vs/editor/contrib/suggest/media/event-light.svg new file mode 100644 index 0000000000..712344d1f9 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/event-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/field-dark.svg b/src/vs/editor/contrib/suggest/media/field-dark.svg new file mode 100644 index 0000000000..15623061c5 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/field-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/field-light.svg b/src/vs/editor/contrib/suggest/media/field-light.svg new file mode 100644 index 0000000000..72dd79504f --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/field-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/file-dark.svg b/src/vs/editor/contrib/suggest/media/file-dark.svg new file mode 100644 index 0000000000..5ed5762a1f --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/file-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/file-light.svg b/src/vs/editor/contrib/suggest/media/file-light.svg new file mode 100644 index 0000000000..ad54e13b1b --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/file-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/folder-dark.svg b/src/vs/editor/contrib/suggest/media/folder-dark.svg new file mode 100644 index 0000000000..43d454e7e5 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/folder-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/folder-light.svg b/src/vs/editor/contrib/suggest/media/folder-light.svg new file mode 100644 index 0000000000..8daecdac6a --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/folder-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/info-dark.svg b/src/vs/editor/contrib/suggest/media/info-dark.svg new file mode 100644 index 0000000000..3f2d84fa64 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/info-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/info-light.svg b/src/vs/editor/contrib/suggest/media/info-light.svg new file mode 100644 index 0000000000..f25ac7c78d --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/info-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/interface-dark.svg b/src/vs/editor/contrib/suggest/media/interface-dark.svg new file mode 100644 index 0000000000..6d482b2abd --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/interface-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/interface-light.svg b/src/vs/editor/contrib/suggest/media/interface-light.svg new file mode 100644 index 0000000000..a397dd00b0 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/interface-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/keyword-dark.svg b/src/vs/editor/contrib/suggest/media/keyword-dark.svg new file mode 100644 index 0000000000..70ba6ea933 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/keyword-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/keyword-light.svg b/src/vs/editor/contrib/suggest/media/keyword-light.svg new file mode 100644 index 0000000000..fc57528a3e --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/keyword-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/method-dark.svg b/src/vs/editor/contrib/suggest/media/method-dark.svg new file mode 100644 index 0000000000..970d7b6148 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/method-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/method-light.svg b/src/vs/editor/contrib/suggest/media/method-light.svg new file mode 100644 index 0000000000..403a9b90dd --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/method-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/namespace-dark.svg b/src/vs/editor/contrib/suggest/media/namespace-dark.svg new file mode 100644 index 0000000000..9a725bb41f --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/namespace-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/namespace-light.svg b/src/vs/editor/contrib/suggest/media/namespace-light.svg new file mode 100644 index 0000000000..1339da7ce2 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/namespace-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/operator-dark.svg b/src/vs/editor/contrib/suggest/media/operator-dark.svg new file mode 100644 index 0000000000..957f5f44f1 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/operator-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/operator-light.svg b/src/vs/editor/contrib/suggest/media/operator-light.svg new file mode 100644 index 0000000000..bf6ed57996 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/operator-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/property-dark.svg b/src/vs/editor/contrib/suggest/media/property-dark.svg new file mode 100644 index 0000000000..23e07ffa19 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/property-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/property-light.svg b/src/vs/editor/contrib/suggest/media/property-light.svg new file mode 100644 index 0000000000..be642dd152 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/property-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/reference-dark.svg b/src/vs/editor/contrib/suggest/media/reference-dark.svg new file mode 100644 index 0000000000..ed302ae139 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/reference-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/reference-light.svg b/src/vs/editor/contrib/suggest/media/reference-light.svg new file mode 100644 index 0000000000..392a840c5e --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/reference-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/ruler-dark.svg b/src/vs/editor/contrib/suggest/media/ruler-dark.svg new file mode 100644 index 0000000000..1957dbad34 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/ruler-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/ruler-light.svg b/src/vs/editor/contrib/suggest/media/ruler-light.svg new file mode 100644 index 0000000000..bc321cdffa --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/ruler-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/snippet-dark.svg b/src/vs/editor/contrib/suggest/media/snippet-dark.svg new file mode 100644 index 0000000000..79799f98c2 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/snippet-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/snippet-light.svg b/src/vs/editor/contrib/suggest/media/snippet-light.svg new file mode 100644 index 0000000000..45fa3a001e --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/snippet-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/string-dark.svg b/src/vs/editor/contrib/suggest/media/string-dark.svg new file mode 100644 index 0000000000..80fb9d6567 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/string-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/string-light.svg b/src/vs/editor/contrib/suggest/media/string-light.svg new file mode 100644 index 0000000000..02a0282e90 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/string-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/structure-dark.svg b/src/vs/editor/contrib/suggest/media/structure-dark.svg new file mode 100644 index 0000000000..13766a5dce --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/structure-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/structure-light.svg b/src/vs/editor/contrib/suggest/media/structure-light.svg new file mode 100644 index 0000000000..c96bcfa61b --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/structure-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 878dffacdb..9022269946 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -109,13 +109,13 @@ } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close { - background-image: url('./close.svg'); + background-image: url('./close-light.svg'); float: right; margin-right: 5px; } .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore { - background-image: url('./info.svg'); + background-image: url('./info-light.svg'); } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close:hover, @@ -179,7 +179,6 @@ .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.suggest-icon::before { content: ' '; - background-image: url('Misc_16x.svg'); background-repeat: no-repeat; background-position: center; background-size: 75%; @@ -187,30 +186,30 @@ .monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, .monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('Method_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('Field_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('Event_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('Operator_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('LocalVariable_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('Class_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('Interface_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('Structure_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('Template_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('Namespace_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('Property_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('Ruler_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('Constant_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('method-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('field-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('event-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('operator-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('variable-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('class-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('interface-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('structure-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('template-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('namespace-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('property-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('ruler-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('constant-light.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('Enumerator_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('EnumItem_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('IntelliSenseKeyword_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('String_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('ColorPalette_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('Document_16x.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('ImportFile_16x_vscode.svg'); } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('Snippet_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('enumerator-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('enumerator-item-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('keyword-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('string-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('color-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('file-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('reference-light.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('snippet-light.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before { background-image: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('Folder_16x.svg'); } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('folder-light.svg'); } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon.customcolor .colorspan { margin: 0 0 0 0.3em; @@ -301,80 +300,82 @@ background-image: url('./close-dark.svg'); } -.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon::before { background-image: url('Misc_inverse_16x.svg'); } +.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore { + background-image: url('./info-dark.svg'); +} .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.function::before, .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('Method_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constructor::before { background-image: url('method-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('Field_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.field::before { background-image: url('field-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('Event_16x_vscode_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.event::before { background-image: url('event-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('Operator_16x_vscode_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.operator::before { background-image: url('operator-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('LocalVariable_16x_vscode_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.variable::before { background-image: url('variable-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('Class_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.class::before { background-image: url('class-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('Interface_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.interface::before { background-image: url('interface-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('Structure_16x_vscode_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.struct::before { background-image: url('structure-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('Template_16x_vscode_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.type-parameter::before { background-image: url('template-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('Namespace_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.module::before { background-image: url('namespace-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('Property_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.property::before { background-image: url('property-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('Ruler_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.unit::before { background-image: url('ruler-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('Constant_16x_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.constant::before { background-image: url('constant-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.value::before, .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('Enumerator_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum::before { background-image: url('enumerator-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('EnumItem_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.enum-member::before { background-image: url('enumerator-item-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('IntelliSenseKeyword_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.keyword::before { background-image: url('keyword-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('String_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.text::before { background-image: url('string-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('ColorPalette_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.color::before { background-image: url('color-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('Document_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.file::before { background-image: url('file-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('ImportFile_16x_vscode_inverse.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.reference::before { background-image: url('reference-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('Snippet_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.snippet::before { background-image: url('snippet-dark.svg'); } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before, .monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.customcolor::before { background-image: none; } .monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before, -.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('Folder_inverse_16x.svg'); } +.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .suggest-icon.folder::before { background-image: url('folder-dark.svg'); } diff --git a/src/vs/editor/contrib/suggest/media/template-dark.svg b/src/vs/editor/contrib/suggest/media/template-dark.svg new file mode 100644 index 0000000000..425ced36f0 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/template-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/template-light.svg b/src/vs/editor/contrib/suggest/media/template-light.svg new file mode 100644 index 0000000000..496d8f7c85 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/template-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/variable-dark.svg b/src/vs/editor/contrib/suggest/media/variable-dark.svg new file mode 100644 index 0000000000..687fcabfff --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/variable-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/media/variable-light.svg b/src/vs/editor/contrib/suggest/media/variable-light.svg new file mode 100644 index 0000000000..ede7e9434d --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/variable-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 463f8d3df4..f02c7365c8 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -16,6 +16,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Range } from 'vs/editor/common/core/range'; import { FuzzyScore } from 'vs/base/common/filters'; +import { isDisposable, DisposableStore } from 'vs/base/common/lifecycle'; export const Context = { Visible: new RawContextKey('suggestWidgetVisible', false), @@ -121,7 +122,6 @@ export function provideSuggestionItems( token: CancellationToken = CancellationToken.None ): Promise { - const allSuggestions: CompletionItem[] = []; const wordUntil = model.getWordUntilPosition(position); const defaultRange = new Range(position.lineNumber, wordUntil.startColumn, position.lineNumber, wordUntil.endColumn); @@ -135,9 +135,12 @@ export function provideSuggestionItems( supports.unshift([_snippetSuggestSupport]); } + const allSuggestions: CompletionItem[] = []; + const disposables = new DisposableStore(); + let hasResult = false; + // add suggestions from contributed providers - providers are ordered in groups of // equal score and once a group produces a result the process stops - let hasResult = false; const factory = supports.map(supports => () => { // for each support in the group ask for suggestions return Promise.all(supports.map(provider => { @@ -162,6 +165,9 @@ export function provideSuggestionItems( allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model)); } } + if (isDisposable(container)) { + disposables.add(container); + } } if (len !== allSuggestions.length && provider !== _snippetSuggestSupport) { @@ -177,6 +183,7 @@ export function provideSuggestionItems( return hasResult || token.isCancellationRequested; }).then(() => { if (token.isCancellationRequested) { + disposables.dispose(); return Promise.reject(canceled()); } return allSuggestions.sort(getSuggestionComparator(options.snippetSortOrder)); @@ -244,29 +251,35 @@ export function getSuggestionComparator(snippetConfig: SnippetSortOrder): (a: Co return _snippetComparators.get(snippetConfig)!; } -registerDefaultLanguageCommand('_executeCompletionItemProvider', (model, position, args) => { +registerDefaultLanguageCommand('_executeCompletionItemProvider', async (model, position, args) => { const result: modes.CompletionList = { incomplete: false, suggestions: [] }; - let resolving: Promise[] = []; - let maxItemsToResolve = args['maxItemsToResolve'] || 0; + const disposables = new DisposableStore(); + const resolving: Promise[] = []; + const maxItemsToResolve = args['maxItemsToResolve'] || 0; - return provideSuggestionItems(model, position).then(items => { - for (const item of items) { - if (resolving.length < maxItemsToResolve) { - resolving.push(item.resolve(CancellationToken.None)); - } - result.incomplete = result.incomplete || item.container.incomplete; - result.suggestions.push(item.completion); + const items = await provideSuggestionItems(model, position); + for (const item of items) { + if (resolving.length < maxItemsToResolve) { + resolving.push(item.resolve(CancellationToken.None)); } - }).then(() => { - return Promise.all(resolving); - }).then(() => { + result.incomplete = result.incomplete || item.container.incomplete; + result.suggestions.push(item.completion); + if (isDisposable(item.container)) { + disposables.add(item.container); + } + } + + try { + await Promise.all(resolving); return result; - }); + } finally { + setTimeout(() => disposables.dispose(), 0); + } }); interface SuggestController extends IEditorContribution { diff --git a/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts b/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts index b8709f1f49..e767cc22ac 100644 --- a/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts +++ b/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ISelectedSuggestion, SuggestWidget } from './suggestWidget'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; export class CommitCharacterController { - private _disposables: IDisposable[] = []; + private readonly _disposables = new DisposableStore(); private _active?: { readonly acceptCharacters: CharacterSet; @@ -20,11 +20,11 @@ export class CommitCharacterController { constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) { - this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem()))); - this._disposables.push(widget.onDidFocus(this._onItem, this)); - this._disposables.push(widget.onDidHide(this.reset, this)); + this._disposables.add(widget.onDidShow(() => this._onItem(widget.getFocusedItem()))); + this._disposables.add(widget.onDidFocus(this._onItem, this)); + this._disposables.add(widget.onDidHide(this.reset, this)); - this._disposables.push(editor.onWillType(text => { + this._disposables.add(editor.onWillType(text => { if (this._active) { const ch = text.charCodeAt(text.length - 1); if (this._active.acceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) { @@ -54,6 +54,6 @@ export class CommitCharacterController { } dispose() { - dispose(this._disposables); + this._disposables.dispose(); } } diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index a53be5034b..84a6297d40 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -7,7 +7,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -45,7 +45,7 @@ export class SuggestController implements IEditorContribution { private readonly _model: SuggestModel; private readonly _widget: IdleValue; private readonly _alternatives: IdleValue; - private _toDispose: IDisposable[] = []; + private readonly _toDispose = new DisposableStore(); private readonly _sticky = false; // for development purposes only @@ -63,23 +63,21 @@ export class SuggestController implements IEditorContribution { const widget = this._instantiationService.createInstance(SuggestWidget, this._editor); - this._toDispose.push(widget); - this._toDispose.push(widget.onDidSelect(item => this._insertSuggestion(item, false, true), this)); + this._toDispose.add(widget); + this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, false, true), this)); // Wire up logic to accept a suggestion on certain characters const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, false, true)); - this._toDispose.push( - commitCharacterController, - this._model.onDidSuggest(e => { - if (e.completionModel.items.length === 0) { - commitCharacterController.reset(); - } - }) - ); + this._toDispose.add(commitCharacterController); + this._toDispose.add(this._model.onDidSuggest(e => { + if (e.completionModel.items.length === 0) { + commitCharacterController.reset(); + } + })); // Wire up makes text edit context key let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService); - this._toDispose.push(widget.onDidFocus(({ item }) => { + this._toDispose.add(widget.onDidFocus(({ item }) => { const position = this._editor.getPosition()!; const startColumn = item.completion.range.startColumn; @@ -103,38 +101,35 @@ export class SuggestController implements IEditorContribution { } makesTextEdit.set(value); })); - this._toDispose.push({ - dispose() { makesTextEdit.reset(); } - }); + this._toDispose.add(toDisposable(() => makesTextEdit.reset())); return widget; }); this._alternatives = new IdleValue(() => { - let res = new SuggestAlternatives(this._editor, this._contextKeyService); - this._toDispose.push(res); - return res; + return this._toDispose.add(new SuggestAlternatives(this._editor, this._contextKeyService)); }); - this._toDispose.push(_instantiationService.createInstance(WordContextKey, _editor)); + this._toDispose.add(_instantiationService.createInstance(WordContextKey, _editor)); - this._toDispose.push(this._model.onDidTrigger(e => { + this._toDispose.add(this._model.onDidTrigger(e => { this._widget.getValue().showTriggered(e.auto, e.shy ? 250 : 50); })); - this._toDispose.push(this._model.onDidSuggest(e => { + this._toDispose.add(this._model.onDidSuggest(e => { if (!e.shy) { let index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, e.completionModel.items); this._widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto); } })); - this._toDispose.push(this._model.onDidCancel(e => { + this._toDispose.add(this._model.onDidCancel(e => { if (this._widget && !e.retrigger) { this._widget.getValue().hideWidget(); } })); - this._toDispose.push(this._editor.onDidBlurEditorWidget(() => { + this._toDispose.add(this._editor.onDidBlurEditorWidget(() => { if (!this._sticky) { this._model.cancel(); + this._model.clear(); } })); @@ -144,7 +139,7 @@ export class SuggestController implements IEditorContribution { const { acceptSuggestionOnEnter } = this._editor.getConfiguration().contribInfo; acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart'); }; - this._toDispose.push(this._editor.onDidChangeConfiguration((e) => updateFromConfig())); + this._toDispose.add(this._editor.onDidChangeConfiguration(() => updateFromConfig())); updateFromConfig(); } @@ -154,17 +149,17 @@ export class SuggestController implements IEditorContribution { } dispose(): void { - this._toDispose = dispose(this._toDispose); + this._alternatives.dispose(); + this._toDispose.dispose(); this._widget.dispose(); - if (this._model) { - this._model.dispose(); - } + this._model.dispose(); } protected _insertSuggestion(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void { if (!event || !event.item) { this._alternatives.getValue().reset(); this._model.cancel(); + this._model.clear(); return; } if (!this._editor.hasModel()) { @@ -198,13 +193,13 @@ export class SuggestController implements IEditorContribution { const overwriteBefore = position.column - suggestion.range.startColumn; const overwriteAfter = suggestion.range.endColumn - position.column; - SnippetController2.get(this._editor).insert( - insertText, - overwriteBefore + columnDelta, + SnippetController2.get(this._editor).insert(insertText, { + overwriteBefore: overwriteBefore + columnDelta, overwriteAfter, - false, false, - !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) - ); + undoStopBefore: false, + undoStopAfter: false, + adjustWhitespace: !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) + }); if (undoStops) { this._editor.pushUndoStop(); @@ -213,6 +208,7 @@ export class SuggestController implements IEditorContribution { if (!suggestion.command) { // done this._model.cancel(); + this._model.clear(); } else if (suggestion.command.id === TriggerSuggestAction.id) { // retigger @@ -220,7 +216,9 @@ export class SuggestController implements IEditorContribution { } else { // exec command, done - this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : [])).catch(onUnexpectedError); + this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : [])) + .catch(onUnexpectedError) + .finally(() => this._model.clear()); // <- clear only now, keep commands alive this._model.cancel(); } @@ -340,6 +338,7 @@ export class SuggestController implements IEditorContribution { cancelSuggestWidget(): void { this._model.cancel(); + this._model.clear(); this._widget.getValue().hideWidget(); } diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index 52660ee70f..8da7ffb489 100644 --- a/src/vs/editor/contrib/suggest/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/suggestMemory.ts @@ -5,7 +5,7 @@ import { LRUCache, TernarySearchTree } from 'vs/base/common/map'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ITextModel } from 'vs/editor/common/model'; import { IPosition } from 'vs/editor/common/core/position'; import { CompletionItemKind, completionKindFromString } from 'vs/editor/common/modes'; @@ -221,7 +221,11 @@ export class SuggestMemoryService extends Disposable implements ISuggestMemorySe }; this._persistSoon = this._register(new RunOnceScheduler(() => this._saveState(), 500)); - this._register(_storageService.onWillSaveState(() => this._saveState())); + this._register(_storageService.onWillSaveState(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + this._saveState(); + } + })); this._register(this._configService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor.suggestSelection') || e.affectsConfiguration('editor.suggest.shareSuggestSelections')) { diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 152d45260f..a951fefb5a 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -7,7 +7,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore, isDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; @@ -91,7 +91,7 @@ export const enum State { export class SuggestModel implements IDisposable { - private _toDispose: IDisposable[] = []; + private readonly _toDispose = new DisposableStore(); private _quickSuggestDelay: number; private _triggerCharacterListener: IDisposable; private readonly _triggerQuickSuggest = new TimeoutTimer(); @@ -101,7 +101,8 @@ export class SuggestModel implements IDisposable { private _context?: LineContext; private _currentSelection: Selection; - private _completionModel?: CompletionModel; + private _completionModel: CompletionModel | undefined; + private readonly _completionDisposables = new DisposableStore(); private readonly _onDidCancel = new Emitter(); private readonly _onDidTrigger = new Emitter(); private readonly _onDidSuggest = new Emitter(); @@ -117,36 +118,36 @@ export class SuggestModel implements IDisposable { this._currentSelection = this._editor.getSelection() || new Selection(1, 1, 1, 1); // wire up various listeners - this._toDispose.push(this._editor.onDidChangeModel(() => { + this._toDispose.add(this._editor.onDidChangeModel(() => { this._updateTriggerCharacters(); this.cancel(); })); - this._toDispose.push(this._editor.onDidChangeModelLanguage(() => { + this._toDispose.add(this._editor.onDidChangeModelLanguage(() => { this._updateTriggerCharacters(); this.cancel(); })); - this._toDispose.push(this._editor.onDidChangeConfiguration(() => { + this._toDispose.add(this._editor.onDidChangeConfiguration(() => { this._updateTriggerCharacters(); this._updateQuickSuggest(); })); - this._toDispose.push(CompletionProviderRegistry.onDidChange(() => { + this._toDispose.add(CompletionProviderRegistry.onDidChange(() => { this._updateTriggerCharacters(); this._updateActiveSuggestSession(); })); - this._toDispose.push(this._editor.onDidChangeCursorSelection(e => { + this._toDispose.add(this._editor.onDidChangeCursorSelection(e => { this._onCursorChange(e); })); let editorIsComposing = false; - this._toDispose.push(this._editor.onCompositionStart(() => { + this._toDispose.add(this._editor.onCompositionStart(() => { editorIsComposing = true; })); - this._toDispose.push(this._editor.onCompositionEnd(() => { + this._toDispose.add(this._editor.onCompositionEnd(() => { // refilter when composition ends editorIsComposing = false; this._refilterCompletionItems(); })); - this._toDispose.push(this._editor.onDidChangeModelContent(() => { + this._toDispose.add(this._editor.onDidChangeModelContent(() => { // only filter completions when the editor isn't // composing a character, e.g. ¨ + u makes ü but just // ¨ cannot be used for filtering @@ -161,8 +162,8 @@ export class SuggestModel implements IDisposable { dispose(): void { dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerCharacterListener, this._triggerQuickSuggest]); - this._toDispose = dispose(this._toDispose); - dispose(this._completionModel); + this._toDispose.dispose(); + this._completionDisposables.dispose(); this.cancel(); } @@ -226,13 +227,16 @@ export class SuggestModel implements IDisposable { this._requestToken = undefined; } this._state = State.Idle; - dispose(this._completionModel); this._completionModel = undefined; this._context = undefined; this._onDidCancel.fire({ retrigger }); } } + clear() { + this._completionDisposables.clear(); + } + private _updateActiveSuggestSession(): void { if (this._state !== State.Idle) { if (!this._editor.hasModel() || !CompletionProviderRegistry.has(this._editor.getModel())) { @@ -289,6 +293,9 @@ export class SuggestModel implements IDisposable { this.cancel(); this._triggerQuickSuggest.cancelAndSet(() => { + if (this._state !== State.Idle) { + return; + } if (!LineContext.shouldAutoTrigger(this._editor)) { return; } @@ -435,7 +442,6 @@ export class SuggestModel implements IDisposable { } const ctx = new LineContext(model, this._editor.getPosition(), auto, context.shy); - dispose(this._completionModel); this._completionModel = new CompletionModel(items, this._context!.column, { leadingLineContent: ctx.leadingLineContent, characterCountDelta: ctx.column - this._context!.column @@ -443,6 +449,14 @@ export class SuggestModel implements IDisposable { wordDistance, this._editor.getConfiguration().contribInfo.suggest ); + + // store containers so that they can be disposed later + for (const item of items) { + if (isDisposable(item.container)) { + this._completionDisposables.add(item.container); + } + } + this._onNewContext(ctx); }).catch(onUnexpectedError); diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 1bf00c62e9..415afc45cb 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -9,8 +9,8 @@ import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from 'vs/base/browser/dom'; +import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass, addDisposableListener } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -29,7 +29,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { CompletionItemKind, completionKindToCssClass } from 'vs/editor/common/modes'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; @@ -37,6 +37,9 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { FileKind } from 'vs/platform/files/common/files'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; const expandSuggestionDocsByDefault = false; @@ -103,7 +106,9 @@ class Renderer implements IListRenderer renderTemplate(container: HTMLElement): ISuggestionTemplateData { const data = Object.create(null); - data.disposables = []; + const disposables = new DisposableStore(); + data.disposables = [disposables]; + data.root = container; addClass(data.root, 'show-file-icons'); @@ -114,7 +119,7 @@ class Renderer implements IListRenderer const main = append(text, $('.main')); data.iconLabel = new IconLabel(main, { supportHighlights: true }); - data.disposables.push(data.iconLabel); + disposables.add(data.iconLabel); data.typeLabel = append(main, $('span.type-label')); @@ -142,9 +147,9 @@ class Renderer implements IListRenderer configureFont(); - Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) + disposables.add(Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) .filter(e => e.fontInfo || e.contribInfo) - .on(configureFont, null, data.disposables); + .on(configureFont, null)); return data; } @@ -226,6 +231,18 @@ const enum State { Details } + +let _explainMode = false; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'suggest.toggleExplainMode', + handler() { + _explainMode = !_explainMode; + }, + when: SuggestContext.Visible, + weight: KeybindingWeight.EditorContrib, + primary: KeyMod.CtrlCmd | KeyCode.US_SLASH, +}); + class SuggestionDetails { private el: HTMLElement; @@ -245,7 +262,7 @@ class SuggestionDetails { private readonly widget: SuggestWidget, private readonly editor: ICodeEditor, private readonly markdownRenderer: MarkdownRenderer, - private readonly triggerKeybindingLabel: string + private readonly triggerKeybindingLabel: string, ) { this.disposables = []; @@ -279,10 +296,27 @@ class SuggestionDetails { return this.el; } - render(item: CompletionItem): void { + renderLoading(): void { + this.type.textContent = nls.localize('loading', "Loading..."); + this.docs.textContent = ''; + } + + renderItem(item: CompletionItem): void { this.renderDisposeable = dispose(this.renderDisposeable); - if (!item || !canExpandCompletionItem(item)) { + let { documentation, detail } = item.completion; + // --- documentation + + if (_explainMode) { + let md = ''; + md += `score: ${item.score[0]}${item.word ? `, compared '${item.completion.filterText && (item.completion.filterText + ' (filterText)') || item.completion.label}' with '${item.word}'` : ' (no prefix)'}\n`; + md += `distance: ${item.distance}, see localityBonus-setting\n`; + md += `index: ${item.idx}, based on ${item.completion.sortText && `sortText: "${item.completion.sortText}"` || 'label'}\n`; + documentation = new MarkdownString().appendCodeblock('empty', md); + detail = `Provider: ${item.provider._debugDisplayName}`; + } + + if (!_explainMode && !canExpandCompletionItem(item)) { this.type.textContent = ''; this.docs.textContent = ''; addClass(this.el, 'no-docs'); @@ -290,19 +324,20 @@ class SuggestionDetails { return; } removeClass(this.el, 'no-docs'); - if (typeof item.completion.documentation === 'string') { + if (typeof documentation === 'string') { removeClass(this.docs, 'markdown-docs'); - this.docs.textContent = item.completion.documentation; + this.docs.textContent = documentation; } else { addClass(this.docs, 'markdown-docs'); this.docs.innerHTML = ''; - const renderedContents = this.markdownRenderer.render(item.completion.documentation); + const renderedContents = this.markdownRenderer.render(documentation); this.renderDisposeable = renderedContents; this.docs.appendChild(renderedContents.element); } - if (item.completion.detail) { - this.type.innerText = item.completion.detail; + // --- details + if (detail) { + this.type.innerText = detail; show(this.type); } else { this.type.innerText = ''; @@ -326,8 +361,8 @@ class SuggestionDetails { this.ariaLabel = strings.format( '{0}{1}', - item.completion.detail || '', - item.completion.documentation ? (typeof item.completion.documentation === 'string' ? item.completion.documentation : item.completion.documentation.value) : ''); + detail || '', + documentation ? (typeof documentation === 'string' ? documentation : documentation.value) : ''); } getAriaLabel() { @@ -399,10 +434,11 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate | null; private focusedItem: CompletionItem | null; private ignoreFocusEvents = false; @@ -420,14 +456,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate(); private onDidFocusEmitter = new Emitter(); private onDidHideEmitter = new Emitter(); private onDidShowEmitter = new Emitter(); - readonly onDidSelect: Event = this.onDidSelectEmitter.event; readonly onDidFocus: Event = this.onDidFocusEmitter.event; readonly onDidHide: Event = this.onDidHideEmitter.event; @@ -457,17 +492,22 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { + if (e.target === this.element) { + this.hideWidget(); + } + })); this.messageElement = append(this.element, $('.message')); this.listElement = append(this.element, $('.tree')); - this.details = new SuggestionDetails(this.element, this, this.editor, markdownRenderer, triggerKeybindingLabel); + this.details = instantiationService.createInstance(SuggestionDetails, this.element, this, this.editor, markdownRenderer, triggerKeybindingLabel); const applyIconStyle = () => toggleClass(this.element, 'no-icons', !this.editor.getConfiguration().contribInfo.suggest.showIcons); applyIconStyle(); @@ -480,19 +520,18 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate this.onThemeChange(t)), - editor.onDidLayoutChange(() => this.onEditorLayoutChange()), - this.list.onMouseDown(e => this.onListMouseDown(e)), - this.list.onSelectionChange(e => this.onListSelection(e)), - this.list.onFocusChange(e => this.onListFocus(e)), - this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()), - this.editor.onDidChangeConfiguration(e => e.contribInfo && applyIconStyle()) - ]; + this.toDispose.add(attachListStyler(this.list, themeService, { + listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, + listInactiveFocusOutline: activeContrastBorder + })); + this.toDispose.add(themeService.onThemeChange(t => this.onThemeChange(t))); + this.toDispose.add(editor.onDidLayoutChange(() => this.onEditorLayoutChange())); + this.toDispose.add(this.list.onMouseDown(e => this.onListMouseDown(e))); + this.toDispose.add(this.list.onSelectionChange(e => this.onListSelection(e))); + this.toDispose.add(this.list.onFocusChange(e => this.onListFocus(e))); + this.toDispose.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged())); + this.toDispose.add(this.editor.onDidChangeConfiguration(e => e.contribInfo && applyIconStyle())); + this.suggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService); this.suggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService); @@ -624,7 +663,13 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate item.resolve(token)); + this.currentSuggestionDetails = createCancelablePromise(async token => { + const loading = disposableTimeout(() => this.showDetails(true), 250); + token.onCancellationRequested(() => loading.dispose()); + const result = await item.resolve(token); + loading.dispose(); + return result; + }); this.currentSuggestionDetails.then(() => { if (this.list.length < index) { @@ -638,7 +683,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { - this.loadingTimeout = null; - this.setState(State.Loading); - }, delay); + this.loadingTimeout = disposableTimeout(() => this.setState(State.Loading), delay); } } @@ -729,10 +771,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { assert.equal(event.auto, true); assert.equal(event.completionModel.items.length, 2); - assert.equal(disposeA, 1); - assert.equal(disposeB, 0); + + // clean up + model.clear(); + assert.equal(disposeA, 2); // provide got called two times! + assert.equal(disposeB, 1); }); + }); }); }); diff --git a/src/vs/editor/contrib/suggest/wordContextKey.ts b/src/vs/editor/contrib/suggest/wordContextKey.ts index ac63aa0b54..6c196825a1 100644 --- a/src/vs/editor/contrib/suggest/wordContextKey.ts +++ b/src/vs/editor/contrib/suggest/wordContextKey.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -export class WordContextKey { +export class WordContextKey extends Disposable { static readonly AtEnd = new RawContextKey('atEndOfWord', false); private readonly _ckAtEnd: IContextKey; - private readonly _confListener: IDisposable; private _enabled: boolean; private _selectionListener?: IDisposable; @@ -21,13 +20,15 @@ export class WordContextKey { private readonly _editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, ) { + super(); this._ckAtEnd = WordContextKey.AtEnd.bindTo(contextKeyService); - this._confListener = this._editor.onDidChangeConfiguration(e => e.contribInfo && this._update()); + this._register(this._editor.onDidChangeConfiguration(e => e.contribInfo && this._update())); this._update(); } dispose(): void { - dispose(this._confListener, this._selectionListener); + super.dispose(); + dispose(this._selectionListener); this._ckAtEnd.reset(); } diff --git a/src/vs/editor/contrib/tokenization/tokenization.ts b/src/vs/editor/contrib/tokenization/tokenization.ts index c193e268fa..6f88b37fa8 100644 --- a/src/vs/editor/contrib/tokenization/tokenization.ts +++ b/src/vs/editor/contrib/tokenization/tokenization.ts @@ -7,6 +7,8 @@ import * as nls from 'vs/nls'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { StopWatch } from 'vs/base/common/stopwatch'; +import { StandardTokenType } from 'vs/editor/common/modes'; +import { ITextModel } from 'vs/editor/common/model'; class ForceRetokenizeAction extends EditorAction { constructor() { @@ -23,11 +25,57 @@ class ForceRetokenizeAction extends EditorAction { return; } const model = editor.getModel(); - model.flushTokens(); + model.resetTokenization(); const sw = new StopWatch(true); model.forceTokenization(model.getLineCount()); sw.stop(); console.log(`tokenization took ${sw.elapsed()}`); + + if (!true) { + extractTokenTypes(model); + } + } +} + +function extractTokenTypes(model: ITextModel): void { + const eolLength = model.getEOL().length; + let result: number[] = []; + let resultLen: number = 0; + let lastTokenType: StandardTokenType = StandardTokenType.Other; + let lastEndOffset: number = 0; + let offset = 0; + for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) { + const lineTokens = model.getLineTokens(lineNumber); + + for (let i = 0, len = lineTokens.getCount(); i < len; i++) { + const tokenType = lineTokens.getStandardTokenType(i); + if (tokenType === StandardTokenType.Other) { + continue; + } + + const startOffset = offset + lineTokens.getStartOffset(i); + const endOffset = offset + lineTokens.getEndOffset(i); + const length = endOffset - startOffset; + + if (length === 0) { + continue; + } + + if (lastTokenType === tokenType && lastEndOffset === startOffset) { + result[resultLen - 2] += length; + lastEndOffset += length; + continue; + } + + result[resultLen++] = startOffset; // - lastEndOffset + result[resultLen++] = length; + result[resultLen++] = tokenType; + + lastTokenType = tokenType; + lastEndOffset = endOffset; + } + + offset += lineTokens.getLineContent().length + eolLength; } } diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 74e2e02be3..1fba1943d0 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -9,7 +9,7 @@ import { CancelablePromise, createCancelablePromise, first, timeout } from 'vs/b import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, IActionOptions, registerDefaultLanguageCommand, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -164,7 +164,7 @@ class WordHighlighter { private occurrencesHighlight: boolean; private readonly model: ITextModel; private _decorationIds: string[]; - private toUnhook: IDisposable[]; + private readonly toUnhook = new DisposableStore(); private workerRequestTokenId: number = 0; private workerRequest: IOccurenceAtPositionRequest | null; @@ -183,8 +183,7 @@ class WordHighlighter { this._ignorePositionChangeEvent = false; this.occurrencesHighlight = this.editor.getConfiguration().contribInfo.occurrencesHighlight; this.model = this.editor.getModel(); - this.toUnhook = []; - this.toUnhook.push(editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { + this.toUnhook.add(editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { if (this._ignorePositionChangeEvent) { // We are changing the position => ignore this event @@ -199,10 +198,10 @@ class WordHighlighter { this._onPositionChanged(e); })); - this.toUnhook.push(editor.onDidChangeModelContent((e) => { + this.toUnhook.add(editor.onDidChangeModelContent((e) => { this._stopAll(); })); - this.toUnhook.push(editor.onDidChangeConfiguration((e) => { + this.toUnhook.add(editor.onDidChangeConfiguration((e) => { let newValue = this.editor.getConfiguration().contribInfo.occurrencesHighlight; if (this.occurrencesHighlight !== newValue) { this.occurrencesHighlight = newValue; @@ -454,7 +453,7 @@ class WordHighlighter { public dispose(): void { this._stopAll(); - this.toUnhook = dispose(this.toUnhook); + this.toUnhook.dispose(); } } diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index 6a36320415..75f4a5a131 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -8,7 +8,7 @@ import * as dom from 'vs/base/browser/dom'; import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; import { Color, RGBA } from 'vs/base/common/color'; import { IdGenerator } from 'vs/base/common/idGenerator'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; @@ -165,7 +165,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { private _positionMarkerId: string[] = []; protected _viewZone: ViewZoneDelegate | null; - protected _disposables: IDisposable[] = []; + protected readonly _disposables = new DisposableStore(); public container: HTMLElement; public domNode: HTMLElement; @@ -183,7 +183,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this.domNode.setAttribute('role', 'presentation'); } - this._disposables.push(this.editor.onDidLayoutChange((info: EditorLayoutInfo) => { + this._disposables.add(this.editor.onDidLayoutChange((info: EditorLayoutInfo) => { const width = this._getWidth(info); this.domNode.style.width = width + 'px'; this.domNode.style.left = this._getLeft(info) + 'px'; @@ -193,7 +193,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { public dispose(): void { - dispose(this._disposables); + this._disposables.dispose(); if (this._overlayWidget) { this.editor.removeOverlayWidget(this._overlayWidget); @@ -225,7 +225,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this.domNode.appendChild(this.container); if (this.options.showArrow) { this._arrow = new Arrow(this.editor); - this._disposables.push(this._arrow); + this._disposables.add(this._arrow); } this._fillContainer(this.container); this._initSash(); @@ -470,7 +470,10 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { // --- sash private _initSash(): void { - this._resizeSash = new Sash(this.domNode, this, { orientation: Orientation.HORIZONTAL }); + if (this._resizeSash) { + return; + } + this._resizeSash = this._disposables.add(new Sash(this.domNode, this, { orientation: Orientation.HORIZONTAL })); if (!this.options.isResizeable) { this._resizeSash.hide(); @@ -478,7 +481,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } let data: { startY: number; heightInLines: number; } | undefined; - this._disposables.push(this._resizeSash.onDidStart((e: ISashEvent) => { + this._disposables.add(this._resizeSash.onDidStart((e: ISashEvent) => { if (this._viewZone) { data = { startY: e.startY, @@ -487,11 +490,11 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } })); - this._disposables.push(this._resizeSash.onDidEnd(() => { + this._disposables.add(this._resizeSash.onDidEnd(() => { data = undefined; })); - this._disposables.push(this._resizeSash.onDidChange((evt: ISashEvent) => { + this._disposables.add(this._resizeSash.onDidChange((evt: ISashEvent) => { if (data) { let lineDelta = (evt.currentY - data.startY) / this.editor.getConfiguration().lineHeight; let roundedLineDelta = lineDelta < 0 ? Math.ceil(lineDelta) : Math.floor(lineDelta); diff --git a/src/vs/editor/editor.worker.ts b/src/vs/editor/editor.worker.ts index 418ba2dd23..39ad6d386c 100644 --- a/src/vs/editor/editor.worker.ts +++ b/src/vs/editor/editor.worker.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { SimpleWorkerServer } from 'vs/base/common/worker/simpleWorker'; -import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker'; +import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; +import { EditorWorkerHost } from 'vs/editor/common/services/editorWorkerServiceImpl'; let initialized = false; @@ -14,10 +15,9 @@ export function initialize(foreignModule: any) { } initialized = true; - const editorWorker = new EditorSimpleWorkerImpl(foreignModule); const simpleWorker = new SimpleWorkerServer((msg) => { (self).postMessage(msg); - }, editorWorker); + }, (host: EditorWorkerHost) => new EditorSimpleWorker(host, foreignModule)); self.onmessage = (e) => { simpleWorker.onmessage(e.data); diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index 8685aa7826..ef85a863ac 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -27,7 +27,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { contrastBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, editorWidgetBackground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; @@ -371,6 +371,11 @@ registerThemingParticipant((theme, collector) => { if (widgetBackground) { collector.addRule(`.monaco-editor .accessibilityHelpWidget { background-color: ${widgetBackground}; }`); } + const widgetForeground = theme.getColor(editorWidgetForeground); + if (widgetForeground) { + collector.addRule(`.monaco-editor .accessibilityHelpWidget { color: ${widgetForeground}; }`); + } + const widgetShadowColor = theme.getColor(widgetShadow); if (widgetShadowColor) { diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css index 2c47d247c8..86def77148 100644 --- a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.css @@ -13,12 +13,12 @@ position: absolute; resize: none; overflow: hidden; - background: url('keyboard.svg') center center no-repeat; + background: url('keyboard-light.svg') center center no-repeat; border: 4px solid #F6F6F6; border-radius: 4px; } .monaco-editor.vs-dark .iPadShowKeyboard { - background: url('keyboard-inverse.svg') center center no-repeat; + background: url('keyboard-dark.svg') center center no-repeat; border: 4px solid #252526; } \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts index ad74dc5e01..2ff84f1def 100644 --- a/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts @@ -6,24 +6,23 @@ import 'vs/css!./iPadShowKeyboard'; import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -export class IPadShowKeyboard implements IEditorContribution { +export class IPadShowKeyboard extends Disposable implements IEditorContribution { private static readonly ID = 'editor.contrib.iPadShowKeyboard'; private readonly editor: ICodeEditor; private widget: ShowKeyboardWidget | null; - private toDispose: IDisposable[]; constructor(editor: ICodeEditor) { + super(); this.editor = editor; - this.toDispose = []; if (browser.isIPad) { - this.toDispose.push(editor.onDidChangeConfiguration(() => this.update())); + this._register(editor.onDidChangeConfiguration(() => this.update())); this.update(); } } @@ -48,7 +47,7 @@ export class IPadShowKeyboard implements IEditorContribution { } public dispose(): void { - this.toDispose = dispose(this.toDispose); + super.dispose(); if (this.widget) { this.widget.dispose(); this.widget = null; @@ -56,25 +55,24 @@ export class IPadShowKeyboard implements IEditorContribution { } } -class ShowKeyboardWidget implements IOverlayWidget { +class ShowKeyboardWidget extends Disposable implements IOverlayWidget { private static readonly ID = 'editor.contrib.ShowKeyboardWidget'; private readonly editor: ICodeEditor; private readonly _domNode: HTMLElement; - private _toDispose: IDisposable[]; constructor(editor: ICodeEditor) { + super(); this.editor = editor; this._domNode = document.createElement('textarea'); this._domNode.className = 'iPadShowKeyboard'; - this._toDispose = []; - this._toDispose.push(dom.addDisposableListener(this._domNode, 'touchstart', (e) => { + this._register(dom.addDisposableListener(this._domNode, 'touchstart', (e) => { this.editor.focus(); })); - this._toDispose.push(dom.addDisposableListener(this._domNode, 'focus', (e) => { + this._register(dom.addDisposableListener(this._domNode, 'focus', (e) => { this.editor.focus(); })); @@ -83,7 +81,7 @@ class ShowKeyboardWidget implements IOverlayWidget { public dispose(): void { this.editor.removeOverlayWidget(this); - this._toDispose = dispose(this._toDispose); + super.dispose(); } // ----- IOverlayWidget API diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-dark.svg b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-dark.svg new file mode 100644 index 0000000000..308c533169 --- /dev/null +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg deleted file mode 100644 index 40bfc47424..0000000000 --- a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-light.svg b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-light.svg new file mode 100644 index 0000000000..152bf777f6 --- /dev/null +++ b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-light.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg b/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg deleted file mode 100644 index bd13224272..0000000000 --- a/src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index acfb20217b..8c4d7744da 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -8,7 +8,7 @@ import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Emitter, Event } from 'vs/base/common/event'; import { Keybinding, ResolvedKeybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; -import { IDisposable, IReference, ImmortalReference, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, IReference, ImmortalReference, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { OS, isLinux, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; @@ -25,7 +25,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; -import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -37,13 +37,14 @@ import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/com import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label'; -import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; -import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress'; +import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; +import { IProgressRunner, IEditorProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ILayoutService, IDimension } from 'vs/platform/layout/browser/layoutService'; import { SimpleServicesNLS } from 'vs/editor/common/standaloneStrings'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; export class SimpleModel implements IResolvedTextEditorModel { @@ -136,7 +137,7 @@ export class SimpleEditorModelResolverService implements ITextModelService { } } -export class SimpleProgressService implements IProgressService { +export class SimpleEditorProgressService implements IEditorProgressService { _serviceBrand: any; private static NULL_PROGRESS_RUNNER: IProgressRunner = { @@ -148,7 +149,7 @@ export class SimpleProgressService implements IProgressService { show(infinite: true, delay?: number): IProgressRunner; show(total: number, delay?: number): IProgressRunner; show(): IProgressRunner { - return SimpleProgressService.NULL_PROGRESS_RUNNER; + return SimpleEditorProgressService.NULL_PROGRESS_RUNNER; } showWhile(promise: Promise, delay?: number): Promise { @@ -220,6 +221,10 @@ export class SimpleNotificationService implements INotificationService { public prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { return SimpleNotificationService.NO_OP; } + + public status(message: string | Error, options?: IStatusMessageOptions): IDisposable { + return Disposable.None; + } } export class StandaloneCommandService implements ICommandService { @@ -291,7 +296,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { throw new Error(`Invalid keybinding`); } - let toDispose: IDisposable[] = []; + const toDispose = new DisposableStore(); this._dynamicKeybindings.push({ keybinding: keybinding, @@ -301,7 +306,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { weight2: 0 }); - toDispose.push(toDisposable(() => { + toDispose.add(toDisposable(() => { for (let i = 0; i < this._dynamicKeybindings.length; i++) { let kb = this._dynamicKeybindings[i]; if (kb.command === commandId) { @@ -314,7 +319,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { let commandService = this._commandService; if (commandService instanceof StandaloneCommandService) { - toDispose.push(commandService.addCommand({ + toDispose.add(commandService.addCommand({ id: commandId, handler: handler })); @@ -323,7 +328,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { } this.updateResolver({ source: KeybindingSource.Default }); - return combinedDisposable(toDispose); + return toDispose; } private updateResolver(event: IKeybindingEvent): void { @@ -386,6 +391,10 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { public _dumpDebugInfo(): string { return ''; } + + public _dumpDebugInfoJSON(): string { + return ''; + } } function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides { @@ -446,7 +455,17 @@ export class SimpleConfigurationService implements IConfigurationService { } public getConfigurationData(): IConfigurationData | null { - return null; + const emptyModel: IConfigurationModel = { + contents: {}, + keys: [], + overrides: [] + }; + return { + defaults: emptyModel, + user: emptyModel, + workspace: emptyModel, + folders: {} + }; } } @@ -507,6 +526,10 @@ export class StandaloneTelemetryService implements ITelemetryService { return Promise.resolve(undefined); } + publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck) { + return this.publicLog(eventName, data as any); + } + public getTelemetryInfo(): Promise { throw new Error(`Not available`); } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 9082b724ec..532eb6fdf7 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -5,7 +5,7 @@ import * as browser from 'vs/base/browser/browser'; import * as aria from 'vs/base/browser/ui/aria/aria'; -import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -227,13 +227,13 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon }; - let toDispose: IDisposable[] = []; + const toDispose = new DisposableStore(); // Generate a unique id to allow the same descriptor.id across multiple editor instances const uniqueId = this.getId() + ':' + id; // Register the command - toDispose.push(CommandsRegistry.registerCommand(uniqueId, run)); + toDispose.add(CommandsRegistry.registerCommand(uniqueId, run)); // Register the context menu item if (contextMenuGroupId) { @@ -246,16 +246,14 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon group: contextMenuGroupId, order: contextMenuOrder }; - toDispose.push(MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem)); + toDispose.add(MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem)); } // Register the keybindings if (Array.isArray(keybindings)) { - toDispose = toDispose.concat( - keybindings.map((kb) => { - return this._standaloneKeybindingService.addDynamicKeybinding(uniqueId, kb, run, keybindingsWhen); - }) - ); + for (const kb of keybindings) { + toDispose.add(this._standaloneKeybindingService.addDynamicKeybinding(uniqueId, kb, run, keybindingsWhen)); + } } // Finally, register an internal editor action @@ -270,11 +268,11 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon // Store it under the original id, such that trigger with the original id will work this._actions[id] = internalAction; - toDispose.push(toDisposable(() => { + toDispose.add(toDisposable(() => { delete this._actions[id]; })); - return combinedDisposable(toDispose); + return toDispose; } } diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 939d6de4e3..56cf187a1c 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -152,15 +152,13 @@ export function createModel(value: string, language?: string, uri?: URI): ITextM value = value || ''; if (!language) { - let path = uri ? uri.path : null; - let firstLF = value.indexOf('\n'); let firstLine = value; if (firstLF !== -1) { firstLine = value.substring(0, firstLF); } - return doCreateModel(value, StaticServices.modeService.get().createByFilepathOrFirstLine(path, firstLine), uri); + return doCreateModel(value, StaticServices.modeService.get().createByFilepathOrFirstLine(uri || null, firstLine), uri); } return doCreateModel(value, StaticServices.modeService.get().create(language), uri); } @@ -354,6 +352,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor { ScrollbarVisibility: standaloneEnums.ScrollbarVisibility, WrappingIndent: standaloneEnums.WrappingIndent, OverviewRulerLane: standaloneEnums.OverviewRulerLane, + MinimapPosition: standaloneEnums.MinimapPosition, EndOfLinePreference: standaloneEnums.EndOfLinePreference, DefaultEndOfLine: standaloneEnums.DefaultEndOfLine, EndOfLineSequence: standaloneEnums.EndOfLineSequence, diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index ee41d4a723..1dc3b704b9 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -250,7 +250,7 @@ export interface IEncodedLineTokens { * - f = foreground ColorId (9 bits) * - b = background ColorId (9 bits) * - The color value for each colorId is defined in IStandaloneThemeData.customTokenColors: - * e.g colorId = 1 is stored in IStandaloneThemeData.customTokenColors[1]. Color id = 0 means no color, + * e.g. colorId = 1 is stored in IStandaloneThemeData.customTokenColors[1]. Color id = 0 means no color, * id = 1 is for the default foreground color, id = 2 for the default background. */ tokens: Uint32Array; @@ -290,14 +290,11 @@ export interface EncodedTokensProvider { } function isEncodedTokensProvider(provider: TokensProvider | EncodedTokensProvider): provider is EncodedTokensProvider { - return provider['tokenizeEncoded']; + return 'tokenizeEncoded' in provider; } function isThenable(obj: any): obj is Thenable { - if (typeof obj.then === 'function') { - return true; - } - return false; + return obj && typeof obj.then === 'function'; } /** @@ -427,7 +424,7 @@ export function registerCodeLensProvider(languageId: string, provider: modes.Cod */ export function registerCodeActionProvider(languageId: string, provider: CodeActionProvider): IDisposable { return modes.CodeActionProviderRegistry.register(languageId, { - provideCodeActions: (model: model.ITextModel, range: Range, context: modes.CodeActionContext, token: CancellationToken): (modes.Command | modes.CodeAction)[] | Promise<(modes.Command | modes.CodeAction)[]> => { + provideCodeActions: (model: model.ITextModel, range: Range, context: modes.CodeActionContext, token: CancellationToken): modes.CodeActionList | Promise => { let markers = StaticServices.markerService.get().read({ resource: model.uri }).filter(m => { return Range.areIntersectingOrTouching(m, range); }); @@ -510,7 +507,7 @@ export interface CodeActionProvider { /** * Provide commands for the given document and range. */ - provideCodeActions(model: model.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): (modes.Command | modes.CodeAction)[] | Promise<(modes.Command | modes.CodeAction)[]>; + provideCodeActions(model: model.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): modes.CodeActionList | Promise; } /** diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 5f9df5ce97..02400fadf1 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -13,7 +13,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; -import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, SimpleLayoutService } from 'vs/editor/standalone/browser/simpleServices'; +import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleEditorProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, SimpleLayoutService } from 'vs/editor/standalone/browser/simpleServices'; import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl'; import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; @@ -36,7 +36,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -44,11 +44,10 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { MenuService } from 'vs/platform/actions/common/menuService'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; -import { ISuggestMemoryService, SuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { ICodeLensCache, CodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache'; +import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; export interface IEditorOverrideServices { [index: string]: any; @@ -100,6 +99,11 @@ export module StaticServices { // Create a fresh service collection let result = new ServiceCollection(); + // make sure to add all services that use `registerSingleton` + for (const [id, descriptor] of getSingletonServiceDescriptors()) { + result.set(id, descriptor); + } + // Initialize the service collection with the overrides for (let serviceId in overrides) { if (overrides.hasOwnProperty(serviceId)) { @@ -150,17 +154,13 @@ export module StaticServices { export const codeEditorService = define(ICodeEditorService, (o) => new StandaloneCodeEditorServiceImpl(standaloneThemeService.get(o))); - export const progressService = define(IProgressService, () => new SimpleProgressService()); + export const editorProgressService = define(IEditorProgressService, () => new SimpleEditorProgressService()); export const storageService = define(IStorageService, () => new InMemoryStorageService()); export const logService = define(ILogService, () => new NullLogService()); export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o))); - - export const suggestMemoryService = define(ISuggestMemoryService, (o) => new SuggestMemoryService(storageService.get(o), configurationService.get(o))); - - export const codeLensCacheService = define(ICodeLensCache, (o) => new CodeLensCache(storageService.get(o))); } export class DynamicStandaloneServices extends Disposable { diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 8078e99670..a81c4c3d0f 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -27,7 +27,7 @@ class StandaloneTheme implements IStandaloneTheme { public readonly themeName: string; private readonly themeData: IStandaloneThemeData; - private colors: { [colorId: string]: Color } | null; + private colors: Map | null; private readonly defaultColors: { [colorId: string]: Color | undefined; }; private _tokenTheme: TokenTheme | null; @@ -57,19 +57,18 @@ class StandaloneTheme implements IStandaloneTheme { } } - private getColors(): { [colorId: string]: Color } { + private getColors(): Map { if (!this.colors) { - let colors: { [colorId: string]: Color } = Object.create(null); + const colors = new Map(); for (let id in this.themeData.colors) { - colors[id] = Color.fromHex(this.themeData.colors[id]); + colors.set(id, Color.fromHex(this.themeData.colors[id])); } if (this.themeData.inherit) { let baseData = getBuiltinRules(this.themeData.base); for (let id in baseData.colors) { - if (!colors[id]) { - colors[id] = Color.fromHex(baseData.colors[id]); + if (!colors.has(id)) { + colors.set(id, Color.fromHex(baseData.colors[id])); } - } } this.colors = colors; @@ -78,7 +77,7 @@ class StandaloneTheme implements IStandaloneTheme { } public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color | undefined { - const color = this.getColors()[colorId]; + const color = this.getColors().get(colorId); if (color) { return color; } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index c53ae2d138..2f3a0b8e4f 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2052,7 +2052,7 @@ suite('Editor Controller - Regression tests', () => { getInitialState: () => NULL_STATE, tokenize: undefined!, tokenize2: (line: string, state: IState): TokenizationResult2 => { - return new TokenizationResult2(null!, state); + return new TokenizationResult2(new Uint32Array(0), state); } }; diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index f0f3000095..a9779c3c49 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -18,7 +18,8 @@ export function testCommand( selection: Selection, commandFactory: (selection: Selection) => editorCommon.ICommand, expectedLines: string[], - expectedSelection: Selection + expectedSelection: Selection, + forceTokenization?: boolean ): void { let model = TextModel.createFromString(lines.join('\n'), undefined, languageIdentifier); withTestCodeEditor('', { model: model }, (_editor, cursor) => { @@ -26,6 +27,10 @@ export function testCommand( return; } + if (forceTokenization) { + model.forceTokenization(model.getLineCount()); + } + cursor.setSelections('tests', [selection]); cursor.trigger('tests', editorCommon.Handler.ExecuteCommand, commandFactory(cursor.getSelection())); diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts index df6196d7aa..38984ff8e9 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -110,7 +110,9 @@ suite('ModelLinesTokens', () => { for (let lineIndex = 0; lineIndex < initial.length; lineIndex++) { const lineTokens = initial[lineIndex].tokens; const lineTextLength = model.getLineMaxColumn(lineIndex + 1) - 1; - model._tokens._setTokens(0, lineIndex, lineTextLength, TestToken.toTokens(lineTokens)); + const tokens = TestToken.toTokens(lineTokens); + LineTokens.convertToEndOffset(tokens, lineTextLength); + model.setLineTokens(lineIndex + 1, tokens); } model.applyEdits(edits.map((ed) => ({ @@ -441,14 +443,16 @@ suite('ModelLinesTokens', () => { test('insertion on empty line', () => { const model = new TextModel('some text', TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0)); - model._tokens._setTokens(0, 0, model.getLineMaxColumn(1) - 1, TestToken.toTokens([new TestToken(0, 1)])); + const tokens = TestToken.toTokens([new TestToken(0, 1)]); + LineTokens.convertToEndOffset(tokens, model.getLineMaxColumn(1) - 1); + model.setLineTokens(1, tokens); model.applyEdits([{ range: new Range(1, 1, 1, 10), text: '' }]); - model._tokens._setTokens(0, 0, model.getLineMaxColumn(1) - 1, new Uint32Array(0)); + model.setLineTokens(1, new Uint32Array(0)); model.applyEdits([{ range: new Range(1, 1, 1, 1), @@ -660,7 +664,7 @@ suite('ModelLinesTokens', () => { test('updates tokens on insertion 10', () => { testLineEditTokens( '', - null!, + [], [{ startColumn: 1, endColumn: 1, diff --git a/src/vs/editor/test/common/model/model.modes.test.ts b/src/vs/editor/test/common/model/model.modes.test.ts index 176e404a05..a80302c304 100644 --- a/src/vs/editor/test/common/model/model.modes.test.ts +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -29,7 +29,7 @@ suite('Editor Model - Model Modes 1', () => { tokenize: undefined!, tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { calledFor.push(line.charAt(0)); - return new TokenizationResult2(null!, state); + return new TokenizationResult2(new Uint32Array(0), state); } }; @@ -170,37 +170,23 @@ suite('Editor Model - Model Modes 2', () => { } } + let calledFor: string[] = []; + + function checkAndClear(arr: string[]): void { + assert.deepEqual(calledFor, arr); + calledFor = []; + } + const tokenizationSupport: modes.ITokenizationSupport = { getInitialState: () => new ModelState2(''), tokenize: undefined!, tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { + calledFor.push(line); (state).prevLineContent = line; - return new TokenizationResult2(null!, state); + return new TokenizationResult2(new Uint32Array(0), state); } }; - function invalidEqual(model: TextModel, expected: number[]): void { - let actual: number[] = []; - for (let i = 0, len = model.getLineCount(); i < len; i++) { - if (model._tokens._isInvalid(i)) { - actual.push(i); - } - } - assert.deepEqual(actual, expected); - } - - function stateEqual(state: modes.IState, content: string): void { - assert.equal((state).prevLineContent, content); - } - - function statesEqual(model: TextModel, states: string[]): void { - let i, len = states.length - 1; - for (i = 0; i < len; i++) { - stateEqual(model._tokens._getState(i)!, states[i]); - } - stateEqual((model)._tokens._lastState, states[len]); - } - let thisModel: TextModel; let languageRegistration: IDisposable; @@ -223,64 +209,54 @@ suite('Editor Model - Model Modes 2', () => { test('getTokensForInvalidLines one text insert', () => { thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.applyEdits([EditOperation.insert(new Position(1, 6), '-')]); - invalidEqual(thisModel, [0]); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1-', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1-', 'Line2']); }); test('getTokensForInvalidLines two text insert', () => { thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.applyEdits([ EditOperation.insert(new Position(1, 6), '-'), EditOperation.insert(new Position(3, 6), '-') ]); - invalidEqual(thisModel, [0, 2]); thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1-', 'Line2', 'Line3-', 'Line4', 'Line5']); + checkAndClear(['Line1-', 'Line2', 'Line3-', 'Line4']); }); test('getTokensForInvalidLines one multi-line text insert, one small text insert', () => { thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.applyEdits([EditOperation.insert(new Position(1, 6), '\nNew line\nAnother new line')]); - invalidEqual(thisModel, [0, 1, 2]); thisModel.applyEdits([EditOperation.insert(new Position(5, 6), '-')]); - invalidEqual(thisModel, [0, 1, 2, 4]); thisModel.forceTokenization(7); - statesEqual(thisModel, ['', 'Line1', 'New line', 'Another new line', 'Line2', 'Line3-', 'Line4', 'Line5']); + checkAndClear(['Line1', 'New line', 'Another new line', 'Line2', 'Line3-', 'Line4']); }); test('getTokensForInvalidLines one delete text', () => { thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 5))]); - invalidEqual(thisModel, [0]); thisModel.forceTokenization(5); - statesEqual(thisModel, ['', '1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['1', 'Line2']); }); test('getTokensForInvalidLines one line delete text', () => { thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 2, 1))]); - invalidEqual(thisModel, [0]); - statesEqual(thisModel, ['', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.forceTokenization(4); - statesEqual(thisModel, ['', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line2']); }); test('getTokensForInvalidLines multiple lines delete text', () => { thisModel.forceTokenization(5); - statesEqual(thisModel, ['', 'Line1', 'Line2', 'Line3', 'Line4', 'Line5']); + checkAndClear(['Line1', 'Line2', 'Line3', 'Line4', 'Line5']); thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 3))]); - invalidEqual(thisModel, [0]); - statesEqual(thisModel, ['', 'Line3', 'Line4', 'Line5']); thisModel.forceTokenization(3); - statesEqual(thisModel, ['', 'ne3', 'Line4', 'Line5']); + checkAndClear(['ne3', 'Line4']); }); }); diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index ef24605e40..e244a529da 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -26,17 +26,21 @@ function testGuessIndentation(defaultInsertSpaces: boolean, defaultTabSize: numb assert.equal(r.tabSize, expectedTabSize, msg); } -function assertGuess(expectedInsertSpaces: boolean | undefined, expectedTabSize: number | undefined, text: string[], msg?: string): void { +function assertGuess(expectedInsertSpaces: boolean | undefined, expectedTabSize: number | undefined | [number], text: string[], msg?: string): void { if (typeof expectedInsertSpaces === 'undefined') { // cannot guess insertSpaces if (typeof expectedTabSize === 'undefined') { // cannot guess tabSize testGuessIndentation(true, 13370, true, 13370, text, msg); testGuessIndentation(false, 13371, false, 13371, text, msg); - } else { + } else if (typeof expectedTabSize === 'number') { // can guess tabSize testGuessIndentation(true, 13370, true, expectedTabSize, text, msg); testGuessIndentation(false, 13371, false, expectedTabSize, text, msg); + } else { + // can only guess tabSize when insertSpaces is true + testGuessIndentation(true, 13370, true, expectedTabSize[0], text, msg); + testGuessIndentation(false, 13371, false, 13371, text, msg); } } else { // can guess insertSpaces @@ -44,10 +48,19 @@ function assertGuess(expectedInsertSpaces: boolean | undefined, expectedTabSize: // cannot guess tabSize testGuessIndentation(true, 13370, expectedInsertSpaces, 13370, text, msg); testGuessIndentation(false, 13371, expectedInsertSpaces, 13371, text, msg); - } else { + } else if (typeof expectedTabSize === 'number') { // can guess tabSize testGuessIndentation(true, 13370, expectedInsertSpaces, expectedTabSize, text, msg); testGuessIndentation(false, 13371, expectedInsertSpaces, expectedTabSize, text, msg); + } else { + // can only guess tabSize when insertSpaces is true + if (expectedInsertSpaces === true) { + testGuessIndentation(true, 13370, expectedInsertSpaces, expectedTabSize[0], text, msg); + testGuessIndentation(false, 13371, expectedInsertSpaces, expectedTabSize[0], text, msg); + } else { + testGuessIndentation(true, 13370, expectedInsertSpaces, 13370, text, msg); + testGuessIndentation(false, 13371, expectedInsertSpaces, 13371, text, msg); + } } } } @@ -219,7 +232,7 @@ suite('Editor Model - TextModel', () => { '\tx' ], '7xTAB'); - assertGuess(undefined, 2, [ + assertGuess(undefined, [2], [ '\tx', ' x', '\tx', @@ -239,7 +252,7 @@ suite('Editor Model - TextModel', () => { '\tx', ' x' ], '4x1, 4xTAB'); - assertGuess(false, 2, [ + assertGuess(false, undefined, [ '\tx', '\tx', ' x', @@ -250,7 +263,7 @@ suite('Editor Model - TextModel', () => { '\tx', ' x', ], '4x2, 5xTAB'); - assertGuess(false, 2, [ + assertGuess(false, undefined, [ '\tx', '\tx', 'x', @@ -261,7 +274,7 @@ suite('Editor Model - TextModel', () => { '\tx', ' x', ], '1x2, 5xTAB'); - assertGuess(false, 4, [ + assertGuess(false, undefined, [ '\tx', '\tx', 'x', @@ -272,7 +285,7 @@ suite('Editor Model - TextModel', () => { '\tx', ' x', ], '1x4, 5xTAB'); - assertGuess(false, 2, [ + assertGuess(false, undefined, [ '\tx', '\tx', 'x', @@ -524,7 +537,7 @@ suite('Editor Model - TextModel', () => { ' \t x', '\tx' ], 'mixed whitespace 1'); - assertGuess(false, 4, [ + assertGuess(false, undefined, [ '\tx', '\t x' ], 'mixed whitespace 2'); @@ -575,6 +588,26 @@ suite('Editor Model - TextModel', () => { ]); }); + test('issue #70832: Broken indentation detection', () => { + assertGuess(false, undefined, [ + 'x', + 'x', + 'x', + 'x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + ' x', + 'x', + ]); + }); + test('validatePosition', () => { let m = TextModel.createFromString('line one\nline two'); diff --git a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts index 687eb4aa71..0c034204cb 100644 --- a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts +++ b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts @@ -5,11 +5,12 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; -import { EditorSimpleWorkerImpl, ICommonModel } from 'vs/editor/common/services/editorSimpleWorker'; +import { EditorSimpleWorker, ICommonModel } from 'vs/editor/common/services/editorSimpleWorker'; +import { EditorWorkerHost } from 'vs/editor/common/services/editorWorkerServiceImpl'; suite('EditorSimpleWorker', () => { - class WorkerWithModels extends EditorSimpleWorkerImpl { + class WorkerWithModels extends EditorSimpleWorker { getModel(uri: string) { return this._getModel(uri); @@ -31,7 +32,7 @@ suite('EditorSimpleWorker', () => { let model: ICommonModel; setup(() => { - worker = new WorkerWithModels(null); + worker = new WorkerWithModels(null!, null); model = worker.addModel([ 'This is line one', //16 'and this is line number two', //27 diff --git a/src/vs/loader.js b/src/vs/loader.js index 3a57c5d59d..bbe5b058a2 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -23,7 +23,7 @@ var _commonjsGlobal = typeof global === 'object' ? global : {}; var AMDLoader; (function (AMDLoader) { AMDLoader.global = _amdLoaderGlobal; - var Environment = (function () { + var Environment = /** @class */ (function () { function Environment() { this._detected = false; this._isWindows = false; @@ -94,7 +94,7 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - var LoaderEvent = (function () { + var LoaderEvent = /** @class */ (function () { function LoaderEvent(type, detail, timestamp) { this.type = type; this.detail = detail; @@ -103,7 +103,7 @@ var AMDLoader; return LoaderEvent; }()); AMDLoader.LoaderEvent = LoaderEvent; - var LoaderEventRecorder = (function () { + var LoaderEventRecorder = /** @class */ (function () { function LoaderEventRecorder(loaderAvailableTimestamp) { this._events = [new LoaderEvent(1 /* LoaderAvailable */, '', loaderAvailableTimestamp)]; } @@ -116,7 +116,7 @@ var AMDLoader; return LoaderEventRecorder; }()); AMDLoader.LoaderEventRecorder = LoaderEventRecorder; - var NullLoaderEventRecorder = (function () { + var NullLoaderEventRecorder = /** @class */ (function () { function NullLoaderEventRecorder() { } NullLoaderEventRecorder.prototype.record = function (type, detail) { @@ -125,9 +125,9 @@ var AMDLoader; NullLoaderEventRecorder.prototype.getEvents = function () { return []; }; + NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); return NullLoaderEventRecorder; }()); - NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); AMDLoader.NullLoaderEventRecorder = NullLoaderEventRecorder; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- @@ -136,7 +136,7 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - var Utilities = (function () { + var Utilities = /** @class */ (function () { function Utilities() { } /** @@ -222,11 +222,11 @@ var AMDLoader; } return (this.HAS_PERFORMANCE_NOW ? AMDLoader.global.performance.now() : Date.now()); }; + Utilities.NEXT_ANONYMOUS_ID = 1; + Utilities.PERFORMANCE_NOW_PROBED = false; + Utilities.HAS_PERFORMANCE_NOW = false; return Utilities; }()); - Utilities.NEXT_ANONYMOUS_ID = 1; - Utilities.PERFORMANCE_NOW_PROBED = false; - Utilities.HAS_PERFORMANCE_NOW = false; AMDLoader.Utilities = Utilities; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- @@ -235,7 +235,8 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - var ConfigurationOptionsUtil = (function () { + ; + var ConfigurationOptionsUtil = /** @class */ (function () { function ConfigurationOptionsUtil() { } /** @@ -278,13 +279,16 @@ var AMDLoader; if (typeof options.catchError === 'undefined') { options.catchError = false; } + if (typeof options.recordStats === 'undefined') { + options.recordStats = false; + } if (typeof options.urlArgs !== 'string') { options.urlArgs = ''; } if (typeof options.onError !== 'function') { options.onError = defaultOnError; } - if (typeof options.ignoreDuplicateModules !== 'object' || !Array.isArray(options.ignoreDuplicateModules)) { + if (!Array.isArray(options.ignoreDuplicateModules)) { options.ignoreDuplicateModules = []; } if (options.baseUrl.length > 0) { @@ -305,22 +309,8 @@ var AMDLoader; if (typeof options.nodeCachedData.writeDelay !== 'number' || options.nodeCachedData.writeDelay < 0) { options.nodeCachedData.writeDelay = 1000 * 7; } - if (typeof options.nodeCachedData.onData !== 'function') { - options.nodeCachedData.onData = function (err) { - if (err && err.errorCode === 'cachedDataRejected') { - console.warn('Rejected cached data from file: ' + err.path); - } - else if (err && err.errorCode) { - console.error('Problems handling cached data file: ' + err.path); - console.error(err.detail); - } - else if (err) { - console.error(err); - } - }; - } if (!options.nodeCachedData.path || typeof options.nodeCachedData.path !== 'string') { - options.nodeCachedData.onData('INVALID cached data configuration, \'path\' MUST be set'); + options.onError('INVALID cached data configuration, \'path\' MUST be set'); options.nodeCachedData = undefined; } } @@ -350,7 +340,7 @@ var AMDLoader; return ConfigurationOptionsUtil; }()); AMDLoader.ConfigurationOptionsUtil = ConfigurationOptionsUtil; - var Configuration = (function () { + var Configuration = /** @class */ (function () { function Configuration(env, options) { this._env = env; this.options = ConfigurationOptionsUtil.mergeConfigurationOptions(options); @@ -561,7 +551,7 @@ var AMDLoader; /** * Load `scriptSrc` only once (avoid multiple + + + + \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index ab8897918e..bd52911061 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -3,41 +3,54 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // @ts-check -'use strict'; /** - * Use polling to track focus of main webview and iframes within the webview - * - * @param {Object} handlers - * @param {() => void} handlers.onFocus - * @param {() => void} handlers.onBlur + * @typedef {{ + * postMessage: (channel: string, data?: any) => void, + * onMessage: (channel: string, handler: any) => void, + * focusIframeOnCreate?: boolean, + * ready?: Promise, + * onIframeLoaded?: (iframe: HTMLIFrameElement) => void, + * fakeLoad: boolean + * }} WebviewHost */ -const trackFocus = ({ onFocus, onBlur }) => { - const interval = 50; - let isFocused = document.hasFocus(); - setInterval(() => { - const isCurrentlyFocused = document.hasFocus(); - if (isCurrentlyFocused === isFocused) { - return; - } - isFocused = isCurrentlyFocused; - if (isCurrentlyFocused) { - onFocus(); - } else { - onBlur(); - } - }, interval); -}; -const getActiveFrame = () => { - return /** @type {HTMLIFrameElement} */ (document.getElementById('active-frame')); -}; +(function () { + 'use strict'; -const getPendingFrame = () => { - return /** @type {HTMLIFrameElement} */ (document.getElementById('pending-frame')); -}; + /** + * Use polling to track focus of main webview and iframes within the webview + * + * @param {Object} handlers + * @param {() => void} handlers.onFocus + * @param {() => void} handlers.onBlur + */ + const trackFocus = ({ onFocus, onBlur }) => { + const interval = 50; + let isFocused = document.hasFocus(); + setInterval(() => { + const isCurrentlyFocused = document.hasFocus(); + if (isCurrentlyFocused === isFocused) { + return; + } + isFocused = isCurrentlyFocused; + if (isCurrentlyFocused) { + onFocus(); + } else { + onBlur(); + } + }, interval); + }; -const defaultCssRules = ` + const getActiveFrame = () => { + return /** @type {HTMLIFrameElement} */ (document.getElementById('active-frame')); + }; + + const getPendingFrame = () => { + return /** @type {HTMLIFrameElement} */ (document.getElementById('pending-frame')); + }; + + const defaultCssRules = ` body { background-color: var(--vscode-editor-background); color: var(--vscode-editor-foreground); @@ -93,151 +106,156 @@ const defaultCssRules = ` background-color: var(--vscode-scrollbarSlider-activeBackground); }`; -/** - * @typedef {{ postMessage: (channel: string, data?: any) => void, onMessage: (channel: string, handler: any) => void }} HostCommunications - */ - -/** - * @param {HostCommunications} host - */ -module.exports = function createWebviewManager(host) { - // state - let firstLoad = true; - let loadTimeout; - let pendingMessages = []; - let isInDevelopmentMode = false; - - const initData = { - initialScrollProgress: undefined - }; - /** - * @param {HTMLDocument?} document - * @param {HTMLElement?} body + * @param {*} [state] + * @return {string} */ - const applyStyles = (document, body) => { - if (!document) { - return; - } + function getVsCodeApiScript(state) { + return ` + const acquireVsCodeApi = (function() { + const originalPostMessage = window.parent.postMessage.bind(window.parent); + const targetOrigin = '*'; + let acquired = false; - if (body) { - body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); - body.classList.add(initData.activeTheme); - } + let state = ${state ? `JSON.parse(${JSON.stringify(state)})` : undefined}; - if (initData.styles) { - for (const variable of Object.keys(initData.styles)) { - document.documentElement.style.setProperty(`--${variable}`, initData.styles[variable]); - } - } - }; - - /** - * @param {MouseEvent} event - */ - const handleInnerClick = (event) => { - if (!event || !event.view || !event.view.document) { - return; - } - - let baseElement = event.view.document.getElementsByTagName('base')[0]; - /** @type {any} */ - let node = event.target; - while (node) { - if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { - if (node.getAttribute('href') === '#') { - event.view.scrollTo(0, 0); - } else if (node.hash && (node.getAttribute('href') === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) { - let scrollTarget = event.view.document.getElementById(node.hash.substr(1, node.hash.length - 1)); - if (scrollTarget) { - scrollTarget.scrollIntoView(); + return () => { + if (acquired) { + throw new Error('An instance of the VS Code API has already been acquired'); } - } else { - host.postMessage('did-click-link', node.href.baseVal || node.href); - } - event.preventDefault(); - break; - } - node = node.parentNode; - } - }; + acquired = true; + return Object.freeze({ + postMessage: function(msg) { + return originalPostMessage({ command: 'onmessage', data: msg }, targetOrigin); + }, + setState: function(newState) { + state = newState; + originalPostMessage({ command: 'do-update-state', data: JSON.stringify(newState) }, targetOrigin); + return newState; + }, + getState: function() { + return state; + } + }); + }; + })(); + delete window.parent; + delete window.top; + delete window.frameElement; + `; + } /** - * @param {KeyboardEvent} e + * @param {WebviewHost} host */ - const handleInnerKeydown = (e) => { - host.postMessage('did-keydown', { - key: e.key, - keyCode: e.keyCode, - code: e.code, - shiftKey: e.shiftKey, - altKey: e.altKey, - ctrlKey: e.ctrlKey, - metaKey: e.metaKey, - repeat: e.repeat - }); - }; + function createWebviewManager(host) { + // state + let firstLoad = true; + let loadTimeout; + let pendingMessages = []; - const onMessage = (message) => { - host.postMessage(message.data.command, message.data.data); - }; + const initData = { + initialScrollProgress: undefined + }; - let isHandlingScroll = false; - const handleInnerScroll = (event) => { - if (!event.target || !event.target.body) { - return; - } - if (isHandlingScroll) { - return; - } - const progress = event.currentTarget.scrollY / event.target.body.clientHeight; - if (isNaN(progress)) { - return; - } - - isHandlingScroll = true; - window.requestAnimationFrame(() => { - try { - host.postMessage('did-scroll', progress); - } catch (e) { - // noop - } - isHandlingScroll = false; - }); - }; - - document.addEventListener('DOMContentLoaded', () => { - if (!document.body) { - return; - } - - host.onMessage('styles', (_event, variables, activeTheme) => { - initData.styles = variables; - initData.activeTheme = activeTheme; - - const target = getActiveFrame(); - if (!target) { + /** + * @param {HTMLDocument?} document + * @param {HTMLElement?} body + */ + const applyStyles = (document, body) => { + if (!document) { return; } - if (target.contentDocument) { - applyStyles(target.contentDocument, target.contentDocument.body); + if (body) { + body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); + body.classList.add(initData.activeTheme); } - }); - // propagate focus - host.onMessage('focus', () => { - const target = getActiveFrame(); - if (target) { - target.contentWindow.focus(); + if (initData.styles) { + for (const variable of Object.keys(initData.styles)) { + document.documentElement.style.setProperty(`--${variable}`, initData.styles[variable]); + } } - }); + }; - // update iframe-contents - host.onMessage('content', (_event, data) => { + /** + * @param {MouseEvent} event + */ + const handleInnerClick = (event) => { + if (!event || !event.view || !event.view.document) { + return; + } + + let baseElement = event.view.document.getElementsByTagName('base')[0]; + /** @type {any} */ + let node = event.target; + while (node) { + if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { + if (node.getAttribute('href') === '#') { + event.view.scrollTo(0, 0); + } else if (node.hash && (node.getAttribute('href') === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) { + let scrollTarget = event.view.document.getElementById(node.hash.substr(1, node.hash.length - 1)); + if (scrollTarget) { + scrollTarget.scrollIntoView(); + } + } else { + host.postMessage('did-click-link', node.href.baseVal || node.href); + } + event.preventDefault(); + break; + } + node = node.parentNode; + } + }; + + /** + * @param {KeyboardEvent} e + */ + const handleInnerKeydown = (e) => { + host.postMessage('did-keydown', { + key: e.key, + keyCode: e.keyCode, + code: e.code, + shiftKey: e.shiftKey, + altKey: e.altKey, + ctrlKey: e.ctrlKey, + metaKey: e.metaKey, + repeat: e.repeat + }); + }; + + let isHandlingScroll = false; + const handleInnerScroll = (event) => { + if (!event.target || !event.target.body) { + return; + } + if (isHandlingScroll) { + return; + } + + const progress = event.currentTarget.scrollY / event.target.body.clientHeight; + if (isNaN(progress)) { + return; + } + + isHandlingScroll = true; + window.requestAnimationFrame(() => { + try { + host.postMessage('did-scroll', progress); + } catch (e) { + // noop + } + isHandlingScroll = false; + }); + }; + + /** + * @return {string} + */ + function toContentHtml(data) { const options = data.options; - const text = data.contents; const newDocument = new DOMParser().parseFromString(text, 'text/html'); @@ -250,38 +268,7 @@ module.exports = function createWebviewManager(host) { // apply default script if (options.allowScripts) { const defaultScript = newDocument.createElement('script'); - defaultScript.textContent = ` - const acquireVsCodeApi = (function() { - const originalPostMessage = window.parent.postMessage.bind(window.parent); - let acquired = false; - - let state = ${data.state ? `JSON.parse(${JSON.stringify(data.state)})` : undefined}; - - return () => { - if (acquired) { - throw new Error('An instance of the VS Code API has already been acquired'); - } - acquired = true; - return Object.freeze({ - postMessage: function(msg) { - return originalPostMessage({ command: 'onmessage', data: msg }, '*'); - }, - setState: function(newState) { - state = newState; - originalPostMessage({ command: 'do-update-state', data: JSON.stringify(newState) }, '*'); - return newState; - }, - getState: function() { - return state; - } - }); - }; - })(); - delete window.parent; - delete window.top; - delete window.frameElement; - `; - + defaultScript.textContent = getVsCodeApiScript(data.state); newDocument.head.prepend(defaultScript); } @@ -293,154 +280,219 @@ module.exports = function createWebviewManager(host) { applyStyles(newDocument, newDocument.body); - const frame = getActiveFrame(); - const wasFirstLoad = firstLoad; - // keep current scrollY around and use later - let setInitialScrollPosition; - if (firstLoad) { - firstLoad = false; - setInitialScrollPosition = (body, window) => { - if (!isNaN(initData.initialScrollProgress)) { - if (window.scrollY === 0) { - window.scroll(0, body.clientHeight * initData.initialScrollProgress); - } - } - }; - } else { - const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0; - setInitialScrollPosition = (body, window) => { - if (window.scrollY === 0) { - window.scroll(0, scrollY); - } - }; - } - - // Clean up old pending frames and set current one as new one - const previousPendingFrame = getPendingFrame(); - if (previousPendingFrame) { - previousPendingFrame.setAttribute('id', ''); - document.body.removeChild(previousPendingFrame); - } - if (!wasFirstLoad) { - pendingMessages = []; - } - - const newFrame = document.createElement('iframe'); - newFrame.setAttribute('id', 'pending-frame'); - newFrame.setAttribute('frameborder', '0'); - newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); - newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; - document.body.appendChild(newFrame); - - // write new content onto iframe - newFrame.contentDocument.open('text/html', 'replace'); - - newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown); - - newFrame.contentWindow.addEventListener('DOMContentLoaded', e => { - const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; - if (contentDocument) { - applyStyles(contentDocument, contentDocument.body); - } - }); - - newFrame.contentWindow.onbeforeunload = () => { - if (isInDevelopmentMode) { // Allow reloads while developing a webview - host.postMessage('do-reload'); - return false; - } - - // Block navigation when not in development mode - console.log('prevented webview navigation'); - return false; - }; - - const onLoad = (contentDocument, contentWindow) => { - if (contentDocument && contentDocument.body) { - // Workaround for https://github.com/Microsoft/vscode/issues/12865 - // check new scrollY and reset if neccessary - setInitialScrollPosition(contentDocument.body, contentWindow); - } - - const newFrame = getPendingFrame(); - if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) { - const oldActiveFrame = getActiveFrame(); - if (oldActiveFrame) { - document.body.removeChild(oldActiveFrame); - } - // Styles may have changed since we created the element. Make sure we re-style - applyStyles(newFrame.contentDocument, newFrame.contentDocument.body); - newFrame.setAttribute('id', 'active-frame'); - newFrame.style.visibility = 'visible'; - newFrame.contentWindow.focus(); - - contentWindow.addEventListener('scroll', handleInnerScroll); - - pendingMessages.forEach((data) => { - contentWindow.postMessage(data, '*'); - }); - pendingMessages = []; - } - }; - - clearTimeout(loadTimeout); - loadTimeout = undefined; - loadTimeout = setTimeout(() => { - clearTimeout(loadTimeout); - loadTimeout = undefined; - onLoad(newFrame.contentDocument, newFrame.contentWindow); - }, 200); - - newFrame.contentWindow.addEventListener('load', function (e) { - if (loadTimeout) { - clearTimeout(loadTimeout); - loadTimeout = undefined; - onLoad(e.target, this); - } - }); - - // Bubble out link clicks - newFrame.contentWindow.addEventListener('click', handleInnerClick); - // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden - newFrame.contentDocument.write(''); - newFrame.contentDocument.write(newDocument.documentElement.innerHTML); - newFrame.contentDocument.close(); + return '\n' + newDocument.documentElement.outerHTML; + } - host.postMessage('did-set-content', undefined); - }); + document.addEventListener('DOMContentLoaded', () => { + const idMatch = document.location.search.match(/\bid=([\w-]+)/); + const ID = idMatch ? idMatch[1] : undefined; + if (!document.body) { + return; + } + + host.onMessage('styles', (_event, data) => { + initData.styles = data.styles; + initData.activeTheme = data.activeTheme; - // Forward message to the embedded iframe - host.onMessage('message', (_event, data) => { - const pending = getPendingFrame(); - if (!pending) { const target = getActiveFrame(); - if (target) { - target.contentWindow.postMessage(data, '*'); + if (!target) { return; } - } - pendingMessages.push(data); + + if (target.contentDocument) { + applyStyles(target.contentDocument, target.contentDocument.body); + } + }); + + // propagate focus + host.onMessage('focus', () => { + const target = getActiveFrame(); + if (target) { + target.contentWindow.focus(); + } + }); + + // update iframe-contents + let updateId = 0; + host.onMessage('content', async (_event, data) => { + const currentUpdateId = ++updateId; + await host.ready; + if (currentUpdateId !== updateId) { + return; + } + + const options = data.options; + const newDocument = toContentHtml(data); + + const frame = getActiveFrame(); + const wasFirstLoad = firstLoad; + // keep current scrollY around and use later + let setInitialScrollPosition; + if (firstLoad) { + firstLoad = false; + setInitialScrollPosition = (body, window) => { + if (!isNaN(initData.initialScrollProgress)) { + if (window.scrollY === 0) { + window.scroll(0, body.clientHeight * initData.initialScrollProgress); + } + } + }; + } else { + const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0; + setInitialScrollPosition = (body, window) => { + if (window.scrollY === 0) { + window.scroll(0, scrollY); + } + }; + } + + // Clean up old pending frames and set current one as new one + const previousPendingFrame = getPendingFrame(); + if (previousPendingFrame) { + previousPendingFrame.setAttribute('id', ''); + document.body.removeChild(previousPendingFrame); + } + if (!wasFirstLoad) { + pendingMessages = []; + } + + const newFrame = document.createElement('iframe'); + newFrame.setAttribute('id', 'pending-frame'); + newFrame.setAttribute('frameborder', '0'); + newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); + if (host.fakeLoad) { + // We should just be able to use srcdoc, but I wasn't + // seeing the service worker applying properly. + // Fake load an empty on the correct origin and then write real html + // into it to get around this. + newFrame.src = `./fake.html?id=${ID}`; + } + newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; + document.body.appendChild(newFrame); + + if (!host.fakeLoad) { + // write new content onto iframe + newFrame.contentDocument.open(); + } + + newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown); + + newFrame.contentWindow.addEventListener('DOMContentLoaded', e => { + if (host.fakeLoad) { + newFrame.contentDocument.open(); + newFrame.contentDocument.write(newDocument); + newFrame.contentDocument.close(); + hookupOnLoadHandlers(newFrame); + } + const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; + if (contentDocument) { + applyStyles(contentDocument, contentDocument.body); + } + }); + + const onLoad = (contentDocument, contentWindow) => { + if (contentDocument && contentDocument.body) { + // Workaround for https://github.com/Microsoft/vscode/issues/12865 + // check new scrollY and reset if neccessary + setInitialScrollPosition(contentDocument.body, contentWindow); + } + + const newFrame = getPendingFrame(); + if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) { + const oldActiveFrame = getActiveFrame(); + if (oldActiveFrame) { + document.body.removeChild(oldActiveFrame); + } + // Styles may have changed since we created the element. Make sure we re-style + applyStyles(newFrame.contentDocument, newFrame.contentDocument.body); + newFrame.setAttribute('id', 'active-frame'); + newFrame.style.visibility = 'visible'; + if (host.focusIframeOnCreate) { + newFrame.contentWindow.focus(); + } + + contentWindow.addEventListener('scroll', handleInnerScroll); + + pendingMessages.forEach((data) => { + contentWindow.postMessage(data, '*'); + }); + pendingMessages = []; + } + }; + + /** + * @param {HTMLIFrameElement} newFrame + */ + function hookupOnLoadHandlers(newFrame) { + clearTimeout(loadTimeout); + loadTimeout = undefined; + loadTimeout = setTimeout(() => { + clearTimeout(loadTimeout); + loadTimeout = undefined; + onLoad(newFrame.contentDocument, newFrame.contentWindow); + }, 200); + + newFrame.contentWindow.addEventListener('load', function (e) { + if (loadTimeout) { + clearTimeout(loadTimeout); + loadTimeout = undefined; + onLoad(e.target, this); + } + }); + + // Bubble out link clicks + newFrame.contentWindow.addEventListener('click', handleInnerClick); + + if (host.onIframeLoaded) { + host.onIframeLoaded(newFrame); + } + } + + if (!host.fakeLoad) { + hookupOnLoadHandlers(newFrame); + } + + if (!host.fakeLoad) { + newFrame.contentDocument.write(newDocument); + newFrame.contentDocument.close(); + } + + host.postMessage('did-set-content', undefined); + }); + + // Forward message to the embedded iframe + host.onMessage('message', (_event, data) => { + const pending = getPendingFrame(); + if (!pending) { + const target = getActiveFrame(); + if (target) { + target.contentWindow.postMessage(data, '*'); + return; + } + } + pendingMessages.push(data); + }); + + host.onMessage('initial-scroll-position', (_event, progress) => { + initData.initialScrollProgress = progress; + }); + + + trackFocus({ + onFocus: () => host.postMessage('did-focus'), + onBlur: () => host.postMessage('did-blur') + }); + + // signal ready + host.postMessage('webview-ready', {}); }); + } - host.onMessage('initial-scroll-position', (_event, progress) => { - initData.initialScrollProgress = progress; - }); - - host.onMessage('devtools-opened', () => { - isInDevelopmentMode = true; - }); - - trackFocus({ - onFocus: () => host.postMessage('did-focus'), - onBlur: () => host.postMessage('did-blur') - }); - - // Forward messages from the embedded iframe - window.onmessage = onMessage; - - // signal ready - host.postMessage('webview-ready', process.pid); - }); -}; + if (typeof module !== 'undefined') { + module.exports = createWebviewManager; + } else { + window.createWebviewManager = createWebviewManager; + } +}()); diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js new file mode 100644 index 0000000000..4bb23b031a --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +const VERSION = 1; + +const rootPath = self.location.pathname.replace(/\/service-worker.js$/, ''); + +/** + * Root path for resources + */ +const resourceRoot = rootPath + '/vscode-resource'; + +const resolveTimeout = 30000; + +/** + * @template T + * @typedef {{ + * resolve: (x: T) => void, + * promise: Promise + * }} RequestStoreEntry + */ + +/** + * @template T + */ +class RequestStore { + constructor() { + /** @type {Map>} */ + this.map = new Map(); + } + + /** + * @param {string} webviewId + * @param {string} path + * @return {Promise | undefined} + */ + get(webviewId, path) { + const entry = this.map.get(this._key(webviewId, path)); + return entry && entry.promise; + } + + /** + * @param {string} webviewId + * @param {string} path + * @returns {Promise} + */ + create(webviewId, path) { + const existing = this.get(webviewId, path); + if (existing) { + return existing; + } + let resolve; + const promise = new Promise(r => resolve = r); + const entry = { resolve, promise }; + const key = this._key(webviewId, path); + this.map.set(key, entry); + + const dispose = () => { + clearTimeout(timeout); + const existingEntry = this.map.get(key); + if (existingEntry === entry) { + return this.map.delete(key); + } + }; + const timeout = setTimeout(dispose, resolveTimeout); + return promise; + } + + /** + * @param {string} webviewId + * @param {string} path + * @param {T} result + * @return {boolean} + */ + resolve(webviewId, path, result) { + const entry = this.map.get(this._key(webviewId, path)); + if (!entry) { + return false; + } + entry.resolve(result); + return true; + } + + /** + * @param {string} webviewId + * @param {string} path + * @return {string} + */ + _key(webviewId, path) { + return `${webviewId}@@@${path}`; + } +} + +/** + * Map of requested paths to responses. + * + * @type {RequestStore<{ body: any, mime: string } | undefined>} + */ +const resourceRequestStore = new RequestStore(); + +/** + * Map of requested localhost origins to optional redirects. + * + * @type {RequestStore} + */ +const localhostRequestStore = new RequestStore(); + +const notFound = () => + new Response('Not Found', { status: 404, }); + +self.addEventListener('message', async (event) => { + switch (event.data.channel) { + case 'version': + { + self.clients.get(event.source.id).then(client => { + if (client) { + client.postMessage({ + channel: 'version', + version: VERSION + }); + } + }); + return; + } + case 'did-load-resource': + { + const webviewId = getWebviewIdForClient(event.source); + const data = event.data.data; + const response = data.status === 200 + ? { body: data.data, mime: data.mime } + : undefined; + + if (!resourceRequestStore.resolve(webviewId, data.path, response)) { + console.log('Could not resolve unknown resource', data.path); + } + return; + } + + case 'did-load-localhost': + { + const webviewId = getWebviewIdForClient(event.source); + const data = event.data.data; + if (!localhostRequestStore.resolve(webviewId, data.origin, data.location)) { + console.log('Could not resolve unknown localhost', data.origin); + } + return; + } + } + + console.log('Unknown message'); +}); + +self.addEventListener('fetch', (event) => { + const requestUrl = new URL(event.request.url); + + // See if it's a resource request + if (requestUrl.origin === self.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { + return event.respondWith(processResourceRequest(event, requestUrl)); + } + + // See if it's a localhost request + if (requestUrl.origin !== self.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { + return event.respondWith(processLocalhostRequest(event, requestUrl)); + } +}); + +self.addEventListener('install', (event) => { + event.waitUntil(self.skipWaiting()); // Activate worker immediately +}); + +self.addEventListener('activate', (event) => { + event.waitUntil(self.clients.claim()); // Become available to all pages +}); + +async function processResourceRequest(event, requestUrl) { + const client = await self.clients.get(event.clientId); + if (!client) { + console.log('Could not find inner client for request'); + return notFound(); + } + + const webviewId = getWebviewIdForClient(client); + const resourcePath = requestUrl.pathname.startsWith(resourceRoot + '/') ? requestUrl.pathname.slice(resourceRoot.length) : requestUrl.pathname; + + function resolveResourceEntry(entry) { + if (!entry) { + return notFound(); + } + return new Response(entry.body, { + status: 200, + headers: { 'Content-Type': entry.mime } + }); + } + + const parentClient = await getOuterIframeClient(webviewId); + if (!parentClient) { + console.log('Could not find parent client for request'); + return notFound(); + } + + // Check if we've already resolved this request + const existing = resourceRequestStore.get(webviewId, resourcePath); + if (existing) { + return existing.then(resolveResourceEntry); + } + + parentClient.postMessage({ + channel: 'load-resource', + path: resourcePath + }); + + return resourceRequestStore.create(webviewId, resourcePath) + .then(resolveResourceEntry); +} + +/** + * @param {*} event + * @param {URL} requestUrl + */ +async function processLocalhostRequest(event, requestUrl) { + const client = await self.clients.get(event.clientId); + if (!client) { + // This is expected when requesting resources on other localhost ports + // that are not spawned by vs code + return undefined; + } + const webviewId = getWebviewIdForClient(client); + const origin = requestUrl.origin; + + const resolveRedirect = redirectOrigin => { + if (!redirectOrigin) { + return fetch(event.request); + } + const location = event.request.url.replace(new RegExp(`^${requestUrl.origin}(/|$)`), `${redirectOrigin}$1`); + return new Response(null, { + status: 302, + headers: { + Location: location + } + }); + }; + + const parentClient = await getOuterIframeClient(webviewId); + if (!parentClient) { + console.log('Could not find parent client for request'); + return notFound(); + } + + // Check if we've already resolved this request + const existing = localhostRequestStore.get(webviewId, origin); + if (existing) { + return existing.then(resolveRedirect); + } + + parentClient.postMessage({ + channel: 'load-localhost', + origin: origin + }); + + return localhostRequestStore.create(webviewId, origin) + .then(resolveRedirect); +} + +function getWebviewIdForClient(client) { + const requesterClientUrl = new URL(client.url); + return requesterClientUrl.search.match(/\bid=([a-z0-9-]+)/i)[1]; +} + +async function getOuterIframeClient(webviewId) { + const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + return allClients.find(client => { + const clientUrl = new URL(client.url); + return (clientUrl.pathname === `${rootPath}/` || clientUrl.pathname === `${rootPath}/index.html`) && clientUrl.search.match(new RegExp('\\bid=' + webviewId)); + }); +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 461f1d65d7..bb193a440d 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { isMacintosh } from 'vs/base/common/platform'; import { localize } from 'vs/nls'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -17,8 +15,8 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/contrib/webview/common/webview'; -import { CopyWebviewEditorCommand, CutWebviewEditorCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, PasteWebviewEditorCommand, RedoWebviewEditorCommand, ReloadWebviewAction, SelectAllWebviewEditorCommand, ShowWebViewEditorFindWidgetCommand, UndoWebviewEditorCommand } from '../browser/webviewCommands'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewEditorInput } from '../browser/webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService'; @@ -35,12 +33,9 @@ Registry.as(EditorInputExtensions.EditorInputFactor registerSingleton(IWebviewEditorService, WebviewEditorService, true); - -const webviewDeveloperCategory = localize('developer', "Developer"); - const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -export function registerWebViewCommands(editorId: string): void { +function registerWebViewCommands(editorId: string): void { const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */); const showNextFindWidgetCommand = new ShowWebViewEditorFindWidgetCommand({ @@ -63,76 +58,11 @@ export function registerWebViewCommands(editorId: string): void { weight: KeybindingWeight.EditorContrib } })).register(); - - (new SelectAllWebviewEditorCommand({ - id: SelectAllWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_A, - weight: KeybindingWeight.EditorContrib - } - })).register(); - - // These commands are only needed on MacOS where we have to disable the menu bar commands - if (isMacintosh) { - (new CopyWebviewEditorCommand({ - id: CopyWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - weight: KeybindingWeight.EditorContrib - } - })).register(); - - (new PasteWebviewEditorCommand({ - id: PasteWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - weight: KeybindingWeight.EditorContrib - } - })).register(); - - - (new CutWebviewEditorCommand({ - id: CutWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - weight: KeybindingWeight.EditorContrib - } - })).register(); - - (new UndoWebviewEditorCommand({ - id: UndoWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, - weight: KeybindingWeight.EditorContrib - } - })).register(); - - (new RedoWebviewEditorCommand({ - id: RedoWebviewEditorCommand.ID, - precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), - kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, - weight: KeybindingWeight.EditorContrib - } - })).register(); - } } registerWebViewCommands(WebviewEditor.ID); -actionRegistry.registerWorkbenchAction( - new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL), - 'Webview Tools', - webviewDeveloperCategory); - actionRegistry.registerWorkbenchAction( new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL), - 'Reload Webview', + 'Reload Webviews', webviewDeveloperCategory); diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index 6cada72933..a8de3a3395 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -32,96 +32,6 @@ export class HideWebViewEditorFindCommand extends Command { } } -export class SelectAllWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.webvieweditor.selectAll'; - - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.selectAll(); - } - } -} - -export class CopyWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.webvieweditor.copy'; - - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.copy(); - } - } -} - -export class PasteWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.webvieweditor.paste'; - - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.paste(); - } - } -} - -export class CutWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.webvieweditor.cut'; - - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.cut(); - } - } -} - -export class UndoWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.webvieweditor.undo'; - - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.undo(); - } - } -} - -export class RedoWebviewEditorCommand extends Command { - public static readonly ID = 'editor.action.webvieweditor.redo'; - - public runCommand(accessor: ServicesAccessor, args: any): void { - const webViewEditor = getActiveWebviewEditor(accessor); - if (webViewEditor) { - webViewEditor.redo(); - } - } -} - -export class OpenWebviewDeveloperToolsAction extends Action { - static readonly ID = 'workbench.action.webview.openDeveloperTools'; - static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); - - public constructor( - id: string, - label: string - ) { - super(id, label); - } - - public run(): Promise { - const elements = document.querySelectorAll('webview.ready'); - for (let i = 0; i < elements.length; i++) { - try { - (elements.item(i) as Electron.WebviewTag).openDevTools(); - } catch (e) { - console.error(e); - } - } - return Promise.resolve(true); - } -} - export class ReloadWebviewAction extends Action { static readonly ID = 'workbench.action.webview.reloadWebviewAction'; static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews"); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index eb035a02e6..18aecf082c 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -17,29 +17,29 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions } from 'vs/workbench/common/editor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { IWebviewService, Webview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview } from 'vs/workbench/contrib/webview/common/webview'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class WebviewEditor extends BaseEditor { - protected _webview: Webview | undefined; - protected findWidgetVisible: IContextKey; - public static readonly ID = 'WebviewEditor'; + private _webview: Webview | undefined; + private _findWidgetVisible: IContextKey; + private _editorFrame: HTMLElement; private _content?: HTMLElement; private _webviewContent: HTMLElement | undefined; - private _webviewFocusTrackerDisposables: IDisposable[] = []; - private _onFocusWindowHandler?: IDisposable; + private readonly _webviewFocusTrackerDisposables = this._register(new DisposableStore()); + private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); private readonly _onDidFocusWebview = this._register(new Emitter()); public get onDidFocus(): Event { return this._onDidFocusWebview.event; } - private pendingMessages: any[] = []; + private _pendingMessages: any[] = []; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -53,7 +53,7 @@ export class WebviewEditor extends BaseEditor { ) { super(WebviewEditor.ID, telemetryService, themeService, storageService); if (_contextKeyService) { - this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(_contextKeyService); + this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(_contextKeyService); } } @@ -78,7 +78,7 @@ export class WebviewEditor extends BaseEditor { } public dispose(): void { - this.pendingMessages = []; + this._pendingMessages = []; // Let the editor input dispose of the webview. this._webview = undefined; @@ -89,12 +89,6 @@ export class WebviewEditor extends BaseEditor { this._content = undefined; } - this._webviewFocusTrackerDisposables = dispose(this._webviewFocusTrackerDisposables); - - if (this._onFocusWindowHandler) { - this._onFocusWindowHandler.dispose(); - } - super.dispose(); } @@ -102,18 +96,18 @@ export class WebviewEditor extends BaseEditor { if (this._webview) { this._webview.sendMessage(data); } else { - this.pendingMessages.push(data); + this._pendingMessages.push(data); } } public showFind() { if (this._webview) { this._webview.showFind(); - this.findWidgetVisible.set(true); + this._findWidgetVisible.set(true); } } public hideFind() { - this.findWidgetVisible.reset(); + this._findWidgetVisible.reset(); if (this._webview) { this._webview.hideFind(); } @@ -136,10 +130,10 @@ export class WebviewEditor extends BaseEditor { public focus(): void { super.focus(); - if (!this._onFocusWindowHandler) { + if (!this._onFocusWindowHandler.value) { // Make sure we restore focus when switching back to a VS Code window - this._onFocusWindowHandler = this._windowService.onDidChangeFocus(focused => { + this._onFocusWindowHandler.value = this._windowService.onDidChangeFocus(focused => { if (focused && this._editorService.activeControl === this) { this.focus(); } @@ -148,31 +142,7 @@ export class WebviewEditor extends BaseEditor { this.withWebview(webview => webview.focus()); } - public selectAll(): void { - this.withWebview(webview => webview.selectAll()); - } - - public copy(): void { - this.withWebview(webview => webview.copy()); - } - - public paste(): void { - this.withWebview(webview => webview.paste()); - } - - public cut(): void { - this.withWebview(webview => webview.cut()); - } - - public undo(): void { - this.withWebview(webview => webview.undo()); - } - - public redo(): void { - this.withWebview(webview => webview.redo()); - } - - private withWebview(f: (element: Webview) => void): void { + public withWebview(f: (element: Webview) => void): void { if (this._webview) { f(this._webview); } @@ -208,7 +178,7 @@ export class WebviewEditor extends BaseEditor { this._webview = undefined; this._webviewContent = undefined; - this.pendingMessages = []; + this._pendingMessages = []; super.clearInput(); } @@ -219,7 +189,7 @@ export class WebviewEditor extends BaseEditor { this._webview = undefined; this._webviewContent = undefined; } - this.pendingMessages = []; + this._pendingMessages = []; return super.setInput(input, options, token) .then(() => input.resolve()) .then(() => { @@ -270,10 +240,10 @@ export class WebviewEditor extends BaseEditor { } else { if (input.options.enableFindWidget) { this._contextKeyService = this._register(this._contextKeyService.createScoped(this._webviewContent)); - this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); + this._findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); } - this._webview = this._webviewService.createWebview( + this._webview = this._webviewService.createWebview(input.id, { allowSvgs: true, extension: input.extension, @@ -286,17 +256,17 @@ export class WebviewEditor extends BaseEditor { this._webview.initialScrollProgress = input.scrollYPercentage; } - this._webview.state = input.webviewState; + this._webview.state = input.state ? input.state.state : undefined; this._content!.setAttribute('aria-flowto', this._webviewContent.id); this.doUpdateContainer(); } - for (const message of this.pendingMessages) { + for (const message of this._pendingMessages) { this._webview.sendMessage(message); } - this.pendingMessages = []; + this._pendingMessages = []; this.trackFocus(); @@ -304,14 +274,14 @@ export class WebviewEditor extends BaseEditor { } private trackFocus() { - this._webviewFocusTrackerDisposables = dispose(this._webviewFocusTrackerDisposables); + this._webviewFocusTrackerDisposables.clear(); // Track focus in webview content const webviewContentFocusTracker = DOM.trackFocus(this._webviewContent!); - this._webviewFocusTrackerDisposables.push(webviewContentFocusTracker); - this._webviewFocusTrackerDisposables.push(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); + this._webviewFocusTrackerDisposables.add(webviewContentFocusTracker); + this._webviewFocusTrackerDisposables.add(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); // Track focus in webview element - this._webviewFocusTrackerDisposables.push(this._webview!.onDidFocus(() => this._onDidFocusWebview.fire())); + this._webviewFocusTrackerDisposables.add(this._webview!.onDidFocus(() => this._onDidFocusWebview.fire())); } } diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 65b46d808a..a557703f3e 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -13,15 +13,14 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { WebviewEvents, WebviewInputOptions } from './webviewEditorService'; import { Webview, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; -export class WebviewEditorInput extends EditorInput { - private static handlePool = 0; +export class WebviewEditorInput extends EditorInput { private static _styleElement?: HTMLStyleElement; - private static _icons = new Map(); + private static _icons = new Map(); private static updateStyleElement( - id: number, + id: string, iconPath: { light: URI, dark: URI } | undefined ) { if (!this._styleElement) { @@ -39,10 +38,10 @@ export class WebviewEditorInput extends EditorInput { this._icons.forEach((value, key) => { const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`; if (URI.isUri(value)) { - cssRules.push(`${webviewSelector} { content: ""; background-image: url(${value.toString()}); }`); + cssRules.push(`${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value).toString()}); }`); } else { - cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: url(${value.light.toString()}); }`); - cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${value.dark.toString()}); }`); + cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.light).toString()}); }`); + cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.dark).toString()}); }`); } }); this._styleElement.innerHTML = cssRules.join('\n'); @@ -59,22 +58,22 @@ export class WebviewEditorInput extends EditorInput { private _container?: HTMLElement; private _webview?: Webview; private _webviewOwner: any; - private _webviewDisposables: IDisposable[] = []; + private readonly _webviewDisposables = this._register(new DisposableStore()); private _group?: GroupIdentifier; private _scrollYPercentage: number = 0; - private _state: any; + private _state: State; public readonly extension?: { readonly location: URI; readonly id: ExtensionIdentifier; }; - private readonly _id: number; constructor( + public readonly id: string, public readonly viewType: string, name: string, options: WebviewInputOptions, - state: any, + state: State, events: WebviewEvents, extension: undefined | { readonly location: URI; @@ -84,8 +83,6 @@ export class WebviewEditorInput extends EditorInput { ) { super(); - this._id = WebviewEditorInput.handlePool++; - this._name = name; this._options = options; this._events = events; @@ -120,7 +117,7 @@ export class WebviewEditorInput extends EditorInput { public getResource(): URI { return URI.from({ scheme: 'webview-panel', - path: `webview-panel/webview-${this._id}` + path: `webview-panel/webview-${this.id}` }); } @@ -133,7 +130,7 @@ export class WebviewEditorInput extends EditorInput { } public getDescription() { - return null; + return undefined; } public setName(value: string): void { @@ -147,7 +144,7 @@ export class WebviewEditorInput extends EditorInput { public set iconPath(value: { light: URI, dark: URI } | undefined) { this._iconPath = value; - WebviewEditorInput.updateStyleElement(this._id, value); + WebviewEditorInput.updateStyleElement(this.id, value); } public matches(other: IEditorInput): boolean { @@ -175,18 +172,14 @@ export class WebviewEditorInput extends EditorInput { } } - public get state(): any { + public get state(): State { return this._state; } - public set state(value: any) { + public set state(value: State) { this._state = value; } - public get webviewState() { - return this._state.state; - } - public get options(): WebviewInputOptions { return this._options; } @@ -217,7 +210,7 @@ export class WebviewEditorInput extends EditorInput { public get container(): HTMLElement { if (!this._container) { this._container = document.createElement('div'); - this._container.id = `webview-${this._id}`; + this._container.id = `webview-${this.id}`; const part = this._layoutService.getContainer(Parts.EDITOR_PART); part.appendChild(this._container); } @@ -229,7 +222,7 @@ export class WebviewEditorInput extends EditorInput { } public set webview(value: Webview | undefined) { - this._webviewDisposables = dispose(this._webviewDisposables); + this._webviewDisposables.clear(); this._webview = value; if (!this._webview) { @@ -253,7 +246,9 @@ export class WebviewEditorInput extends EditorInput { }, null, this._webviewDisposables); this._webview.onDidUpdateState(newState => { - this._state.state = newState; + if (this._events && this._events.onDidUpdateWebviewState) { + this._events.onDidUpdateWebviewState(newState); + } }, null, this._webviewDisposables); } @@ -262,7 +257,6 @@ export class WebviewEditorInput extends EditorInput { } public claimWebview(owner: any) { - this._webviewOwner = owner; } @@ -284,8 +278,7 @@ export class WebviewEditorInput extends EditorInput { this._webview = undefined; } - this._webviewDisposables = dispose(this._webviewDisposables); - + this._webviewDisposables.clear(); this._webviewOwner = undefined; if (this._container) { @@ -305,6 +298,7 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput { private _revived: boolean = false; constructor( + id: string, viewType: string, name: string, options: WebviewInputOptions, @@ -317,7 +311,7 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput { private readonly reviver: (input: WebviewEditorInput) => Promise, @IWorkbenchLayoutService partService: IWorkbenchLayoutService, ) { - super(viewType, name, options, state, events, extension, partService); + super(id, viewType, name, options, state, events, extension, partService); } public async resolve(): Promise { @@ -327,4 +321,4 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput { } return super.resolve(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts index ea1b40b6da..1494af9704 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts @@ -9,6 +9,7 @@ import { WebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewInputOptions } from './webviewEditorService'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { generateUuid } from 'vs/base/common/uuid'; interface SerializedIconPath { light: string | UriComponents; @@ -67,7 +68,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { const extensionLocation = reviveUri(data.extensionLocation); const extensionId = data.extensionId ? new ExtensionIdentifier(data.extensionId) : undefined; const iconPath = reviveIconPath(data.iconPath); - return this._webviewService.reviveWebview(data.viewType, data.title, iconPath, data.state, data.options, extensionLocation ? { + return this._webviewService.reviveWebview(generateUuid(), data.viewType, data.title, iconPath, data.state, data.options, extensionLocation ? { location: extensionLocation, id: extensionId } : undefined, data.group); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts index 1151c54489..ab03a02e70 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts @@ -26,6 +26,7 @@ export interface IWebviewEditorService { _serviceBrand: any; createWebview( + id: string, viewType: string, title: string, showOptions: ICreateWebViewShowOptions, @@ -38,6 +39,7 @@ export interface IWebviewEditorService { ): WebviewEditorInput; reviveWebview( + id: string, viewType: string, title: string, iconPath: { light: URI, dark: URI } | undefined, @@ -79,6 +81,7 @@ export interface WebviewEvents { onMessage?(message: any): void; onDispose?(): void; onDidClickLink?(link: URI, options: IWebviewOptions): void; + onDidUpdateWebviewState?(newState: any): void; } export interface WebviewInputOptions extends IWebviewOptions, IWebviewPanelOptions { @@ -92,7 +95,7 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn && a.retainContextWhenHidden === b.retainContextWhenHidden && a.tryRestoreScrollPosition === b.tryRestoreScrollPosition && (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))) - && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.from === b.from && a.to === b.to))); + && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort))); } function canRevive(reviver: WebviewReviver, webview: WebviewEditorInput): boolean { @@ -132,6 +135,7 @@ export class WebviewEditorService implements IWebviewEditorService { ) { } public createWebview( + id: string, viewType: string, title: string, showOptions: ICreateWebViewShowOptions, @@ -142,7 +146,7 @@ export class WebviewEditorService implements IWebviewEditorService { }, events: WebviewEvents ): WebviewEditorInput { - const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, title, options, {}, events, extension); + const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, id, viewType, title, options, {}, events, extension); this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus }, showOptions.group); return webviewInput; } @@ -163,6 +167,7 @@ export class WebviewEditorService implements IWebviewEditorService { } public reviveWebview( + id: string, viewType: string, title: string, iconPath: { light: URI, dark: URI } | undefined, @@ -174,7 +179,7 @@ export class WebviewEditorService implements IWebviewEditorService { }, group: number | undefined, ): WebviewEditorInput { - const webviewInput = this._instantiationService.createInstance(RevivedWebviewEditorInput, viewType, title, options, state, {}, extension, async (webview: WebviewEditorInput): Promise => { + const webviewInput = this._instantiationService.createInstance(RevivedWebviewEditorInput, id, viewType, title, options, state, {}, extension, async (webview: WebviewEditorInput): Promise => { const didRevive = await this.tryRevive(webview); if (didRevive) { return Promise.resolve(undefined); diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts new file mode 100644 index 0000000000..53a926d48c --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -0,0 +1,323 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { addDisposableListener, addClass } from 'vs/base/browser/dom'; +import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; +import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; + +interface WebviewContent { + readonly html: string; + readonly options: WebviewContentOptions; + readonly state: string | undefined; +} + +export class IFrameWebview extends Disposable implements Webview { + private element?: HTMLIFrameElement; + + private readonly _ready: Promise; + + private content: WebviewContent; + private _focused = false; + + private readonly _portMappingManager: WebviewPortMappingManager; + + constructor( + private readonly id: string, + private _options: WebviewOptions, + contentOptions: WebviewContentOptions, + @IThemeService themeService: IThemeService, + @ITunnelService tunnelService: ITunnelService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileService private readonly fileService: IFileService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + ) { + super(); + if (typeof environmentService.webviewEndpoint !== 'string') { + throw new Error('To use iframe based webviews, you must configure `environmentService.webviewEndpoint`'); + } + + this._portMappingManager = this._register(new WebviewPortMappingManager( + this._options.extension ? this._options.extension.location : undefined, + () => this.content.options.portMappings || [], + tunnelService + )); + + this.content = { + html: '', + options: contentOptions, + state: undefined + }; + + this.element = document.createElement('iframe'); + this.element.sandbox.add('allow-scripts', 'allow-same-origin'); + this.element.setAttribute('src', `${this.endpoint}/index.html?id=${this.id}`); + this.element.style.border = 'none'; + this.element.style.width = '100%'; + this.element.style.height = '100%'; + + this._register(addDisposableListener(window, 'message', e => { + if (!e || !e.data || e.data.target !== this.id) { + return; + } + + switch (e.data.channel) { + case 'onmessage': + if (e.data.data) { + this._onMessage.fire(e.data.data); + } + return; + + case 'did-click-link': + const uri = e.data.data; + this._onDidClickLink.fire(URI.parse(uri)); + return; + + case 'did-scroll': + // if (e.args && typeof e.args[0] === 'number') { + // this._onDidScroll.fire({ scrollYPercentage: e.args[0] }); + // } + return; + + case 'do-reload': + this.reload(); + return; + + case 'do-update-state': + const state = e.data.data; + this.state = state; + this._onDidUpdateState.fire(state); + return; + + case 'did-focus': + this.handleFocusChange(true); + return; + + case 'did-blur': + this.handleFocusChange(false); + return; + + case 'load-resource': + { + const requestPath = e.data.data.path; + const uri = URI.file(decodeURIComponent(requestPath)); + this.loadResource(requestPath, uri); + return; + } + + case 'load-localhost': + { + this.localLocalhost(e.data.data.origin); + return; + } + } + })); + + this._ready = new Promise(resolve => { + const subscription = this._register(addDisposableListener(window, 'message', (e) => { + if (e.data && e.data.target === this.id && e.data.channel === 'webview-ready') { + if (this.element) { + addClass(this.element, 'ready'); + } + subscription.dispose(); + resolve(); + } + })); + }); + + this.style(themeService.getTheme()); + this._register(themeService.onThemeChange(this.style, this)); + } + + private get endpoint(): string { + const endpoint = this.environmentService.webviewEndpoint!.replace('{{uuid}}', this.id); + if (endpoint[endpoint.length - 1] === '/') { + return endpoint.slice(0, endpoint.length - 1); + } + return endpoint; + } + + public mountTo(parent: HTMLElement) { + if (this.element) { + parent.appendChild(this.element); + } + } + + public set options(options: WebviewContentOptions) { + if (areWebviewInputOptionsEqual(options, this.content.options)) { + return; + } + + this.content = { + html: this.content.html, + options: options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + public set html(value: string) { + this.content = { + html: this.preprocessHtml(value), + options: this.content.options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + private preprocessHtml(value: string): string { + return value.replace(/(["'])vscode-resource:([^\s'"]+?)(["'])/gi, (_, startQuote, path, endQuote) => + `${startQuote}${this.endpoint}/vscode-resource${path}${endQuote}`); + } + + public update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { + if (retainContextWhenHidden && html === this.content.html && areWebviewInputOptionsEqual(options, this.content.options)) { + return; + } + this.content = { + html: this.preprocessHtml(html), + options: options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + private doUpdateContent() { + this._send('content', { + contents: this.content.html, + options: this.content.options, + state: this.content.state + }); + } + + private handleFocusChange(isFocused: boolean): void { + this._focused = isFocused; + if (this._focused) { + this._onDidFocus.fire(); + } + } + + initialScrollProgress: number; + + private readonly _onDidFocus = this._register(new Emitter()); + public readonly onDidFocus = this._onDidFocus.event; + + private readonly _onDidClickLink = this._register(new Emitter()); + public readonly onDidClickLink = this._onDidClickLink.event; + + private readonly _onDidScroll = this._register(new Emitter<{ scrollYPercentage: number }>()); + public readonly onDidScroll = this._onDidScroll.event; + + private readonly _onDidUpdateState = this._register(new Emitter()); + public readonly onDidUpdateState = this._onDidUpdateState.event; + + private readonly _onMessage = this._register(new Emitter()); + public readonly onMessage = this._onMessage.event; + + sendMessage(data: any): void { + this._send('message', data); + } + + layout(): void { + // noop + } + + focus(): void { + if (this.element) { + this.element.focus(); + } + } + + dispose(): void { + if (this.element) { + if (this.element.parentElement) { + this.element.parentElement.removeChild(this.element); + } + } + + this.element = undefined!; + super.dispose(); + } + + reload(): void { + this.doUpdateContent(); + } + + showFind(): void { + throw new Error('Method not implemented.'); + } + + hideFind(): void { + throw new Error('Method not implemented.'); + } + + public set state(state: string | undefined) { + this.content = { + html: this.content.html, + options: this.content.options, + state, + }; + } + + private _send(channel: string, data: any): void { + this._ready + .then(() => { + if (!this.element) { + return; + } + this.element.contentWindow!.postMessage({ + channel: channel, + args: data + }, '*'); + }) + .catch(err => console.error(err)); + } + + private style(theme: ITheme): void { + const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); + this._send('styles', { styles, activeTheme }); + } + + private async loadResource(requestPath: string, uri: URI) { + try { + const result = await loadLocalResource(uri, this.fileService, this._options.extension ? this._options.extension.location : undefined, + () => (this.content.options.localResourceRoots || [])); + + if (result.type === 'success') { + return this._send('did-load-resource', { + status: 200, + path: requestPath, + mime: result.mimeType, + data: result.data.buffer + }); + } + } catch { + // noop + } + + return this._send('did-load-resource', { + status: 404, + path: uri.path + }); + } + + private async localLocalhost(origin: string) { + const redirect = await this._portMappingManager.getRedirect(origin); + return this._send('did-load-localhost', { + origin, + location: redirect + }); + } +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts index 56c7e5fb5a..6778d00b96 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts @@ -44,6 +44,7 @@ export class WebviewFindWidget extends SimpleFindWidget { } else { this._delegate.stopFind(false); } + return false; } protected onFocusTrackerFocus() { } diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts new file mode 100644 index 0000000000..5b01459905 --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/webviewService.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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IFrameWebview as WebviewElement } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { IWebviewService, WebviewOptions, WebviewContentOptions, Webview } from 'vs/workbench/contrib/webview/common/webview'; + +export class WebviewService implements IWebviewService { + _serviceBrand: any; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { } + + createWebview( + id: string, + options: WebviewOptions, + contentOptions: WebviewContentOptions + ): Webview { + return this._instantiationService.createInstance(WebviewElement, + id, + options, + contentOptions); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/mimeTypes.ts b/src/vs/workbench/contrib/webview/common/mimeTypes.ts new file mode 100644 index 0000000000..7139af71ee --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/mimeTypes.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime'; +import { extname } from 'vs/base/common/path'; +import { URI } from 'vs/base/common/uri'; + +const webviewMimeTypes = new Map([ + ['.svg', 'image/svg+xml'], + ['.txt', 'text/plain'], + ['.css', 'text/css'], + ['.js', 'application/javascript'], + ['.json', 'application/json'], + ['.html', 'text/html'], + ['.htm', 'text/html'], + ['.xhtml', 'application/xhtml+xml'], + ['.oft', 'font/otf'], + ['.xml', 'application/xml'], +]); + +export function getWebviewContentMimeType(normalizedPath: URI): string { + const ext = extname(normalizedPath.fsPath).toLowerCase(); + return webviewMimeTypes.get(ext) || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN; +} diff --git a/src/vs/workbench/contrib/webview/common/portMapping.ts b/src/vs/workbench/contrib/webview/common/portMapping.ts new file mode 100644 index 0000000000..60b401ad43 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/portMapping.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 { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; + +export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: string, port: number } | undefined { + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return undefined; + } + const localhostMatch = /^(localhost|127\.0\.0\.1):(\d+)$/.exec(uri.authority); + if (!localhostMatch) { + return undefined; + } + return { + address: localhostMatch[1], + port: +localhostMatch[2], + }; +} + +export class WebviewPortMappingManager extends Disposable { + + private readonly _tunnels = new Map>(); + + constructor( + private readonly extensionLocation: URI | undefined, + private readonly mappings: () => ReadonlyArray, + private readonly tunnelService: ITunnelService + ) { + super(); + } + + public async getRedirect(url: string): Promise { + const uri = URI.parse(url); + const requestLocalHostInfo = extractLocalHostUriMetaDataForPortMapping(uri); + if (!requestLocalHostInfo) { + return undefined; + } + + for (const mapping of this.mappings()) { + if (mapping.webviewPort === requestLocalHostInfo.port) { + if (this.extensionLocation && this.extensionLocation.scheme === REMOTE_HOST_SCHEME) { + const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); + if (tunnel) { + return uri.with({ + authority: `127.0.0.1:${tunnel.tunnelLocalPort}`, + }).toString(); + } + } + + if (mapping.webviewPort !== mapping.extensionHostPort) { + return uri.with({ + authority: `${requestLocalHostInfo.address}:${mapping.extensionHostPort}` + }).toString(); + } + } + } + + return undefined; + } + + dispose() { + super.dispose(); + + for (const tunnel of this._tunnels.values()) { + tunnel.then(tunnel => tunnel.dispose()); + } + this._tunnels.clear(); + } + + private getOrCreateTunnel(remotePort: number): Promise | undefined { + const existing = this._tunnels.get(remotePort); + if (existing) { + return existing; + } + const tunnel = this.tunnelService.openTunnel(remotePort); + if (tunnel) { + this._tunnels.set(remotePort, tunnel); + } + return tunnel; + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/resourceLoader.ts b/src/vs/workbench/contrib/webview/common/resourceLoader.ts new file mode 100644 index 0000000000..96189fbfa6 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/resourceLoader.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. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { sep } from 'vs/base/common/path'; +import { startsWith, endsWith } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { getWebviewContentMimeType } from 'vs/workbench/contrib/webview/common/mimeTypes'; + +class Success { + readonly type = 'success'; + + constructor( + public readonly data: VSBuffer, + public readonly mimeType: string + ) { } +} + +const Failed = new class { readonly type = 'failed'; }; +const AccessDenied = new class { readonly type = 'access-denied'; }; + +type LocalResourceResponse = Success | typeof Failed | typeof AccessDenied; + +async function resolveContent( + fileService: IFileService, + resource: URI, + mime: string +): Promise { + try { + const contents = await fileService.readFile(resource); + return new Success(contents.value, mime); + } catch (err) { + console.log(err); + return Failed; + } +} + +export async function loadLocalResource( + requestUri: URI, + fileService: IFileService, + extensionLocation: URI | undefined, + getRoots: () => ReadonlyArray +): Promise { + const normalizedPath = requestUri.with({ + scheme: 'file', + fragment: '', + query: '', + }); + + for (const root of getRoots()) { + if (!containsResource(root, normalizedPath)) { + continue; + } + + if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { + const redirectedUri = URI.from({ + scheme: REMOTE_HOST_SCHEME, + authority: extensionLocation.authority, + path: '/vscode-resource', + query: JSON.stringify({ + requestResourcePath: requestUri.path + }) + }); + return resolveContent(fileService, redirectedUri, getWebviewContentMimeType(requestUri)); + } else { + return resolveContent(fileService, normalizedPath, getWebviewContentMimeType(normalizedPath)); + } + } + + return AccessDenied; +} + +function containsResource(root: URI, resource: URI): boolean { + const rootPath = root.fsPath + (endsWith(root.fsPath, sep) ? '' : sep); + return startsWith(resource.fsPath, rootPath); +} diff --git a/src/vs/workbench/contrib/webview/common/themeing.ts b/src/vs/workbench/contrib/webview/common/themeing.ts new file mode 100644 index 0000000000..ec6a4d9190 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/themeing.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 { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService'; + +interface WebviewThemeData { + readonly activeTheme: string; + readonly styles: { readonly [key: string]: string | number }; +} + +export function getWebviewThemeData( + theme: ITheme, + configurationService: IConfigurationService +): WebviewThemeData { + const configuration = configurationService.getValue('editor'); + const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; + const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; + const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; + + const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => { + const color = theme.getColor(entry.id); + if (color) { + colors['vscode-' + entry.id.replace('.', '-')] = color.toString(); + } + return colors; + }, {} as { [key: string]: string }); + + const styles = { + 'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", ans-serif', + 'vscode-font-weight': 'normal', + 'vscode-font-size': '13px', + 'vscode-editor-font-family': editorFontFamily, + 'vscode-editor-font-weight': editorFontWeight, + 'vscode-editor-font-size': editorFontSize, + ...exportedColors + }; + + const activeTheme = ApiThemeClassName.fromTheme(theme); + return { styles, activeTheme }; +} + +enum ApiThemeClassName { + light = 'vscode-light', + dark = 'vscode-dark', + highContrast = 'vscode-high-contrast' +} + +namespace ApiThemeClassName { + export function fromTheme(theme: ITheme): ApiThemeClassName { + if (theme.type === LIGHT) { + return ApiThemeClassName.light; + } else if (theme.type === DARK) { + return ApiThemeClassName.dark; + } else { + return ApiThemeClassName.highContrast; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/webview.ts b/src/vs/workbench/contrib/webview/common/webview.ts index c3f0a5a9da..40db7d7fed 100644 --- a/src/vs/workbench/contrib/webview/common/webview.ts +++ b/src/vs/workbench/contrib/webview/common/webview.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import * as modes from 'vs/editor/common/modes'; +import * as nls from 'vs/nls'; /** * Set when the find widget in a webview is visible. @@ -24,11 +26,14 @@ export interface IWebviewService { _serviceBrand: any; createWebview( + id: string, options: WebviewOptions, contentOptions: WebviewContentOptions, ): Webview; } +export const WebviewResourceScheme = 'vscode-resource'; + export interface WebviewOptions { readonly allowSvgs?: boolean; readonly extension?: { @@ -45,7 +50,7 @@ export interface WebviewContentOptions { readonly portMappings?: ReadonlyArray; } -export interface Webview { +export interface Webview extends IDisposable { html: string; options: WebviewContentOptions; @@ -68,17 +73,10 @@ export interface Webview { layout(): void; mountTo(parent: HTMLElement): void; focus(): void; - dispose(): void; - - reload(): void; - selectAll(): void; - copy(): void; - paste(): void; - cut(): void; - undo(): void; - redo(): void; showFind(): void; hideFind(): void; } + +export const webviewDeveloperCategory = nls.localize('developer', "Developer"); diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js index 1a300af6ed..07f7d132d5 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js @@ -28,16 +28,64 @@ // @ts-ignore const ipcRenderer = require('electron').ipcRenderer; - require('../../browser/pre/main')({ + let isInDevelopmentMode = false; + + /** + * @type {import('../../browser/pre/main').WebviewHost} + */ + const host = { postMessage: (channel, data) => { ipcRenderer.sendToHost(channel, data); }, onMessage: (channel, handler) => { ipcRenderer.on(channel, handler); + }, + focusIframeOnCreate: true, + onIframeLoaded: (newFrame) => { + newFrame.contentWindow.onbeforeunload = () => { + if (isInDevelopmentMode) { // Allow reloads while developing a webview + host.postMessage('do-reload'); + return false; + } + // Block navigation when not in development mode + console.log('prevented webview navigation'); + return false; + }; + + // Electron 4 eats mouseup events from inside webviews + // https://github.com/microsoft/vscode/issues/75090 + // Try to fix this by rebroadcasting mouse moves and mouseups so that we can + // emulate these on the main window + let isMouseDown = false; + newFrame.contentWindow.addEventListener('mousedown', () => { + isMouseDown = true; + }); + + const tryDispatchSyntheticMouseEvent = (e) => { + if (!isMouseDown) { + host.postMessage('synthetic-mouse-event', { type: e.type, screenX: e.screenX, screenY: e.screenY, clientX: e.clientX, clientY: e.clientY }); + } + }; + newFrame.contentWindow.addEventListener('mouseup', e => { + tryDispatchSyntheticMouseEvent(e); + isMouseDown = false; + }); + newFrame.contentWindow.addEventListener('mousemove', tryDispatchSyntheticMouseEvent); } + }; + + host.onMessage('devtools-opened', () => { + isInDevelopmentMode = true; }); document.addEventListener('DOMContentLoaded', () => { registerVscodeResourceScheme(); + + // Forward messages from the embedded iframe + window.onmessage = (message) => { + ipcRenderer.sendToHost(message.data.command, message.data.data); + }; }); + + require('../../browser/pre/main')(host); }()); \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index 98bfa3e8b7..1b1d9eebf2 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -3,8 +3,90 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { isMacintosh } from 'vs/base/common/platform'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWebviewService } from 'vs/workbench/contrib/webview/common/webview'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; +import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands'; import { WebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService'; registerSingleton(IWebviewService, WebviewService, true); + +const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor(webviewCommands.OpenWebviewDeveloperToolsAction, webviewCommands.OpenWebviewDeveloperToolsAction.ID, webviewCommands.OpenWebviewDeveloperToolsAction.LABEL), + webviewCommands.OpenWebviewDeveloperToolsAction.ALIAS, + webviewDeveloperCategory); + +function registerWebViewCommands(editorId: string): void { + const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */); + + (new webviewCommands.SelectAllWebviewEditorCommand({ + id: webviewCommands.SelectAllWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_A, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + // These commands are only needed on MacOS where we have to disable the menu bar commands + if (isMacintosh) { + (new webviewCommands.CopyWebviewEditorCommand({ + id: webviewCommands.CopyWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new webviewCommands.PasteWebviewEditorCommand({ + id: webviewCommands.PasteWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new webviewCommands.CutWebviewEditorCommand({ + id: webviewCommands.CutWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_X, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new webviewCommands.UndoWebviewEditorCommand({ + id: webviewCommands.UndoWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Z, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new webviewCommands.RedoWebviewEditorCommand({ + id: webviewCommands.RedoWebviewEditorCommand.ID, + precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)), + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }, + weight: KeybindingWeight.EditorContrib + } + })).register(); + } +} + +registerWebViewCommands(WebviewEditor.ID); \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts new file mode 100644 index 0000000000..f3390ed47d --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { Command, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; + +export class OpenWebviewDeveloperToolsAction extends Action { + static readonly ID = 'workbench.action.webview.openDeveloperTools'; + static readonly ALIAS = 'Open Webview Developer Tools'; + static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); + + public constructor(id: string, label: string) { + super(id, label); + } + + public run(): Promise { + const elements = document.querySelectorAll('webview.ready'); + for (let i = 0; i < elements.length; i++) { + try { + (elements.item(i) as Electron.WebviewTag).openDevTools(); + } catch (e) { + console.error(e); + } + } + return Promise.resolve(true); + } +} + +export class SelectAllWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.selectAll'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + withActiveWebviewBasedWebview(accessor, webview => webview.selectAll()); + } +} + +export class CopyWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.copy'; + + public runCommand(accessor: ServicesAccessor, _args: any): void { + withActiveWebviewBasedWebview(accessor, webview => webview.copy()); + } +} + +export class PasteWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.paste'; + + public runCommand(accessor: ServicesAccessor, _args: any): void { + withActiveWebviewBasedWebview(accessor, webview => webview.paste()); + } +} + +export class CutWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.cut'; + + public runCommand(accessor: ServicesAccessor, _args: any): void { + withActiveWebviewBasedWebview(accessor, webview => webview.cut()); + } +} + +export class UndoWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.undo'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + withActiveWebviewBasedWebview(accessor, webview => webview.undo()); + } +} + +export class RedoWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.redo'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + withActiveWebviewBasedWebview(accessor, webview => webview.redo()); + } +} + +function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | undefined { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl as WebviewEditor; + return activeControl.isWebviewEditor ? activeControl : undefined; +} + +function withActiveWebviewBasedWebview(accessor: ServicesAccessor, f: (webview: WebviewElement) => void): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.withWebview(webview => { + if (webview instanceof WebviewElement) { + f(webview); + } + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 2b9d8448ad..5c58b20395 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -11,20 +11,17 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as modes from 'vs/editor/common/modes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; -import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; -import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; -import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; +import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; +import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/webview'; +import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService'; import { WebviewFindWidget } from '../browser/webviewFindWidget'; @@ -117,7 +114,6 @@ class WebviewProtocolProvider extends Disposable { webview: Electron.WebviewTag, private readonly _extensionLocation: URI | undefined, private readonly _getLocalResourceRoots: () => ReadonlyArray, - private readonly _environmentService: IEnvironmentService, private readonly _fileService: IFileService, ) { super(); @@ -135,13 +131,7 @@ class WebviewProtocolProvider extends Disposable { return; } - const appRootUri = URI.file(this._environmentService.appRoot); - - registerFileProtocol(contents, WebviewProtocol.CoreResource, this._fileService, undefined, () => [ - appRootUri - ]); - - registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, this._extensionLocation, () => + registerFileProtocol(contents, WebviewResourceScheme, this._fileService, this._extensionLocation, () => this._getLocalResourceRoots() ); } @@ -149,88 +139,22 @@ class WebviewProtocolProvider extends Disposable { class WebviewPortMappingProvider extends Disposable { - private readonly _tunnels = new Map>(); + private readonly _manager: WebviewPortMappingManager; constructor( session: WebviewSession, extensionLocation: URI | undefined, mappings: () => ReadonlyArray, - private readonly tunnelService: ITunnelService, - extensionId: ExtensionIdentifier | undefined, - @ITelemetryService telemetryService: ITelemetryService + tunnelService: ITunnelService, ) { super(); + this._manager = this._register(new WebviewPortMappingManager(extensionLocation, mappings, tunnelService)); - let hasLogged = false; - - session.onBeforeRequest(async (details) => { - const uri = URI.parse(details.url); - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return undefined; - } - - const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority); - if (localhostMatch) { - if (!hasLogged && extensionId) { - hasLogged = true; - - /* __GDPR__ - "webview.accessLocalhost" : { - "extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - telemetryService.publicLog('webview.accessLocalhost', { extension: extensionId.value }); - } - - const port = +localhostMatch[1]; - for (const mapping of mappings()) { - if (mapping.webviewPort === port) { - if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { - const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); - if (tunnel) { - return { - redirectURL: details.url.replace( - new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`), - `${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`) - }; - } - } - - if (mapping.webviewPort !== mapping.extensionHostPort) { - return { - redirectURL: details.url.replace( - new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`), - `${uri.scheme}://localhost:${mapping.extensionHostPort}/`) - }; - } - } - } - } - - return undefined; + session.onBeforeRequest(async details => { + const redirect = await this._manager.getRedirect(details.url); + return redirect ? { redirectURL: redirect } : undefined; }); } - - dispose() { - super.dispose(); - - for (const tunnel of this._tunnels.values()) { - tunnel.then(tunnel => tunnel.dispose()); - } - this._tunnels.clear(); - } - - private getOrCreateTunnel(remotePort: number): Promise | undefined { - const existing = this._tunnels.get(remotePort); - if (existing) { - return existing; - } - const tunnel = this.tunnelService.openTunnel(remotePort); - if (tunnel) { - this._tunnels.set(remotePort, tunnel); - } - return tunnel; - } } class SvgBlocker extends Disposable { @@ -257,7 +181,8 @@ class SvgBlocker extends Disposable { }); session.onHeadersReceived((details) => { - const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']; + const headers: any = details.responseHeaders; + const contentType: string[] = headers['content-type'] || headers['Content-Type']; if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { const uri = URI.parse(details.url); if (uri && !this.isAllowedSvg(uri)) { @@ -361,27 +286,25 @@ interface WebviewContent { } export class WebviewElement extends Disposable implements Webview { - private _webview: Electron.WebviewTag; + private _webview: Electron.WebviewTag | undefined; private _ready: Promise; - private _webviewFindWidget: WebviewFindWidget; + private _webviewFindWidget: WebviewFindWidget | undefined; private _findStarted: boolean = false; private content: WebviewContent; private _focused = false; private readonly _onDidFocus = this._register(new Emitter()); - public get onDidFocus(): Event { return this._onDidFocus.event; } + public readonly onDidFocus: Event = this._onDidFocus.event; constructor( private readonly _options: WebviewOptions, contentOptions: WebviewContentOptions, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, - @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, @ITunnelService tunnelService: ITunnelService, - @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); @@ -404,8 +327,8 @@ export class WebviewElement extends Disposable implements Webview { this._webview.src = 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; this._ready = new Promise(resolve => { - const subscription = this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { - if (event.channel === 'webview-ready') { + const subscription = this._register(addDisposableListener(this._webview!, 'ipc-message', (event) => { + if (this._webview && event.channel === 'webview-ready') { // console.info('[PID Webview] ' event.args[0]); addClass(this._webview, 'ready'); // can be found by debug command @@ -421,7 +344,6 @@ export class WebviewElement extends Disposable implements Webview { this._webview, this._options.extension ? this._options.extension.location : undefined, () => (this.content.options.localResourceRoots || []), - environmentService, fileService)); this._register(new WebviewPortMappingProvider( @@ -429,8 +351,6 @@ export class WebviewElement extends Disposable implements Webview { _options.extension ? _options.extension.location : undefined, () => (this.content.options.portMappings || []), tunnelService, - _options.extension ? _options.extension.id : undefined, - telemetryService )); if (!this._options.allowSvgs) { @@ -447,7 +367,7 @@ export class WebviewElement extends Disposable implements Webview { this.layout(); // Workaround for https://github.com/electron/electron/issues/14474 - if (this._focused || document.activeElement === this._webview) { + if (this._webview && (this._focused || document.activeElement === this._webview)) { this._webview.blur(); this._webview.focus(); } @@ -456,6 +376,10 @@ export class WebviewElement extends Disposable implements Webview { console.error('embedded page crashed'); })); this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { + if (!this._webview) { + return; + } + switch (event.channel) { case 'onmessage': if (event.args && event.args.length) { @@ -468,6 +392,18 @@ export class WebviewElement extends Disposable implements Webview { this._onDidClickLink.fire(URI.parse(uri)); return; + case 'synthetic-mouse-event': + { + const rawEvent = event.args[0]; + const bounds = this._webview.getBoundingClientRect(); + window.dispatchEvent(new MouseEvent(rawEvent.type, { + ...rawEvent, + clientX: rawEvent.clientX + bounds.left, + clientY: rawEvent.clientY + bounds.top, + })); + return; + } + case 'did-set-content': this._webview.style.flex = ''; this._webview.style.width = '100%'; @@ -509,10 +445,14 @@ export class WebviewElement extends Disposable implements Webview { } this.style(themeService.getTheme()); - themeService.onThemeChange(this.style, this, this._toDispose); + this._register(themeService.onThemeChange(this.style, this)); } public mountTo(parent: HTMLElement) { + if (!this._webview) { + return; + } + if (this._webviewFindWidget) { parent.appendChild(this._webviewFindWidget.getDomNode()!); } @@ -524,10 +464,13 @@ export class WebviewElement extends Disposable implements Webview { if (this._webview.parentElement) { this._webview.parentElement.removeChild(this._webview); } + this._webview = undefined; } - this._webview = undefined!; - this._webviewFindWidget = undefined!; + if (this._webviewFindWidget) { + this._webviewFindWidget.dispose(); + this._webviewFindWidget = undefined; + } super.dispose(); } @@ -543,9 +486,13 @@ export class WebviewElement extends Disposable implements Webview { private readonly _onMessage = this._register(new Emitter()); public readonly onMessage = this._onMessage.event; - private _send(channel: string, ...args: any[]): void { + private _send(channel: string, data?: any): void { this._ready - .then(() => this._webview.send(channel, ...args)) + .then(() => { + if (this._webview) { + this._webview.send(channel, data); + } + }) .catch(err => console.error(err)); } @@ -604,6 +551,9 @@ export class WebviewElement extends Disposable implements Webview { } public focus(): void { + if (!this._webview) { + return; + } this._webview.focus(); this._send('focus'); @@ -629,31 +579,8 @@ export class WebviewElement extends Disposable implements Webview { } private style(theme: ITheme): void { - const configuration = this._configurationService.getValue('editor'); - const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; - const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; - const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; - - const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => { - const color = theme.getColor(entry.id); - if (color) { - colors['vscode-' + entry.id.replace('.', '-')] = color.toString(); - } - return colors; - }, {} as { [key: string]: string }); - - const styles = { - 'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif', - 'vscode-font-weight': 'normal', - 'vscode-font-size': '13px', - 'vscode-editor-font-family': editorFontFamily, - 'vscode-editor-font-weight': editorFontWeight, - 'vscode-editor-font-size': editorFontSize, - ...exportedColors - }; - - const activeTheme = ApiThemeClassName.fromTheme(theme); - this._send('styles', styles, activeTheme); + const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); + this._send('styles', { styles, activeTheme }); if (this._webviewFindWidget) { this._webviewFindWidget.updateTheme(theme); @@ -661,6 +588,9 @@ export class WebviewElement extends Disposable implements Webview { } public layout(): void { + if (!this._webview) { + return; + } const contents = this._webview.getWebContents(); if (!contents || contents.isDestroyed()) { return; @@ -679,7 +609,7 @@ export class WebviewElement extends Disposable implements Webview { } public startFind(value: string, options?: Electron.FindInPageOptions) { - if (!value) { + if (!value || !this._webview) { return; } @@ -706,6 +636,10 @@ export class WebviewElement extends Disposable implements Webview { * @param value The string to search for. Empty strings are ignored. */ public find(value: string, previous: boolean): void { + if (!this._webview) { + return; + } + // Searching with an empty value will throw an exception if (!value) { return; @@ -721,6 +655,9 @@ export class WebviewElement extends Disposable implements Webview { } public stopFind(keepSelection?: boolean): void { + if (!this._webview) { + return; + } this._findStarted = false; this._webview.stopFindInPage(keepSelection ? 'keepSelection' : 'clearSelection'); } @@ -742,45 +679,38 @@ export class WebviewElement extends Disposable implements Webview { } public selectAll() { - this._webview.selectAll(); + if (this._webview) { + this._webview.selectAll(); + } } public copy() { - this._webview.copy(); + if (this._webview) { + this._webview.copy(); + } } public paste() { - this._webview.paste(); + if (this._webview) { + this._webview.paste(); + } } public cut() { - this._webview.cut(); + if (this._webview) { + this._webview.cut(); + } } public undo() { - this._webview.undo(); + if (this._webview) { + this._webview.undo(); + } } public redo() { - this._webview.redo(); - } -} - - -enum ApiThemeClassName { - light = 'vscode-light', - dark = 'vscode-dark', - highContrast = 'vscode-high-contrast' -} - -namespace ApiThemeClassName { - export function fromTheme(theme: ITheme): ApiThemeClassName { - if (theme.type === LIGHT) { - return ApiThemeClassName.light; - } else if (theme.type === DARK) { - return ApiThemeClassName.dark; - } else { - return ApiThemeClassName.highContrast; + if (this._webview) { + this._webview.redo(); } } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts index 927675daa7..810c3da9d4 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts @@ -2,67 +2,36 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime'; -import { extname, sep } from 'vs/base/common/path'; -import { startsWith } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { IFileService } from 'vs/platform/files/common/files'; import * as electron from 'electron'; - -type BufferProtocolCallback = (buffer?: Buffer | electron.MimeTypedBuffer | { error: number }) => void; - -export const enum WebviewProtocol { - CoreResource = 'vscode-core-resource', - VsCodeResource = 'vscode-resource', -} - -function resolveContent(fileService: IFileService, resource: URI, mime: string, callback: BufferProtocolCallback): void { - fileService.readFile(resource).then(contents => { - callback({ - data: Buffer.from(contents.value.buffer), - mimeType: mime - }); - }, (err) => { - console.log(err); - callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - }); -} +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; export function registerFileProtocol( contents: electron.WebContents, - protocol: WebviewProtocol, + protocol: string, fileService: IFileService, extensionLocation: URI | undefined, getRoots: () => ReadonlyArray ) { - contents.session.protocol.registerBufferProtocol(protocol, (request, callback: any) => { - const requestPath = URI.parse(request.url).path; - const normalizedPath = URI.file(requestPath); - for (const root of getRoots()) { - if (!startsWith(normalizedPath.fsPath, root.fsPath + sep)) { - continue; - } - - if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { - const requestUri = URI.parse(request.url); - const redirectedUri = URI.from({ - scheme: REMOTE_HOST_SCHEME, - authority: extensionLocation.authority, - path: '/vscode-resource', - query: JSON.stringify({ - requestResourcePath: requestUri.path - }) + contents.session.protocol.registerBufferProtocol(protocol, async (request, callback: any) => { + try { + const result = await loadLocalResource(URI.parse(request.url), fileService, extensionLocation, getRoots); + if (result.type === 'success') { + return callback({ + data: Buffer.from(result.data.buffer), + mimeType: result.mimeType }); - resolveContent(fileService, redirectedUri, getMimeType(requestUri), callback); - return; - } else { - resolveContent(fileService, normalizedPath, getMimeType(normalizedPath), callback); - return; } + if (result.type === 'access-denied') { + console.error('Webview: Cannot load resource outside of protocol root'); + return callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + } + } catch { + // noop } - console.error('Webview: Cannot load resource outside of protocol root'); - callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + + return callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); }, (error) => { if (error) { console.error(`Failed to register '${protocol}' protocol`); @@ -70,20 +39,3 @@ export function registerFileProtocol( }); } -const webviewMimeTypes = { - '.svg': 'image/svg+xml', - '.txt': 'text/plain', - '.css': 'text/css', - '.js': 'application/javascript', - '.json': 'application/json', - '.html': 'text/html', - '.htm': 'text/html', - '.xhtml': 'application/xhtml+xml', - '.oft': 'font/otf', - '.xml': 'application/xml', -}; - -function getMimeType(normalizedPath: URI): string { - const ext = extname(normalizedPath.fsPath).toLowerCase(); - return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN; -} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index 5cf75f076c..af3447e078 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -15,13 +15,12 @@ export class WebviewService implements IWebviewService { ) { } createWebview( + _id: string, options: WebviewOptions, contentOptions: WebviewContentOptions ): Webview { - const element = this._instantiationService.createInstance(WebviewElement, + return this._instantiationService.createInstance(WebviewElement, options, contentOptions); - - return element; } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts index 2368e7047e..525b019d8a 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts @@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IExperimentService, ExperimentState } from 'vs/workbench/contrib/experiments/node/experimentService'; +import { IExperimentService, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { language, locale } from 'vs/base/common/platform'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -91,13 +91,13 @@ export class TelemetryOptOut implements IWorkbenchContribution { return undefined; } const extensionToFetchTranslationsFrom = tagResult.firstPage.filter(e => e.publisher === 'MS-CEINTL' && e.name.indexOf('vscode-language-pack') === 0)[0] || tagResult.firstPage[0]; - if (!extensionToFetchTranslationsFrom.assets || !extensionToFetchTranslationsFrom.assets.coreTranslations) { + if (!extensionToFetchTranslationsFrom.assets || !extensionToFetchTranslationsFrom.assets.coreTranslations.length) { return undefined; } return this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, locale!) .then(translation => { - const translationsFromPack = translation && translation.contents ? translation.contents['vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut'] : {}; + const translationsFromPack: any = translation && translation.contents ? translation.contents['vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut'] : {}; if (!!translationsFromPack[promptMessageKey] && !!translationsFromPack[yesLabelKey] && !!translationsFromPack[noLabelKey]) { promptMessage = translationsFromPack[promptMessageKey].replace('{0}', this.privacyUrl) + ' (Please help Microsoft improve Visual Studio Code by allowing the collection of usage data.)'; yesLabel = translationsFromPack[yesLabelKey] + ' (Yes)'; diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index ed9b2cd793..a48253928e 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -15,7 +15,7 @@ import { Action } from 'vs/base/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -150,9 +150,8 @@ export class HideWelcomeOverlayAction extends Action { } } -class WelcomeOverlay { +class WelcomeOverlay extends Disposable { - private _toDispose: IDisposable[] = []; private _overlayVisible: IContextKey; private _overlay: HTMLElement; @@ -163,6 +162,7 @@ class WelcomeOverlay { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly keybindingService: IKeybindingService ) { + super(); this._overlayVisible = OVERLAY_VISIBLE.bindTo(this._contextKeyService); this.create(); } @@ -177,7 +177,7 @@ class WelcomeOverlay { this._overlay.style.display = 'none'; this._overlay.tabIndex = -1; - this._toDispose.push(dom.addStandardDisposableListener(this._overlay, 'click', () => this.hide())); + this._register(dom.addStandardDisposableListener(this._overlay, 'click', () => this.hide())); this.commandService.onWillExecuteCommand(() => this.hide()); dom.append(this._overlay, $('.commandPalettePlaceholder')); @@ -214,7 +214,7 @@ class WelcomeOverlay { } private updateProblemsKey() { - const problems = document.querySelector('.task-statusbar-item'); + const problems = document.querySelector('div[id="workbench.parts.statusbar"] .statusbar-item.left .octicon.octicon-warning'); const key = this._overlay.querySelector('.key.problems') as HTMLElement; if (problems instanceof HTMLElement) { const target = problems.getBoundingClientRect(); @@ -237,10 +237,6 @@ class WelcomeOverlay { this._overlayVisible.reset(); } } - - dispose() { - this._toDispose = dispose(this._toDispose); - } } // {SQL CARBON EDIT} diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 9104521bc0..6d46fee585 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -17,7 +17,7 @@ import { IWindowService, IURIToOpen } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; +import { Action, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -26,7 +26,7 @@ import { IExtensionEnablementService, IExtensionManagementService, IExtensionGal // {{SQL CARBON EDIT}} - Redirect to ADS welcome page import { used } from 'sql/workbench/contrib/welcome/page/browser/az_data_welcome_page'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -152,7 +152,7 @@ const extensionPacks: ExtensionSuggestion[] = [ // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, - { name: localize('welcomePage.docker', "Docker"), id: 'peterjausovec.vscode-docker' }, + { name: localize('welcomePage.docker', "Docker"), id: 'ms-azuretools.vscode-docker' }, ]; const keymapExtensions: ExtensionSuggestion[] = [ @@ -246,9 +246,7 @@ const keymapStrings: Strings = { const welcomeInputTypeId = 'workbench.editors.welcomePageInput'; -class WelcomePage { - - private disposables: IDisposable[] = []; +class WelcomePage extends Disposable { readonly editorInput: WalkThroughInput; @@ -268,7 +266,8 @@ class WelcomePage { @ILifecycleService lifecycleService: ILifecycleService, @ITelemetryService private readonly telemetryService: ITelemetryService ) { - this.disposables.push(lifecycleService.onShutdown(() => this.dispose())); + super(); + this._register(lifecycleService.onShutdown(() => this.dispose())); const recentlyOpened = this.windowService.getRecentlyOpened(); const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); @@ -323,14 +322,14 @@ class WelcomePage { ul.append(...listEntries, moreRecent); }; updateEntries(); - this.disposables.push(this.labelService.onDidChangeFormatters(updateEntries)); + this._register(this.labelService.onDidChangeFormatters(updateEntries)); }).then(undefined, onUnexpectedError); this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings); this.addExtensionList(container, '.keymapList', keymapExtensions, keymapStrings); this.updateInstalledExtensions(container, installedExtensions); - this.disposables.push(this.instantiationService.invokeFunction(onExtensionChanged)(ids => { + this._register(this.instantiationService.invokeFunction(onExtensionChanged)(ids => { for (const id of ids) { if (container.querySelector(`.installExtension[data-extension="${id.id}"], .enabledExtension[data-extension="${id.id}"]`)) { const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); @@ -363,13 +362,7 @@ class WelcomePage { a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentPath)); a.href = 'javascript:void(0)'; a.addEventListener('click', e => { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { + this.telemetryService.publicLog2('workbenchActionExecuted', { id: 'openRecentFolder', from: telemetryFrom }); @@ -595,10 +588,6 @@ class WelcomePage { }); }).then(undefined, onUnexpectedError); } - - dispose(): void { - this.disposables = dispose(this.disposables); - } } export class WelcomeInputFactory implements IEditorInputFactory { diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index 5febf0f2ea..4180e3ea56 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -8,7 +8,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -56,7 +56,7 @@ export class WalkThroughPart extends BaseEditor { static readonly ID: string = 'workbench.editor.walkThroughPart'; - private disposables: IDisposable[] = []; + private readonly disposables = new DisposableStore(); private contentDisposables: IDisposable[] = []; private content: HTMLDivElement; private scrollbar: DomScrollableElement; @@ -92,13 +92,13 @@ export class WalkThroughPart extends BaseEditor { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto }); - this.disposables.push(this.scrollbar); + this.disposables.add(this.scrollbar); container.appendChild(this.scrollbar.getDomNode()); this.registerFocusHandlers(); this.registerClickHandler(); - this.disposables.push(this.scrollbar.onScroll(e => this.updatedScrollPosition())); + this.disposables.add(this.scrollbar.onScroll(e => this.updatedScrollPosition())); } private updatedScrollPosition() { @@ -120,16 +120,16 @@ export class WalkThroughPart extends BaseEditor { } private registerFocusHandlers() { - this.disposables.push(this.addEventListener(this.content, 'mousedown', e => { + this.disposables.add(this.addEventListener(this.content, 'mousedown', e => { this.focus(); })); - this.disposables.push(this.addEventListener(this.content, 'focus', e => { + this.disposables.add(this.addEventListener(this.content, 'focus', e => { this.editorFocus.set(true); })); - this.disposables.push(this.addEventListener(this.content, 'blur', e => { + this.disposables.add(this.addEventListener(this.content, 'blur', e => { this.editorFocus.reset(); })); - this.disposables.push(this.addEventListener(this.content, 'focusin', e => { + this.disposables.add(this.addEventListener(this.content, 'focusin', (e: FocusEvent) => { // Work around scrolling as side-effect of setting focus on the offscreen zone widget (#18929) if (e.target instanceof HTMLElement && e.target.classList.contains('zone-widget-container')) { const scrollPosition = this.scrollbar.getScrollPosition(); @@ -514,7 +514,7 @@ export class WalkThroughPart extends BaseEditor { dispose(): void { this.editorFocus.reset(); this.contentDisposables = dispose(this.contentDisposables); - this.disposables = dispose(this.disposables); + this.disposables.dispose(); super.dispose(); } } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts index f04046e9ef..e86280e45e 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts @@ -39,7 +39,7 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW return content.then(content => { let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) { - codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource.fsPath), resource); + codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource), resource); } else { this.modelService.updateModel(codeEditorModel, content); } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts index d8e02a8ec5..1f558f35ea 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts @@ -6,7 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { EditorInput, EditorModel, ITextEditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { IReference, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as marked from 'vs/base/common/marked/marked'; import { Schemas } from 'vs/base/common/network'; @@ -46,8 +46,6 @@ export interface WalkThroughInputOptions { export class WalkThroughInput extends EditorInput { - private disposables: IDisposable[] = []; - private promise: Promise | null = null; private maxTopScroll = 0; @@ -139,8 +137,6 @@ export class WalkThroughInput extends EditorInput { } dispose(): void { - this.disposables = dispose(this.disposables); - if (this.promise) { this.promise.then(model => model.dispose()); this.promise = null; diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts index c22cca3e4a..d851036c61 100644 --- a/src/vs/workbench/electron-browser/actions/developerActions.ts +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -6,16 +6,6 @@ import { Action } from 'vs/base/common/actions'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { domEvent } from 'vs/base/browser/event'; -import { Event } from 'vs/base/common/event'; -import { IDisposable, toDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $ } from 'vs/base/browser/dom'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { timeout } from 'vs/base/common/async'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; export class ToggleDevToolsAction extends Action { @@ -44,176 +34,3 @@ export class ToggleSharedProcessAction extends Action { return this.windowsService.toggleSharedProcess(); } } - -export class InspectContextKeysAction extends Action { - - static readonly ID = 'workbench.action.inspectContextKeys'; - static LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); - - constructor( - id: string, - label: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IWindowService private readonly windowService: IWindowService, - ) { - super(id, label); - } - - run(): Promise { - const disposables: IDisposable[] = []; - - const stylesheet = createStyleSheet(); - disposables.push(toDisposable(() => { - if (stylesheet.parentNode) { - stylesheet.parentNode.removeChild(stylesheet); - } - })); - createCSSRule('*', 'cursor: crosshair !important;', stylesheet); - - const hoverFeedback = document.createElement('div'); - document.body.appendChild(hoverFeedback); - disposables.push(toDisposable(() => document.body.removeChild(hoverFeedback))); - - hoverFeedback.style.position = 'absolute'; - hoverFeedback.style.pointerEvents = 'none'; - hoverFeedback.style.backgroundColor = 'rgba(255, 0, 0, 0.5)'; - hoverFeedback.style.zIndex = '1000'; - - const onMouseMove = domEvent(document.body, 'mousemove', true); - disposables.push(onMouseMove(e => { - const target = e.target as HTMLElement; - const position = getDomNodePagePosition(target); - - hoverFeedback.style.top = `${position.top}px`; - hoverFeedback.style.left = `${position.left}px`; - hoverFeedback.style.width = `${position.width}px`; - hoverFeedback.style.height = `${position.height}px`; - })); - - const onMouseDown = Event.once(domEvent(document.body, 'mousedown', true)); - onMouseDown(e => { e.preventDefault(); e.stopPropagation(); }, null, disposables); - - const onMouseUp = Event.once(domEvent(document.body, 'mouseup', true)); - onMouseUp(e => { - e.preventDefault(); - e.stopPropagation(); - - const context = this.contextKeyService.getContext(e.target as HTMLElement) as Context; - console.log(context.collectAllValues()); - this.windowService.openDevTools(); - - dispose(disposables); - }, null, disposables); - - return Promise.resolve(); - } -} - -export class ToggleScreencastModeAction extends Action { - - static readonly ID = 'workbench.action.toggleScreencastMode'; - static LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode"); - - static disposable: IDisposable | undefined; - - constructor( - id: string, - label: string, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); - } - - async run(): Promise { - if (ToggleScreencastModeAction.disposable) { - ToggleScreencastModeAction.disposable.dispose(); - ToggleScreencastModeAction.disposable = undefined; - return; - } - - const container = this.layoutService.getWorkbenchElement(); - - const mouseMarker = append(container, $('div')); - mouseMarker.style.position = 'absolute'; - mouseMarker.style.border = '2px solid red'; - mouseMarker.style.borderRadius = '20px'; - mouseMarker.style.width = '20px'; - mouseMarker.style.height = '20px'; - mouseMarker.style.top = '0'; - mouseMarker.style.left = '0'; - mouseMarker.style.zIndex = '100000'; - mouseMarker.style.content = ' '; - mouseMarker.style.pointerEvents = 'none'; - mouseMarker.style.display = 'none'; - - const onMouseDown = domEvent(container, 'mousedown', true); - const onMouseUp = domEvent(container, 'mouseup', true); - const onMouseMove = domEvent(container, 'mousemove', true); - - const mouseListener = onMouseDown(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; - mouseMarker.style.display = 'block'; - - const mouseMoveListener = onMouseMove(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; - }); - - Event.once(onMouseUp)(() => { - mouseMarker.style.display = 'none'; - mouseMoveListener.dispose(); - }); - }); - - const keyboardMarker = append(container, $('div')); - keyboardMarker.style.position = 'absolute'; - keyboardMarker.style.backgroundColor = 'rgba(0, 0, 0 ,0.5)'; - keyboardMarker.style.width = '100%'; - keyboardMarker.style.height = '100px'; - keyboardMarker.style.bottom = '20%'; - keyboardMarker.style.left = '0'; - keyboardMarker.style.zIndex = '100000'; - keyboardMarker.style.pointerEvents = 'none'; - keyboardMarker.style.color = 'white'; - keyboardMarker.style.lineHeight = '100px'; - keyboardMarker.style.textAlign = 'center'; - keyboardMarker.style.fontSize = '56px'; - keyboardMarker.style.display = 'none'; - - const onKeyDown = domEvent(container, 'keydown', true); - let keyboardTimeout: IDisposable = Disposable.None; - - const keyboardListener = onKeyDown(e => { - keyboardTimeout.dispose(); - - const event = new StandardKeyboardEvent(e); - const keybinding = this.keybindingService.resolveKeyboardEvent(event); - const label = keybinding.getLabel(); - - if (!event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && this.keybindingService.mightProducePrintableCharacter(event) && label) { - keyboardMarker.textContent += ' ' + label; - } else { - keyboardMarker.textContent = label; - } - - keyboardMarker.style.display = 'block'; - - const promise = timeout(800); - keyboardTimeout = toDisposable(() => promise.cancel()); - - promise.then(() => { - keyboardMarker.textContent = ''; - keyboardMarker.style.display = 'none'; - }); - }); - - ToggleScreencastModeAction.disposable = toDisposable(() => { - mouseListener.dispose(); - keyboardListener.dispose(); - mouseMarker.remove(); - keyboardMarker.remove(); - }); - } -} diff --git a/src/vs/workbench/electron-browser/actions/media/remove-dark.svg b/src/vs/workbench/electron-browser/actions/media/remove-dark.svg deleted file mode 100644 index 751e89b3b0..0000000000 --- a/src/vs/workbench/electron-browser/actions/media/remove-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/actions/media/remove.svg b/src/vs/workbench/electron-browser/actions/media/remove.svg deleted file mode 100644 index fde34404d4..0000000000 --- a/src/vs/workbench/electron-browser/actions/media/remove.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 86166c9768..7361023329 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -3,29 +3,22 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/actions'; - import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; -import { IWindowService, IWindowsService, IURIToOpen } from 'vs/platform/windows/common/windows'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { isMacintosh } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { webFrame } from 'electron'; import { FileKind } from 'vs/platform/files/common/files'; -import { ILabelService } from 'vs/platform/label/common/label'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IQuickInputService, IQuickInputButton, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import product from 'vs/platform/product/node/product'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IRecentFolder, IRecentFile, IRecentWorkspace, IRecent, isRecentFolder, isRecentWorkspace } from 'vs/platform/history/common/history'; -import { splitName } from 'vs/base/common/labels'; export class CloseCurrentWindowAction extends Action { @@ -61,20 +54,6 @@ export class NewWindowAction extends Action { } } -export class ToggleFullScreenAction extends Action { - - static readonly ID = 'workbench.action.toggleFullScreen'; - static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); - - constructor(id: string, label: string, @IWindowService private readonly windowService: IWindowService) { - super(id, label); - } - - run(): Promise { - return this.windowService.toggleFullScreen(); - } -} - export abstract class BaseZoomAction extends Action { private static readonly SETTING_KEY = 'window.zoomLevel'; @@ -164,26 +143,6 @@ export class ZoomResetAction extends BaseZoomAction { } } -export class ReloadWindowAction extends Action { - - static readonly ID = 'workbench.action.reloadWindow'; - static LABEL = nls.localize('reloadWindow', "Reload Window"); - - constructor( - id: string, - label: string, - @IWindowService private readonly windowService: IWindowService - ) { - super(id, label); - } - - async run(): Promise { - await this.windowService.reloadWindow(); - - return true; - } -} - export class ReloadWindowWithExtensionsDisabledAction extends Action { static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled'; @@ -308,150 +267,6 @@ export class QuickSwitchWindow extends BaseSwitchWindow { } } -export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; - -export abstract class BaseOpenRecentAction extends Action { - - private removeFromRecentlyOpened: IQuickInputButton = { - iconClass: 'action-remove-from-recently-opened', - tooltip: nls.localize('remove', "Remove from Recently Opened") - }; - - constructor( - id: string, - label: string, - private windowService: IWindowService, - private windowsService: IWindowsService, - private quickInputService: IQuickInputService, - private contextService: IWorkspaceContextService, - private labelService: ILabelService, - private keybindingService: IKeybindingService, - private modelService: IModelService, - private modeService: IModeService, - ) { - super(id, label); - } - - protected abstract isQuickNavigate(): boolean; - - async run(): Promise { - const { workspaces, files } = await this.windowService.getRecentlyOpened(); - - this.openRecent(workspaces, files); - } - - private async openRecent(recentWorkspaces: Array, recentFiles: IRecentFile[]): Promise { - - const toPick = (recent: IRecent, labelService: ILabelService, buttons: IQuickInputButton[] | undefined) => { - let uriToOpen: IURIToOpen | undefined; - let iconClasses: string[]; - let fullLabel: string | undefined; - let resource: URI | undefined; - if (isRecentFolder(recent)) { - resource = recent.folderUri; - iconClasses = getIconClasses(this.modelService, this.modeService, resource, FileKind.FOLDER); - uriToOpen = { folderUri: resource }; - fullLabel = recent.label || labelService.getWorkspaceLabel(resource, { verbose: true }); - } else if (isRecentWorkspace(recent)) { - resource = recent.workspace.configPath; - iconClasses = getIconClasses(this.modelService, this.modeService, resource, FileKind.ROOT_FOLDER); - uriToOpen = { workspaceUri: resource }; - fullLabel = recent.label || labelService.getWorkspaceLabel(recent.workspace, { verbose: true }); - } else { - resource = recent.fileUri; - iconClasses = getIconClasses(this.modelService, this.modeService, resource, FileKind.FILE); - uriToOpen = { fileUri: resource }; - fullLabel = recent.label || labelService.getUriLabel(resource); - } - const { name, parentPath } = splitName(fullLabel); - return { - iconClasses, - label: name, - description: parentPath, - buttons, - uriToOpen, - resource - }; - }; - const workspacePicks = recentWorkspaces.map(workspace => toPick(workspace, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : undefined)); - const filePicks = recentFiles.map(p => toPick(p, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : undefined)); - - // focus second entry if the first recent workspace is the current workspace - const firstEntry = recentWorkspaces[0]; - let autoFocusSecondEntry: boolean = firstEntry && this.contextService.isCurrentWorkspace(isRecentWorkspace(firstEntry) ? firstEntry.workspace : firstEntry.folderUri); - - let keyMods: IKeyMods | undefined; - const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('workspaces', "workspaces") }; - const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; - const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; - const pick = await this.quickInputService.pick(picks, { - contextKey: inRecentFilesPickerContextKey, - activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], - placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), - matchOnDescription: true, - onKeyMods: mods => keyMods = mods, - quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: async context => { - await this.windowsService.removeFromRecentlyOpened([context.item.resource]); - context.removeItem(); - } - }); - - if (pick) { - return this.windowService.openWindow([pick.uriToOpen], { forceNewWindow: keyMods && keyMods.ctrlCmd }); - } - } -} - -export class OpenRecentAction extends BaseOpenRecentAction { - - static readonly ID = 'workbench.action.openRecent'; - static readonly LABEL = nls.localize('openRecent', "Open Recent..."); - - constructor( - id: string, - label: string, - @IWindowService windowService: IWindowService, - @IWindowsService windowsService: IWindowsService, - @IQuickInputService quickInputService: IQuickInputService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IKeybindingService keybindingService: IKeybindingService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - @ILabelService labelService: ILabelService - ) { - super(id, label, windowService, windowsService, quickInputService, contextService, labelService, keybindingService, modelService, modeService); - } - - protected isQuickNavigate(): boolean { - return false; - } -} - -export class QuickOpenRecentAction extends BaseOpenRecentAction { - - static readonly ID = 'workbench.action.quickOpenRecent'; - static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent..."); - - constructor( - id: string, - label: string, - @IWindowService windowService: IWindowService, - @IWindowsService windowsService: IWindowsService, - @IQuickInputService quickInputService: IQuickInputService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IKeybindingService keybindingService: IKeybindingService, - @IModelService modelService: IModelService, - @IModeService modeService: IModeService, - @ILabelService labelService: ILabelService - ) { - super(id, label, windowService, windowsService, quickInputService, contextService, labelService, keybindingService, modelService, modeService); - } - - protected isQuickNavigate(): boolean { - return true; - } -} export class ShowAboutDialogAction extends Action { diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 4116bbcbdb..94ed2fc297 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -12,23 +12,20 @@ import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/action import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenNewsletterSignupUrlAction } from 'vs/workbench/electron-browser/actions/helpActions'; -import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; -import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ReloadWindowAction, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; -import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { ToggleSharedProcessAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; +import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; +import { AddRootFolderAction, GlobalRemoveRootFolderAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext, RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; +import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { LogStorageAction } from 'vs/platform/storage/node/storageService'; +import product from 'vs/platform/product/node/product'; -// {{SQL CARBON EDIT}} -import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; -// {{SQL CARBON EDIT}} - End +import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; // {{SQL CARBON EDIT}} add import // Actions (function registerActions(): void { @@ -38,41 +35,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow (function registerFileActions(): void { const fileCategory = nls.localize('file', "File"); - if (isMacintosh) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFileFolderAction, OpenLocalFileFolderAction.ID, OpenLocalFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, RemoteFileDialogContext), 'File: Open Local...', fileCategory); - } else { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFileAction, OpenLocalFileAction.ID, OpenLocalFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, RemoteFileDialogContext), 'File: Open Local File...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFolderAction, OpenLocalFolderAction.ID, OpenLocalFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }, RemoteFileDialogContext), 'File: Open Local Folder...', fileCategory); - } - - registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); - - const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); - - const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker'; - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: quickOpenNavigateNextInRecentFilesPickerId, - weight: KeybindingWeight.WorkbenchContrib + 50, - handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true), - when: recentFilesPickerContext, - primary: KeyMod.CtrlCmd | KeyCode.KEY_R, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } - }); - - const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker'; - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: quickOpenNavigatePreviousInRecentFilesPicker, - weight: KeybindingWeight.WorkbenchContrib + 50, - handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false), - when: recentFilesPickerContext, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, - mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } - }); })(); // Actions: View @@ -82,7 +45,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomInAction, ZoomInAction.ID, ZoomInAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_EQUAL, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_EQUAL, KeyMod.CtrlCmd | KeyCode.NUMPAD_ADD] }), 'View: Zoom In', viewCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomOutAction, ZoomOutAction.ID, ZoomOutAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS, KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT], linux: { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT] } }), 'View: Zoom Out', viewCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomResetAction, ZoomResetAction.ID, ZoomResetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.NUMPAD_0 }), 'View: Reset Zoom', viewCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); })(); // Actions: Window @@ -122,7 +84,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); @@ -164,20 +125,9 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow (function registerDeveloperActions(): void { const developerCategory = nls.localize('developer', "Developer"); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload Window Without Extensions', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Developer: Reload Window', developerCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload Window With Extensions Disabled', developerCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL), 'Developer: Toggle Developer Tools', developerCategory); - KeybindingsRegistry.registerKeybindingRule({ - id: ReloadWindowAction.ID, - weight: KeybindingWeight.WorkbenchContrib + 50, - when: IsDevelopmentContext, - primary: KeyMod.CtrlCmd | KeyCode.KEY_R - }); - KeybindingsRegistry.registerKeybindingRule({ id: ToggleDevToolsAction.ID, weight: KeybindingWeight.WorkbenchContrib + 50, @@ -215,7 +165,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), 'Help: About', helpCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), `Help: About ${product.applicationName}`, helpCategory); })(); })(); @@ -230,52 +180,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow order: 2 }); - if (isMacintosh) { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenFileFolderAction.ID, - title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...") - }, - order: 1 - }); - } else { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenFileAction.ID, - title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenFolderAction.ID, - title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") - }, - order: 2 - }); - } - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenWorkspaceAction.ID, - title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") - }, - order: 3, - when: SupportsWorkspacesContext - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), - submenu: MenuId.MenubarRecentMenu, - group: '2_open', - order: 4 - }); - // {{SQL CARBON EDIT}} - Add install VSIX menu item MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '5.1_installExtension', @@ -284,17 +188,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow title: nls.localize({ key: 'miinstallVsix', comment: ['&& denotes a mnemonic'] }, "Install Extension from VSIX Package") } }); - // {{SQL CARBON EDIT}} - End - - // More - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: 'y_more', - command: { - id: OpenRecentAction.ID, - title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") - }, - order: 1 - }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '3_workspace', @@ -316,14 +209,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow when: SupportsWorkspacesContext }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), - submenu: MenuId.MenubarPreferencesMenu, - group: '5_autosave', - order: 2, - when: IsMacContext.toNegated() - }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { @@ -364,23 +249,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow when: IsMacContext.toNegated() }); - // Appereance menu - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), - submenu: MenuId.MenubarAppearanceMenu, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: ToggleFullScreenAction.ID, - title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen") - }, - order: 1 - }); - // Zoom MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { @@ -566,18 +434,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") : nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") }, - 'window.openFoldersInNewWindow': { - 'type': 'string', - 'enum': ['on', 'off', 'default'], - 'enumDescriptions': [ - nls.localize('window.openFoldersInNewWindow.on', "Folders will open in a new window."), - nls.localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window."), - nls.localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu).") - ], - 'default': 'default', - 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") - }, 'window.openWithoutArgumentsInNewWindow': { 'type': 'string', 'enum': ['on', 'off'], @@ -631,27 +487,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow 'default': false, 'description': nls.localize('closeWhenEmpty', "Controls whether closing the last editor should also close the window. This setting only applies for windows that do not show folders.") }, - 'window.menuBarVisibility': { - 'type': 'string', - 'enum': ['default', 'visible', 'toggle', 'hidden'], - 'enumDescriptions': [ - nls.localize('window.menuBarVisibility.default', "Menu is only hidden in full screen mode."), - nls.localize('window.menuBarVisibility.visible', "Menu is always visible even in full screen mode."), - nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed via Alt key."), - nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden.") - ], - 'default': 'default', - 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. By default, the menu bar will be visible, unless the window is full screen."), - 'included': isWindows || isLinux - }, - 'window.enableMenuBarMnemonics': { - 'type': 'boolean', - 'default': true, - 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('enableMenuBarMnemonics', "If enabled, the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."), - 'included': isWindows || isLinux - }, 'window.autoDetectHighContrast': { 'type': 'boolean', 'default': true, @@ -684,7 +519,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow 'default': true, 'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."), 'scope': ConfigurationScope.APPLICATION, - 'included': isMacintosh + 'included': false /* isMacintosh */ }, 'window.clickThroughInactive': { 'type': 'boolean', diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index a2d7daece9..c99e1e1515 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -19,17 +19,15 @@ import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/n import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { stat } from 'vs/base/node/pfs'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { webFrame } from 'electron'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; import { StorageService } from 'vs/platform/storage/node/storageService'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { Schemas } from 'vs/base/common/network'; import { sanitizeFilePath } from 'vs/base/common/extpath'; -import { basename } from 'vs/base/common/path'; import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -41,21 +39,27 @@ import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-brow import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-browser/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; import { DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationExportHelper'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; -import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { SignService } from 'vs/platform/sign/node/signService'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { basename } from 'vs/base/common/resources'; class CodeRendererMain extends Disposable { private workbench: Workbench; + private readonly environmentService: WorkbenchEnvironmentService; - constructor(private readonly configuration: IWindowConfiguration) { + constructor(configuration: IWindowConfiguration) { super(); + this.environmentService = new WorkbenchEnvironmentService(configuration, configuration.execPath); this.init(); } @@ -69,29 +73,29 @@ class CodeRendererMain extends Disposable { this.reviveUris(); // Setup perf - importEntries(this.configuration.perfEntries); + importEntries(this.environmentService.configuration.perfEntries); // Browser config setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151) - setFullscreen(!!this.configuration.fullscreen); + setFullscreen(!!this.environmentService.configuration.fullscreen); // Keyboard support KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); } private reviveUris() { - if (this.configuration.folderUri) { - this.configuration.folderUri = URI.revive(this.configuration.folderUri); + if (this.environmentService.configuration.folderUri) { + this.environmentService.configuration.folderUri = URI.revive(this.environmentService.configuration.folderUri); } - if (this.configuration.workspace) { - this.configuration.workspace = reviveWorkspaceIdentifier(this.configuration.workspace); + if (this.environmentService.configuration.workspace) { + this.environmentService.configuration.workspace = reviveWorkspaceIdentifier(this.environmentService.configuration.workspace); } - const filesToWait = this.configuration.filesToWait; + const filesToWait = this.environmentService.configuration.filesToWait; const filesToWaitPaths = filesToWait && filesToWait.paths; - [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff].forEach(paths => { + [filesToWaitPaths, this.environmentService.configuration.filesToOpenOrCreate, this.environmentService.configuration.filesToDiff].forEach(paths => { if (Array.isArray(paths)) { paths.forEach(path => { if (path.fileUri) { @@ -128,17 +132,17 @@ class CodeRendererMain extends Disposable { this._register(instantiationService.createInstance(ElectronWindow)); // Driver - if (this.configuration.driver) { + if (this.environmentService.configuration.driver) { instantiationService.invokeFunction(async accessor => this._register(await registerWindowDriver(accessor))); } // Config Exporter - if (this.configuration['export-default-configuration']) { + if (this.environmentService.configuration['export-default-configuration']) { instantiationService.createInstance(DefaultConfigurationExportHelper); } // Logging - services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); + services.logService.trace('workbench configuration', JSON.stringify(this.environmentService.configuration)); } private onWindowResize(e: Event, retry: boolean): void { @@ -168,22 +172,25 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Main Process - const mainProcessService = this._register(new MainProcessService(this.configuration.windowId)); + const mainProcessService = this._register(new MainProcessService(this.environmentService.configuration.windowId)); serviceCollection.set(IMainProcessService, mainProcessService); // Environment - const environmentService = new WorkbenchEnvironmentService(this.configuration, this.configuration.execPath); - serviceCollection.set(IWorkbenchEnvironmentService, environmentService); + serviceCollection.set(IWorkbenchEnvironmentService, this.environmentService); // Log - const logService = this._register(this.createLogService(mainProcessService, environmentService)); + const logService = this._register(this.createLogService(mainProcessService, this.environmentService)); serviceCollection.set(ILogService, logService); // Remote const remoteAuthorityResolverService = new RemoteAuthorityResolverService(); serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); - const remoteAgentService = this._register(new RemoteAgentService(this.configuration, environmentService, remoteAuthorityResolverService)); + // Sign + const signService = new SignService(); + serviceCollection.set(ISignService, signService); + + const remoteAgentService = this._register(new RemoteAgentService(this.environmentService.configuration, this.environmentService, remoteAuthorityResolverService, signService)); serviceCollection.set(IRemoteAgentService, remoteAgentService); // Files @@ -193,6 +200,9 @@ class CodeRendererMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // User Data Provider + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(this.environmentService.appSettingsHome, this.environmentService.backupHome, diskFileSystemProvider, this.environmentService)); + const connection = remoteAgentService.getConnection(); if (connection) { const channel = connection.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); @@ -200,10 +210,10 @@ class CodeRendererMain extends Disposable { fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); } - const payload = await this.resolveWorkspaceInitializationPayload(environmentService); + const payload = await this.resolveWorkspaceInitializationPayload(); const services = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { + this.createWorkspaceService(payload, fileService, remoteAgentService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -214,7 +224,7 @@ class CodeRendererMain extends Disposable { return service; }), - this.createStorageService(payload, environmentService, logService, mainProcessService).then(service => { + this.createStorageService(payload, logService, mainProcessService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -226,25 +236,25 @@ class CodeRendererMain extends Disposable { return { serviceCollection, logService, storageService: services[1] }; } - private async resolveWorkspaceInitializationPayload(environmentService: IWorkbenchEnvironmentService): Promise { + private async resolveWorkspaceInitializationPayload(): Promise { // Multi-root workspace - if (this.configuration.workspace) { - return Promise.resolve(this.configuration.workspace); + if (this.environmentService.configuration.workspace) { + return this.environmentService.configuration.workspace; } // Single-folder workspace let workspaceInitializationPayload: IWorkspaceInitializationPayload | undefined; - if (this.configuration.folderUri) { - workspaceInitializationPayload = await this.resolveSingleFolderWorkspaceInitializationPayload(this.configuration.folderUri); + if (this.environmentService.configuration.folderUri) { + workspaceInitializationPayload = await this.resolveSingleFolderWorkspaceInitializationPayload(this.environmentService.configuration.folderUri); } // Fallback to empty workspace if we have no payload yet. if (!workspaceInitializationPayload) { let id: string; - if (this.configuration.backupPath) { - id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID - } else if (environmentService.isExtensionDevelopment) { + if (this.environmentService.configuration.backupWorkspaceResource) { + id = basename(this.environmentService.configuration.backupWorkspaceResource); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else if (this.environmentService.isExtensionDevelopment) { id = 'ext-dev'; // extension development window never stores backups and is a singleton } else { throw new Error('Unexpected window configuration without backupPath'); @@ -260,7 +270,7 @@ class CodeRendererMain extends Disposable { // Return early the folder is not local if (folderUri.scheme !== Schemas.file) { - return Promise.resolve({ id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri }); + return { id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri }; } function computeLocalDiskFolderId(folder: URI, stat: fs.Stats): string { @@ -299,11 +309,8 @@ class CodeRendererMain extends Disposable { return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check } - private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; - - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService({ remoteAuthority: this.environmentService.configuration.remoteAuthority, configurationCache: new ConfigurationCache(this.environmentService) }, this.environmentService, fileService, remoteAgentService); try { await workspaceService.initialize(payload); @@ -317,9 +324,9 @@ class CodeRendererMain extends Disposable { } } - private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, logService: ILogService, mainProcessService: IMainProcessService): Promise { + private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise { const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')); - const storageService = new StorageService(globalStorageDatabase, logService, environmentService); + const storageService = new StorageService(globalStorageDatabase, logService, this.environmentService); try { await storageService.initialize(payload); @@ -334,8 +341,8 @@ class CodeRendererMain extends Disposable { } private createLogService(mainProcessService: IMainProcessService, environmentService: IWorkbenchEnvironmentService): ILogService { - const spdlogService = createBufferSpdLogService(`renderer${this.configuration.windowId}`, this.configuration.logLevel, environmentService.logsPath); - const consoleLogService = new ConsoleLogService(this.configuration.logLevel); + const spdlogService = new SpdLogService(`renderer${this.environmentService.configuration.windowId}`, environmentService.logsPath, this.environmentService.configuration.logLevel); + const consoleLogService = new ConsoleLogService(this.environmentService.configuration.logLevel); const logService = new MultiplexLogService([consoleLogService, spdlogService]); const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 068ced2e25..0ad99b32ba 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -21,14 +21,14 @@ import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/theme import * as browser from 'vs/base/browser/browser'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { fillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; @@ -61,7 +61,7 @@ export class ElectronWindow extends Disposable { private touchBarMenu?: IMenu; private touchBarUpdater: RunOnceScheduler; - private touchBarDisposables: IDisposable[]; + private readonly touchBarDisposables = this._register(new DisposableStore()); private lastInstalledTouchedBar: ICommandAction[][]; private previousConfiguredZoomLevel: number; @@ -95,8 +95,6 @@ export class ElectronWindow extends Disposable { ) { super(); - this.touchBarDisposables = []; - this.pendingFoldersToAdd = []; this.addFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddFolders(), 100)); @@ -314,22 +312,24 @@ export class ElectronWindow extends Disposable { this.integrityService.isPure().then(res => this.titleService.updateProperties({ isPure: res.isPure })); // Root warning - this.lifecycleService.when(LifecyclePhase.Restored).then(async () => { - let isAdmin: boolean; + this.lifecycleService.when(LifecyclePhase.Restored).then(() => { + let isAdminPromise: Promise; if (isWindows) { - const isElevated = await import('native-is-elevated'); - isAdmin = isElevated(); + isAdminPromise = import('native-is-elevated').then(isElevated => isElevated()); // not using async here due to https://github.com/microsoft/vscode/issues/74321 } else { - isAdmin = isRootUser(); + isAdminPromise = Promise.resolve(isRootUser()); } - // Update title - this.titleService.updateProperties({ isAdmin }); + return isAdminPromise.then(isAdmin => { - // Show warning message (unix only) - if (isAdmin && !isWindows) { - this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", product.nameShort)); - } + // Update title + this.titleService.updateProperties({ isAdmin }); + + // Show warning message (unix only) + if (isAdmin && !isWindows) { + this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", product.nameShort)); + } + }); }); // Touchbar menu (if enabled) @@ -350,26 +350,26 @@ export class ElectronWindow extends Disposable { } // Dispose old - this.touchBarDisposables = dispose(this.touchBarDisposables); + this.touchBarDisposables.clear(); this.touchBarMenu = undefined; // Create new (delayed) this.touchBarUpdater = new RunOnceScheduler(() => this.doUpdateTouchbarMenu(), 300); - this.touchBarDisposables.push(this.touchBarUpdater); + this.touchBarDisposables.add(this.touchBarUpdater); this.touchBarUpdater.schedule(); } private doUpdateTouchbarMenu(): void { if (!this.touchBarMenu) { this.touchBarMenu = this.editorService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.TouchBarContext, accessor.get(IContextKeyService))); - this.touchBarDisposables.push(this.touchBarMenu); - this.touchBarDisposables.push(this.touchBarMenu.onDidChange(() => this.touchBarUpdater.schedule())); + this.touchBarDisposables.add(this.touchBarMenu); + this.touchBarDisposables.add(this.touchBarMenu.onDidChange(() => this.touchBarUpdater.schedule())); } const actions: Array = []; // Fill actions into groups respecting order - fillInActionBarActions(this.touchBarMenu, undefined, actions); + this.touchBarDisposables.add(createAndFillInActionBarActions(this.touchBarMenu, undefined, actions)); // Convert into command action multi array const items: ICommandAction[][] = []; @@ -408,7 +408,7 @@ export class ElectronWindow extends Disposable { const options = { companyName: product.crashReporter.companyName, productName: product.crashReporter.productName, - submitURL: isWindows ? product.hockeyApp[`win32-${process.arch}`] : isLinux ? product.hockeyApp[`linux-${process.arch}`] : product.hockeyApp.darwin, + submitURL: isWindows ? product.hockeyApp[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? product.hockeyApp[`linux-x64`] : product.hockeyApp.darwin, extra: { vscode_version: pkg.version, vscode_commit: product.commit @@ -531,10 +531,4 @@ export class ElectronWindow extends Disposable { // Otherwise open all return this.editorService.openEditors(resources); } - - dispose(): void { - this.touchBarDisposables = dispose(this.touchBarDisposables); - - super.dispose(); - } } diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index e9d7e45c72..ccc26b0b63 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { joinPath, relativePath } from 'vs/base/common/resources'; export const IBackupFileService = createDecorator('backupFileService'); @@ -18,13 +20,19 @@ export interface IResolvedBackup { * A service that handles any I/O and state associated with the backup system. */ export interface IBackupFileService { - _serviceBrand: any; + + _serviceBrand: ServiceIdentifier; /** * Finds out if there are any backups stored. */ hasBackups(): Promise; + /** + * Finds out if the provided resource with the given version is backed up. + */ + hasBackupSync(resource: URI, versionId?: number): boolean; + /** * Loads the backup resource for a particular resource within the current workspace. * @@ -80,3 +88,7 @@ export interface IBackupFileService { */ discardAllWorkspaceBackups(): Promise; } + +export function toBackupWorkspaceResource(backupWorkspacePath: string, environmentService: IEnvironmentService): URI { + return joinPath(environmentService.userRoamingDataHome, relativePath(URI.file(environmentService.userDataPath), URI.file(backupWorkspacePath))!); +} \ No newline at end of file diff --git a/src/vs/workbench/services/backup/common/backupFileService.ts b/src/vs/workbench/services/backup/common/backupFileService.ts new file mode 100644 index 0000000000..b9a452d5df --- /dev/null +++ b/src/vs/workbench/services/backup/common/backupFileService.ts @@ -0,0 +1,443 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { join } from 'vs/base/common/path'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { hash } from 'vs/base/common/hash'; +import { coalesce } from 'vs/base/common/arrays'; +import { equals, deepClone } from 'vs/base/common/objects'; +import { ResourceQueue } from 'vs/base/common/async'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ITextSnapshot } from 'vs/editor/common/model'; +import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; +import { keys, ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/textfiles'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; + +export interface IBackupFilesModel { + resolve(backupRoot: URI): Promise; + + add(resource: URI, versionId?: number, meta?: object): void; + has(resource: URI, versionId?: number, meta?: object): boolean; + get(): URI[]; + remove(resource: URI): void; + count(): number; + clear(): void; +} + +interface IBackupCacheEntry { + versionId?: number; + meta?: object; +} + +export class BackupFilesModel implements IBackupFilesModel { + private cache: ResourceMap = new ResourceMap(); + + constructor(private fileService: IFileService) { } + + async resolve(backupRoot: URI): Promise { + try { + const backupRootStat = await this.fileService.resolve(backupRoot); + if (backupRootStat.children) { + await Promise.all(backupRootStat.children + .filter(child => child.isDirectory) + .map(async backupSchema => { + + // Read backup directory for backups + const backupSchemaStat = await this.fileService.resolve(backupSchema.resource); + + // Remember known backups in our caches + if (backupSchemaStat.children) { + backupSchemaStat.children.forEach(backupHash => this.add(backupHash.resource)); + } + })); + } + } catch (error) { + // ignore any errors + } + + return this; + } + + add(resource: URI, versionId = 0, meta?: object): void { + this.cache.set(resource, { versionId, meta: deepClone(meta) }); // make sure to not store original meta in our cache... + } + + count(): number { + return this.cache.size; + } + + has(resource: URI, versionId?: number, meta?: object): boolean { + const entry = this.cache.get(resource); + if (!entry) { + return false; // unknown resource + } + + if (typeof versionId === 'number' && versionId !== entry.versionId) { + return false; // different versionId + } + + if (meta && !equals(meta, entry.meta)) { + return false; // different metadata + } + + return true; + } + + get(): URI[] { + return this.cache.keys(); + } + + remove(resource: URI): void { + this.cache.delete(resource); + } + + clear(): void { + this.cache.clear(); + } +} + +export class BackupFileService implements IBackupFileService { + + _serviceBrand: ServiceIdentifier; + + private impl: IBackupFileService; + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IFileService fileService: IFileService + ) { + const backupWorkspaceResource = environmentService.configuration.backupWorkspaceResource; + if (backupWorkspaceResource) { + this.impl = new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, fileService); + } else { + this.impl = new InMemoryBackupFileService(this.hashPath); + } + } + + protected hashPath(resource: URI): string { + const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); + + return hash(str).toString(16); + } + + initialize(backupWorkspaceResource: URI): void { + if (this.impl instanceof BackupFileServiceImpl) { + this.impl.initialize(backupWorkspaceResource); + } + } + + hasBackups(): Promise { + return this.impl.hasBackups(); + } + + hasBackupSync(resource: URI, versionId?: number): boolean { + return this.impl.hasBackupSync(resource, versionId); + } + + loadBackupResource(resource: URI): Promise { + return this.impl.loadBackupResource(resource); + } + + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { + return this.impl.backupResource(resource, content, versionId, meta); + } + + discardResourceBackup(resource: URI): Promise { + return this.impl.discardResourceBackup(resource); + } + + discardAllWorkspaceBackups(): Promise { + return this.impl.discardAllWorkspaceBackups(); + } + + getWorkspaceFileBackups(): Promise { + return this.impl.getWorkspaceFileBackups(); + } + + resolveBackupContent(backup: URI): Promise> { + return this.impl.resolveBackupContent(backup); + } + + toBackupResource(resource: URI): URI { + return this.impl.toBackupResource(resource); + } +} + +class BackupFileServiceImpl implements IBackupFileService { + + private static readonly PREAMBLE_END_MARKER = '\n'; + private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator + private static readonly PREAMBLE_MAX_LENGTH = 10000; + + _serviceBrand: ServiceIdentifier; + + private backupWorkspacePath: URI; + + private isShuttingDown: boolean; + private ioOperationQueues: ResourceQueue; // queue IO operations to ensure write order + + private ready: Promise; + private model: IBackupFilesModel; + + constructor( + backupWorkspaceResource: URI, + private readonly hashPath: (resource: URI) => string, + @IFileService private readonly fileService: IFileService + ) { + this.isShuttingDown = false; + this.ioOperationQueues = new ResourceQueue(); + + this.initialize(backupWorkspaceResource); + } + + initialize(backupWorkspaceResource: URI): void { + this.backupWorkspacePath = backupWorkspaceResource; + + this.ready = this.init(); + } + + private init(): Promise { + this.model = new BackupFilesModel(this.fileService); + + return this.model.resolve(this.backupWorkspacePath); + } + + async hasBackups(): Promise { + const model = await this.ready; + + return model.count() > 0; + } + + hasBackupSync(resource: URI, versionId?: number): boolean { + const backupResource = this.toBackupResource(resource); + + return this.model.has(backupResource, versionId); + } + + async loadBackupResource(resource: URI): Promise { + const model = await this.ready; + + // Return directly if we have a known backup with that resource + const backupResource = this.toBackupResource(resource); + if (model.has(backupResource)) { + return backupResource; + } + + return undefined; + } + + async backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { + if (this.isShuttingDown) { + return; + } + + const model = await this.ready; + + const backupResource = this.toBackupResource(resource); + if (model.has(backupResource, versionId, meta)) { + return; // return early if backup version id matches requested one + } + + return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + let preamble: string | undefined = undefined; + + // With Metadata: URI + META-START + Meta + END + if (meta) { + const preambleWithMeta = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_META_SEPARATOR}${JSON.stringify(meta)}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + if (preambleWithMeta.length < BackupFileServiceImpl.PREAMBLE_MAX_LENGTH) { + preamble = preambleWithMeta; + } + } + + // Without Metadata: URI + END + if (!preamble) { + preamble = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + } + + // Update content with value + await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)); + + // Update model + model.add(backupResource, versionId, meta); + }); + } + + async discardResourceBackup(resource: URI): Promise { + const model = await this.ready; + const backupResource = this.toBackupResource(resource); + + return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + await this.fileService.del(backupResource, { recursive: true }); + + model.remove(backupResource); + }); + } + + async discardAllWorkspaceBackups(): Promise { + this.isShuttingDown = true; + + const model = await this.ready; + + await this.fileService.del(this.backupWorkspacePath, { recursive: true }); + + model.clear(); + } + + async getWorkspaceFileBackups(): Promise { + const model = await this.ready; + + const backups = await Promise.all(model.get().map(async fileBackup => { + const backupPreamble = await this.readToMatchingString(fileBackup, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH); + if (!backupPreamble) { + return undefined; + } + + // Preamble with metadata: URI + META-START + Meta + END + const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); + if (metaStartIndex > 0) { + return URI.parse(backupPreamble.substring(0, metaStartIndex)); + } + + // Preamble without metadata: URI + END + else { + return URI.parse(backupPreamble); + } + })); + + return coalesce(backups); + } + + private async readToMatchingString(file: URI, matchingString: string, maximumBytesToRead: number): Promise { + const contents = (await this.fileService.readFile(file, { length: maximumBytesToRead })).value.toString(); + + const newLineIndex = contents.indexOf(matchingString); + if (newLineIndex >= 0) { + return contents.substr(0, newLineIndex); + } + + throw new Error(`Could not find ${JSON.stringify(matchingString)} in first ${maximumBytesToRead} bytes of ${file}`); + } + + async resolveBackupContent(backup: URI): Promise> { + + // Metadata extraction + let metaRaw = ''; + let metaEndFound = false; + + // Add a filter method to filter out everything until the meta end marker + const metaPreambleFilter = (chunk: VSBuffer) => { + const chunkString = chunk.toString(); + + if (!metaEndFound) { + const metaEndIndex = chunkString.indexOf(BackupFileServiceImpl.PREAMBLE_END_MARKER); + if (metaEndIndex === -1) { + metaRaw += chunkString; + + return VSBuffer.fromString(''); // meta not yet found, return empty string + } + + metaEndFound = true; + metaRaw += chunkString.substring(0, metaEndIndex); // ensure to get last chunk from metadata + + return VSBuffer.fromString(chunkString.substr(metaEndIndex + 1)); // meta found, return everything after + } + + return chunk; + }; + + // Read backup into factory + const content = await this.fileService.readFileStream(backup); + const factory = await createTextBufferFactoryFromStream(content.value, metaPreambleFilter); + + // Trigger read for meta data extraction from the filter above + factory.getFirstLineText(1); + + let meta: T | undefined; + const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); + if (metaStartIndex !== -1) { + try { + meta = JSON.parse(metaRaw.substr(metaStartIndex + 1)); + } catch (error) { + // ignore JSON parse errors + } + } + + return { value: factory, meta }; + } + + toBackupResource(resource: URI): URI { + return joinPath(this.backupWorkspacePath, resource.scheme, this.hashPath(resource)); + } +} + +export class InMemoryBackupFileService implements IBackupFileService { + + _serviceBrand: ServiceIdentifier; + + private backups: Map = new Map(); + + constructor(private readonly hashPath: (resource: URI) => string) { } + + hasBackups(): Promise { + return Promise.resolve(this.backups.size > 0); + } + + hasBackupSync(resource: URI, versionId?: number): boolean { + const backupResource = this.toBackupResource(resource); + + return this.backups.has(backupResource.toString()); + } + + loadBackupResource(resource: URI): Promise { + const backupResource = this.toBackupResource(resource); + if (this.backups.has(backupResource.toString())) { + return Promise.resolve(backupResource); + } + + return Promise.resolve(undefined); + } + + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { + const backupResource = this.toBackupResource(resource); + this.backups.set(backupResource.toString(), content); + + return Promise.resolve(); + } + + resolveBackupContent(backupResource: URI): Promise> { + const snapshot = this.backups.get(backupResource.toString()); + if (snapshot) { + return Promise.resolve({ value: createTextBufferFactoryFromSnapshot(snapshot) }); + } + + return Promise.reject('Unexpected backup resource to resolve'); + } + + getWorkspaceFileBackups(): Promise { + return Promise.resolve(keys(this.backups).map(key => URI.parse(key))); + } + + discardResourceBackup(resource: URI): Promise { + this.backups.delete(this.toBackupResource(resource).toString()); + + return Promise.resolve(); + } + + discardAllWorkspaceBackups(): Promise { + this.backups.clear(); + + return Promise.resolve(); + } + + toBackupResource(resource: URI): URI { + return URI.file(join(resource.scheme, this.hashPath(resource))); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index ef880c0dca..1be121ab01 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -3,407 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { join } from 'vs/base/common/path'; -import { joinPath } from 'vs/base/common/resources'; -import { createHash } from 'crypto'; +import { BackupFileService as CommonBackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { URI } from 'vs/base/common/uri'; -import { coalesce } from 'vs/base/common/arrays'; -import { equals, deepClone } from 'vs/base/common/objects'; -import { ResourceQueue } from 'vs/base/common/async'; -import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; -import { IFileService } from 'vs/platform/files/common/files'; -import { readToMatchingString } from 'vs/base/node/stream'; -import { ITextSnapshot } from 'vs/editor/common/model'; -import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { keys, ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/textfiles'; -import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import * as crypto from 'crypto'; -export interface IBackupFilesModel { - resolve(backupRoot: URI): Promise; +export class BackupFileService extends CommonBackupFileService { - add(resource: URI, versionId?: number, meta?: object): void; - has(resource: URI, versionId?: number, meta?: object): boolean; - get(): URI[]; - remove(resource: URI): void; - count(): number; - clear(): void; -} - -interface IBackupCacheEntry { - versionId?: number; - meta?: object; -} - -export class BackupFilesModel implements IBackupFilesModel { - private cache: ResourceMap = new ResourceMap(); - - constructor(private fileService: IFileService) { } - - async resolve(backupRoot: URI): Promise { - try { - const backupRootStat = await this.fileService.resolve(backupRoot); - if (backupRootStat.children) { - await Promise.all(backupRootStat.children - .filter(child => child.isDirectory) - .map(async backupSchema => { - - // Read backup directory for backups - const backupSchemaStat = await this.fileService.resolve(backupSchema.resource); - - // Remember known backups in our caches - if (backupSchemaStat.children) { - backupSchemaStat.children.forEach(backupHash => this.add(backupHash.resource)); - } - })); - } - } catch (error) { - // ignore any errors - } - - return this; + protected hashPath(resource: URI): string { + return hashPath(resource); } - add(resource: URI, versionId = 0, meta?: object): void { - this.cache.set(resource, { versionId, meta: deepClone(meta) }); // make sure to not store original meta in our cache... - } - - count(): number { - return this.cache.size; - } - - has(resource: URI, versionId?: number, meta?: object): boolean { - const entry = this.cache.get(resource); - if (!entry) { - return false; // unknown resource - } - - if (typeof versionId === 'number' && versionId !== entry.versionId) { - return false; // different versionId - } - - if (meta && !equals(meta, entry.meta)) { - return false; // different metadata - } - - return true; - } - - get(): URI[] { - return this.cache.keys(); - } - - remove(resource: URI): void { - this.cache.delete(resource); - } - - clear(): void { - this.cache.clear(); - } -} - -export class BackupFileService implements IBackupFileService { - - _serviceBrand: ServiceIdentifier; - - private impl: IBackupFileService; - - constructor( - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @IFileService fileService: IFileService - ) { - const backupWorkspacePath = environmentService.configuration.backupPath; - if (backupWorkspacePath) { - this.impl = new BackupFileServiceImpl(backupWorkspacePath, fileService); - } else { - this.impl = new InMemoryBackupFileService(); - } - } - - initialize(backupWorkspacePath: string): void { - if (this.impl instanceof BackupFileServiceImpl) { - this.impl.initialize(backupWorkspacePath); - } - } - - hasBackups(): Promise { - return this.impl.hasBackups(); - } - - loadBackupResource(resource: URI): Promise { - return this.impl.loadBackupResource(resource); - } - - backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { - return this.impl.backupResource(resource, content, versionId, meta); - } - - discardResourceBackup(resource: URI): Promise { - return this.impl.discardResourceBackup(resource); - } - - discardAllWorkspaceBackups(): Promise { - return this.impl.discardAllWorkspaceBackups(); - } - - getWorkspaceFileBackups(): Promise { - return this.impl.getWorkspaceFileBackups(); - } - - resolveBackupContent(backup: URI): Promise> { - return this.impl.resolveBackupContent(backup); - } - - toBackupResource(resource: URI): URI { - return this.impl.toBackupResource(resource); - } -} - -class BackupFileServiceImpl implements IBackupFileService { - - private static readonly PREAMBLE_END_MARKER = '\n'; - private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator - private static readonly PREAMBLE_MAX_LENGTH = 10000; - - _serviceBrand: any; - - private backupWorkspacePath: URI; - - private isShuttingDown: boolean; - private ready: Promise; - private ioOperationQueues: ResourceQueue; // queue IO operations to ensure write order - - constructor( - backupWorkspacePath: string, - @IFileService private readonly fileService: IFileService - ) { - this.isShuttingDown = false; - this.ioOperationQueues = new ResourceQueue(); - - this.initialize(backupWorkspacePath); - } - - initialize(backupWorkspacePath: string): void { - this.backupWorkspacePath = URI.file(backupWorkspacePath); - - this.ready = this.init(); - } - - private init(): Promise { - const model = new BackupFilesModel(this.fileService); - - return model.resolve(this.backupWorkspacePath); - } - - async hasBackups(): Promise { - const model = await this.ready; - - return model.count() > 0; - } - - async loadBackupResource(resource: URI): Promise { - const model = await this.ready; - - // Return directly if we have a known backup with that resource - const backupResource = this.toBackupResource(resource); - if (model.has(backupResource)) { - return backupResource; - } - - return undefined; - } - - async backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { - if (this.isShuttingDown) { - return; - } - - const model = await this.ready; - - const backupResource = this.toBackupResource(resource); - if (model.has(backupResource, versionId, meta)) { - return; // return early if backup version id matches requested one - } - - return this.ioOperationQueues.queueFor(backupResource).queue(async () => { - let preamble: string | undefined = undefined; - - // With Metadata: URI + META-START + Meta + END - if (meta) { - const preambleWithMeta = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_META_SEPARATOR}${JSON.stringify(meta)}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; - if (preambleWithMeta.length < BackupFileServiceImpl.PREAMBLE_MAX_LENGTH) { - preamble = preambleWithMeta; - } - } - - // Without Metadata: URI + END - if (!preamble) { - preamble = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; - } - - // Update content with value - await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)); - - // Update model - model.add(backupResource, versionId, meta); - }); - } - - async discardResourceBackup(resource: URI): Promise { - const model = await this.ready; - const backupResource = this.toBackupResource(resource); - - return this.ioOperationQueues.queueFor(backupResource).queue(async () => { - await this.fileService.del(backupResource, { recursive: true }); - - model.remove(backupResource); - }); - } - - async discardAllWorkspaceBackups(): Promise { - this.isShuttingDown = true; - - const model = await this.ready; - - await this.fileService.del(this.backupWorkspacePath, { recursive: true }); - - model.clear(); - } - - async getWorkspaceFileBackups(): Promise { - const model = await this.ready; - - const backups = await Promise.all(model.get().map(async fileBackup => { - const backupPreamble = await readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH / 5, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH); - if (!backupPreamble) { - return undefined; - } - - // Preamble with metadata: URI + META-START + Meta + END - const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); - if (metaStartIndex > 0) { - return URI.parse(backupPreamble.substring(0, metaStartIndex)); - } - - // Preamble without metadata: URI + END - else { - return URI.parse(backupPreamble); - } - })); - - return coalesce(backups); - } - - async resolveBackupContent(backup: URI): Promise> { - - // Metadata extraction - let metaRaw = ''; - let metaEndFound = false; - - // Add a filter method to filter out everything until the meta end marker - const metaPreambleFilter = (chunk: VSBuffer) => { - const chunkString = chunk.toString(); - - if (!metaEndFound) { - const metaEndIndex = chunkString.indexOf(BackupFileServiceImpl.PREAMBLE_END_MARKER); - if (metaEndIndex === -1) { - metaRaw += chunkString; - - return VSBuffer.fromString(''); // meta not yet found, return empty string - } - - metaEndFound = true; - metaRaw += chunkString.substring(0, metaEndIndex); // ensure to get last chunk from metadata - - return VSBuffer.fromString(chunkString.substr(metaEndIndex + 1)); // meta found, return everything after - } - - return chunk; - }; - - // Read backup into factory - const content = await this.fileService.readFileStream(backup); - const factory = await createTextBufferFactoryFromStream(content.value, metaPreambleFilter); - - // Trigger read for meta data extraction from the filter above - factory.getFirstLineText(1); - - let meta: T | undefined; - const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); - if (metaStartIndex !== -1) { - try { - meta = JSON.parse(metaRaw.substr(metaStartIndex + 1)); - } catch (error) { - // ignore JSON parse errors - } - } - - return { value: factory, meta }; - } - - toBackupResource(resource: URI): URI { - return joinPath(this.backupWorkspacePath, resource.scheme, hashPath(resource)); - } -} - -export class InMemoryBackupFileService implements IBackupFileService { - - _serviceBrand: any; - - private backups: Map = new Map(); - - hasBackups(): Promise { - return Promise.resolve(this.backups.size > 0); - } - - loadBackupResource(resource: URI): Promise { - const backupResource = this.toBackupResource(resource); - if (this.backups.has(backupResource.toString())) { - return Promise.resolve(backupResource); - } - - return Promise.resolve(undefined); - } - - backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { - const backupResource = this.toBackupResource(resource); - this.backups.set(backupResource.toString(), content); - - return Promise.resolve(); - } - - resolveBackupContent(backupResource: URI): Promise> { - const snapshot = this.backups.get(backupResource.toString()); - if (snapshot) { - return Promise.resolve({ value: createTextBufferFactoryFromSnapshot(snapshot) }); - } - - return Promise.reject('Unexpected backup resource to resolve'); - } - - getWorkspaceFileBackups(): Promise { - return Promise.resolve(keys(this.backups).map(key => URI.parse(key))); - } - - discardResourceBackup(resource: URI): Promise { - this.backups.delete(this.toBackupResource(resource).toString()); - - return Promise.resolve(); - } - - discardAllWorkspaceBackups(): Promise { - this.backups.clear(); - - return Promise.resolve(); - } - - toBackupResource(resource: URI): URI { - return URI.file(join(resource.scheme, hashPath(resource))); - } } /* @@ -411,7 +21,5 @@ export class InMemoryBackupFileService implements IBackupFileService { */ export function hashPath(resource: URI): string { const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); - return createHash('md5').update(str).digest('hex'); -} - -registerSingleton(IBackupFileService, BackupFileService); \ No newline at end of file + return crypto.createHash('md5').update(str).digest('hex'); +} \ No newline at end of file diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index 9785ba18bf..51e799ae62 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -11,22 +11,26 @@ import * as fs from 'fs'; import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; -import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { BackupFilesModel } from 'vs/workbench/services/backup/common/backupFileService'; import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; +import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { BACKUPS } from 'vs/platform/environment/common/environment'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; -const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); -const backupHome = path.join(parentDir, 'Backups'); +const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); +const appSettingsHome = path.join(userdataDir, 'User'); +const backupHome = path.join(userdataDir, 'Backups'); const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); @@ -43,18 +47,10 @@ const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(u class TestBackupEnvironmentService extends WorkbenchEnvironmentService { - private config: IWindowConfiguration; - - constructor(workspaceBackupPath: string) { - super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); - - this.config = Object.create(null); - this.config.backupPath = workspaceBackupPath; + constructor(backupPath: string) { + super({ ...parseArgs(process.argv), ...{ backupPath, 'user-data-dir': userdataDir } } as IWindowConfiguration, process.execPath); } - get configuration(): IWindowConfiguration { - return this.config; - } } class TestBackupFileService extends BackupFileService { @@ -62,9 +58,11 @@ class TestBackupFileService extends BackupFileService { readonly fileService: IFileService; constructor(workspace: URI, backupHome: string, workspacesJsonPath: string) { - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); + const fileService = new FileService(new NullLogService()); + const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService)); super(environmentService, fileService); @@ -124,8 +122,8 @@ suite('BackupFileService', () => { const backupResource = fooFile; const workspaceHash = hashPath(workspaceResource); const filePathHash = hashPath(backupResource); - const expectedPath = URI.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath; - assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); + const expectedPath = URI.file(path.join(appSettingsHome, BACKUPS, workspaceHash, Schemas.file, filePathHash)).with({ scheme: Schemas.userData }).toString(); + assert.equal(service.toBackupResource(backupResource).toString(), expectedPath); }); test('should get the correct backup path for untitled files', () => { @@ -133,8 +131,8 @@ suite('BackupFileService', () => { const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const workspaceHash = hashPath(workspaceResource); const filePathHash = hashPath(backupResource); - const expectedPath = URI.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; - assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); + const expectedPath = URI.file(path.join(appSettingsHome, BACKUPS, workspaceHash, Schemas.untitled, filePathHash)).with({ scheme: Schemas.userData }).toString(); + assert.equal(service.toBackupResource(backupResource).toString(), expectedPath); }); }); @@ -157,6 +155,16 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); assert.equal(fs.existsSync(fooBackupPath), true); assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + assert.ok(service.hasBackupSync(fooFile)); + }); + + test('text file (with version)', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), 666); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + assert.ok(!service.hasBackupSync(fooFile, 555)); + assert.ok(service.hasBackupSync(fooFile, 666)); }); test('text file (with meta)', async () => { @@ -164,6 +172,7 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); assert.equal(fs.existsSync(fooBackupPath), true); assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`); + assert.ok(service.hasBackupSync(fooFile)); }); test('untitled file', async () => { @@ -171,6 +180,7 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); assert.equal(fs.existsSync(untitledBackupPath), true); assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); + assert.ok(service.hasBackupSync(untitledFile)); }); test('text file (ITextSnapshot)', async () => { @@ -180,6 +190,8 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); assert.equal(fs.existsSync(fooBackupPath), true); assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + assert.ok(service.hasBackupSync(fooFile)); + model.dispose(); }); @@ -190,6 +202,7 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); assert.equal(fs.existsSync(untitledBackupPath), true); assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); + model.dispose(); }); @@ -201,6 +214,8 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); assert.equal(fs.existsSync(fooBackupPath), true); assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n${largeString}`); + assert.ok(service.hasBackupSync(fooFile)); + model.dispose(); }); @@ -212,6 +227,8 @@ suite('BackupFileService', () => { assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); assert.equal(fs.existsSync(untitledBackupPath), true); assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\n${largeString}`); + assert.ok(service.hasBackupSync(untitledFile)); + model.dispose(); }); }); @@ -220,9 +237,12 @@ suite('BackupFileService', () => { test('text file', async () => { await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.ok(service.hasBackupSync(fooFile)); + await service.discardResourceBackup(fooFile); assert.equal(fs.existsSync(fooBackupPath), false); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); + assert.ok(!service.hasBackupSync(fooFile)); }); test('untitled file', async () => { diff --git a/src/vs/workbench/services/broadcast/common/broadcast.ts b/src/vs/workbench/services/broadcast/common/broadcast.ts deleted file mode 100644 index a6a9151020..0000000000 --- a/src/vs/workbench/services/broadcast/common/broadcast.ts +++ /dev/null @@ -1,30 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; - -export const IBroadcastService = createDecorator('broadcastService'); - -export interface IBroadcast { - channel: string; - payload: any; -} - -export interface IBroadcastService { - _serviceBrand: any; - - onBroadcast: Event; - - broadcast(b: IBroadcast): void; -} - -export class NullBroadcastService implements IBroadcastService { - _serviceBrand: any; - onBroadcast: Event = Event.None; - broadcast(_b: IBroadcast): void { - - } -} diff --git a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts b/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts deleted file mode 100644 index d278ceba6a..0000000000 --- a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts +++ /dev/null @@ -1,51 +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 { Event, Emitter } from 'vs/base/common/event'; -import { ipcRenderer as ipc } from 'electron'; -import { ILogService } from 'vs/platform/log/common/log'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IBroadcastService, IBroadcast } from 'vs/workbench/services/broadcast/common/broadcast'; - -class BroadcastService extends Disposable implements IBroadcastService { - _serviceBrand: any; - - private readonly _onBroadcast: Emitter = this._register(new Emitter()); - get onBroadcast(): Event { return this._onBroadcast.event; } - - private windowId: number; - - constructor( - @IWindowService readonly windowService: IWindowService, - @ILogService private readonly logService: ILogService - ) { - super(); - - this.windowId = windowService.windowId; - - this.registerListeners(); - } - - private registerListeners(): void { - ipc.on('vscode:broadcast', (event: unknown, b: IBroadcast) => { - this.logService.trace(`Received broadcast from main in window ${this.windowId}: `, b); - - this._onBroadcast.fire(b); - }); - } - - broadcast(b: IBroadcast): void { - this.logService.trace(`Sending broadcast to main from window ${this.windowId}: `, b); - - ipc.send('vscode:broadcast', this.windowId, { - channel: b.channel, - payload: b.payload - }); - } -} - -registerSingleton(IBroadcastService, BroadcastService, true); diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 47381fca8e..a1ce7a201d 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -18,7 +18,7 @@ import { localize } from 'vs/nls'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { emptyProgressRunner, IProgress, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressStep, emptyProgress } from 'vs/platform/progress/common/progress'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -231,11 +231,11 @@ export class BulkEdit { private _edits: Edit[] = []; private _editor: ICodeEditor | undefined; - private _progress?: IProgressRunner; + private _progress: IProgress; constructor( editor: ICodeEditor | undefined, - progress: IProgressRunner | undefined, + progress: IProgress | undefined, @ILogService private readonly _logService: ILogService, @ITextModelService private readonly _textModelService: ITextModelService, @IFileService private readonly _fileService: IFileService, @@ -244,7 +244,7 @@ export class BulkEdit { @IConfigurationService private readonly _configurationService: IConfigurationService ) { this._editor = editor; - this._progress = progress || emptyProgressRunner; + this._progress = progress || emptyProgress; } add(edits: Edit[] | Edit): void { @@ -294,10 +294,9 @@ export class BulkEdit { // define total work and progress callback // for child operations - if (this._progress) { - this._progress.total(total); - } - let progress: IProgress = { report: _ => this._progress && this._progress.worked(1) }; + this._progress.report({ total }); + + let progress: IProgress = { report: _ => this._progress.report({ increment: 1 }) }; // do it. for (const group of groups) { diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index e1b4f1b525..d2f338fb44 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -103,7 +103,7 @@ suite('CommandService', function () { test('Stop waiting for * extensions to activate when trigger is satisfied #62457', function () { let callCounter = 0; - let dispoables: IDisposable[] = []; + const dispoables = new DisposableStore(); let events: string[] = []; let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { @@ -118,7 +118,7 @@ suite('CommandService', function () { let reg = CommandsRegistry.registerCommand(event.substr('onCommand:'.length), () => { callCounter += 1; }); - dispoables.push(reg); + dispoables.add(reg); resolve(); }, 0); }); @@ -131,14 +131,15 @@ suite('CommandService', function () { return service.executeCommand('farboo').then(() => { assert.equal(callCounter, 1); assert.deepEqual(events.sort(), ['*', 'onCommand:farboo'].sort()); - dispose(dispoables); + }).finally(() => { + dispoables.dispose(); }); }); test('issue #71471: wait for onCommand activation even if a command is registered', () => { let expectedOrder: string[] = ['registering command', 'resolving activation event', 'executing command']; let actualOrder: string[] = []; - let disposables: IDisposable[] = []; + const disposables = new DisposableStore(); let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { activateByEvent(event: string): Promise { @@ -153,7 +154,7 @@ suite('CommandService', function () { let reg = CommandsRegistry.registerCommand(event.substr('onCommand:'.length), () => { actualOrder.push('executing command'); }); - disposables.push(reg); + disposables.add(reg); setTimeout(() => { // Resolve the activation event after some more time @@ -170,7 +171,8 @@ suite('CommandService', function () { return service.executeCommand('farboo2').then(() => { assert.deepEqual(actualOrder, expectedOrder); - dispose(disposables); + }).finally(() => { + disposables.dispose(); }); }); }); diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 01ae4ffdab..6355ce2e2b 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -9,10 +9,10 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; +import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; -import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, IConfigurationFileService, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES, ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -20,15 +20,54 @@ import { ConfigurationScope } from 'vs/platform/configuration/common/configurati import { extname, join } from 'vs/base/common/path'; import { equals } from 'vs/base/common/objects'; import { Schemas } from 'vs/base/common/network'; -import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration'; -import { createSHA1 } from 'vs/base/browser/hash'; +import { IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { hash } from 'vs/base/common/hash'; + +export class UserConfiguration extends Disposable { + + private readonly parser: ConfigurationModelParser; + private readonly reloadConfigurationScheduler: RunOnceScheduler; + protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + constructor( + private readonly userSettingsResource: URI, + private readonly scopes: ConfigurationScope[] | undefined, + private readonly fileService: IFileService + ) { + super(); + + this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes); + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50)); + this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.userSettingsResource))(() => this.reloadConfigurationScheduler.schedule())); + } + + async initialize(): Promise { + return this.reload(); + } + + async reload(): Promise { + try { + const content = await this.fileService.readFile(this.userSettingsResource); + this.parser.parseContent(content.value.toString() || '{}'); + return this.parser.configurationModel; + } catch (e) { + return new ConfigurationModel(); + } + } + + reprocess(): ConfigurationModel { + this.parser.parse(); + return this.parser.configurationModel; + } +} export class RemoteUserConfiguration extends Disposable { - private readonly _cachedConfiguration: CachedUserConfiguration; - private readonly _configurationFileService: IConfigurationFileService; - private _userConfiguration: UserConfiguration | CachedUserConfiguration; + private readonly _cachedConfiguration: CachedRemoteUserConfiguration; + private readonly _configurationFileService: ConfigurationFileService; + private _userConfiguration: FileServiceBasedRemoteUserConfiguration | CachedRemoteUserConfiguration; private _userConfigurationInitializationPromise: Promise | null = null; private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); @@ -37,15 +76,15 @@ export class RemoteUserConfiguration extends Disposable { constructor( remoteAuthority: string, configurationCache: IConfigurationCache, - configurationFileService: IConfigurationFileService, + configurationFileService: ConfigurationFileService, remoteAgentService: IRemoteAgentService ) { super(); this._configurationFileService = configurationFileService; - this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, configurationCache); + this._userConfiguration = this._cachedConfiguration = new CachedRemoteUserConfiguration(remoteAuthority, configurationCache); remoteAgentService.getEnvironment().then(async environment => { if (environment) { - const userConfiguration = this._register(new UserConfiguration(environment.settingsPath, REMOTE_MACHINE_SCOPES, this._configurationFileService)); + const userConfiguration = this._register(new FileServiceBasedRemoteUserConfiguration(environment.settingsPath, REMOTE_MACHINE_SCOPES, this._configurationFileService)); this._register(userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel))); this._userConfigurationInitializationPromise = userConfiguration.initialize(); const configurationModel = await this._userConfigurationInitializationPromise; @@ -57,7 +96,7 @@ export class RemoteUserConfiguration extends Disposable { } async initialize(): Promise { - if (this._userConfiguration instanceof UserConfiguration) { + if (this._userConfiguration instanceof FileServiceBasedRemoteUserConfiguration) { return this._userConfiguration.initialize(); } @@ -90,7 +129,7 @@ export class RemoteUserConfiguration extends Disposable { } } -export class UserConfiguration extends Disposable { +class FileServiceBasedRemoteUserConfiguration extends Disposable { private readonly parser: ConfigurationModelParser; private readonly reloadConfigurationScheduler: RunOnceScheduler; @@ -103,7 +142,7 @@ export class UserConfiguration extends Disposable { constructor( private readonly configurationResource: URI, private readonly scopes: ConfigurationScope[] | undefined, - private readonly configurationFileService: IConfigurationFileService + private readonly configurationFileService: ConfigurationFileService ) { super(); @@ -138,11 +177,7 @@ export class UserConfiguration extends Disposable { async initialize(): Promise { const exists = await this.configurationFileService.exists(this.configurationResource); this.onResourceExists(exists); - const configuraitonModel = await this.reload(); - if (!this.configurationFileService.isWatching) { - this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted(configuraitonModel)); - } - return configuraitonModel; + return this.reload(); } async reload(): Promise { @@ -160,14 +195,6 @@ export class UserConfiguration extends Disposable { return this.parser.configurationModel; } - private async onWatchStarted(currentModel: ConfigurationModel): Promise { - const configuraitonModel = await this.reload(); - const { added, removed, updated } = compare(currentModel, configuraitonModel); - if (added.length || removed.length || updated.length) { - this._onDidChangeConfiguration.fire(configuraitonModel); - } - } - private async handleFileEvents(event: FileChangesEvent): Promise { const events = event.changes; @@ -202,7 +229,7 @@ export class UserConfiguration extends Disposable { } } -class CachedUserConfiguration extends Disposable { +class CachedRemoteUserConfiguration extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; @@ -252,7 +279,7 @@ class CachedUserConfiguration extends Disposable { export class WorkspaceConfiguration extends Disposable { - private readonly _configurationFileService: IConfigurationFileService; + private readonly _configurationFileService: ConfigurationFileService; private readonly _cachedConfiguration: CachedWorkspaceConfiguration; private _workspaceConfiguration: IWorkspaceConfiguration; private _workspaceConfigurationChangeDisposable: IDisposable = Disposable.None; @@ -266,7 +293,7 @@ export class WorkspaceConfiguration extends Disposable { constructor( configurationCache: IConfigurationCache, - configurationFileService: IConfigurationFileService + configurationFileService: ConfigurationFileService ) { super(); this._configurationFileService = configurationFileService; @@ -369,7 +396,7 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork protected readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - constructor(private configurationFileService: IConfigurationFileService) { + constructor(private configurationFileService: ConfigurationFileService) { super(); this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(''); @@ -378,10 +405,6 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork this._register(configurationFileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); this.workspaceConfigWatcher = this._register(this.watchWorkspaceConfigurationFile()); - - if (!this.configurationFileService.isWatching) { - this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted()); - } } get workspaceIdentifier(): IWorkspaceIdentifier | null { @@ -426,18 +449,6 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork return this.getWorkspaceSettings(); } - private async onWatchStarted(): Promise { - if (this.workspaceIdentifier) { - const currentModel = this.getConfigurationModel(); - await this.load(this.workspaceIdentifier); - const newModel = this.getConfigurationModel(); - const { added, removed, updated } = compare(currentModel, newModel); - if (added.length || removed.length || updated.length) { - this._onDidChange.fire(); - } - } - } - private consolidate(): void { this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel); } @@ -546,7 +557,7 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC protected readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, private configurationFileService: IConfigurationFileService) { + constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, private configurationFileService: ConfigurationFileService) { super(); this.configurationNames = [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY]; @@ -557,9 +568,6 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC this.changeEventTriggerScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); this._register(configurationFileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); - if (!this.configurationFileService.isWatching) { - this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted()); - } } async loadConfiguration(): Promise { @@ -611,15 +619,6 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC this._cache = this._folderSettingsModelParser.configurationModel.merge(...this._standAloneConfigurations); } - private async onWatchStarted(): Promise { - const currentModel = this._cache; - const newModel = await this.loadConfiguration(); - const { added, removed, updated } = compare(currentModel, newModel); - if (added.length || removed.length || updated.length) { - this._onDidChange.fire(); - } - } - private handleWorkspaceFileEvents(event: FileChangesEvent): void { const events = event.changes; let affectedByChanges = false; @@ -672,7 +671,7 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati readonly onDidChange: Event = this._onDidChange.event; private configurationModel: ConfigurationModel; - private readonly key: Thenable; + private readonly key: ConfigurationKey; constructor( folder: URI, @@ -680,14 +679,13 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati private readonly configurationCache: IConfigurationCache ) { super(); - this.key = createSHA1(join(folder.path, configFolderRelativePath)).then(key => ({ type: 'folder', key })); + this.key = { type: 'folder', key: hash(join(folder.path, configFolderRelativePath)).toString(16) }; this.configurationModel = new ConfigurationModel(); } async loadConfiguration(): Promise { try { - const key = await this.key; - const contents = await this.configurationCache.read(key); + const contents = await this.configurationCache.read(this.key); const parsed: IConfigurationModel = JSON.parse(contents.toString()); this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides); } catch (e) { @@ -696,11 +694,10 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati } async updateConfiguration(configurationModel: ConfigurationModel): Promise { - const key = await this.key; if (configurationModel.keys.length) { - await this.configurationCache.write(key, JSON.stringify(configurationModel.toJSON())); + await this.configurationCache.write(this.key, JSON.stringify(configurationModel.toJSON())); } else { - await this.configurationCache.remove(key); + await this.configurationCache.remove(this.key); } } @@ -727,7 +724,7 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat readonly workspaceFolder: IWorkspaceFolder, configFolderRelativePath: string, private readonly workbenchState: WorkbenchState, - configurationFileService: IConfigurationFileService, + configurationFileService: ConfigurationFileService, configurationCache: IConfigurationCache ) { super(); diff --git a/src/vs/workbench/services/configuration/browser/configurationCache.ts b/src/vs/workbench/services/configuration/browser/configurationCache.ts new file mode 100644 index 0000000000..4089b4d5cb --- /dev/null +++ b/src/vs/workbench/services/configuration/browser/configurationCache.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 { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/configuration/common/configuration'; + +export class ConfigurationCache implements IConfigurationCache { + + constructor() { + } + + async read(key: ConfigurationKey): Promise { + return ''; + } + + async write(key: ConfigurationKey, content: string): Promise { + } + + async remove(key: ConfigurationKey): Promise { + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 77f59f88bb..f74b2766b1 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -11,11 +11,10 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Queue, Barrier } from 'vs/base/common/async'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { isLinux } from 'vs/base/common/platform'; import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; -import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, IConfigurationFileService, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; @@ -28,6 +27,8 @@ import { localize } from 'vs/nls'; import { isEqual, dirname } from 'vs/base/common/resources'; import { mark } from 'vs/base/common/performance'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService { @@ -37,14 +38,16 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private completeWorkspaceBarrier: Barrier; private readonly configurationCache: IConfigurationCache; private _configuration: Configuration; + private initialized: boolean = false; private defaultConfiguration: DefaultConfigurationModel; - private localUserConfiguration: UserConfiguration | null = null; + private localUserConfiguration: UserConfiguration; private remoteUserConfiguration: RemoteUserConfiguration | null = null; private workspaceConfiguration: WorkspaceConfiguration; private cachedFolderConfigs: ResourceMap; - private workspaceEditingQueue: Queue; + private readonly configurationFileService: ConfigurationFileService; + protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -64,21 +67,23 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private cyclicDependency = new Promise(resolve => this.cyclicDependencyReady = resolve); constructor( - { userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource?: URI, remoteAuthority?: string, configurationCache: IConfigurationCache }, - private readonly configurationFileService: IConfigurationFileService, - remoteAgentService: IRemoteAgentService, + { remoteAuthority, configurationCache }: { remoteAuthority?: string, configurationCache: IConfigurationCache }, + environmentService: IWorkbenchEnvironmentService, + fileService: IFileService, + remoteAgentService: IRemoteAgentService ) { super(); this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = new DefaultConfigurationModel(); this.configurationCache = configurationCache; - if (userSettingsResource) { - this.localUserConfiguration = this._register(new UserConfiguration(userSettingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, configurationFileService)); - this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); - } + this.configurationFileService = new ConfigurationFileService(fileService); + this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); + this.cachedFolderConfigs = new ResourceMap(); + this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService)); + this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { - this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, configurationFileService, remoteAgentService)); + this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, this.configurationFileService, remoteAgentService)); this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration))); } this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, this.configurationFileService)); @@ -225,13 +230,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private contains(resources: URI[], toCheck: URI): boolean { - return resources.some(resource => { - if (isLinux) { - return resource.toString() === toCheck.toString(); - } - - return resource.toString().toLowerCase() === toCheck.toString().toLowerCase(); - }); + return resources.some(resource => isEqual(resource, toCheck)); } // Workspace Configuration Service Impl @@ -420,7 +419,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private initializeUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> { - return Promise.all([this.localUserConfiguration ? this.localUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel()), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())]) + return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())]) .then(([local, remote]) => ({ local, remote })); } @@ -429,7 +428,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private reloadLocalUserConfiguration(key?: string): Promise { - return this.localUserConfiguration ? this.localUserConfiguration.reload() : Promise.resolve(new ConfigurationModel()); + return this.localUserConfiguration.reload(); } private reloadRemoeUserConfiguration(key?: string): Promise { @@ -466,11 +465,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic const currentConfiguration = this._configuration; this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.workspace); - if (currentConfiguration) { + if (this.initialized) { const changedKeys = this._configuration.compare(currentConfiguration); this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE); } else { this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE))); + this.initialized = true; } }); } @@ -489,7 +489,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private onDefaultConfigurationChanged(keys: string[]): void { this.defaultConfiguration = new DefaultConfigurationModel(); this.registerConfigurationSchemas(); - if (this.workspace && this._configuration) { + if (this.workspace) { this._configuration.updateDefaultConfiguration(this.defaultConfiguration); if (this.remoteUserConfiguration) { this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reprocess()); @@ -545,14 +545,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private onRemoteUserConfigurationChanged(userConfiguration: ConfigurationModel): void { - if (this._configuration) { - const keys = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration); - this.triggerConfigurationChange(keys, ConfigurationTarget.USER); - } + const keys = this._configuration.compareAndUpdateRemoteUserConfiguration(userConfiguration); + this.triggerConfigurationChange(keys, ConfigurationTarget.USER); } private onWorkspaceConfigurationChanged(): Promise { - if (this.workspace && this.workspace.configuration && this._configuration) { + if (this.workspace && this.workspace.configuration) { const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration()); let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration); const changes = this.compareFolders(this.workspace.folders, configuredFolders); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index 543506e415..ece67feab0 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -5,8 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; -import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; export const FOLDER_CONFIG_FOLDER_NAME = '.azuredatastudio'; @@ -42,24 +41,11 @@ export interface IConfigurationCache { } -export interface IConfigurationFileService { - fileService: IFileService | null; - readonly onFileChanges: Event; - readonly isWatching: boolean; - readonly whenWatchingStarted: Promise; - whenProviderRegistered(scheme: string): Promise; - watch(resource: URI): IDisposable; - exists(resource: URI): Promise; - readFile(resource: URI): Promise; -} +export class ConfigurationFileService { -export class ConfigurationFileService implements IConfigurationFileService { - - constructor(public fileService: IFileService) { } + constructor(private readonly fileService: IFileService) { } get onFileChanges() { return this.fileService.onFileChanges; } - readonly whenWatchingStarted: Promise = Promise.resolve(); - readonly isWatching: boolean = true; whenProviderRegistered(scheme: string): Promise { if (this.fileService.canHandleResource(URI.from({ scheme }))) { diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 73725d8b0e..404feb75ef 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -528,7 +528,7 @@ export class ConfigurationEditingService { private getConfigurationFileResource(target: EditableConfigurationTarget, config: IConfigurationValue, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { - return URI.file(this.environmentService.appSettingsPath); + return this.environmentService.settingsResource; } if (target === EditableConfigurationTarget.USER_REMOTE) { return this.remoteSettingsResource; diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index c897ba7570..122041d4dd 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -39,10 +39,11 @@ export class JSONEditingService implements IJSONEditingService { return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(resource, value, save))); // queue up writes to prevent race conditions } - private doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise { - return this.resolveAndValidate(resource, save) - .then(reference => this.writeToBuffer(reference.object.textEditorModel, value) - .then(() => reference.dispose())); + private async doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise { + const reference = await this.resolveAndValidate(resource, save); + await this.writeToBuffer(reference.object.textEditorModel, value); + + reference.dispose(); } private async writeToBuffer(model: ITextModel, value: IJSONValue): Promise { @@ -97,21 +98,21 @@ export class JSONEditingService implements IJSONEditingService { return parseErrors.length > 0; } - private resolveAndValidate(resource: URI, checkDirty: boolean): Promise> { - return this.resolveModelReference(resource) - .then(reference => { - const model = reference.object.textEditorModel; + private async resolveAndValidate(resource: URI, checkDirty: boolean): Promise> { + const reference = await this.resolveModelReference(resource); - if (this.hasParseErrors(model)) { - return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); - } + const model = reference.object.textEditorModel; - // Target cannot be dirty if not writing into buffer - if (checkDirty && this.textFileService.isDirty(resource)) { - return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); - } - return reference; - }); + if (this.hasParseErrors(model)) { + return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); + } + + // Target cannot be dirty if not writing into buffer + if (checkDirty && this.textFileService.isDirty(resource)) { + return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); + } + + return reference; } private reject(code: JSONEditingErrorCode): Promise { diff --git a/src/vs/workbench/services/configuration/node/configurationCache.ts b/src/vs/workbench/services/configuration/node/configurationCache.ts index f98034591c..cd79394a9f 100644 --- a/src/vs/workbench/services/configuration/node/configurationCache.ts +++ b/src/vs/workbench/services/configuration/node/configurationCache.ts @@ -7,12 +7,13 @@ import * as pfs from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { join } from 'vs/base/common/path'; import { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/configuration/common/configuration'; +import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService'; export class ConfigurationCache implements IConfigurationCache { private readonly cachedConfigurations: Map = new Map(); - constructor(private readonly environmentService: IEnvironmentService) { + constructor(private readonly environmentService: IWorkbenchEnvironmentService) { } read(key: ConfigurationKey): Promise { diff --git a/src/vs/workbench/services/configuration/node/configurationFileService.ts b/src/vs/workbench/services/configuration/node/configurationFileService.ts deleted file mode 100644 index e5ef5300e9..0000000000 --- a/src/vs/workbench/services/configuration/node/configurationFileService.ts +++ /dev/null @@ -1,79 +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 pfs from 'vs/base/node/pfs'; -import { IConfigurationFileService, ConfigurationFileService as FileServiceBasedConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration'; -import { URI } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; -import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; - -export class ConfigurationFileService extends Disposable implements IConfigurationFileService { - - private _fileServiceBasedConfigurationFileService: FileServiceBasedConfigurationFileService | null = null; - private _fileServiceBasedConfigurationFileServiceCallback: (fileServiceBasedConfigurationFileService: FileServiceBasedConfigurationFileService) => void; - private _whenFileServiceBasedConfigurationFileServiceAvailable: Promise = new Promise((c) => this._fileServiceBasedConfigurationFileServiceCallback = c); - private _watchResources: { resource: URI, disposable: { disposable: IDisposable | null } }[] = []; - readonly whenWatchingStarted: Promise = this._whenFileServiceBasedConfigurationFileServiceAvailable.then(() => undefined); - - private readonly _onFileChanges: Emitter = this._register(new Emitter()); - readonly onFileChanges: Event = this._onFileChanges.event; - - get isWatching(): boolean { - return this._fileServiceBasedConfigurationFileService ? this._fileServiceBasedConfigurationFileService.isWatching : false; - } - - watch(resource: URI): IDisposable { - if (this._fileServiceBasedConfigurationFileService) { - return this._fileServiceBasedConfigurationFileService.watch(resource); - } - const disposable: { disposable: IDisposable | null } = { disposable: null }; - this._watchResources.push({ resource, disposable }); - return toDisposable(() => { - if (disposable.disposable) { - disposable.disposable.dispose(); - } - }); - } - - whenProviderRegistered(scheme: string): Promise { - if (scheme === Schemas.file) { - return Promise.resolve(); - } - return this._whenFileServiceBasedConfigurationFileServiceAvailable - .then(fileServiceBasedConfigurationFileService => fileServiceBasedConfigurationFileService.whenProviderRegistered(scheme)); - } - - exists(resource: URI): Promise { - return this._fileServiceBasedConfigurationFileService ? this._fileServiceBasedConfigurationFileService.exists(resource) : pfs.exists(resource.fsPath); - } - - async readFile(resource: URI): Promise { - if (this._fileServiceBasedConfigurationFileService) { - return this._fileServiceBasedConfigurationFileService.readFile(resource); - } else { - const contents = await pfs.readFile(resource.fsPath); - return contents.toString(); - } - } - - private _fileService: IFileService | null; - get fileService(): IFileService | null { - return this._fileService; - } - - set fileService(fileService: IFileService | null) { - if (fileService && !this._fileServiceBasedConfigurationFileService) { - this._fileServiceBasedConfigurationFileService = new FileServiceBasedConfigurationFileService(fileService); - this._fileService = fileService; - this._register(this._fileServiceBasedConfigurationFileService.onFileChanges(e => this._onFileChanges.fire(e))); - for (const { resource, disposable } of this._watchResources) { - disposable.disposable = this._fileServiceBasedConfigurationFileService.watch(resource); - } - this._fileServiceBasedConfigurationFileServiceCallback(this._fileServiceBasedConfigurationFileService); - } - } -} diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts index a711f8b6b8..66fb94acc4 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationEditingService.test.ts @@ -10,10 +10,9 @@ import * as path from 'vs/base/common/path'; import * as fs from 'fs'; import * as json from 'vs/base/common/json'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; import * as uuid from 'vs/base/common/uuid'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -33,21 +32,25 @@ import { URI } from 'vs/base/common/uri'; import { createHash } from 'crypto'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { Schemas } from 'vs/base/common/network'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { IFileService } from 'vs/platform/files/common/files'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; -import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; +import { KeybindingsEditingService, IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; +import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; -class SettingsTestEnvironmentService extends EnvironmentService { +class TestEnvironmentService extends WorkbenchEnvironmentService { - constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) { - super(args, _execPath); + constructor(private _appSettingsHome: URI) { + super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); } - get appSettingsPath(): string { return this.customAppSettingsHome; } + get appSettingsHome() { return this._appSettingsHome; } + } suite('ConfigurationEditingService', () => { @@ -90,9 +93,8 @@ suite('ConfigurationEditingService', () => { const id = uuid.generateUuid(); parentDir = path.join(os.tmpdir(), 'vsctests', id); workspaceDir = path.join(parentDir, 'workspaceconfig', id); - globalSettingsFile = path.join(workspaceDir, 'config.json'); - // {{SQL CARBON EDIT}} - workspaceSettingsDir = path.join(workspaceDir, '.azuredatastudio'); + globalSettingsFile = path.join(workspaceDir, 'settings.json'); + workspaceSettingsDir = path.join(workspaceDir, '.azuredatastudio'); // {{SQL CARBON EDIT}} .vscode to .azuredatastudio return await mkdirp(workspaceSettingsDir, 493); } @@ -102,17 +104,20 @@ suite('ConfigurationEditingService', () => { clearServices(); instantiationService = workbenchInstantiationService(); - const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const environmentService = new TestEnvironmentService(URI.file(workspaceDir)); instantiationService.stub(IEnvironmentService, environmentService); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const fileService = new FileService(new NullLogService()); + const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService)); + instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, new ConfigurationFileService(), remoteAgentService); + const workspaceService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { instantiationService.stub(IConfigurationService, workspaceService); - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - instantiationService.stub(IFileService, fileService); + instantiationService.stub(IKeybindingEditingService, instantiationService.createInstance(KeybindingsEditingService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); instantiationService.stub(ICommandService, CommandService); @@ -122,7 +127,10 @@ suite('ConfigurationEditingService', () => { teardown(() => { clearServices(); - return clearWorkspace(); + if (workspaceDir) { + return rimraf(workspaceDir, RimRafMode.MOVE); + } + return undefined; }); function clearServices(): void { @@ -135,16 +143,6 @@ suite('ConfigurationEditingService', () => { } } - function clearWorkspace(): Promise { - return new Promise((c, e) => { - if (parentDir) { - rimraf(parentDir, RimRafMode.MOVE).then(c, c); - } else { - c(undefined); - } - }).then(() => parentDir = null!); - } - test('errors cases - invalid key', () => { return testObject.writeConfiguration(EditableConfigurationTarget.WORKSPACE, { key: 'unknown.key', value: 'value' }) .then(() => assert.fail('Should fail with ERROR_UNKNOWN_KEY'), @@ -187,7 +185,7 @@ suite('ConfigurationEditingService', () => { test('do not notify error', () => { instantiationService.stub(ITextFileService, 'isDirty', true); const target = sinon.stub(); - instantiationService.stub(INotificationService, { prompt: target, _serviceBrand: null, notify: null!, error: null!, info: null!, warn: null! }); + instantiationService.stub(INotificationService, { prompt: target, _serviceBrand: null!, notify: null!, error: null!, info: null!, warn: null!, status: null! }); return testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }) .then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'), (error: ConfigurationEditingError) => { diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index 6c0b3a728d..9f2ea8b1dc 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -10,8 +10,7 @@ import * as path from 'vs/base/common/path'; import * as os from 'os'; import { URI } from 'vs/base/common/uri'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { parseArgs } from 'vs/platform/environment/node/argv'; import * as pfs from 'vs/base/node/pfs'; import * as uuid from 'vs/base/common/uuid'; @@ -37,21 +36,27 @@ import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; -import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IConfigurationCache } from 'vs/workbench/services/configuration/common/configuration'; +// import { VSBuffer } from 'vs/base/common/buffer'; +import { SignService } from 'vs/platform/sign/browser/signService'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; +import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -class SettingsTestEnvironmentService extends EnvironmentService { +class TestEnvironmentService extends WorkbenchEnvironmentService { - constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome: string) { - super(args, _execPath); + constructor(private _appSettingsHome: URI) { + super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); } - get appSettingsPath(): string { return this.customAppSettingsHome; } + get appSettingsHome() { return this._appSettingsHome; } + } function setUpFolderWorkspace(folderName: string): Promise<{ parentDir: string, folderDir: string }> { @@ -93,6 +98,14 @@ function setUpWorkspace(folders: string[]): Promise<{ parentDir: string, configP suite('WorkspaceContextService - Folder', () => { + setup(() => { + // {{SQL CARBON EDIT}} - Remove tests + }); + + teardown(() => { + // {{SQL CARBON EDIT}} - Remove tests + }); + test('getWorkspace()', () => { // {{SQL CARBON EDIT}} - Remove tests assert.equal(0, 0); @@ -145,15 +158,14 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { remoteSettingsFile = path.join(parentDir, 'remote-settings.json'); instantiationService = workbenchInstantiationService(); - const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const environmentService = new TestEnvironmentService(URI.file(parentDir)); const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: URI.file(remoteSettingsFile).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }) })); const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService)); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve() }; - testObject = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache, remoteAuthority }, configurationFileService, remoteAgentService); + testObject = new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); @@ -264,7 +276,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { // } // }); // }); - // fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); + // await instantiationService.get(IFileService).writeFile(URI.file(remoteSettingsFile), VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); // return promise; // }); diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 0d2e5c32b6..7da69f1ed6 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -19,8 +19,9 @@ import { AbstractVariableResolverService } from 'vs/workbench/services/configura import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; -import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ConfiguredInput, IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService { @@ -304,4 +305,6 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi ) { super(environmentService.configuration.userEnv, editorService, environmentService, configurationService, commandService, workspaceContextService, quickInputService); } -} \ No newline at end of file +} + +registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index ee89a336a3..9280fb48bf 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -35,7 +35,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe private _context: IVariableResolveContext, private _envVariables: IProcessEnvironment ) { - if (isWindows) { + if (isWindows && _envVariables) { this._envVariables = Object.create(null); Object.keys(_envVariables).forEach(key => { this._envVariables[key.toLowerCase()] = _envVariables[key]; diff --git a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts index f3a66bf4e4..f6a0172da7 100644 --- a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; +import { IAction, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import * as dom from 'vs/base/browser/dom'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -66,7 +66,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService _serviceBrand: any; private _onDidContextMenu = this._register(new Emitter()); - get onDidContextMenu(): Event { return this._onDidContextMenu.event; } + readonly onDidContextMenu: Event = this._onDidContextMenu.event; constructor( @INotificationService private readonly notificationService: INotificationService, @@ -115,7 +115,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService } } - private createMenu(delegate: IContextMenuDelegate, entries: Array, onHide: () => void): IContextMenuItem[] { + private createMenu(delegate: IContextMenuDelegate, entries: ReadonlyArray, onHide: () => void): IContextMenuItem[] { const actionRunner = delegate.actionRunner || new ActionRunner(); return entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide)); @@ -173,13 +173,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService } private async runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IContextMenuEvent): Promise { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); + this.telemetryService.publicLog2('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); const context = delegate.getActionsContext ? delegate.getActionsContext(event) : event; diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index dec166f697..45fc694a4e 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { IDecorationsService, IDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IDecorationData } from './decorations'; import { TernarySearchTree } from 'vs/base/common/map'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { isThenable } from 'vs/base/common/async'; import { LinkedList } from 'vs/base/common/linkedList'; import { createStyleSheet, createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom'; @@ -95,20 +95,20 @@ class DecorationRule { } } -class DecorationStyles { +class DecorationStyles extends Disposable { - private readonly _disposables: IDisposable[]; private readonly _styleElement = createStyleSheet(); private readonly _decorationRules = new Map(); constructor( private _themeService: IThemeService, ) { - this._disposables = [this._themeService.onThemeChange(this._onThemeChange, this)]; + super(); + this._register(this._themeService.onThemeChange(this._onThemeChange, this)); } dispose(): void { - dispose(this._disposables); + super.dispose(); const parent = this._styleElement.parentElement; if (parent) { @@ -334,15 +334,14 @@ class DecorationProviderWrapper { } } -export class FileDecorationsService implements IDecorationsService { +export class FileDecorationsService extends Disposable implements IDecorationsService { _serviceBrand: any; private readonly _data = new LinkedList(); - private readonly _onDidChangeDecorationsDelayed = new Emitter(); - private readonly _onDidChangeDecorations = new Emitter(); + private readonly _onDidChangeDecorationsDelayed = this._register(new Emitter()); + private readonly _onDidChangeDecorations = this._register(new Emitter()); private readonly _decorationStyles: DecorationStyles; - private readonly _disposables: IDisposable[]; readonly onDidChangeDecorations: Event = Event.any( this._onDidChangeDecorations.event, @@ -356,27 +355,17 @@ export class FileDecorationsService implements IDecorationsService { constructor( @IThemeService themeService: IThemeService ) { - this._decorationStyles = new DecorationStyles(themeService); + super(); + this._decorationStyles = this._register(new DecorationStyles(themeService)); // every so many events we check if there are // css styles that we don't need anymore let count = 0; - let reg = this.onDidChangeDecorations(() => { + this._register(this.onDidChangeDecorations(() => { if (++count % 17 === 0) { this._decorationStyles.cleanUp(this._data.iterator()); } - }); - - this._disposables = [ - reg, - this._decorationStyles - ]; - } - - dispose(): void { - dispose(this._disposables); - dispose(this._onDidChangeDecorations); - dispose(this._onDidChangeDecorationsDelayed); + })); } registerDecorationsProvider(provider: IDecorationsProvider): IDisposable { diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index e9821d1030..b1302e920d 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -86,14 +86,16 @@ export class FileDialogService implements IFileDialogService { private shouldUseSimplified(schema: string): boolean { const setting = this.configurationService.getValue('files.simpleDialog.enable'); + return (schema !== Schemas.file) || (setting === true); } private ensureFileSchema(schema: string): string[] { - return schema !== Schemas.file ? [schema, Schemas.file] : [schema]; + // Don't allow untitled schema through. + return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]); } - pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { + async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -103,21 +105,23 @@ export class FileDialogService implements IFileDialogService { if (this.shouldUseSimplified(schema)) { const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder'); const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => { - if (uri) { - return (this.fileService.resolve(uri)).then(stat => { - const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri }; - return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); - }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }); + + if (uri) { + const stat = await this.fileService.resolve(uri); + + const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri }; + return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); } - pickFileAndOpen(options: IPickAndOpenOptions): Promise { + async pickFileAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -127,18 +131,19 @@ export class FileDialogService implements IFileDialogService { if (this.shouldUseSimplified(schema)) { const title = nls.localize('openFile.title', 'Open File'); const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => { - if (uri) { - return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }); + if (uri) { + return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); } - pickFolderAndOpen(options: IPickAndOpenOptions): Promise { + async pickFolderAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -148,18 +153,19 @@ export class FileDialogService implements IFileDialogService { if (this.shouldUseSimplified(schema)) { const title = nls.localize('openFolder.title', 'Open Folder'); const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => { - if (uri) { - return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }); + if (uri) { + return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); } - pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { + async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -170,18 +176,39 @@ export class FileDialogService implements IFileDialogService { const title = nls.localize('openWorkspace.title', 'Open Workspace'); const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }]; const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }).then(uri => { - if (uri) { - return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }); + if (uri) { + return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); } + async pickFileToSave(options: ISaveDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (this.shouldUseSimplified(schema)) { + if (!options.availableFileSystems) { + options.availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + } + + options.title = nls.localize('saveFileAs.title', 'Save As'); + return this.saveRemoteResource(options); + } + + const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)); + if (result) { + return URI.file(result); + } + + return undefined; // {{SQL CARBON EDIT}} strict-null-check + } + private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { + options.defaultUri = options.defaultUri ? URI.file(options.defaultUri.path) : undefined; return { defaultPath: options.defaultUri && options.defaultUri.fsPath, buttonLabel: options.saveLabel, @@ -190,33 +217,34 @@ export class FileDialogService implements IFileDialogService { }; } - showSaveDialog(options: ISaveDialogOptions): Promise { + async showSaveDialog(options: ISaveDialogOptions): Promise { const schema = this.getFileSystemSchema(options); if (this.shouldUseSimplified(schema)) { if (!options.availableFileSystems) { options.availableFileSystems = [schema]; // by default only allow saving in the own file system } + return this.saveRemoteResource(options); } - return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { - if (result) { - return URI.file(result); - } + const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)); + if (result) { + return URI.file(result); + } - return undefined; - }); + return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check } - showOpenDialog(options: IOpenDialogOptions): Promise { + async showOpenDialog(options: IOpenDialogOptions): Promise { const schema = this.getFileSystemSchema(options); if (this.shouldUseSimplified(schema)) { if (!options.availableFileSystems) { options.availableFileSystems = [schema]; // by default only allow loading in the own file system } - return this.pickRemoteResource(options).then(uri => { - return uri ? [uri] : undefined; - }); + + const uri = await this.pickRemoteResource(options); + + return uri ? [uri] : undefined; } const defaultUri = options.defaultUri; @@ -243,27 +271,30 @@ export class FileDialogService implements IFileDialogService { newOptions.properties!.push('multiSelections'); } - return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); + const result = await this.windowService.showOpenDialog(newOptions); + + return result ? result.map(URI.file) : undefined; } private pickRemoteResource(options: IOpenDialogOptions): Promise { const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showOpenDialog(options); } private saveRemoteResource(options: ISaveDialogOptions): Promise { const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showSaveDialog(options); } - private getSchemeFilterForWindow() { + private getSchemeFilterForWindow(): string { return !this.environmentService.configuration.remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME; } private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string { - return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow(); + return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow(); } - } function isUntitledWorkspace(path: URI, environmentService: IWorkbenchEnvironmentService): boolean { diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 604bb31337..4289f43321 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -23,11 +23,15 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; -import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { OpenLocalFileCommand, OpenLocalFileFolderCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { isValidBasename } from 'vs/base/common/extpath'; import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; +import { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; interface FileQuickPickItem extends IQuickPickItem { uri: URI; @@ -60,6 +64,12 @@ export class RemoteFileDialog { private badPath: string | undefined; private remoteAgentEnvironment: IRemoteAgentEnvironment | null; private separator: string; + private onBusyChangeEmitter = new Emitter(); + private updatingPromise: CancelablePromise | undefined; + + protected disposables: IDisposable[] = [ + this.onBusyChangeEmitter + ]; constructor( @IFileService private readonly fileService: IFileService, @@ -79,8 +89,19 @@ export class RemoteFileDialog { this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService); } + set busy(busy: boolean) { + if (this.filePickBox.busy !== busy) { + this.filePickBox.busy = busy; + this.onBusyChangeEmitter.fire(busy); + } + } + + get busy(): boolean { + return this.filePickBox.busy; + } + public async showOpenDialog(options: IOpenDialogOptions = {}): Promise { - this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); + this.scheme = this.getScheme(options.availableFileSystems); this.userHome = await this.getUserHome(); const newOptions = await this.getOptions(options); if (!newOptions) { @@ -91,7 +112,7 @@ export class RemoteFileDialog { } public async showSaveDialog(options: ISaveDialogOptions): Promise { - this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); + this.scheme = this.getScheme(options.availableFileSystems); this.userHome = await this.getUserHome(); this.requiresTrailing = true; const newOptions = await this.getOptions(options, true); @@ -110,9 +131,13 @@ export class RemoteFileDialog { } private getOptions(options: ISaveDialogOptions | IOpenDialogOptions, isSave: boolean = false): IOpenDialogOptions | undefined { - let defaultUri = options.defaultUri; - const filename = (defaultUri && isSave && (resources.dirname(defaultUri).path === '/')) ? resources.basename(defaultUri) : undefined; - if (!defaultUri || filename) { + let defaultUri: URI | undefined = undefined; + let filename: string | undefined = undefined; + if (options.defaultUri) { + defaultUri = (this.scheme === options.defaultUri.scheme) ? options.defaultUri : undefined; + filename = isSave ? resources.basename(options.defaultUri) : undefined; + } + if (!defaultUri) { defaultUri = this.userHome; if (filename) { defaultUri = resources.joinPath(defaultUri, filename); @@ -132,8 +157,8 @@ export class RemoteFileDialog { return resources.toLocalResource(URI.from({ scheme: this.scheme, path }), this.scheme === Schemas.file ? undefined : this.remoteAuthority); } - private getScheme(defaultUri: URI | undefined, available: string[] | undefined): string { - return defaultUri ? defaultUri.scheme : (available ? available[0] : Schemas.file); + private getScheme(available: string[] | undefined): string { + return available ? available[0] : Schemas.file; } private async getRemoteAgentEnvironment(): Promise { @@ -185,15 +210,20 @@ export class RemoteFileDialog { return new Promise(async (resolve) => { this.filePickBox = this.quickInputService.createQuickPick(); - this.filePickBox.busy = true; + this.busy = true; this.filePickBox.matchOnLabel = false; this.filePickBox.autoFocusOnList = false; this.filePickBox.ignoreFocusOut = true; this.filePickBox.ok = true; - if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) { + if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) { this.filePickBox.customButton = true; this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); - const action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction; + let action; + if (isSave) { + action = SaveLocalFileCommand; + } else { + action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderCommand : OpenLocalFileCommand) : OpenLocalFolderCommand; + } const keybinding = this.keybindingService.lookupKeybinding(action.ID); if (keybinding) { const label = keybinding.getLabel(); @@ -203,9 +233,9 @@ export class RemoteFileDialog { } } - let isResolving = false; + let isResolving: number = 0; let isAcceptHandled = false; - this.currentFolder = homedir; + this.currentFolder = resources.dirname(homedir); this.userEnteredPathSegment = ''; this.autoCompletePathSegment = ''; @@ -215,24 +245,27 @@ export class RemoteFileDialog { this.filePickBox.items = []; function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) { + if (uri) { + uri = resources.removeTrailingPathSeparator(uri); + } resolve(uri); dialog.contextKey.set(false); dialog.filePickBox.dispose(); + dispose(dialog.disposables); } this.filePickBox.onDidCustom(() => { - if (isAcceptHandled || this.filePickBox.busy) { + if (isAcceptHandled || this.busy) { return undefined; // {{SQL CARBON EDIT}} @todo anthonydresser return to return; when we do strict null checks } isAcceptHandled = true; - isResolving = true; + isResolving++; if (this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) { this.options.availableFileSystems.shift(); } - this.options.defaultUri = undefined; this.filePickBox.hide(); - if (this.requiresTrailing) { + if (isSave) { return this.fileDialogService.showSaveDialog(this.options).then(result => { doResolve(this, result); }); @@ -243,38 +276,56 @@ export class RemoteFileDialog { } }); - this.filePickBox.onDidAccept(_ => { - if (isAcceptHandled || this.filePickBox.busy) { + function handleAccept(dialog: RemoteFileDialog) { + if (dialog.busy) { + // Save the accept until the file picker is not busy. + dialog.onBusyChangeEmitter.event((busy: boolean) => { + if (!busy) { + handleAccept(dialog); + } + }); + return; + } else if (isAcceptHandled) { return; } isAcceptHandled = true; - isResolving = true; - this.onDidAccept().then(resolveValue => { + isResolving++; + dialog.onDidAccept().then(resolveValue => { if (resolveValue) { - this.filePickBox.hide(); - doResolve(this, resolveValue); - } else if (this.hidden) { - doResolve(this, undefined); + dialog.filePickBox.hide(); + doResolve(dialog, resolveValue); + } else if (dialog.hidden) { + doResolve(dialog, undefined); } else { - isResolving = false; + isResolving--; isAcceptHandled = false; } }); + } + + this.filePickBox.onDidAccept(_ => { + handleAccept(this); }); + this.filePickBox.onDidChangeActive(i => { isAcceptHandled = false; // update input box to match the first selected item - if ((i.length === 1) && this.isChangeFromUser()) { + if ((i.length === 1) && this.isSelectionChangeFromUser()) { this.filePickBox.validationMessage = undefined; - this.setAutoComplete(this.constructFullUserPath(), this.userEnteredPathSegment, i[0], true); + const userPath = this.constructFullUserPath(); + if (!equalsIgnoreCase(this.filePickBox.value.substring(0, userPath.length), userPath)) { + this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; + this.insertText(userPath, userPath); + } + this.setAutoComplete(userPath, this.userEnteredPathSegment, i[0], true); } }); this.filePickBox.onDidChangeValue(async value => { try { // onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything - if (this.isChangeFromUser()) { + if (this.isValueChangeFromUser()) { // If the user has just entered more bad path, don't change anything if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) { this.filePickBox.validationMessage = undefined; @@ -288,6 +339,7 @@ export class RemoteFileDialog { } } else { this.filePickBox.activeItems = []; + this.userEnteredPathSegment = ''; } } } catch { @@ -296,7 +348,7 @@ export class RemoteFileDialog { }); this.filePickBox.onDidHide(() => { this.hidden = true; - if (!isResolving) { + if (isResolving === 0) { doResolve(this, undefined); } }); @@ -309,7 +361,7 @@ export class RemoteFileDialog { } else { this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; } - this.filePickBox.busy = false; + this.busy = false; }); } @@ -317,16 +369,27 @@ export class RemoteFileDialog { return this.badPath && (value.length > this.badPath.length) && equalsIgnoreCase(value.substring(0, this.badPath.length), this.badPath); } - private isChangeFromUser(): boolean { - if (equalsIgnoreCase(this.filePickBox.value, this.pathAppend(this.currentFolder, this.userEnteredPathSegment + this.autoCompletePathSegment)) - && (this.activeItem === (this.filePickBox.activeItems ? this.filePickBox.activeItems[0] : undefined))) { + private isValueChangeFromUser(): boolean { + if (equalsIgnoreCase(this.filePickBox.value, this.pathAppend(this.currentFolder, this.userEnteredPathSegment + this.autoCompletePathSegment))) { + return false; + } + return true; + } + + private isSelectionChangeFromUser(): boolean { + if (this.activeItem === (this.filePickBox.activeItems ? this.filePickBox.activeItems[0] : undefined)) { return false; } return true; } private constructFullUserPath(): string { - return this.pathAppend(this.currentFolder, this.userEnteredPathSegment); + const currentFolderPath = this.pathFromUri(this.currentFolder); + if (equalsIgnoreCase(this.filePickBox.value.substr(0, this.userEnteredPathSegment.length), this.userEnteredPathSegment) && equalsIgnoreCase(this.filePickBox.value.substr(0, currentFolderPath.length), currentFolderPath)) { + return currentFolderPath; + } else { + return this.pathAppend(this.currentFolder, this.userEnteredPathSegment); + } } private filePickBoxValue(): URI { @@ -340,14 +403,19 @@ export class RemoteFileDialog { const relativePath = resources.relativePath(currentDisplayUri, directUri); const isSameRoot = (this.filePickBox.value.length > 1 && currentPath.length > 1) ? equalsIgnoreCase(this.filePickBox.value.substr(0, 2), currentPath.substr(0, 2)) : false; if (relativePath && isSameRoot) { - return resources.joinPath(this.currentFolder, relativePath); + let path = resources.joinPath(this.currentFolder, relativePath); + const directBasename = resources.basename(directUri); + if ((directBasename === '.') || (directBasename === '..')) { + path = this.remoteUriFrom(this.pathAppend(path, directBasename)); + } + return resources.hasTrailingPathSeparator(directUri) ? resources.addTrailingPathSeparator(path) : path; } else { return directUri; } } private async onDidAccept(): Promise { - this.filePickBox.busy = true; + this.busy = true; if (this.filePickBox.activeItems.length === 1) { const item = this.filePickBox.selectedItems[0]; if (item.isFolder) { @@ -357,10 +425,9 @@ export class RemoteFileDialog { // When possible, cause the update to happen by modifying the input box. // This allows all input box updates to happen first, and uses the same code path as the user typing. const newPath = this.pathFromUri(item.uri); - if (startsWithIgnoreCase(newPath, this.filePickBox.value)) { - const insertValue = newPath.substring(this.filePickBox.value.length, newPath.length); - this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; - this.insertText(newPath, insertValue); + if (startsWithIgnoreCase(newPath, this.filePickBox.value) && (equalsIgnoreCase(item.label, resources.basename(item.uri)))) { + this.filePickBox.valueSelection = [this.pathFromUri(this.currentFolder).length, this.filePickBox.value.length]; + this.insertText(newPath, item.label); } else if ((item.label === '..') && startsWithIgnoreCase(this.filePickBox.value, newPath)) { this.filePickBox.valueSelection = [newPath.length, this.filePickBox.value.length]; this.insertText(newPath, ''); @@ -368,11 +435,13 @@ export class RemoteFileDialog { await this.updateItems(item.uri, true); } } + this.filePickBox.busy = false; return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check } } else { // If the items have updated, don't try to resolve if ((await this.tryUpdateItems(this.filePickBox.value, this.filePickBoxValue())) !== UpdateResult.NotUpdated) { + this.filePickBox.busy = false; return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check } } @@ -388,10 +457,10 @@ export class RemoteFileDialog { resolveValue = this.addPostfix(resolveValue); } if (await this.validate(resolveValue)) { - this.filePickBox.busy = false; + this.busy = false; return resolveValue; } - this.filePickBox.busy = false; + this.busy = false; return undefined; } @@ -429,7 +498,7 @@ export class RemoteFileDialog { } catch (e) { // do nothing } - if (statWithoutTrailing && statWithoutTrailing.isDirectory && (resources.basename(valueUri) !== '.')) { + if (statWithoutTrailing && statWithoutTrailing.isDirectory) { await this.updateItems(inputUriDirname, false, resources.basename(valueUri)); this.badPath = undefined; return UpdateResult.Updated; @@ -447,11 +516,13 @@ export class RemoteFileDialog { const userPath = this.constructFullUserPath(); if (equalsIgnoreCase(userPath, value.substring(0, userPath.length))) { let hasMatch = false; - for (let i = 0; i < this.filePickBox.items.length; i++) { - const item = this.filePickBox.items[i]; - if (this.setAutoComplete(value, inputBasename, item)) { - hasMatch = true; - break; + if (inputBasename.length > this.userEnteredPathSegment.length) { + for (let i = 0; i < this.filePickBox.items.length; i++) { + const item = this.filePickBox.items[i]; + if (this.setAutoComplete(value, inputBasename, item)) { + hasMatch = true; + break; + } } } if (!hasMatch) { @@ -460,17 +531,13 @@ export class RemoteFileDialog { this.filePickBox.activeItems = []; } } else { - if (!equalsIgnoreCase(inputBasename, resources.basename(this.currentFolder))) { - this.userEnteredPathSegment = inputBasename; - } else { - this.userEnteredPathSegment = ''; - } + this.userEnteredPathSegment = inputBasename; this.autoCompletePathSegment = ''; } } private setAutoComplete(startingValue: string, startingBasename: string, quickPickItem: FileQuickPickItem, force: boolean = false): boolean { - if (this.filePickBox.busy) { + if (this.busy) { // We're in the middle of something else. Doing an auto complete now can result jumbled or incorrect autocompletes. this.userEnteredPathSegment = startingBasename; this.autoCompletePathSegment = ''; @@ -480,7 +547,7 @@ export class RemoteFileDialog { // Either force the autocomplete, or the old value should be one smaller than the new value and match the new value. if (itemBasename === '..') { // Don't match on the up directory item ever. - this.userEnteredPathSegment = startingValue; + this.userEnteredPathSegment = ''; this.autoCompletePathSegment = ''; this.activeItem = quickPickItem; if (force) { @@ -494,9 +561,6 @@ export class RemoteFileDialog { // Changing the active items will trigger the onDidActiveItemsChanged. Clear the autocomplete first, then set it after. this.autoCompletePathSegment = ''; this.filePickBox.activeItems = [quickPickItem]; - this.autoCompletePathSegment = this.trimTrailingSlash(itemBasename.substr(startingBasename.length)); - this.insertText(startingValue + this.autoCompletePathSegment, this.autoCompletePathSegment); - this.filePickBox.valueSelection = [startingValue.length, this.filePickBox.value.length]; return true; } else if (force && (!equalsIgnoreCase(quickPickItem.label, (this.userEnteredPathSegment + this.autoCompletePathSegment)))) { this.userEnteredPathSegment = ''; @@ -641,30 +705,46 @@ export class RemoteFileDialog { } private async updateItems(newFolder: URI, force: boolean = false, trailing?: string) { - this.filePickBox.busy = true; + this.busy = true; this.userEnteredPathSegment = trailing ? trailing : ''; this.autoCompletePathSegment = ''; - const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true); + const newValue = trailing ? this.pathAppend(newFolder, trailing) : this.pathFromUri(newFolder, true); this.currentFolder = resources.addTrailingPathSeparator(newFolder, this.separator); - return this.createItems(this.currentFolder).then(items => { - this.filePickBox.items = items; - if (this.allowFolderSelection) { - this.filePickBox.activeItems = []; - } - // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory. - if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) { - this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; - this.insertText(newValue, newValue); - } - if (force && trailing) { - // Keep the cursor position in front of the save as name. - this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - trailing.length]; - } else if (!trailing) { - // If there is trailing, we don't move the cursor. If there is no trailing, cursor goes at the end. - this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; - } - this.filePickBox.busy = false; + + const updatingPromise = createCancelablePromise(async token => { + return this.createItems(this.currentFolder, token).then(items => { + if (token.isCancellationRequested) { + this.busy = false; + return; + } + + this.filePickBox.items = items; + if (this.allowFolderSelection) { + this.filePickBox.activeItems = []; + } + // the user might have continued typing while we were updating. Only update the input box if it doesn't matche directory. + if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) { + this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; + this.insertText(newValue, newValue); + } + if (force && trailing) { + // Keep the cursor position in front of the save as name. + this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - trailing.length]; + } else if (!trailing) { + // If there is trailing, we don't move the cursor. If there is no trailing, cursor goes at the end. + this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; + } + this.busy = false; + this.updatingPromise = undefined; + }); }); + + if (this.updatingPromise !== undefined) { + this.updatingPromise.cancel(); + } + this.updatingPromise = updatingPromise; + + return updatingPromise; } private pathFromUri(uri: URI, endWithSeparator: boolean = false): string { @@ -683,7 +763,7 @@ export class RemoteFileDialog { private pathAppend(uri: URI, additional: string): string { if ((additional === '..') || (additional === '.')) { const basePath = this.pathFromUri(uri); - return basePath + (this.endsWithSlash(basePath) ? '' : this.separator) + additional; + return basePath + this.separator + additional; } else { return this.pathFromUri(resources.joinPath(uri, additional)); } @@ -716,14 +796,14 @@ export class RemoteFileDialog { return null; } - private async createItems(currentFolder: URI): Promise { + private async createItems(currentFolder: URI, token: CancellationToken): Promise { const result: FileQuickPickItem[] = []; const backDir = this.createBackItem(currentFolder); try { const folder = await this.fileService.resolve(currentFolder); const fileNames = folder.children ? folder.children.map(child => child.name) : []; - const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder))); + const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder, token))); for (let item of items) { if (item) { result.push(item); @@ -733,6 +813,9 @@ export class RemoteFileDialog { // ignore console.log(e); } + if (token.isCancellationRequested) { + return []; + } const sorted = result.sort((i1, i2) => { if (i1.isFolder !== i2.isFolder) { return i1.isFolder ? -1 : 1; @@ -763,7 +846,10 @@ export class RemoteFileDialog { return true; } - private async createItem(filename: string, parent: URI): Promise { + private async createItem(filename: string, parent: URI, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return undefined; + } let fullPath = resources.joinPath(parent, filename); try { const stat = await this.fileService.resolve(fullPath); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index e7a83af351..dcadeaf6f9 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -21,13 +21,13 @@ import { localize } from 'vs/nls'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IResourceEditor, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types'; +import { withNullAsUndefined } from 'vs/base/common/types'; //{{SQL CARBON EDIT}} import { convertEditorInput, getFileMode } from 'sql/workbench/common/customInputConverter'; @@ -44,16 +44,16 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region events private readonly _onDidActiveEditorChange: Emitter = this._register(new Emitter()); - get onDidActiveEditorChange(): Event { return this._onDidActiveEditorChange.event; } + readonly onDidActiveEditorChange: Event = this._onDidActiveEditorChange.event; private readonly _onDidVisibleEditorsChange: Emitter = this._register(new Emitter()); - get onDidVisibleEditorsChange(): Event { return this._onDidVisibleEditorsChange.event; } + readonly onDidVisibleEditorsChange: Event = this._onDidVisibleEditorsChange.event; private readonly _onDidCloseEditor: Emitter = this._register(new Emitter()); - get onDidCloseEditor(): Event { return this._onDidCloseEditor.event; } + readonly onDidCloseEditor: Event = this._onDidCloseEditor.event; private readonly _onDidOpenEditorFail: Emitter = this._register(new Emitter()); - get onDidOpenEditorFail(): Event { return this._onDidOpenEditorFail.event; } + readonly onDidOpenEditorFail: Event = this._onDidOpenEditorFail.event; //#endregion @@ -122,29 +122,29 @@ export class EditorService extends Disposable implements EditorServiceImpl { } private registerGroupListeners(group: IEditorGroupView): void { - const groupDisposeables: IDisposable[] = []; + const groupDisposables = new DisposableStore(); - groupDisposeables.push(group.onDidGroupChange(e => { + groupDisposables.add(group.onDidGroupChange(e => { if (e.kind === GroupChangeKind.EDITOR_ACTIVE) { this.handleActiveEditorChange(group); this._onDidVisibleEditorsChange.fire(); } })); - groupDisposeables.push(group.onDidCloseEditor(event => { + groupDisposables.add(group.onDidCloseEditor(event => { this._onDidCloseEditor.fire(event); })); - groupDisposeables.push(group.onWillOpenEditor(event => { + groupDisposables.add(group.onWillOpenEditor(event => { this.onGroupWillOpenEditor(group, event); })); - groupDisposeables.push(group.onDidOpenEditorFail(editor => { + groupDisposables.add(group.onDidOpenEditorFail(editor => { this._onDidOpenEditorFail.fire({ editor, groupId: group.id }); })); Event.once(group.onWillDispose)(() => { - dispose(groupDisposeables); + dispose(groupDisposables); }); } @@ -506,7 +506,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } // Side by Side Support - const resourceSideBySideInput = input; + const resourceSideBySideInput = input as IResourceSideBySideInput; if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) { const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource, forceFile: resourceSideBySideInput.forceFile }); const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource, forceFile: resourceSideBySideInput.forceFile }); @@ -521,17 +521,17 @@ export class EditorService extends Disposable implements EditorServiceImpl { } // Diff Editor Support - const resourceDiffInput = input; + const resourceDiffInput = input as IResourceDiffInput; if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { const leftInput = this.createInput({ resource: resourceDiffInput.leftResource, forceFile: resourceDiffInput.forceFile }); const rightInput = this.createInput({ resource: resourceDiffInput.rightResource, forceFile: resourceDiffInput.forceFile }); const label = resourceDiffInput.label || localize('compareLabels', "{0} ↔ {1}", this.toDiffLabel(leftInput), this.toDiffLabel(rightInput)); - return new DiffEditorInput(label, withUndefinedAsNull(resourceDiffInput.description), leftInput, rightInput); + return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); } // Untitled file support - const untitledInput = input; + const untitledInput = input as IUntitledResourceInput; if (untitledInput.forceUntitled || !untitledInput.resource || (untitledInput.resource && untitledInput.resource.scheme === Schemas.untitled)) { // {{SQL CARBON EDIT}} // Need to get mode for QueryEditor and Notebook @@ -540,7 +540,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } // Resource Editor Support - const resourceInput = input; + const resourceInput = input as IResourceInput; if (resourceInput.resource instanceof URI) { let label = resourceInput.label; if (!label && resourceInput.resource.scheme !== Schemas.data) { @@ -658,18 +658,17 @@ export class DelegatingEditorService extends EditorService { this.editorOpenHandler = handler; } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { if (!this.editorOpenHandler) { return super.doOpenEditor(group, editor, options); } - return this.editorOpenHandler(group, editor, options).then(control => { - if (control) { - return control; // the opening was handled, so return early - } + const control = await this.editorOpenHandler(group, editor, options); + if (control) { + return control; // the opening was handled, so return early + } - return super.doOpenEditor(group, editor, options); - }); + return super.doOpenEditor(group, editor, options); } } diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index d646c19c6d..13e7407833 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -232,12 +232,12 @@ export interface IEditorGroupsService { /** * Returns the size of a group. */ - getSize(group: IEditorGroup | GroupIdentifier): number; + getSize(group: IEditorGroup | GroupIdentifier): { width: number, height: number }; /** * Sets the size of a group. */ - setSize(group: IEditorGroup | GroupIdentifier, size: number): void; + setSize(group: IEditorGroup | GroupIdentifier, size: { width: number, height: number }): void; /** * Arrange all groups according to the provided arrangement. @@ -411,7 +411,7 @@ export interface IEditorGroup { /** * Returns the editor at a specific index of the group. */ - getEditor(index: number): IEditorInput | null; + getEditor(index: number): IEditorInput | undefined; /** * Get all editors that are currently opened in the group optionally diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index 987bd9c093..a60a343135 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -8,7 +8,7 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; import { URI } from 'vs/base/common/uri'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorInput, EditorOptions, IFileEditorInput, IEditorInput } from 'vs/workbench/common/editor'; -import { workbenchInstantiationService, TestStorageService, NullFileSystemProvider } from 'vs/workbench/test/workbenchTestServices'; +import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { EditorService, DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService'; @@ -31,6 +31,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; +import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider'; // {{SQL CARBON EDIT}} - Disable editor tests /* @@ -84,7 +85,7 @@ class FileServiceProvider extends Disposable { */suite('EditorService', () => {/* function registerTestEditorInput(): void { - Registry.as(Extensions.Editors).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), new SyncDescriptor(TestEditorInput)); + Registry.as(Extensions.Editors).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), [new SyncDescriptor(TestEditorInput)]); } registerTestEditorInput(); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts new file mode 100644 index 0000000000..81b9f0a070 --- /dev/null +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -0,0 +1,154 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWindowConfiguration, IPath, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; +import { IEnvironmentService, IExtensionHostDebugParams, IDebugParams, BACKUPS } from 'vs/platform/environment/common/environment'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ExportData } from 'vs/base/common/performance'; +import { LogLevel } from 'vs/platform/log/common/log'; +import { joinPath } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; + +export class BrowserWindowConfiguration implements IWindowConfiguration { + + _: any[]; + + machineId: string; + windowId: number; + logLevel: LogLevel; + + mainPid: number; + + appRoot: string; + execPath: string; + isInitialStartup?: boolean; + + userEnv: IProcessEnvironment; + nodeCachedDataDir?: string; + + backupPath?: string; + + workspace?: IWorkspaceIdentifier; + folderUri?: ISingleFolderWorkspaceIdentifier; + + remoteAuthority: string; + + zoomLevel?: number; + fullscreen?: boolean; + maximized?: boolean; + highContrast?: boolean; + frameless?: boolean; + accessibilitySupport?: boolean; + partsSplashPath?: string; + + perfStartTime?: number; + perfAppReady?: number; + perfWindowLoadTime?: number; + perfEntries: ExportData; + + filesToOpenOrCreate?: IPath[]; + filesToDiff?: IPath[]; + filesToWait?: IPathsToWaitFor; + termProgram?: string; +} + +export interface IBrowserWindowConfiguration { + workspaceId: string; + remoteAuthority?: string; + webviewEndpoint?: string; +} + +export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { + _serviceBrand: ServiceIdentifier; + + readonly configuration: IWindowConfiguration = new BrowserWindowConfiguration(); + + constructor(configuration: IBrowserWindowConfiguration) { + this.args = { _: [] }; + this.appRoot = '/web/'; + this.appNameLong = 'Visual Studio Code - Web'; + + this.configuration.remoteAuthority = configuration.remoteAuthority; + this.userRoamingDataHome = URI.file('/User').with({ scheme: Schemas.userData }); + this.settingsResource = joinPath(this.userRoamingDataHome, 'settings.json'); + this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json'); + this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); + this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json'); + this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS); + this.configuration.backupWorkspaceResource = joinPath(this.backupHome, configuration.workspaceId); + + this.logsPath = '/web/logs'; + + this.debugExtensionHost = { + port: null, + break: false + }; + + this.webviewEndpoint = configuration.webviewEndpoint; + this.untitledWorkspacesHome = URI.from({ scheme: Schemas.untitled, path: 'Workspaces' }); + } + + untitledWorkspacesHome: URI; + extensionTestsLocationURI?: URI; + args: any; + execPath: string; + cliPath: string; + appRoot: string; + userHome: string; + userDataPath: string; + appNameLong: string; + appQuality?: string; + appSettingsHome: URI; + userRoamingDataHome: URI; + settingsResource: URI; + keybindingsResource: URI; + keyboardLayoutResource: URI; + localeResource: URI; + machineSettingsHome: URI; + machineSettingsResource: URI; + globalStorageHome: string; + workspaceStorageHome: string; + backupHome: URI; + backupWorkspacesPath: string; + workspacesHome: string; + isExtensionDevelopment: boolean; + disableExtensions: boolean | string[]; + builtinExtensionsPath: string; + extensionsPath: string; + extensionDevelopmentLocationURI?: URI[]; + extensionTestsPath?: string; + debugExtensionHost: IExtensionHostDebugParams; + debugSearch: IDebugParams; + logExtensionHostCommunication: boolean; + isBuilt: boolean; + wait: boolean; + status: boolean; + log?: string; + logsPath: string; + verbose: boolean; + skipGettingStarted: boolean; + skipReleaseNotes: boolean; + skipAddToRecentlyOpened: boolean; + mainIPCHandle: string; + sharedIPCHandle: string; + nodeCachedDataDir?: string; + installSourcePath: string; + disableUpdates: boolean; + disableCrashReporter: boolean; + driverHandle?: string; + driverVerbose: boolean; + webviewEndpoint?: string; + + get webviewResourceRoot(): string { + return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource{{resource}}' : 'vscode-resource:{{resource}}'; + } + + get webviewCspSource(): string { + return this.webviewEndpoint ? this.webviewEndpoint : 'vscode-resource:'; + } +} diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 1cb5c43394..0ad1ed8659 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -3,14 +3,15 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export const IWorkbenchEnvironmentService = createDecorator('environmentService'); export interface IWorkbenchEnvironmentService extends IEnvironmentService { - _serviceBrand: any; + + _serviceBrand: ServiceIdentifier; readonly configuration: IWindowConfiguration; } diff --git a/src/vs/workbench/services/environment/node/environmentService.ts b/src/vs/workbench/services/environment/node/environmentService.ts index 6f49558a4a..d40325d451 100644 --- a/src/vs/workbench/services/environment/node/environmentService.ts +++ b/src/vs/workbench/services/environment/node/environmentService.ts @@ -6,6 +6,10 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { memoize } from 'vs/base/common/decorators'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService { @@ -16,9 +20,13 @@ export class WorkbenchEnvironmentService extends EnvironmentService implements I execPath: string ) { super(_configuration, execPath); + this._configuration.backupWorkspaceResource = this._configuration.backupPath ? toBackupWorkspaceResource(this._configuration.backupPath, this) : undefined; } get configuration(): IWindowConfiguration { return this._configuration; } + + @memoize + get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } } diff --git a/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts index 8db1f8421d..4755baa5ee 100644 --- a/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts @@ -14,8 +14,9 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IProductService } from 'vs/platform/product/common/product'; const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled'; const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled'; @@ -36,6 +37,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IProductService private readonly productService: IProductService, ) { super(); this.storageManger = this._register(new StorageManager(storageService)); @@ -137,7 +139,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension return disabledExtensions.some(id => areSameExtensions({ id }, extension.identifier)); } if (this.environmentService.configuration.remoteAuthority) { - const server = isUIExtension(extension.manifest, this.configurationService) ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer; + const server = isUIExtension(extension.manifest, this.productService, this.configurationService) ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer; return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; } return false; diff --git a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index aa1f950c78..a35380c732 100644 --- a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -15,6 +15,7 @@ import { IExtensionContributions, ExtensionType, IExtension } from 'vs/platform/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ProductService } from 'vs/platform/product/node/productService'; function storageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -38,7 +39,9 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IWorkbenchEnvironmentService) || instantiationService.stub(IWorkbenchEnvironmentService, { configuration: Object.create(null) } as IWorkbenchEnvironmentService), instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, { onDidInstallExtension: new Emitter().event, onDidUninstallExtension: new Emitter().event } as IExtensionManagementService), - instantiationService.get(IConfigurationService), instantiationService.get(IExtensionManagementServerService)); + instantiationService.get(IConfigurationService), instantiationService.get(IExtensionManagementServerService), + new ProductService() + ); } public reset(): void { diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts new file mode 100644 index 0000000000..de1dcd86b5 --- /dev/null +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IProductService } from 'vs/platform/product/common/product'; +import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; +import { browserWebSocketFactory } from 'vs/platform/remote/browser/browserWebSocketFactory'; +import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; +import { RemoteExtensionHostClient, IInitDataProvider } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; + +export class ExtensionService extends AbstractExtensionService implements IExtensionService { + + private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @INotificationService notificationService: INotificationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ITelemetryService telemetryService: ITelemetryService, + @IExtensionEnablementService extensionEnablementService: IExtensionEnablementService, + @IFileService fileService: IFileService, + @IProductService productService: IProductService, + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + ) { + super( + instantiationService, + notificationService, + environmentService, + telemetryService, + extensionEnablementService, + fileService, + productService, + ); + + this._remoteExtensionsEnvironmentData = null; + this._initialize(); + } + + private _createProvider(remoteAuthority: string): IInitDataProvider { + return { + remoteAuthority: remoteAuthority, + getInitData: () => { + return this.whenInstalledExtensionsRegistered().then(() => { + return this._remoteExtensionsEnvironmentData!; + }); + } + }; + } + + protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { + const result: ExtensionHostProcessManager[] = []; + + const remoteAgentConnection = this._remoteAgentService.getConnection()!; + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), browserWebSocketFactory); + const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + result.push(remoteExtHostProcessManager); + + return result; + } + + protected async _scanAndHandleExtensions(): Promise { + // fetch the remote environment + const remoteEnv = (await this._remoteAgentService.getEnvironment())!; + + // enable or disable proposed API per extension + this._checkEnableProposedApi(remoteEnv.extensions); + + // remove disabled extensions + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension)); + + // save for remote extension's init data + this._remoteExtensionsEnvironmentData = remoteEnv; + + // this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); + const result = this._registry.deltaExtensions(remoteEnv.extensions, []); + if (result.removedDueToLooping.length > 0) { + this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); + } + + this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); + } + + public _onExtensionHostExit(code: number): void { + console.log(`vscode:exit`, code); + // ipc.send('vscode:exit', code); + } +} + +registerSingleton(IExtensionService, ExtensionService); diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts new file mode 100644 index 0000000000..18657a637b --- /dev/null +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -0,0 +1,498 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { Barrier } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as perf from 'vs/base/common/performance'; +import { isEqualOrParent } from 'vs/base/common/resources'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; +import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; +import { IProductService } from 'vs/platform/product/common/product'; + +const hasOwnProperty = Object.hasOwnProperty; +const NO_OP_VOID_PROMISE = Promise.resolve(undefined); + +export abstract class AbstractExtensionService extends Disposable implements IExtensionService { + + public _serviceBrand: any; + + protected readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); + public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; + + protected readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; + + protected readonly _onDidChangeExtensions: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensions: Event = this._onDidChangeExtensions.event; + + protected readonly _onWillActivateByEvent = this._register(new Emitter()); + public readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; + + protected readonly _onDidChangeResponsiveChange = this._register(new Emitter()); + public readonly onDidChangeResponsiveChange: Event = this._onDidChangeResponsiveChange.event; + + protected readonly _registry: ExtensionDescriptionRegistry; + private readonly _installedExtensionsReady: Barrier; + protected readonly _isDev: boolean; + private readonly _extensionsMessages: Map; + protected readonly _allRequestedActivateEvents = new Set(); + private readonly _proposedApiController: ProposedApiController; + private readonly _isExtensionDevHost: boolean; + protected readonly _isExtensionDevTestFromCli: boolean; + + // --- Members used per extension host process + protected _extensionHostProcessManagers: ExtensionHostProcessManager[]; + protected _extensionHostActiveExtensions: Map; + private _extensionHostProcessActivationTimes: Map; + private _extensionHostExtensionRuntimeErrors: Map; + + constructor( + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @INotificationService protected readonly _notificationService: INotificationService, + @IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService, + @ITelemetryService protected readonly _telemetryService: ITelemetryService, + @IExtensionEnablementService protected readonly _extensionEnablementService: IExtensionEnablementService, + @IFileService protected readonly _fileService: IFileService, + @IProductService protected readonly _productService: IProductService + ) { + super(); + + // help the file service to activate providers by activating extensions by file system event + this._register(this._fileService.onWillActivateFileSystemProvider(e => { + e.join(this.activateByEvent(`onFileSystem:${e.scheme}`)); + })); + + this._registry = new ExtensionDescriptionRegistry([]); + this._installedExtensionsReady = new Barrier(); + this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; + this._extensionsMessages = new Map(); + this._proposedApiController = new ProposedApiController(this._environmentService, this._productService); + + this._extensionHostProcessManagers = []; + this._extensionHostActiveExtensions = new Map(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); + + const devOpts = parseExtensionDevOptions(this._environmentService); + this._isExtensionDevHost = devOpts.isExtensionDevHost; + this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + } + + protected async _initialize(): Promise { + perf.mark('willLoadExtensions'); + this._startExtensionHostProcess(true, []); + this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions')); + await this._scanAndHandleExtensions(); + this._releaseBarrier(); + } + + private _releaseBarrier(): void { + perf.mark('extensionHostReady'); + this._installedExtensionsReady.open(); + this._onDidRegisterExtensions.fire(undefined); + this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier)); + } + + private _stopExtensionHostProcess(): void { + let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; + this._extensionHostActiveExtensions.forEach((value) => { + previouslyActivatedExtensionIds.push(value); + }); + + for (const manager of this._extensionHostProcessManagers) { + manager.dispose(); + } + this._extensionHostProcessManagers = []; + this._extensionHostActiveExtensions = new Map(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); + + if (previouslyActivatedExtensionIds.length > 0) { + this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds); + } + } + + private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void { + this._stopExtensionHostProcess(); + + const processManagers = this._createExtensionHosts(isInitialStart, initialActivationEvents); + processManagers.forEach((processManager) => { + processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)); + processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); + this._extensionHostProcessManagers.push(processManager); + }); + } + + private _onExtensionHostCrashOrExit(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + + // Unexpected termination + if (!this._isExtensionDevHost) { + this._onExtensionHostCrashed(extensionHost, code, signal); + return; + } + + this._onExtensionHostExit(code); + } + + protected _onExtensionHostCrashed(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + this._stopExtensionHostProcess(); + } + + //#region IExtensionService + + public canAddExtension(extension: IExtensionDescription): boolean { + return false; + } + + public canRemoveExtension(extension: IExtensionDescription): boolean { + return false; + } + + public restartExtensionHost(): void { + this._stopExtensionHostProcess(); + this._startExtensionHostProcess(false, Array.from(this._allRequestedActivateEvents.keys())); + } + + public startExtensionHost(): void { + this._startExtensionHostProcess(false, Array.from(this._allRequestedActivateEvents.keys())); + } + + public stopExtensionHost(): void { + this._stopExtensionHostProcess(); + } + + public activateByEvent(activationEvent: string): Promise { + if (this._installedExtensionsReady.isOpen()) { + // Extensions have been scanned and interpreted + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents.add(activationEvent); + + if (!this._registry.containsActivationEvent(activationEvent)) { + // There is no extension that is interested in this activation event + return NO_OP_VOID_PROMISE; + } + + return this._activateByEvent(activationEvent); + } else { + // Extensions have not been scanned yet. + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents.add(activationEvent); + + return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); + } + } + + private _activateByEvent(activationEvent: string): Promise { + const result = Promise.all( + this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) + ).then(() => { }); + this._onWillActivateByEvent.fire({ + event: activationEvent, + activation: result + }); + return result; + } + + public whenInstalledExtensionsRegistered(): Promise { + return this._installedExtensionsReady.wait(); + } + + public getExtensions(): Promise { + return this._installedExtensionsReady.wait().then(() => { + return this._registry.getAllExtensionDescriptions(); + }); + } + + public getExtension(id: string): Promise { + return this._installedExtensionsReady.wait().then(() => { + return this._registry.getExtensionDescription(id); + }); + } + + public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { + return this._installedExtensionsReady.wait().then(() => { + const availableExtensions = this._registry.getAllExtensionDescriptions(); + + const result: ExtensionPointContribution[] = []; + for (const desc of availableExtensions) { + if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { + result.push(new ExtensionPointContribution(desc, desc.contributes[extPoint.name])); // {{SQL CARBON EDIT}} strict-null-checks + } + } + + return result; + }); + } + + public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { + let result: { [id: string]: IExtensionsStatus; } = Object.create(null); + if (this._registry) { + const extensions = this._registry.getAllExtensionDescriptions(); + for (const extension of extensions) { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier); + result[extension.identifier.value] = { + messages: this._extensionsMessages.get(extensionKey) || [], + activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey), + runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [], + }; + } + } + return result; + } + + public getInspectPort(): number { + return 0; + } + + //#endregion + + // --- impl + + protected _checkEnableProposedApi(extensions: IExtensionDescription[]): void { + for (let extension of extensions) { + this._proposedApiController.updateEnableProposedApi(extension); + } + } + + private _isExtensionUnderDevelopment(extension: IExtensionDescription): boolean { + if (this._environmentService.isExtensionDevelopment) { + const extDevLocs = this._environmentService.extensionDevelopmentLocationURI; + if (extDevLocs) { + const extLocation = extension.extensionLocation; + for (let p of extDevLocs) { + if (isEqualOrParent(extLocation, p)) { + return true; + } + } + } + } + return false; + } + + protected _isEnabled(extension: IExtensionDescription): boolean { + if (this._isExtensionUnderDevelopment(extension)) { + // Never disable extensions under development + return true; + } + + if (ExtensionIdentifier.equals(extension.identifier, BetterMergeId)) { + // Check if this is the better merge extension which was migrated to a built-in extension + return false; + } + + return this._extensionEnablementService.isEnabled(toExtension(extension)); + } + + protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void { + const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); + for (let extensionDescription of affectedExtensions) { + if (extensionDescription.contributes) { + for (let extPointName in extensionDescription.contributes) { + if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { + affectedExtensionPoints[extPointName] = true; + } + } + } + } + + const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); + const availableExtensions = this._registry.getAllExtensionDescriptions(); + const extensionPoints = ExtensionsRegistry.getExtensionPoints(); + for (const extensionPoint of extensionPoints) { + if (affectedExtensionPoints[extensionPoint.name]) { + AbstractExtensionService._handleExtensionPoint(extensionPoint, availableExtensions, messageHandler); + } + } + } + + private _handleExtensionPointMessage(msg: IMessage) { + const extensionKey = ExtensionIdentifier.toKey(msg.extensionId); + + if (!this._extensionsMessages.has(extensionKey)) { + this._extensionsMessages.set(extensionKey, []); + } + this._extensionsMessages.get(extensionKey)!.push(msg); + + const extension = this._registry.getExtensionDescription(msg.extensionId); + const strMsg = `[${msg.extensionId.value}]: ${msg.message}`; + if (extension && extension.isUnderDevelopment) { + // This message is about the extension currently being developed + this._showMessageToUser(msg.type, strMsg); + } else { + this._logMessageInConsole(msg.type, strMsg); + } + + if (!this._isDev && msg.extensionId) { + const { type, extensionId, extensionPointId, message } = msg; + type ExtensionsMessageClassification = { + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + extensionPointId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + message: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + type ExtensionsMessageEvent = { + type: Severity; + extensionId: string; + extensionPointId: string; + message: string; + }; + this._telemetryService.publicLog2('extensionsMessage', { + type, extensionId: extensionId.value, extensionPointId, message + }); + } + } + + private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { + const users: IExtensionPointUser[] = []; + for (const desc of availableExtensions) { + if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { + users.push({ + description: desc, + value: desc.contributes[extensionPoint.name], // {{SQL CARBON EDIT}} strict-null-checks + collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) + }); + } + } + perf.mark(`willHandleExtensionPoint/${extensionPoint.name}`); + extensionPoint.acceptUsers(users); + perf.mark(`didHandleExtensionPoint/${extensionPoint.name}`); + } + + private _showMessageToUser(severity: Severity, msg: string): void { + if (severity === Severity.Error || severity === Severity.Warning) { + this._notificationService.notify({ severity, message: msg }); + } else { + this._logMessageInConsole(severity, msg); + } + } + + private _logMessageInConsole(severity: Severity, msg: string): void { + if (severity === Severity.Error) { + console.error(msg); + } else if (severity === Severity.Warning) { + console.warn(msg); + } else { + console.log(msg); + } + } + + //#region Called by extension host + + public _logOrShowMessage(severity: Severity, msg: string): void { + if (this._isDev) { + this._showMessageToUser(severity, msg); + } else { + this._logMessageInConsole(severity, msg); + } + } + + public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise { + const results = await Promise.all( + this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent)) + ); + const activated = results.some(e => e); + if (!activated) { + throw new Error(`Unknown extension ${extensionId.value}`); + } + } + + public _onWillActivateExtension(extensionId: ExtensionIdentifier): void { + this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId); + } + + public _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { + this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent)); + this._onDidChangeExtensionsStatus.fire([extensionId]); + } + + public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void { + const extensionKey = ExtensionIdentifier.toKey(extensionId); + if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) { + this._extensionHostExtensionRuntimeErrors.set(extensionKey, []); + } + this._extensionHostExtensionRuntimeErrors.get(extensionKey)!.push(err); + this._onDidChangeExtensionsStatus.fire([extensionId]); + } + + //#endregion + + protected abstract _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[]; + protected abstract _scanAndHandleExtensions(): Promise; + public abstract _onExtensionHostExit(code: number): void; +} + +class ProposedApiController { + + private readonly enableProposedApiFor: string | string[]; + private readonly enableProposedApiForAll: boolean; + private readonly productAllowProposedApi: Set; + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IProductService productService: IProductService + ) { + this.enableProposedApiFor = environmentService.args['enable-proposed-api'] || []; + if (this.enableProposedApiFor.length) { + // Make enabled proposed API be lowercase for case insensitive comparison + if (Array.isArray(this.enableProposedApiFor)) { + this.enableProposedApiFor = this.enableProposedApiFor.map(id => id.toLowerCase()); + } else { + this.enableProposedApiFor = this.enableProposedApiFor.toLowerCase(); + } + } + + this.enableProposedApiForAll = !environmentService.isBuilt || + (!!environmentService.extensionDevelopmentLocationURI && productService.nameLong !== 'Visual Studio Code') || + (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); + + this.productAllowProposedApi = new Set(); + if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { + productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); + } + } + + public updateEnableProposedApi(extension: IExtensionDescription): void { + if (this._allowProposedApiFromProduct(extension.identifier)) { + // fast lane -> proposed api is available to all extensions + // that are listed in product.json-files + extension.enableProposedApi = true; + + } else if (extension.enableProposedApi && !extension.isBuiltin) { + if ( + !this.enableProposedApiForAll && + this.enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0 + ) { + extension.enableProposedApi = false; + console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); + + } else { + // proposed api is available when developing or when an extension was explicitly + // spelled out via a command line argument + console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`); + } + } + } + + private _allowProposedApiFromProduct(id: ExtensionIdentifier): boolean { + return this.productAllowProposedApi.has(ExtensionIdentifier.toKey(id)); + } +} diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index 33f1773a81..fad4872759 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -35,7 +35,7 @@ const NO_OP_VOID_PROMISE = Promise.resolve(undefined); export class ExtensionHostProcessManager extends Disposable { - public readonly onDidCrash: Event<[number, string | null]>; + public readonly onDidExit: Event<[number, string | null]>; private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; @@ -54,6 +54,7 @@ export class ExtensionHostProcessManager extends Disposable { private _resolveAuthorityAttempt: number; constructor( + public readonly isLocal: boolean, extensionHostProcessWorker: IExtensionHostStarter, private readonly _remoteAuthority: string, initialActivationEvents: string[], @@ -66,7 +67,7 @@ export class ExtensionHostProcessManager extends Disposable { this._extensionHostProcessCustomers = []; this._extensionHostProcessWorker = extensionHostProcessWorker; - this.onDidCrash = this._extensionHostProcessWorker.onCrashed; + this.onDidExit = this._extensionHostProcessWorker.onExit; this._extensionHostProcessProxy = this._extensionHostProcessWorker.start()!.then( (protocol) => { return { value: this._createExtensionHostCustomers(protocol) }; @@ -203,7 +204,8 @@ export class ExtensionHostProcessManager extends Disposable { // Check that no named customers are missing // {{SQL CARBON EDIT}} filter out services we don't expose - const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => (MainContext)[key]).filter(v => v !== MainContext.MainThreadDebugService); + const filtered: ProxyIdentifier[] = [MainContext.MainThreadDebugService, MainContext.MainThreadTask]; + const expected: ProxyIdentifier[] = Object.keys(MainContext).map((key) => (MainContext)[key]).filter(v => !filtered.includes(v)); this._extensionHostProcessRPCProtocol.assertRegistered(expected); return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService); diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 663d522d83..b06566e887 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -12,6 +12,7 @@ export interface IExtHostReadyMessage { export interface IExtHostSocketMessage { type: 'VSCODE_EXTHOST_IPC_SOCKET'; initialDataChunk: string; + skipWebSocketFrames: boolean; } export const enum MessageType { diff --git a/src/vs/workbench/services/extensions/common/extensionPoints.ts b/src/vs/workbench/services/extensions/common/extensionPoints.ts new file mode 100644 index 0000000000..f5291468d2 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionPoints.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 { Severity } from 'vs/platform/notification/common/notification'; + +export interface Translations { + [id: string]: string; +} + +export namespace Translations { + export function equals(a: Translations, b: Translations): boolean { + if (a === b) { + return true; + } + let aKeys = Object.keys(a); + let bKeys: Set = new Set(); + for (let key of Object.keys(b)) { + bKeys.add(key); + } + if (aKeys.length !== bKeys.size) { + return false; + } + + for (let key of aKeys) { + if (a[key] !== b[key]) { + return false; + } + bKeys.delete(key); + } + return bKeys.size === 0; + } +} + +export interface ILog { + error(source: string, message: string): void; + warn(source: string, message: string): void; + info(source: string, message: string): void; +} + +export class Logger implements ILog { + + private readonly _messageHandler: (severity: Severity, source: string, message: string) => void; + + constructor( + messageHandler: (severity: Severity, source: string, message: string) => void + ) { + this._messageHandler = messageHandler; + } + + public error(source: string, message: string): void { + this._messageHandler(Severity.Error, source, message); + } + + public warn(source: string, message: string): void { + this._messageHandler(Severity.Warning, source, message); + } + + public info(source: string, message: string): void { + this._messageHandler(Severity.Info, source, message); + } +} diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index a234d6c846..3f66df503d 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -84,7 +84,8 @@ export interface IExtensionHostProfile { } export interface IExtensionHostStarter { - readonly onCrashed: Event<[number, string | null]>; + readonly onExit: Event<[number, string | null]>; + start(): Promise | null; getInspectPort(): number | undefined; dispose(): void; diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index f38eb6b508..8a546aa432 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -206,7 +206,7 @@ export const schema = { type: 'object', properties: { // extensions will fill in - }, + } as { [key: string]: any }, default: {} }, preview: { @@ -341,6 +341,19 @@ export const schema = { pattern: EXTENSION_IDENTIFIER_PATTERN } }, + extensionKind: { + description: nls.localize('extensionKind', "Define the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote."), + type: 'string', + enum: [ + 'ui', + 'workspace' + ], + enumDescriptions: [ + nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), + nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + ], + default: 'workspace' + }, scripts: { type: 'object', properties: { diff --git a/src/vs/workbench/services/extensions/node/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts similarity index 86% rename from src/vs/workbench/services/extensions/node/extensionsUtil.ts rename to src/vs/workbench/services/extensions/common/extensionsUtil.ts index 8fe148d902..9b4699fc75 100644 --- a/src/vs/workbench/services/extensions/node/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -8,9 +8,9 @@ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import product from 'vs/platform/product/node/product'; +import { IProductService } from 'vs/platform/product/common/product'; -export function isUIExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { +export function isUIExtension(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { const uiContributions = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name); const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); const extensionKind = getExtensionKind(manifest, configurationService); @@ -19,7 +19,7 @@ export function isUIExtension(manifest: IExtensionManifest, configurationService case 'workspace': return false; default: { // Tagged as UI extension in product - if (isNonEmptyArray(product.uiExtensions) && product.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { + if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { return true; } // Not an UI extension if it has main diff --git a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts index 1dc53d5a8e..e82f6fb6bf 100644 --- a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { EnablementState, IExtensionEnablementService, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -70,10 +70,10 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.handleURL(URI.revive(JSON.parse(urlToHandleValue)), true); } - this.disposable = combinedDisposable([ + this.disposable = combinedDisposable( urlService.registerHandler(this), toDisposable(() => clearInterval(interval)) - ]); + ); } async handleURL(uri: URI, confirmed?: boolean): Promise { diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts similarity index 84% rename from src/vs/workbench/services/extensions/electron-browser/remoteExtensionHostClient.ts rename to src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index 4ba888e057..f61554294d 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -3,17 +3,13 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcRenderer as ipc } from 'electron'; import { Emitter, Event } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; -import { connectRemoteAgentExtensionHost, IRemoteExtensionHostStartParams, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; +import { connectRemoteAgentExtensionHost, IRemoteExtensionHostStartParams, IConnectionOptions, IWebSocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @@ -28,8 +24,9 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; -import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug'; +import { IProductService } from 'vs/platform/product/common/product'; +import { ISignService } from 'vs/platform/sign/common/sign'; export interface IInitDataProvider { readonly remoteAuthority: string; @@ -38,28 +35,29 @@ export interface IInitDataProvider { export class RemoteExtensionHostClient extends Disposable implements IExtensionHostStarter { - private _onCrashed: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>()); - public readonly onCrashed: Event<[number, string | null]> = this._onCrashed.event; + private _onExit: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>()); + public readonly onExit: Event<[number, string | null]> = this._onExit.event; private _protocol: PersistentProtocol | null; private readonly _isExtensionDevHost: boolean; - private readonly _isExtensionDevTestFromCli: boolean; private _terminating: boolean; constructor( private readonly _allExtensions: Promise, private readonly _initDataProvider: IInitDataProvider, + private readonly _webSocketFactory: IWebSocketFactory, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IWindowService private readonly _windowService: IWindowService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @ILogService private readonly _logService: ILogService, @ILabelService private readonly _labelService: ILabelService, @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService + @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService, + @IProductService private readonly _productService: IProductService, + @ISignService private readonly _signService: ISignService ) { super(); this._protocol = null; @@ -69,20 +67,20 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; - this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; } public start(): Promise { const options: IConnectionOptions = { isBuilt: this._environmentService.isBuilt, - commit: product.commit, - webSocketFactory: nodeWebSocketFactory, + commit: this._productService.commit, + webSocketFactory: this._webSocketFactory, addressProvider: { getAddress: async () => { const { host, port } = await this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority); return { host, port }; } - } + }, + signService: this._signService }; return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolvedAuthority) => { @@ -168,20 +166,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH return; } - // Unexpected termination - if (!this._isExtensionDevHost) { - this._onCrashed.fire([0, null]); - } - - // Expected development extension termination: When the extension host goes down we also shutdown the window - else if (!this._isExtensionDevTestFromCli) { - this._windowService.closeWindow(); - } - - // When CLI testing make sure to exit with proper exit code - else { - ipc.send('vscode:exit', 0); - } + this._onExit.fire([0, null]); } private _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise { @@ -191,26 +176,32 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier); const workspace = this._contextService.getWorkspace(); const r: IInitData = { - commit: product.commit, - version: pkg.version, + commit: this._productService.commit, + version: this._productService.version, parentPid: remoteExtensionHostData.pid, environment: { isExtensionDevelopmentDebug, appRoot: remoteExtensionHostData.appRoot, appSettingsHome: remoteExtensionHostData.appSettingsHome, - appName: product.nameLong, - appUriScheme: product.urlProtocol, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: remoteExtensionHostData.globalStorageHome, - userHome: remoteExtensionHostData.userHome + userHome: remoteExtensionHostData.userHome, + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { configuration: workspace.configuration, id: workspace.id, name: this._labelService.getWorkspaceLabel(workspace) }, + remote: { + isRemote: true, + authority: this._initDataProvider.remoteAuthority + }, resolvedExtensions: resolvedExtensions, hostExtensions: hostExtensions, extensions: remoteExtensionHostData.extensions, @@ -218,7 +209,6 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH logLevel: this._logService.getLevel(), logsLocation: remoteExtensionHostData.extensionHostLogsPath, autoStart: true, - remoteAuthority: this._initDataProvider.remoteAuthority, }; return r; }); diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index 399a45d821..b82afa42ad 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -21,7 +21,8 @@ import pkg from 'vs/platform/product/node/package'; import product from 'vs/platform/product/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, ILog, IRelaxedExtensionDescription, Translations } from 'vs/workbench/services/extensions/node/extensionPoints'; +import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints'; +import { Translations, ILog } from 'vs/workbench/services/extensions/common/extensionPoints'; interface IExtensionCacheData { input: ExtensionScannerInput; @@ -387,26 +388,3 @@ class NullLogger implements ILog { public info(source: string, message: string): void { } } - -export class Logger implements ILog { - - private readonly _messageHandler: (severity: Severity, source: string, message: string) => void; - - constructor( - messageHandler: (severity: Severity, source: string, message: string) => void - ) { - this._messageHandler = messageHandler; - } - - public error(source: string, message: string): void { - this._messageHandler(Severity.Error, source, message); - } - - public warn(source: string, message: string): void { - this._messageHandler(Severity.Warning, source, message); - } - - public info(source: string, message: string): void { - this._messageHandler(Severity.Info, source, message); - } -} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 60355932d6..4b286efbe7 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -5,13 +5,12 @@ import * as nls from 'vs/nls'; import { ChildProcess, fork } from 'child_process'; -import { ipcRenderer as ipc } from 'electron'; import { Server, Socket, createServer } from 'net'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import pkg from 'vs/platform/product/node/package'; @@ -42,10 +41,10 @@ import { isEqualOrParent } from 'vs/base/common/resources'; export class ExtensionHostProcessWorker implements IExtensionHostStarter { - private readonly _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); - public readonly onCrashed: Event<[number, string]> = this._onCrashed.event; + private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>(); + public readonly onExit: Event<[number, string]> = this._onExit.event; - private readonly _toDispose: IDisposable[]; + private readonly _toDispose = new DisposableStore(); private readonly _isExtensionDevHost: boolean; private readonly _isExtensionDevDebug: boolean; @@ -58,7 +57,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Resources, in order they get acquired/created when .start() is called: private _namedPipeServer: Server | null; - private _inspectPort: number; + private _inspectPort: number | null; private _extensionHostProcess: ChildProcess | null; private _extensionHostConnection: Socket | null; private _messageProtocol: Promise | null; @@ -92,16 +91,15 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostConnection = null; this._messageProtocol = null; - this._toDispose = []; - this._toDispose.push(this._onCrashed); - this._toDispose.push(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); - this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate())); - this._toDispose.push(this._extensionHostDebugService.onClose(event => { + this._toDispose.add(this._onExit); + this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); + this._toDispose.add(this._lifecycleService.onShutdown(reason => this.terminate())); + this._toDispose.add(this._extensionHostDebugService.onClose(event => { if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) { this._windowService.closeWindow(); } })); - this._toDispose.push(this._extensionHostDebugService.onReload(event => { + this._toDispose.add(this._extensionHostDebugService.onReload(event => { if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) { this._windowService.reloadWindow(); } @@ -109,7 +107,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const globalExitListener = () => this.terminate(); process.once('exit', globalExitListener); - this._toDispose.push(toDisposable(() => { + this._toDispose.add(toDisposable(() => { process.removeListener('exit', globalExitListener); })); } @@ -125,7 +123,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } if (!this._messageProtocol) { - this._messageProtocol = Promise.all([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => { + this._messageProtocol = Promise.all([ + this._tryListenOnPipe(), + !this._environmentService.args['disable-inspect'] ? this._tryFindDebugPort() : Promise.resolve(null) + ]).then(data => { const pipeName = data[0]; const portData = data[1]; @@ -148,7 +149,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { silent: true }; - if (portData.actual) { + if (portData && portData.actual) { opts.execArgv = [ '--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portData.actual @@ -215,10 +216,12 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); // Notify debugger that we are ready to attach to the process if we run a development extension - if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { - this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portData.actual); + if (portData) { + if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { + this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portData.actual); + } + this._inspectPort = portData.actual; } - this._inspectPort = portData.actual; // Help in case we fail to start it let startupTimeoutHandle: any; @@ -389,14 +392,16 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { environment: { isExtensionDevelopmentDebug: this._isExtensionDevDebug, appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, - appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : undefined, + appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, appName: product.nameLong, appUriScheme: product.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: URI.file(this._environmentService.userHome) + userHome: URI.file(this._environmentService.userHome), + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { configuration: withNullAsUndefined(workspace.configuration), @@ -404,6 +409,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { name: this._labelService.getWorkspaceLabel(workspace), isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false }, + remote: { + authority: this._environmentService.configuration.remoteAuthority, + isRemote: false + }, resolvedExtensions: [], hostExtensions: [], extensions: extensionDescriptions, @@ -451,36 +460,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { return; } - // Unexpected termination - if (!this._isExtensionDevHost) { - this._onCrashed.fire([code, signal]); - } - - // Expected development extension termination: When the extension host goes down we also shutdown the window - else if (!this._isExtensionDevTestFromCli) { - this._windowService.closeWindow(); - } - - // When CLI testing make sure to exit with proper exit code - else { - ipc.send('vscode:exit', code); - } + this._onExit.fire([code, signal]); } - public enableInspector(): Promise { - if (this._inspectPort) { - return Promise.resolve(); - } - // send SIGUSR1 and wait a little the actual port is read from the process stdout which we - // scan here: https://github.com/Microsoft/vscode/blob/67ffab8dcd1a6752d8b62bcd13d7020101eef568/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts#L225-L240 - if (this._extensionHostProcess) { - this._extensionHostProcess.kill('SIGUSR1'); - } - return timeout(1000); - } - - public getInspectPort(): number { - return this._inspectPort; + public getInspectPort(): number | undefined { + return withNullAsUndefined(this._inspectPort); } public terminate(): void { @@ -489,7 +473,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } this._terminating = true; - dispose(this._toDispose); + this._toDispose.dispose(); if (!this._messageProtocol) { // .start() was not called diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts index f2bb48b6b6..551043ae78 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts @@ -6,13 +6,17 @@ import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { RemoteExtensionManagementChannelClient } from 'vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IProductService } from 'vs/platform/product/common/product'; const localExtensionManagementServerAuthority: string = 'vscode-local'; @@ -25,14 +29,18 @@ export class ExtensionManagementServerService implements IExtensionManagementSer constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IExtensionGalleryService galleryService: IExtensionGalleryService, + @IConfigurationService configurationService: IConfigurationService, + @IProductService productService: IProductService, + @ILogService logService: ILogService ) { const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions')); + const extensionManagementService = new RemoteExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer.extensionManagementService, galleryService, logService, configurationService, productService); this.remoteExtensionManagementServer = { authority: remoteAgentConnection.remoteAuthority, extensionManagementService, label: localize('remote', "Remote") }; } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 495a81cbfd..6173dc662c 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -3,62 +3,37 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ipcRenderer as ipc } from 'electron'; +import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; +import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import { ipcRenderer as ipc } from 'electron'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { Barrier, runWhenIdle } from 'vs/base/common/async'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as perf from 'vs/base/common/performance'; -import { isEqualOrParent } from 'vs/base/common/resources'; +import { runWhenIdle } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { BetterMergeId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionEnablementService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/electron-browser/remoteExtensionHostClient'; +import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService, ResolvedAuthority, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; -import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; +import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Schemas } from 'vs/base/common/network'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; -import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { PersistenConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; - -const hasOwnProperty = Object.hasOwnProperty; -const NO_OP_VOID_PROMISE = Promise.resolve(undefined); - -schema.properties.engines.properties.vscode.default = `^${pkg.version}`; - -let productAllowProposedApi: Set | null = null; -function allowProposedApiFromProduct(id: ExtensionIdentifier): boolean { - // create set if needed - if (!productAllowProposedApi) { - productAllowProposedApi = new Set(); - if (isNonEmptyArray(product.extensionAllowedProposedApi)) { - product.extensionAllowedProposedApi.forEach((id) => productAllowProposedApi!.add(ExtensionIdentifier.toKey(id))); - } - } - return productAllowProposedApi.has(ExtensionIdentifier.toKey(id)); -} +import { IProductService } from 'vs/platform/product/common/product'; +import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; class DeltaExtensionsQueueItem { constructor( @@ -67,80 +42,38 @@ class DeltaExtensionsQueueItem { ) { } } -export class ExtensionService extends Disposable implements IExtensionService { +export class ExtensionService extends AbstractExtensionService implements IExtensionService { - public _serviceBrand: any; - - private _remoteExtensionsEnvironmentData: Map; + private readonly _remoteExtensionsEnvironmentData: Map; private readonly _extensionHostLogsLocation: URI; - private readonly _registry: ExtensionDescriptionRegistry; - private readonly _installedExtensionsReady: Barrier; - private readonly _isDev: boolean; - private readonly _extensionsMessages: Map; - private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; private readonly _extensionScanner: CachedExtensionScanner; private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; - private readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); - public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; - - private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); - public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; - - private readonly _onDidChangeExtensions: Emitter = this._register(new Emitter()); - public readonly onDidChangeExtensions: Event = this._onDidChangeExtensions.event; - - private readonly _onWillActivateByEvent = this._register(new Emitter()); - public readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; - - private readonly _onDidChangeResponsiveChange = this._register(new Emitter()); - public readonly onDidChangeResponsiveChange: Event = this._onDidChangeResponsiveChange.event; - - // --- Members used per extension host process - private _extensionHostProcessManagers: ExtensionHostProcessManager[]; - private _extensionHostActiveExtensions: Map; - private _extensionHostProcessActivationTimes: Map; - private _extensionHostExtensionRuntimeErrors: Map; - constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @INotificationService private readonly _notificationService: INotificationService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, + @IInstantiationService instantiationService: IInstantiationService, + @INotificationService notificationService: INotificationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ITelemetryService telemetryService: ITelemetryService, + @IExtensionEnablementService extensionEnablementService: IExtensionEnablementService, + @IFileService fileService: IFileService, + @IProductService productService: IProductService, @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, - @IWindowService private readonly _windowService: IWindowService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IConfigurationService private readonly _configurationService: IConfigurationService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IFileService fileService: IFileService + @IWindowService protected readonly _windowService: IWindowService, ) { - super(); - - // help the file service to activate providers by activating extensions by file system event - this._register(fileService.onWillActivateFileSystemProvider(e => { - e.join(this.activateByEvent(`onFileSystem:${e.scheme}`)); - })); - - this._remoteExtensionsEnvironmentData = new Map(); - - this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${_windowService.windowId}`)); - this._registry = new ExtensionDescriptionRegistry([]); - this._installedExtensionsReady = new Barrier(); - this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; - this._extensionsMessages = new Map(); - this._allRequestedActivateEvents = Object.create(null); - this._extensionScanner = this._instantiationService.createInstance(CachedExtensionScanner); - this._deltaExtensionsQueue = []; - - this._extensionHostProcessManagers = []; - this._extensionHostActiveExtensions = new Map(); - this._extensionHostProcessActivationTimes = new Map(); - this._extensionHostExtensionRuntimeErrors = new Map(); - - this._startDelayed(this._lifecycleService); + super( + instantiationService, + notificationService, + environmentService, + telemetryService, + extensionEnablementService, + fileService, + productService, + ); if (this._extensionEnablementService.allUserExtensionsDisabled) { this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ @@ -151,6 +84,12 @@ export class ExtensionService extends Disposable implements IExtensionService { }]); } + this._remoteExtensionsEnvironmentData = new Map(); + + this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${this._windowService.windowId}`)); + this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner); + this._deltaExtensionsQueue = []; + this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { let toAdd: IExtension[] = []; let toRemove: string[] = []; @@ -181,8 +120,24 @@ export class ExtensionService extends Disposable implements IExtensionService { this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); } })); + + // delay extension host creation and extension scanning + // until the workbench is running. we cannot defer the + // extension host more (LifecyclePhase.Restored) because + // some editors require the extension host to restore + // and this would result in a deadlock + // see https://github.com/Microsoft/vscode/issues/41322 + this._lifecycleService.when(LifecyclePhase.Ready).then(() => { + // reschedule to ensure this runs after restoring viewlets, panels, and editors + runWhenIdle(() => { + this._initialize(); + }, 50 /*max delay*/); + }); } + + //#region deltaExtensions + private _inHandleDeltaExtensions = false; private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { this._deltaExtensionsQueue.push(item); @@ -211,13 +166,7 @@ export class ExtensionService extends Disposable implements IExtensionService { for (let i = 0, len = _toAdd.length; i < len; i++) { const extension = _toAdd[i]; - if (extension.location.scheme !== Schemas.file) { - continue; - } - - const existingExtensionDescription = this._registry.getExtensionDescription(extension.identifier.id); - if (existingExtensionDescription) { - // this extension is already running (most likely at a different version) + if (!this._canAddExtension(extension)) { continue; } @@ -258,6 +207,9 @@ export class ExtensionService extends Disposable implements IExtensionService { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } + // enable or disable proposed API per extension + this._checkEnableProposedApi(toAdd); + // Update extension points this._rehandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); @@ -274,48 +226,36 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _rehandleExtensionPoints(extensionDescriptions: IExtensionDescription[]): void { - const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); - for (let extensionDescription of extensionDescriptions) { - if (extensionDescription.contributes) { - for (let extPointName in extensionDescription.contributes) { - if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { - affectedExtensionPoints[extPointName] = true; - } - } - } - } - - const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); - - const availableExtensions = this._registry.getAllExtensionDescriptions(); - const extensionPoints = ExtensionsRegistry.getExtensionPoints(); - for (let i = 0, len = extensionPoints.length; i < len; i++) { - if (affectedExtensionPoints[extensionPoints[i].name]) { - ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); - } - } + this._doHandleExtensionPoints(extensionDescriptions); } - public canAddExtension(extension: IExtensionDescription): boolean { + public canAddExtension(extensionDescription: IExtensionDescription): boolean { + return this._canAddExtension(toExtension(extensionDescription)); + } + + public _canAddExtension(extension: IExtension): boolean { if (this._environmentService.configuration.remoteAuthority) { return false; } - if (extension.extensionLocation.scheme !== Schemas.file) { + if (extension.location.scheme !== Schemas.file) { return false; } // {{ SQL CARBON EDIT }} - if (extension.forceReload) { + if (extension.manifest.forceReload) { return false; } - const extensionDescription = this._registry.getExtensionDescription(extension.identifier); + const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id); if (extensionDescription) { - // ignore adding an extension which is already running and cannot be removed - if (!this._canRemoveExtension(extensionDescription)) { - return false; - } + // this extension is already running (most likely at a different version) + return false; + } + + // Check if extension is renamed + if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) { + return false; } return true; @@ -359,7 +299,7 @@ export class ExtensionService extends Disposable implements IExtensionService { activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; } - if (this._allRequestedActivateEvents[activationEvent]) { + if (this._allRequestedActivateEvents.has(activationEvent)) { // This activation event was fired before the extension was added shouldActivate = true; shouldActivateReason = activationEvent; @@ -388,108 +328,50 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - private _startDelayed(lifecycleService: ILifecycleService): void { - // delay extension host creation and extension scanning - // until the workbench is running. we cannot defer the - // extension host more (LifecyclePhase.Restored) because - // some editors require the extension host to restore - // and this would result in a deadlock - // see https://github.com/Microsoft/vscode/issues/41322 - lifecycleService.when(LifecyclePhase.Ready).then(() => { - // reschedule to ensure this runs after restoring viewlets, panels, and editors - runWhenIdle(() => { - perf.mark('willLoadExtensions'); - this._startExtensionHostProcess(true, []); - this._scanAndHandleExtensions(); - this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions')); - }, 50 /*max delay*/); - }); - } - - public dispose(): void { - super.dispose(); - this._onWillActivateByEvent.dispose(); - this._onDidChangeResponsiveChange.dispose(); - } - - public restartExtensionHost(): void { - this._stopExtensionHostProcess(); - this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)); - } - - public startExtensionHost(): void { - this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)); - } - - public stopExtensionHost(): void { - this._stopExtensionHostProcess(); - } - - private _stopExtensionHostProcess(): void { - let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; - this._extensionHostActiveExtensions.forEach((value) => { - previouslyActivatedExtensionIds.push(value); - }); - - for (const manager of this._extensionHostProcessManagers) { - manager.dispose(); - } - this._extensionHostProcessManagers = []; - this._extensionHostActiveExtensions = new Map(); - this._extensionHostProcessActivationTimes = new Map(); - this._extensionHostExtensionRuntimeErrors = new Map(); - - if (previouslyActivatedExtensionIds.length > 0) { - this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds); - } - } + //#endregion private _createProvider(remoteAuthority: string): IInitDataProvider { return { remoteAuthority: remoteAuthority, getInitData: () => { - return this._installedExtensionsReady.wait().then(() => { + return this.whenInstalledExtensionsRegistered().then(() => { return this._remoteExtensionsEnvironmentData.get(remoteAuthority)!; }); } }; } - private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void { - this._stopExtensionHostProcess(); - + protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { let autoStart: boolean; let extensions: Promise; if (isInitialStart) { autoStart = false; - extensions = this._extensionScanner.scannedExtensions; + extensions = this._extensionScanner.scannedExtensions.then(extensions => extensions.filter(extension => this._isEnabled(extension))); // remove disabled extensions } else { // restart case autoStart = true; extensions = this.getExtensions().then((extensions) => extensions.filter(ext => ext.extensionLocation.scheme === Schemas.file)); } + const result: ExtensionHostProcessManager[] = []; const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); - const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); - extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal, true)); - extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); - this._extensionHostProcessManagers.push(extHostProcessManager); + const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, true, extHostProcessWorker, null, initialActivationEvents); + result.push(extHostProcessManager); const remoteAgentConnection = this._remoteAgentService.getConnection(); if (remoteAgentConnection) { - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority)); - const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); - remoteExtHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal, false)); - remoteExtHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); - this._extensionHostProcessManagers.push(remoteExtHostProcessManager); + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), nodeWebSocketFactory); + const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + result.push(remoteExtHostProcessManager); } + + return result; } - private _onExtensionHostCrashed(code: number, signal: string | null, showNotification: boolean): void { - console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); - this._stopExtensionHostProcess(); + protected _onExtensionHostCrashed(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + super._onExtensionHostCrashed(extensionHost, code, signal); - if (showNotification) { + if (extensionHost.isLocal) { if (code === 55) { this._notificationService.prompt( Severity.Error, @@ -507,118 +389,19 @@ export class ExtensionService extends Disposable implements IExtensionService { return; } - let message = nls.localize('extensionService.crash', "Extension host terminated unexpectedly."); - if (code === 87) { - message = nls.localize('extensionService.unresponsiveCrash', "Extension host terminated because it was not responsive."); - } - - this._notificationService.prompt(Severity.Error, message, + this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly."), [{ label: nls.localize('devTools', "Open Developer Tools"), run: () => this._windowService.openDevTools() }, { label: nls.localize('restart', "Restart Extension Host"), - run: () => this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)) + run: () => this.startExtensionHost() }] ); } } - // ---- begin IExtensionService - - public activateByEvent(activationEvent: string): Promise { - if (this._installedExtensionsReady.isOpen()) { - // Extensions have been scanned and interpreted - - // Record the fact that this activationEvent was requested (in case of a restart) - this._allRequestedActivateEvents[activationEvent] = true; - - if (!this._registry.containsActivationEvent(activationEvent)) { - // There is no extension that is interested in this activation event - return NO_OP_VOID_PROMISE; - } - - return this._activateByEvent(activationEvent); - } else { - // Extensions have not been scanned yet. - - // Record the fact that this activationEvent was requested (in case of a restart) - this._allRequestedActivateEvents[activationEvent] = true; - - return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); - } - } - - private _activateByEvent(activationEvent: string): Promise { - const result = Promise.all( - this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) - ).then(() => { }); - this._onWillActivateByEvent.fire({ - event: activationEvent, - activation: result - }); - return result; - } - - public whenInstalledExtensionsRegistered(): Promise { - return this._installedExtensionsReady.wait(); - } - - public getExtensions(): Promise { - return this._installedExtensionsReady.wait().then(() => { - return this._registry.getAllExtensionDescriptions(); - }); - } - - public getExtension(id: string): Promise { - return this._installedExtensionsReady.wait().then(() => { - return this._registry.getExtensionDescription(id); - }); - } - - public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { - return this._installedExtensionsReady.wait().then(() => { - let availableExtensions = this._registry.getAllExtensionDescriptions(); - - let result: ExtensionPointContribution[] = [], resultLen = 0; - for (let i = 0, len = availableExtensions.length; i < len; i++) { - let desc = availableExtensions[i]; - - if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { - result[resultLen++] = new ExtensionPointContribution(desc, desc.contributes[extPoint.name]); - } - } - - return result; - }); - } - - public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { - let result: { [id: string]: IExtensionsStatus; } = Object.create(null); - if (this._registry) { - const extensions = this._registry.getAllExtensionDescriptions(); - for (const extension of extensions) { - const extensionKey = ExtensionIdentifier.toKey(extension.identifier); - result[extension.identifier.value] = { - messages: this._extensionsMessages.get(extensionKey) || [], - activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey), - runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [], - }; - } - } - return result; - } - - public getInspectPort(): number { - if (this._extensionHostProcessManagers.length > 0) { - return this._extensionHostProcessManagers[0].getInspectPort(); - } - return 0; - } - - // ---- end IExtensionService - // --- impl private createLogger(): Logger { @@ -647,7 +430,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - private async _scanAndHandleExtensions(): Promise { + protected async _scanAndHandleExtensions(): Promise { this._extensionScanner.startScanningExtensions(this.createLogger()); const remoteAuthority = this._environmentService.configuration.remoteAuthority; @@ -655,6 +438,12 @@ export class ExtensionService extends Disposable implements IExtensionService { let localExtensions = await this._extensionScanner.scannedExtensions; + // enable or disable proposed API per extension + this._checkEnableProposedApi(localExtensions); + + // remove disabled extensions + localExtensions = localExtensions.filter(extension => this._isEnabled(extension)); + if (remoteAuthority) { let resolvedAuthority: ResolvedAuthority; @@ -698,37 +487,28 @@ export class ExtensionService extends Disposable implements IExtensionService { // fetch the remote environment const remoteEnv = (await this._remoteAgentService.getEnvironment())!; - // revive URIs - remoteEnv.extensions.forEach((extension) => { - (extension).extensionLocation = URI.revive(extension.extensionLocation); - }); + // enable or disable proposed API per extension + this._checkEnableProposedApi(remoteEnv.extensions); + + // remove disabled extensions + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension)); // remove UI extensions from the remote extensions - remoteEnv.extensions = remoteEnv.extensions.filter(extension => !isUIExtension(extension, this._configurationService)); + remoteEnv.extensions = remoteEnv.extensions.filter(extension => !isUIExtension(extension, this._productService, this._configurationService)); // remove non-UI extensions from the local extensions - localExtensions = localExtensions.filter(extension => extension.isBuiltin || isUIExtension(extension, this._configurationService)); + localExtensions = localExtensions.filter(extension => extension.isBuiltin || isUIExtension(extension, this._productService, this._configurationService)); // in case of overlap, the remote wins const isRemoteExtension = new Set(); remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); - // compute enabled extensions - const enabledExtensions = await this._getRuntimeExtensions(([]).concat(remoteEnv.extensions).concat(localExtensions)); - - // remove disabled extensions - const isEnabled = new Set(); - enabledExtensions.forEach(extension => isEnabled.add(ExtensionIdentifier.toKey(extension.identifier))); - remoteEnv.extensions = remoteEnv.extensions.filter(extension => isEnabled.has(ExtensionIdentifier.toKey(extension.identifier))); - localExtensions = localExtensions.filter(extension => isEnabled.has(ExtensionIdentifier.toKey(extension.identifier))); - // save for remote extension's init data this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv); - this._handleExtensionPoints(enabledExtensions); + this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); extensionHost.start(localExtensions.map(extension => extension.identifier)); - this._releaseBarrier(); } else { await this._startLocalExtensionHost(extensionHost, localExtensions); @@ -736,11 +516,8 @@ export class ExtensionService extends Disposable implements IExtensionService { } private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, localExtensions: IExtensionDescription[]): Promise { - const enabledExtensions = await this._getRuntimeExtensions(localExtensions); - - this._handleExtensionPoints(enabledExtensions); - extensionHost.start(enabledExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); - this._releaseBarrier(); + this._handleExtensionPoints(localExtensions); + extensionHost.start(localExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); } private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void { @@ -749,234 +526,19 @@ export class ExtensionService extends Disposable implements IExtensionService { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } - let availableExtensions = this._registry.getAllExtensionDescriptions(); - let extensionPoints = ExtensionsRegistry.getExtensionPoints(); - - let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); - - for (let i = 0, len = extensionPoints.length; i < len; i++) { - ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); - } + this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); } - private _releaseBarrier(): void { - perf.mark('extensionHostReady'); - this._installedExtensionsReady.open(); - this._onDidRegisterExtensions.fire(undefined); - this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier)); - } - - private isExtensionUnderDevelopment(extension: IExtensionDescription): boolean { - if (this._environmentService.isExtensionDevelopment) { - const extDevLocs = this._environmentService.extensionDevelopmentLocationURI; - if (extDevLocs) { - const extLocation = extension.extensionLocation; - for (let p of extDevLocs) { - if (isEqualOrParent(extLocation, p)) { - return true; - } - } - } + public getInspectPort(): number { + if (this._extensionHostProcessManagers.length > 0) { + return this._extensionHostProcessManagers[0].getInspectPort(); } - return false; - } - - private async _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise { - - const runtimeExtensions: IExtensionDescription[] = []; - const extensionsToDisable: IExtensionDescription[] = []; - const userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }]; - - let enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; - - const notFound = (id: string) => nls.localize('notFound', "Extension \`{0}\` cannot use PROPOSED API as it cannot be found", id); - - if (enableProposedApiFor.length) { - let allProposed = (enableProposedApiFor instanceof Array ? enableProposedApiFor : [enableProposedApiFor]); - allProposed.forEach(id => { - if (!allExtensions.some(description => ExtensionIdentifier.equals(description.identifier, id))) { - console.error(notFound(id)); - } - }); - // Make enabled proposed API be lowercase for case insensitive comparison - if (Array.isArray(enableProposedApiFor)) { - enableProposedApiFor = enableProposedApiFor.map(id => id.toLowerCase()); - } else { - enableProposedApiFor = enableProposedApiFor.toLowerCase(); - } - } - - const enableProposedApiForAll = !this._environmentService.isBuilt || - (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong !== 'Visual Studio Code') || - (enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args); - - - for (const extension of allExtensions) { - - // Do not disable extensions under development - if (!this.isExtensionUnderDevelopment(extension)) { - if (!this._extensionEnablementService.isEnabled(toExtension(extension))) { - continue; - } - } - - if (!extension.isBuiltin) { - // Check if the extension is changed to system extension - const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.identifier.value }))[0]; - if (userMigratedSystemExtension) { - extensionsToDisable.push(extension); - continue; - } - } - runtimeExtensions.push(this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor)); - } - - this._telemetryService.publicLog('extensionsScanned', { - totalCount: runtimeExtensions.length, - disabledCount: allExtensions.length - runtimeExtensions.length - }); - - if (extensionsToDisable.length) { - return this._extensionEnablementService.setEnablement(extensionsToDisable.map(e => toExtension(e)), EnablementState.Disabled) - .then(() => runtimeExtensions); - } else { - return runtimeExtensions; - } - } - - private _updateEnableProposedApi(extension: IExtensionDescription, enableProposedApiForAll: boolean, enableProposedApiFor: string | string[]): IExtensionDescription { - if (allowProposedApiFromProduct(extension.identifier)) { - // fast lane -> proposed api is available to all extensions - // that are listed in product.json-files - extension.enableProposedApi = true; - - } else if (extension.enableProposedApi && !extension.isBuiltin) { - if ( - !enableProposedApiForAll && - enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0 - ) { - extension.enableProposedApi = false; - console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); - - } else { - // proposed api is available when developing or when an extension was explicitly - // spelled out via a command line argument - console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`); - } - } - return extension; - } - - private _handleExtensionPointMessage(msg: IMessage) { - const extensionKey = ExtensionIdentifier.toKey(msg.extensionId); - - if (!this._extensionsMessages.has(extensionKey)) { - this._extensionsMessages.set(extensionKey, []); - } - this._extensionsMessages.get(extensionKey)!.push(msg); - - const extension = this._registry.getExtensionDescription(msg.extensionId); - const strMsg = `[${msg.extensionId.value}]: ${msg.message}`; - if (extension && extension.isUnderDevelopment) { - // This message is about the extension currently being developed - this._showMessageToUser(msg.type, strMsg); - } else { - this._logMessageInConsole(msg.type, strMsg); - } - - if (!this._isDev && msg.extensionId) { - const { type, extensionId, extensionPointId, message } = msg; - /* __GDPR__ - "extensionsMessage" : { - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "extensionId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "extensionPointId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "message": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this._telemetryService.publicLog('extensionsMessage', { - type, extensionId: extensionId.value, extensionPointId, message - }); - } - } - - private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { - let users: IExtensionPointUser[] = [], usersLen = 0; - for (let i = 0, len = availableExtensions.length; i < len; i++) { - let desc = availableExtensions[i]; - - if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { - users[usersLen++] = { - description: desc, - value: desc.contributes[extensionPoint.name], - collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) - }; - } - } - - extensionPoint.acceptUsers(users); - } - - private _showMessageToUser(severity: Severity, msg: string): void { - if (severity === Severity.Error || severity === Severity.Warning) { - this._notificationService.notify({ severity, message: msg }); - } else { - this._logMessageInConsole(severity, msg); - } - } - - private _logMessageInConsole(severity: Severity, msg: string): void { - if (severity === Severity.Error) { - console.error(msg); - } else if (severity === Severity.Warning) { - console.warn(msg); - } else { - console.log(msg); - } - } - - // -- called by extension host - - public _logOrShowMessage(severity: Severity, msg: string): void { - if (this._isDev) { - this._showMessageToUser(severity, msg); - } else { - this._logMessageInConsole(severity, msg); - } - } - - public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise { - const results = await Promise.all( - this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent)) - ); - const activated = results.some(e => e); - if (!activated) { - throw new Error(`Unknown extension ${extensionId.value}`); - } - } - - public _onWillActivateExtension(extensionId: ExtensionIdentifier): void { - this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId); - } - - public _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { - this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent)); - this._onDidChangeExtensionsStatus.fire([extensionId]); - } - - public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void { - const extensionKey = ExtensionIdentifier.toKey(extensionId); - if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) { - this._extensionHostExtensionRuntimeErrors.set(extensionKey, []); - } - this._extensionHostExtensionRuntimeErrors.get(extensionKey)!.push(err); - this._onDidChangeExtensionsStatus.fire([extensionId]); + return 0; } public _onExtensionHostExit(code: number): void { // Expected development extension termination: When the extension host goes down we also shutdown the window - const devOpts = parseExtensionDevOptions(this._environmentService); - if (!devOpts.isExtensionDevTestFromCli) { + if (!this._isExtensionDevTestFromCli) { this._windowService.closeWindow(); } diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts new file mode 100644 index 0000000000..cfd019527e --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { tmpdir } from 'os'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ILogService } from 'vs/platform/log/common/log'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { values } from 'vs/base/common/map'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { localize } from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; + +export class RemoteExtensionManagementChannelClient extends ExtensionManagementChannelClient { + + _serviceBrand: any; + + constructor( + channel: IChannel, + private readonly localExtensionManagementService: IExtensionManagementService, + private readonly galleryService: IExtensionGalleryService, + private readonly logService: ILogService, + private readonly configurationService: IConfigurationService, + private readonly productService: IProductService + ) { + super(channel); + } + + async install(vsix: URI): Promise { + const local = await super.install(vsix); + await this.installUIDependenciesAndPackedExtensions(local); + return local; + } + + async installFromGallery(extension: IGalleryExtension): Promise { + const local = await this.doInstallFromGallery(extension); + await this.installUIDependenciesAndPackedExtensions(local); + return local; + } + + private async doInstallFromGallery(extension: IGalleryExtension): Promise { + try { + const local = await super.installFromGallery(extension); + return local; + } catch (error) { + try { + this.logService.error(`Error while installing '${extension.identifier.id}' extension in the remote server.`, toErrorMessage(error)); + this.logService.info(`Trying to download '${extension.identifier.id}' extension locally and install`); + const local = await this.downloadCompatibleAndInstall(extension); + this.logService.info(`Successfully installed '${extension.identifier.id}' extension`); + return local; + } catch (e) { + this.logService.error(e); + throw error; + } + } + } + + private async downloadCompatibleAndInstall(extension: IGalleryExtension): Promise { + const installed = await this.getInstalled(ExtensionType.User); + const compatible = await this.galleryService.getCompatibleExtension(extension); + if (!compatible) { + return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version))); + } + const manifest = await this.galleryService.getManifest(compatible, CancellationToken.None); + if (manifest) { + const workspaceExtensions = await this.getAllWorkspaceDependenciesAndPackedExtensions(manifest, CancellationToken.None); + await Promise.all(workspaceExtensions.map(e => this.downloadAndInstall(e, installed))); + } + return this.downloadAndInstall(extension, installed); + } + + private async downloadAndInstall(extension: IGalleryExtension, installed: ILocalExtension[]): Promise { + const location = await this.galleryService.download(extension, URI.file(tmpdir()), installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0] ? InstallOperation.Update : InstallOperation.Install); + return super.install(location); + } + + private async installUIDependenciesAndPackedExtensions(local: ILocalExtension): Promise { + const uiExtensions = await this.getAllUIDependenciesAndPackedExtensions(local.manifest, CancellationToken.None); + const installed = await this.localExtensionManagementService.getInstalled(); + const toInstall = uiExtensions.filter(e => installed.every(i => !areSameExtensions(i.identifier, e.identifier))); + await Promise.all(toInstall.map(d => this.localExtensionManagementService.installFromGallery(d))); + } + + private async getAllUIDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { + const result = new Map(); + const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; + await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, true, token); + return values(result); + } + + private async getAllWorkspaceDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { + const result = new Map(); + const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; + await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, false, token); + return values(result); + } + + private async getDependenciesAndPackedExtensionsRecursively(toGet: string[], result: Map, uiExtension: boolean, token: CancellationToken): Promise { + if (toGet.length === 0) { + return Promise.resolve(); + } + + const extensions = (await this.galleryService.query({ names: toGet, pageSize: toGet.length }, token)).firstPage; + const manifests = await Promise.all(extensions.map(e => this.galleryService.getManifest(e, token))); + const extensionsManifests: IExtensionManifest[] = []; + for (let idx = 0; idx < extensions.length; idx++) { + const extension = extensions[idx]; + const manifest = manifests[idx]; + if (manifest && isUIExtension(manifest, this.productService, this.configurationService) === uiExtension) { + result.set(extension.identifier.id.toLowerCase(), extension); + extensionsManifests.push(manifest); + } + } + toGet = []; + for (const extensionManifest of extensionsManifests) { + if (isNonEmptyArray(extensionManifest.extensionDependencies)) { + for (const id of extensionManifest.extensionDependencies) { + if (!result.has(id.toLowerCase())) { + toGet.push(id); + } + } + } + if (isNonEmptyArray(extensionManifest.extensionPack)) { + for (const id of extensionManifest.extensionPack) { + if (!result.has(id.toLowerCase())) { + toGet.push(id); + } + } + } + } + return this.getDependenciesAndPackedExtensionsRecursively(toGet, result, uiExtension, token); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/extensionHostMain.ts b/src/vs/workbench/services/extensions/node/extensionHostMain.ts index 60e47348ba..d2d1cf46aa 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostMain.ts @@ -19,7 +19,6 @@ import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; -import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; // we don't (yet) throw when extensions parse // uris that have no scheme @@ -53,9 +52,7 @@ export class ExtensionHostMain { hostUtils: IHostUtils, consolePatchFn: IConsolePatchFn, logServiceFn: ILogServiceFn, - uriTransformer: IURITransformer | null, - schemeTransformer: ISchemeTransformer | null, - outputChannelName: string, + uriTransformer: IURITransformer | null ) { this._isTerminating = false; this._hostUtils = hostUtils; @@ -86,8 +83,7 @@ export class ExtensionHostMain { extHostConfiguraiton, initData.environment, this._extHostLogService, - schemeTransformer, - outputChannelName + uriTransformer ); // error forwarding and stack trace scanning diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts index f8f0ec2bc9..b43ea6602c 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -3,11 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { startExtensionHostProcess } from 'vs/workbench/services/extensions/node/extensionHostProcessSetup'; -startExtensionHostProcess( - _ => null, - _ => null, - _ => nls.localize('extension host Log', "Extension Host") -).catch((err) => console.log(err)); +startExtensionHostProcess().catch((err) => console.log(err)); diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 2e303b8f5f..0a35b8604c 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -5,23 +5,33 @@ import * as nativeWatchdog from 'native-watchdog'; import * as net from 'net'; +import * as minimist from 'minimist'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { PersistentProtocol, ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net'; -import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { PersistentProtocol, ProtocolConstants, createBufferedEvent } from 'vs/base/parts/ipc/common/ipc.net'; +import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/product/node/product'; import { IInitData, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn, ILogServiceFn } from 'vs/workbench/services/extensions/node/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; -import { createBufferSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; -import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; -import { IURITransformer } from 'vs/base/common/uriIpc'; +import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; import { exists } from 'vs/base/node/pfs'; import { realpath } from 'vs/base/node/extpath'; import { IHostUtils } from 'vs/workbench/api/node/extHostExtensionService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; + +interface ParsedExtHostArgs { + uriTransformerPath?: string; +} + +const args = minimist(process.argv.slice(2), { + string: [ + 'uriTransformerPath' + ] +}) as ParsedExtHostArgs; // With Electron 2.x and node.js 8.x the "natives" module // can cause a native crash (see https://github.com/nodejs/node/issues/19891 and @@ -72,7 +82,7 @@ function patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { }; } -const createLogService: ILogServiceFn = initData => createBufferSpdLogService(ExtensionHostLogFileName, initData.logLevel, initData.logsLocation.fsPath); +const createLogService: ILogServiceFn = initData => new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel); interface IRendererConnection { protocol: IMessagePassingProtocol; @@ -101,27 +111,41 @@ function _createExtHostProtocol(): Promise { process.on('message', (msg: IExtHostSocketMessage, handle: net.Socket) => { if (msg && msg.type === 'VSCODE_EXTHOST_IPC_SOCKET') { const initialDataChunk = VSBuffer.wrap(Buffer.from(msg.initialDataChunk, 'base64')); + let socket: NodeSocket | WebSocketNodeSocket; + if (msg.skipWebSocketFrames) { + socket = new NodeSocket(handle); + } else { + socket = new WebSocketNodeSocket(new NodeSocket(handle)); + } if (protocol) { // reconnection case if (disconnectWaitTimer) { clearTimeout(disconnectWaitTimer); disconnectWaitTimer = null; } - protocol.beginAcceptReconnection(new NodeSocket(handle), initialDataChunk); + protocol.beginAcceptReconnection(socket, initialDataChunk); protocol.endAcceptReconnection(); } else { clearTimeout(timer); - protocol = new PersistentProtocol(new NodeSocket(handle), initialDataChunk); + protocol = new PersistentProtocol(socket, initialDataChunk); protocol.onClose(() => onTerminate()); resolve(protocol); - protocol.onSocketClose(() => { - // The socket has closed, let's give the renderer a certain amount of time to reconnect - disconnectWaitTimer = setTimeout(() => { - disconnectWaitTimer = null; + if (msg.skipWebSocketFrames) { + // Wait for rich client to reconnect + protocol.onSocketClose(() => { + // The socket has closed, let's give the renderer a certain amount of time to reconnect + disconnectWaitTimer = setTimeout(() => { + disconnectWaitTimer = null; + onTerminate(); + }, ProtocolConstants.ReconnectionGraceTime); + }); + } else { + // Do not wait for web companion to reconnect + protocol.onSocketClose(() => { onTerminate(); - }, ProtocolConstants.ReconnectionGraceTime); - }); + }); + } } } }); @@ -155,16 +179,22 @@ async function createExtHostProtocol(): Promise { return new class implements IMessagePassingProtocol { - private _terminating = false; + private readonly _onMessage = new Emitter(); + readonly onMessage: Event = createBufferedEvent(this._onMessage.event); - readonly onMessage: Event = Event.filter(protocol.onMessage, msg => { - if (!isMessageOfType(msg, MessageType.Terminate)) { - return true; - } - this._terminating = true; - onTerminate(); - return false; - }); + private _terminating: boolean; + + constructor() { + this._terminating = false; + protocol.onMessage((msg) => { + if (isMessageOfType(msg, MessageType.Terminate)) { + this._terminating = true; + onTerminate(); + } else { + this._onMessage.fire(msg); + } + }); + } send(msg: any): void { if (!this._terminating) { @@ -272,11 +302,7 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise IURITransformer | null, - schemeTransformerFn: (initData: IInitData) => ISchemeTransformer | null, - outputChannelNameFn: (initData: IInitData) => string, -): Promise { +export async function startExtensionHostProcess(): Promise { const protocol = await createExtHostProtocol(); const renderer = await connectToRenderer(protocol); @@ -291,6 +317,17 @@ export async function startExtensionHostProcess( realpath(path: string) { return realpath(path); } }; + // Attempt to load uri transformer + let uriTransformer: IURITransformer | null = null; + if (initData.remote.authority && args.uriTransformerPath) { + try { + const rawURITransformerFactory = require.__$__nodeRequire(args.uriTransformerPath); + const rawURITransformer = rawURITransformerFactory(initData.remote.authority); + uriTransformer = new URITransformer(rawURITransformer); + } catch (e) { + console.error(e); + } + } const extensionHostMain = new ExtensionHostMain( renderer.protocol, @@ -298,9 +335,7 @@ export async function startExtensionHostProcess( hostUtils, patchPatchedConsole, createLogService, - uriTransformerFn(initData), - schemeTransformerFn(initData), - outputChannelNameFn(initData) + uriTransformer ); // rewrite onTerminate-function to be a proper shutdown diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 90f69da182..5bb8732e6f 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -12,40 +12,13 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; -import { getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; -import { ExtensionIdentifier, ExtensionIdentifierWithVersion, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { getGalleryExtensionId, groupByExtension, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { isValidExtensionVersion } from 'vs/platform/extensions/common/extensionValidator'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { Translations, ILog } from 'vs/workbench/services/extensions/common/extensionPoints'; const MANIFEST_FILE = 'package.json'; -export interface Translations { - [id: string]: string; -} - -namespace Translations { - export function equals(a: Translations, b: Translations): boolean { - if (a === b) { - return true; - } - let aKeys = Object.keys(a); - let bKeys: Set = new Set(); - for (let key of Object.keys(b)) { - bKeys.add(key); - } - if (aKeys.length !== bKeys.size) { - return false; - } - - for (let key of aKeys) { - if (a[key] !== b[key]) { - return false; - } - bKeys.delete(key); - } - return bKeys.size === 0; - } -} - export interface NlsConfiguration { readonly devMode: boolean; readonly locale: string | undefined; @@ -53,12 +26,6 @@ export interface NlsConfiguration { readonly translations: Translations; } -export interface ILog { - error(source: string, message: string): void; - warn(source: string, message: string): void; - info(source: string, message: string): void; -} - abstract class ExtensionManifestHandler { protected readonly _ourVersion: string; diff --git a/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts b/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts index 171b524764..af62e0bdfc 100644 --- a/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts +++ b/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts @@ -8,7 +8,7 @@ import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionType, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -16,10 +16,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; -import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { values } from 'vs/base/common/map'; +import { IProductService } from 'vs/platform/product/common/product'; export class MultiExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -35,7 +34,8 @@ export class MultiExtensionManagementService extends Disposable implements IExte constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService private readonly productService: IProductService, ) { super(); this.servers = this.extensionManagementServerService.remoteExtensionManagementServer ? [this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer] : [this.extensionManagementServerService.localExtensionManagementServer]; @@ -85,7 +85,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); - const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.configurationService) + const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.productService, this.configurationService) && i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); if (dependentNonUIExtensions.length) { return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); @@ -132,44 +132,38 @@ export class MultiExtensionManagementService extends Disposable implements IExte return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(([extensionIdentifier]) => extensionIdentifier); } - async install(vsix: URI): Promise { + async install(vsix: URI): Promise { if (this.extensionManagementServerService.remoteExtensionManagementServer) { const manifest = await getManifest(vsix.fsPath); if (isLanguagePackExtension(manifest)) { // Install on both servers - const [extensionIdentifier] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); - return extensionIdentifier; + const [local] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); + return local; } - if (isUIExtension(manifest, this.configurationService)) { + if (isUIExtension(manifest, this.productService, this.configurationService)) { // Install only on local server return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); } // Install only on remote server - const promise = this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); - // Install UI Dependencies on local server - await this.installUIDependenciesAndPackedExtensions(manifest); - return promise; + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); } return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); } - async installFromGallery(gallery: IGalleryExtension): Promise { + async installFromGallery(gallery: IGalleryExtension): Promise { if (this.extensionManagementServerService.remoteExtensionManagementServer) { const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); if (manifest) { if (isLanguagePackExtension(manifest)) { // Install on both servers - return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(() => undefined); + return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); } - if (isUIExtension(manifest, this.configurationService)) { + if (isUIExtension(manifest, this.productService, this.configurationService)) { // Install only on local server return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } // Install only on remote server - const promise = this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - // Install UI dependencies and packed extensions on local server - await this.installUIDependenciesAndPackedExtensions(manifest); - return promise; + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } else { return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); } @@ -177,13 +171,6 @@ export class MultiExtensionManagementService extends Disposable implements IExte return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } - private async installUIDependenciesAndPackedExtensions(manifest: IExtensionManifest): Promise { - const uiExtensions = await this.getAllUIDependenciesAndPackedExtensions(manifest, CancellationToken.None); - const installed = await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getInstalled(); - const toInstall = uiExtensions.filter(e => installed.every(i => !areSameExtensions(i.identifier, e.identifier))); - await Promise.all(toInstall.map(d => this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(d))); - } - getExtensionsReport(): Promise { return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport(); } @@ -191,49 +178,6 @@ export class MultiExtensionManagementService extends Disposable implements IExte private getServer(extension: ILocalExtension): IExtensionManagementServer | null { return this.extensionManagementServerService.getExtensionManagementServer(extension.location); } - - private async getAllUIDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { - const result = new Map(); - const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; - await this.getAllUIDependenciesAndPackedExtensionsRecursively(extensions, result, token); - return values(result); - } - - private async getAllUIDependenciesAndPackedExtensionsRecursively(toGet: string[], result: Map, token: CancellationToken): Promise { - if (toGet.length === 0) { - return Promise.resolve(); - } - - const extensions = (await this.extensionGalleryService.query({ names: toGet, pageSize: toGet.length }, token)).firstPage; - const manifests = await Promise.all(extensions.map(e => this.extensionGalleryService.getManifest(e, token))); - const uiExtensionsManifests: IExtensionManifest[] = []; - for (let idx = 0; idx < extensions.length; idx++) { - const extension = extensions[idx]; - const manifest = manifests[idx]; - if (manifest && isUIExtension(manifest, this.configurationService)) { - result.set(extension.identifier.id.toLowerCase(), extension); - uiExtensionsManifests.push(manifest); - } - } - toGet = []; - for (const uiExtensionManifest of uiExtensionsManifests) { - if (isNonEmptyArray(uiExtensionManifest.extensionDependencies)) { - for (const id of uiExtensionManifest.extensionDependencies) { - if (!result.has(id.toLowerCase())) { - toGet.push(id); - } - } - } - if (isNonEmptyArray(uiExtensionManifest.extensionPack)) { - for (const id of uiExtensionManifest.extensionPack) { - if (!result.has(id.toLowerCase())) { - toGet.push(id); - } - } - } - } - return this.getAllUIDependenciesAndPackedExtensionsRecursively(toGet, result, token); - } } registerSingleton(IExtensionManagementService, MultiExtensionManagementService); diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 2e3727a962..ec0bec873a 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -103,22 +103,33 @@ function setupProxyResolution( let results: ConnectionResult[] = []; function logEvent() { timeout = undefined; - /* __GDPR__ - "resolveProxy" : { - "count": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "errorCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheSize": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheRolls": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "envCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "settingsCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "localhostCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "envNoProxyCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "results": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, envNoProxyCount, results }); + type ResolveProxyClassification = { + count: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + duration: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + errorCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cacheCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cacheSize: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cacheRolls: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + envCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + settingsCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + localhostCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + envNoProxyCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + results: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + type ResolveProxyEvent = { + count: number; + duration: number; + errorCount: number; + cacheCount: number; + cacheSize: number; + cacheRolls: number; + envCount: number; + settingsCount: number; + localhostCount: number; + envNoProxyCount: number; + results: ConnectionResult[]; + }; + mainThreadTelemetry.$publicLog2('resolveProxy', { count, duration, errorCount, cacheCount, cacheSize: cache.size, cacheRolls, envCount, settingsCount, localhostCount, envNoProxyCount, results }); count = duration = errorCount = cacheCount = envCount = settingsCount = localhostCount = envNoProxyCount = 0; results = []; } @@ -403,7 +414,7 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku const modules = lookup[request]; const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath); if (ext && ext.enableProposedApi) { - return modules[(ext).proxySupport] || modules.onRequest; + return (modules as any)[(ext).proxySupport] || modules.onRequest; } return modules.default; }; @@ -457,8 +468,8 @@ async function readCaCertificates() { return undefined; } -function readWindowsCaCertificates() { - const winCA = require.__$__nodeRequire('vscode-windows-ca-certs'); +async function readWindowsCaCertificates() { + const winCA = await import('vscode-windows-ca-certs'); let ders: any[] = []; const store = winCA(); @@ -471,22 +482,26 @@ function readWindowsCaCertificates() { store.done(); } - const seen = {}; - const certs = ders.map(derToPem) - .filter(pem => !seen[pem] && (seen[pem] = true)); + const certs = new Set(ders.map(derToPem)); return { - certs, + certs: Array.from(certs), append: true }; } async function readMacCaCertificates() { - const stdout = (await promisify(cp.execFile)('/usr/bin/security', ['find-certificate', '-a', '-p'], { encoding: 'utf8', maxBuffer: 1024 * 1024 })).stdout; - const seen = {}; - const certs = stdout.split(/(?=-----BEGIN CERTIFICATE-----)/g) - .filter(pem => !!pem.length && !seen[pem] && (seen[pem] = true)); + const stdout = await new Promise((resolve, reject) => { + const child = cp.spawn('/usr/bin/security', ['find-certificate', '-a', '-p']); + const stdout: string[] = []; + child.stdout.setEncoding('utf8'); + child.stdout.on('data', str => stdout.push(str)); + child.on('error', reject); + child.on('exit', code => code ? reject(code) : resolve(stdout.join(''))); + }); + const certs = new Set(stdout.split(/(?=-----BEGIN CERTIFICATE-----)/g) + .filter(pem => !!pem.length)); return { - certs, + certs: Array.from(certs), append: true }; } @@ -500,11 +515,10 @@ async function readLinuxCaCertificates() { for (const certPath of linuxCaCertificatePaths) { try { const content = await promisify(fs.readFile)(certPath, { encoding: 'utf8' }); - const seen = {}; - const certs = content.split(/(?=-----BEGIN CERTIFICATE-----)/g) - .filter(pem => !!pem.length && !seen[pem] && (seen[pem] = true)); + const certs = new Set(content.split(/(?=-----BEGIN CERTIFICATE-----)/g) + .filter(pem => !!pem.length)); return { - certs, + certs: Array.from(certs), append: false }; } catch (err) { diff --git a/src/vs/workbench/services/files/common/workspaceWatcher.ts b/src/vs/workbench/services/files/common/workspaceWatcher.ts index 0aaa93c437..dbc896aa52 100644 --- a/src/vs/workbench/services/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/services/files/common/workspaceWatcher.ts @@ -16,7 +16,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { FileService } from 'vs/platform/files/common/fileService'; export class WorkspaceWatcher extends Disposable { diff --git a/src/vs/workbench/services/heap/common/heap.ts b/src/vs/workbench/services/heap/common/heap.ts deleted file mode 100644 index a5823376e6..0000000000 --- a/src/vs/workbench/services/heap/common/heap.ts +++ /dev/null @@ -1,33 +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 { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const IHeapService = createDecorator('heapService'); - -export interface ObjectIdentifier { - $ident?: number; -} - -export interface IHeapService { - _serviceBrand: any; - - readonly onGarbageCollection: Event; - - /** - * Track gc-collection for the given object - */ - trackObject(obj: ObjectIdentifier | undefined): void; -} - - - -export class NullHeapService implements IHeapService { - _serviceBrand: any; - onGarbageCollection = Event.None; - trackObject() { } -} diff --git a/src/vs/workbench/services/heap/node/heap.ts b/src/vs/workbench/services/heap/node/heap.ts deleted file mode 100644 index ba4a8cf9ad..0000000000 --- a/src/vs/workbench/services/heap/node/heap.ts +++ /dev/null @@ -1,80 +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 { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { Event, Emitter } from 'vs/base/common/event'; -import { GCSignal } from 'gc-signals'; -import { IHeapService, ObjectIdentifier } from 'vs/workbench/services/heap/common/heap'; - -export class HeapService implements IHeapService { - - _serviceBrand: any; - - private readonly _onGarbageCollection: Emitter = new Emitter(); - public readonly onGarbageCollection: Event = this._onGarbageCollection.event; - - private _activeSignals = new WeakMap(); - private _activeIds = new Set(); - - private _consumeHandle: any; - private _ctor: { new(id: number): GCSignal }; - private _ctorInit: Promise; - - constructor() { - // - } - - dispose() { - clearInterval(this._consumeHandle); - } - - trackObject(obj: ObjectIdentifier | undefined | null): void { - if (!obj) { - return; - } - - const ident = obj.$ident; - if (typeof ident !== 'number') { - return; - } - - if (this._activeIds.has(ident)) { - return; - } - - if (this._ctor) { - // track and leave - this._activeIds.add(ident); - this._activeSignals.set(obj, new this._ctor(ident)); - - } else { - // make sure to load gc-signals, then track and leave - if (!this._ctorInit) { - this._ctorInit = import('gc-signals').then(({ GCSignal, consumeSignals }) => { - this._ctor = GCSignal; - this._consumeHandle = setInterval(() => { - const ids = consumeSignals(); - - if (ids.length > 0) { - // local book-keeping - for (const id of ids) { - this._activeIds.delete(id); - } - // fire event - this._onGarbageCollection.fire(ids); - } - }, 15 * 1000); - }); - } - - this._ctorInit.then(() => { - this._activeIds.add(ident); - this._activeSignals.set(obj, new this._ctor(ident)); - }); - } - } -} - -registerSingleton(IHeapService, HeapService, true); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 68ba90e0a6..a66b46e151 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -13,13 +13,13 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { FileChangesEvent, IFileService, FileChangeType, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; import { Selection } from 'vs/editor/common/core/selection'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { Event } from 'vs/base/common/event'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { getExcludes, ISearchConfiguration } from 'vs/workbench/services/search/common/search'; import { IExpression } from 'vs/base/common/glob'; @@ -32,6 +32,7 @@ import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/cont import { coalesce } from 'vs/base/common/arrays'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -105,11 +106,11 @@ export class HistoryService extends Disposable implements IHistoryService { private static readonly MAX_STACK_ITEMS = 50; private static readonly MAX_RECENTLY_CLOSED_EDITORS = 20; - private activeEditorListeners: IDisposable[]; + private readonly activeEditorListeners = this._register(new DisposableStore()); private lastActiveEditor?: IEditorIdentifier; - private editorHistoryListeners: Map = new Map(); - private editorStackListeners: Map = new Map(); + private readonly editorHistoryListeners: Map = new Map(); + private readonly editorStackListeners: Map = new Map(); private stack: IStackEntry[]; private index: number; @@ -137,15 +138,13 @@ export class HistoryService extends Disposable implements IHistoryService { @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, @IFileService private readonly fileService: IFileService, - @IWindowsService private readonly windowService: IWindowsService, + @IWindowService private readonly windowService: IWindowService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); - this.activeEditorListeners = []; - this.canNavigateBackContextKey = (new RawContextKey('canNavigateBack', false)).bindTo(this.contextKeyService); this.canNavigateForwardContextKey = (new RawContextKey('canNavigateForward', false)).bindTo(this.contextKeyService); this.canNavigateToLastEditLocationContextKey = (new RawContextKey('canNavigateToLastEditLocation', false)).bindTo(this.contextKeyService); @@ -186,6 +185,39 @@ export class HistoryService extends Disposable implements IHistoryService { if (this.editorService.activeControl) { this.onActiveEditorChanged(); } + + // Mouse back/forward support + const mouseBackForwardSupportListener = this._register(new DisposableStore()); + const handleMouseBackForwardSupport = () => { + mouseBackForwardSupportListener.clear(); + + if (this.configurationService.getValue('workbench.editor.mouseBackForwardToNavigate')) { + mouseBackForwardSupportListener.add(addDisposableListener(this.layoutService.getWorkbenchElement(), EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + } + }; + + this._register(this.configurationService.onDidChangeConfiguration(event => { + if (event.affectsConfiguration('workbench.editor.mouseBackForwardToNavigate')) { + handleMouseBackForwardSupport(); + } + })); + + handleMouseBackForwardSupport(); + } + + private onMouseDown(e: MouseEvent): void { + + // Support to navigate in history when mouse buttons 4/5 are pressed + switch (e.button) { + case 3: + EventHelper.stop(e); + this.back(); + break; + case 4: + EventHelper.stop(e); + this.forward(); + break; + } } private onActiveEditorChanged(): void { @@ -198,8 +230,7 @@ export class HistoryService extends Disposable implements IHistoryService { this.lastActiveEditor = activeControl && activeControl.input && activeControl.group ? { editor: activeControl.input, groupId: activeControl.group.id } : undefined; // Dispose old listeners - dispose(this.activeEditorListeners); - this.activeEditorListeners = []; + this.activeEditorListeners.clear(); // Propagate to history this.handleActiveEditorChange(activeControl); @@ -212,14 +243,14 @@ export class HistoryService extends Disposable implements IHistoryService { // Debounce the event with a timeout of 0ms so that multiple calls to // editor.setSelection() are folded into one. We do not want to record // subsequent history navigations for such API calls. - this.activeEditorListeners.push(Event.debounce(activeTextEditorWidget.onDidChangeCursorPosition, (last, event) => event, 0)((event => { + this.activeEditorListeners.add(Event.debounce(activeTextEditorWidget.onDidChangeCursorPosition, (last, event) => event, 0)((event => { this.handleEditorSelectionChangeEvent(activeControl, event); }))); // Track the last edit location by tracking model content change events // Use a debouncer to make sure to capture the correct cursor position // after the model content has changed. - this.activeEditorListeners.push(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => this.rememberLastEditLocation(activeEditor!, activeTextEditorWidget)))); + this.activeEditorListeners.add(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => this.rememberLastEditLocation(activeEditor!, activeTextEditorWidget)))); } } @@ -478,19 +509,19 @@ export class HistoryService extends Disposable implements IHistoryService { } } - private onEditorDispose(editor: EditorInput, listener: Function, mapEditorToDispose: Map): void { + private onEditorDispose(editor: EditorInput, listener: Function, mapEditorToDispose: Map): void { const toDispose = Event.once(editor.onDispose)(() => listener()); let disposables = mapEditorToDispose.get(editor); if (!disposables) { - disposables = []; + disposables = new DisposableStore(); mapEditorToDispose.set(editor, disposables); } - disposables.push(toDispose); + disposables.add(toDispose); } - private clearOnEditorDispose(editor: IEditorInput | IResourceInput | FileChangesEvent, mapEditorToDispose: Map): void { + private clearOnEditorDispose(editor: IEditorInput | IResourceInput | FileChangesEvent, mapEditorToDispose: Map): void { if (editor instanceof EditorInput) { const disposables = mapEditorToDispose.get(editor); if (disposables) { diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts similarity index 68% rename from src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts rename to src/vs/workbench/services/keybinding/browser/keybindingService.ts index 74dda793dd..fdcb63e9e7 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -4,17 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as nativeKeymap from 'native-keymap'; -import { release } from 'os'; +import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { Keybinding, ResolvedKeybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; -import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { ConfigWatcher } from 'vs/base/node/config'; +import { OS, OperatingSystem, isWeb } from 'vs/base/common/platform'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; @@ -22,140 +19,34 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; -import { IKeybindingEvent, IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService, IKeybindingEvent } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { IKeybindingItem, IKeybindingRule2, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IUserKeybindingItem, KeybindingIO, OutputBuilder } from 'vs/workbench/services/keybinding/common/keybindingIO'; -import { CachedKeyboardMapper, IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; -import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; -import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; -import { IWindowsKeyboardMapping, WindowsKeyboardMapper, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -export class KeyboardMapperFactory { - public static readonly INSTANCE = new KeyboardMapperFactory(); - - private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo | null; - private _rawMapping: nativeKeymap.IKeyboardMapping | null; - private _keyboardMapper: IKeyboardMapper | null; - private _initialized: boolean; - - private readonly _onDidChangeKeyboardMapper = new Emitter(); - public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; - - private constructor() { - this._layoutInfo = null; - this._rawMapping = null; - this._keyboardMapper = null; - this._initialized = false; - } - - public _onKeyboardLayoutChanged(): void { - if (this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - } - - public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { - if (!this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - if (dispatchConfig === DispatchConfig.KeyCode) { - // Forcefully set to use keyCode - return new MacLinuxFallbackKeyboardMapper(OS); - } - return this._keyboardMapper!; - } - - public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo | null { - if (!this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - return this._layoutInfo; - } - - private static _isUSStandard(_kbInfo: nativeKeymap.IKeyboardLayoutInfo): boolean { - if (OS === OperatingSystem.Linux) { - const kbInfo = _kbInfo; - return (kbInfo && kbInfo.layout === 'us'); - } - - if (OS === OperatingSystem.Macintosh) { - const kbInfo = _kbInfo; - return (kbInfo && kbInfo.id === 'com.apple.keylayout.US'); - } - - if (OS === OperatingSystem.Windows) { - const kbInfo = _kbInfo; - return (kbInfo && kbInfo.name === '00000409'); - } - - return false; - } - - public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping | null { - if (!this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - return this._rawMapping; - } - - private _setKeyboardData(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { - this._layoutInfo = layoutInfo; - - if (this._initialized && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { - // nothing to do... - return; - } - - this._initialized = true; - this._rawMapping = rawMapping; - this._keyboardMapper = new CachedKeyboardMapper( - KeyboardMapperFactory._createKeyboardMapper(this._layoutInfo, this._rawMapping) - ); - this._onDidChangeKeyboardMapper.fire(); - } - - private static _createKeyboardMapper(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { - const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo); - if (OS === OperatingSystem.Windows) { - return new WindowsKeyboardMapper(isUSStandard, rawMapping); - } - - if (Object.keys(rawMapping).length === 0) { - // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) - return new MacLinuxFallbackKeyboardMapper(OS); - } - - if (OS === OperatingSystem.Macintosh) { - const kbInfo = layoutInfo; - if (kbInfo.id === 'com.apple.keylayout.DVORAK-QWERTYCMD') { - // Use keyCode based dispatching for DVORAK - QWERTY ⌘ - return new MacLinuxFallbackKeyboardMapper(OS); - } - } - - return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); - } - - private static _equals(a: nativeKeymap.IKeyboardMapping | null, b: nativeKeymap.IKeyboardMapping | null): boolean { - if (OS === OperatingSystem.Windows) { - return windowsKeyboardMappingEquals(a, b); - } - - return macLinuxKeyboardMappingEquals(a, b); - } -} +// tslint:disable-next-line: import-patterns +import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { parse } from 'vs/base/common/json'; +import * as objects from 'vs/base/common/objects'; +import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { isArray } from 'vs/base/common/types'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; +import { ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode'; interface ContributedKeyBinding { command: string; @@ -215,7 +106,7 @@ let keybindingType: IJSONSchema = { description: nls.localize('vscode.extension.contributes.keybindings.args', "Arguments to pass to the command to execute.") }, key: { - description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g Ctrl+O and Ctrl+L L for a chord).'), + description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g. Ctrl+O and Ctrl+L L for a chord).'), type: 'string' }, mac: { @@ -239,6 +130,7 @@ let keybindingType: IJSONSchema = { const keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'keybindings', + deps: [commandsExtensionPoint], jsonSchema: { description: nls.localize('vscode.extension.contributes.keybindings', "Contributes keybindings."), oneOf: [ @@ -251,23 +143,11 @@ const keybindingsExtPoint = ExtensionsRegistry.registerExtensionPointkeyboard).dispatch : null); - return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code); -} - export class WorkbenchKeybindingService extends AbstractKeybindingService { private _keyboardMapper: IKeyboardMapper; private _cachedResolver: KeybindingResolver | null; - private _firstTimeComputingResolver: boolean; - private userKeybindings: ConfigWatcher; + private userKeybindings: UserKeybindings; constructor( @IContextKeyService contextKeyService: IContextKeyService, @@ -275,12 +155,13 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, @IEnvironmentService environmentService: IEnvironmentService, - @IStatusbarService statusBarService: IStatusbarService, @IConfigurationService configurationService: IConfigurationService, @IWindowService private readonly windowService: IWindowService, - @IExtensionService extensionService: IExtensionService + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService, + @IKeymapService private readonly keymapService: IKeymapService ) { - super(contextKeyService, commandService, telemetryService, notificationService, statusBarService); + super(contextKeyService, commandService, telemetryService, notificationService); updateSchema(); @@ -292,20 +173,37 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } dispatchConfig = newDispatchConfig; - this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + this._keyboardMapper = this.keymapService.getKeyboardMapper(dispatchConfig); this.updateResolver({ source: KeybindingSource.Default }); }); - this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); - KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { - this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + this._keyboardMapper = this.keymapService.getKeyboardMapper(dispatchConfig); + this.keymapService.onDidChangeKeyboardMapper(() => { + this._keyboardMapper = this.keymapService.getKeyboardMapper(dispatchConfig); this.updateResolver({ source: KeybindingSource.Default }); }); this._cachedResolver = null; - this._firstTimeComputingResolver = true; - this.userKeybindings = this._register(new ConfigWatcher(environmentService.appKeybindingsPath, { defaultConfig: [], onError: error => onUnexpectedError(error) })); + this.userKeybindings = this._register(new UserKeybindings(environmentService.keybindingsResource, fileService)); + this.userKeybindings.initialize().then(() => { + if (this.userKeybindings.keybindings.length) { + this.updateResolver({ source: KeybindingSource.User }); + } + }); + this._register(this.userKeybindings.onDidChange(() => { + type CustomKeybindingsChangedClassification = { + keyCount: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true } + }; + + this._telemetryService.publicLog2<{ keyCount: number }, CustomKeybindingsChangedClassification>('customKeybindingsChanged', { + keyCount: this.userKeybindings.keybindings.length + }); + this.updateResolver({ + source: KeybindingSource.User, + keybindings: this.userKeybindings.keybindings + }); + })); keybindingsExtPoint.setHandler((extensions) => { @@ -321,11 +219,6 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { updateSchema(); this._register(extensionService.onDidRegisterExtensions(() => updateSchema())); - this._register(this.userKeybindings.onDidUpdateConfiguration(event => this.updateResolver({ - source: KeybindingSource.User, - keybindings: event.config - }))); - this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { let keyEvent = new StandardKeyboardEvent(e); let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); @@ -335,7 +228,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { })); keybindingsTelemetry(telemetryService, this); - let data = KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(); + let data = this.keymapService.getCurrentKeyboardLayout(); /* __GDPR__ "keyboardLayout" : { "currentKeyboardLayout": { "${inline}": [ "${IKeyboardLayoutInfo}" ] } @@ -344,27 +237,43 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { telemetryService.publicLog('keyboardLayout', { currentKeyboardLayout: data }); + + this._register(browser.onDidChangeFullscreen(() => { + const keyboard = (navigator).keyboard; + + if (!keyboard) { + return; + } + + if (browser.isFullscreen()) { + keyboard.lock(['Escape']); + } else { + keyboard.unlock(); + } + + // update resolver which will bring back all unbound keyboard shortcuts + this._cachedResolver = null; + this._onDidUpdateKeybindings.fire({ source: KeybindingSource.User }); + })); } public _dumpDebugInfo(): string { - const layoutInfo = JSON.stringify(KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(), null, '\t'); + const layoutInfo = JSON.stringify(this.keymapService.getCurrentKeyboardLayout(), null, '\t'); const mapperInfo = this._keyboardMapper.dumpDebugInfo(); - const rawMapping = JSON.stringify(KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(), null, '\t'); + const rawMapping = JSON.stringify(this.keymapService.getRawKeyboardMapping(), null, '\t'); return `Layout info:\n${layoutInfo}\n${mapperInfo}\n\nRaw mapping:\n${rawMapping}`; } - private _safeGetConfig(): IUserFriendlyKeybinding[] { - let rawConfig = this.userKeybindings.getConfig(); - if (Array.isArray(rawConfig)) { - return rawConfig; - } - return []; + public _dumpDebugInfoJSON(): string { + const info = { + layout: this.keymapService.getCurrentKeyboardLayout(), + rawMapping: this.keymapService.getRawKeyboardMapping() + }; + return JSON.stringify(info, null, '\t'); } public customKeybindingsCount(): number { - let userKeybindings = this._safeGetConfig(); - - return userKeybindings.length; + return this.userKeybindings.keybindings.length; } private updateResolver(event: IKeybindingEvent): void { @@ -375,9 +284,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { protected _getResolver(): KeybindingResolver { if (!this._cachedResolver) { const defaults = this._resolveKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true); - const overrides = this._resolveUserKeybindingItems(this._getExtraKeybindings(this._firstTimeComputingResolver), false); + const overrides = this._resolveUserKeybindingItems(this.userKeybindings.keybindings.map((k) => KeybindingIO.readUserKeybindingItem(k)), false); this._cachedResolver = new KeybindingResolver(defaults, overrides); - this._firstTimeComputingResolver = false; } return this._cachedResolver; } @@ -398,6 +306,10 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // This might be a removal keybinding item in user settings => accept it result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault); } else { + if (this._assertBrowserConflicts(keybinding, item.command)) { + continue; + } + const resolvedKeybindings = this.resolveKeybinding(keybinding); for (const resolvedKeybinding of resolvedKeybindings) { result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault); @@ -427,22 +339,75 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { return result; } - private _getExtraKeybindings(isFirstTime: boolean): IUserKeybindingItem[] { - let extraUserKeybindings: IUserFriendlyKeybinding[] = this._safeGetConfig(); - if (!isFirstTime) { - let cnt = extraUserKeybindings.length; - - /* __GDPR__ - "customKeybindingsChanged" : { - "keyCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this._telemetryService.publicLog('customKeybindingsChanged', { - keyCount: cnt - }); + private _assertBrowserConflicts(kb: Keybinding, commandId: string): boolean { + if (!isWeb) { + return false; } - return extraUserKeybindings.map((k) => KeybindingIO.readUserKeybindingItem(k)); + if (browser.isStandalone) { + return false; + } + + if (browser.isFullscreen() && (navigator).keyboard) { + return false; + } + + for (let part of kb.parts) { + if (!part.metaKey && !part.altKey && !part.ctrlKey && !part.shiftKey) { + continue; + } + + const modifiersMask = KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift; + + let partModifiersMask = 0; + if (part.metaKey) { + partModifiersMask |= KeyMod.CtrlCmd; + } + + if (part.shiftKey) { + partModifiersMask |= KeyMod.Shift; + } + + if (part.altKey) { + partModifiersMask |= KeyMod.Alt; + } + + if (part.ctrlKey && OS === OperatingSystem.Macintosh) { + partModifiersMask |= KeyMod.WinCtrl; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode === KeyCode.KEY_W) { + // console.warn('Ctrl/Cmd+W keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode === KeyCode.KEY_N) { + // console.warn('Ctrl/Cmd+N keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode === KeyCode.KEY_T) { + // console.warn('Ctrl/Cmd+T keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === (KeyMod.CtrlCmd | KeyMod.Alt) && (part.keyCode === KeyCode.LeftArrow || part.keyCode === KeyCode.RightArrow)) { + // console.warn('Ctrl/Cmd+Arrow keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode >= KeyCode.KEY_0 && part.keyCode <= KeyCode.KEY_9) { + // console.warn('Ctrl/Cmd+Num keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + } + + return false; } public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] { @@ -450,6 +415,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { + this.keymapService.validateCurrentKeyboardMapping(keyboardEvent); return this._keyboardMapper.resolveKeyboardEvent(keyboardEvent); } @@ -500,10 +466,21 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { weight = KeybindingWeight.ExternalExtension + idx; } + let commandAction = MenuRegistry.getCommand(command); + let precondition = commandAction && commandAction.precondition; + let fullWhen: ContextKeyExpr | undefined; + if (when && precondition) { + fullWhen = ContextKeyExpr.and(precondition, ContextKeyExpr.deserialize(when)); + } else if (when) { + fullWhen = ContextKeyExpr.deserialize(when); + } else if (precondition) { + fullWhen = precondition; + } + let desc: IKeybindingRule2 = { id: command, args, - when: ContextKeyExpr.deserialize(when), + when: fullWhen, weight: weight, primary: KeybindingParser.parseKeybinding(key, OS), mac: mac ? { primary: KeybindingParser.parseKeybinding(mac, OS) } : null, @@ -553,13 +530,19 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } mightProducePrintableCharacter(event: IKeyboardEvent): boolean { - if (event.ctrlKey || event.metaKey) { - // ignore ctrl/cmd-combination but not shift/alt-combinatios + if (event.ctrlKey || event.metaKey || event.altKey) { + // ignore ctrl/cmd/alt-combination but not shift-combinatios + return false; + } + const code = ScanCodeUtils.toEnum(event.code); + const keycode = IMMUTABLE_CODE_TO_KEY_CODE[code]; + if (keycode !== -1) { + // https://github.com/microsoft/vscode/issues/74934 return false; } // consult the KeyboardMapperFactory to check the given event for // a printable value. - const mapping = KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(); + const mapping = this.keymapService.getRawKeyboardMapping(); if (!mapping) { return false; } @@ -574,6 +557,47 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } } +class UserKeybindings extends Disposable { + + private _keybindings: IUserFriendlyKeybinding[] = []; + get keybindings(): IUserFriendlyKeybinding[] { return this._keybindings; } + + private readonly reloadConfigurationScheduler: RunOnceScheduler; + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + constructor( + private readonly keybindingsResource: URI, + private readonly fileService: IFileService + ) { + super(); + + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => { + if (changed) { + this._onDidChange.fire(); + } + }), 50)); + this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.keybindingsResource))(() => this.reloadConfigurationScheduler.schedule())); + } + + async initialize(): Promise { + await this.reload(); + } + + private async reload(): Promise { + const existing = this._keybindings; + try { + const content = await this.fileService.readFile(this.keybindingsResource); + const value = parse(content.value.toString()); + this._keybindings = isArray(value) ? value : []; + } catch (e) { + this._keybindings = []; + } + return existing ? !objects.equals(existing, this._keybindings) : true; + } +} + let schemaId = 'vscode://schemas/keybindings'; let commandsSchemas: IJSONSchema[] = []; let commandsEnum: string[] = []; @@ -652,8 +676,8 @@ function updateSchema() { }; const allCommands = CommandsRegistry.getCommands(); - for (let commandId in allCommands) { - const commandDescription = allCommands[commandId].description; + for (const [commandId, command] of allCommands) { + const commandDescription = command.description; addKnownCommand(commandId, commandDescription ? commandDescription.description : undefined); @@ -681,7 +705,7 @@ function updateSchema() { } const menuCommands = MenuRegistry.getCommands(); - for (let commandId in menuCommands) { + for (const commandId of menuCommands.keys()) { addKnownCommand(commandId); } } @@ -700,16 +724,11 @@ const keyboardConfiguration: IConfigurationNode = { 'default': 'code', 'markdownDescription': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `code` (recommended) or `keyCode`."), 'included': OS === OperatingSystem.Macintosh || OS === OperatingSystem.Linux - }, - 'keyboard.touchbar.enabled': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."), - 'included': OS === OperatingSystem.Macintosh && parseFloat(release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) } + // no touch bar support } }; configurationRegistry.registerConfiguration(keyboardConfiguration); -registerSingleton(IKeybindingService, WorkbenchKeybindingService); \ No newline at end of file +registerSingleton(IKeybindingService, WorkbenchKeybindingService); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.ts new file mode 100644 index 0000000000..32e198edf6 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IKeymapInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; + +export class KeyboardLayoutContribution { + public static readonly INSTANCE: KeyboardLayoutContribution = new KeyboardLayoutContribution(); + + private _layoutInfos: IKeymapInfo[] = []; + + get layoutInfos() { + return this._layoutInfos; + } + + private constructor() { + } + + registerKeyboardLayout(layout: IKeymapInfo) { + this._layoutInfos.push(layout); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts new file mode 100644 index 0000000000..11c46ba11b --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000405', id: '', text: 'Czech' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '{', '', 0, 'VK_B'], + KeyC: ['c', 'C', '&', '', 0, 'VK_C'], + KeyD: ['d', 'D', 'Đ', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '[', '', 0, 'VK_F'], + KeyG: ['g', 'G', ']', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', 'ł', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'Ł', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '}', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '\\', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'đ', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '@', '', 0, 'VK_V'], + KeyW: ['w', 'W', '|', '', 0, 'VK_W'], + KeyX: ['x', 'X', '#', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '', '', 0, 'VK_Y'], + Digit1: ['+', '1', '~', '', 0, 'VK_1'], + Digit2: ['ě', '2', 'ˇ', '', 0, 'VK_2'], + Digit3: ['š', '3', '^', '', 0, 'VK_3'], + Digit4: ['č', '4', '˘', '', 0, 'VK_4'], + Digit5: ['ř', '5', '°', '', 0, 'VK_5'], + Digit6: ['ž', '6', '˛', '', 0, 'VK_6'], + Digit7: ['ý', '7', '`', '', 0, 'VK_7'], + Digit8: ['á', '8', '˙', '', 0, 'VK_8'], + Digit9: ['í', '9', '´', '', 0, 'VK_9'], + Digit0: ['é', '0', '˝', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['=', '%', '¨', '', 0, 'VK_OEM_PLUS'], + Equal: ['´', 'ˇ', '¸', '', 0, 'VK_OEM_2'], + BracketLeft: ['ú', '/', '÷', '', 0, 'VK_OEM_4'], + BracketRight: [')', '(', '×', '', 0, 'VK_OEM_6'], + Backslash: ['¨', '\'', '¤', '', 0, 'VK_OEM_5'], + Semicolon: ['ů', '"', '$', '', 0, 'VK_OEM_1'], + Quote: ['§', '!', 'ß', '', 0, 'VK_OEM_7'], + Backquote: [';', '°', '', '', 0, 'VK_OEM_3'], + Comma: [',', '?', '<', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '>', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '*', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts new file mode 100644 index 0000000000..9d65af3d6d --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000807', id: '', text: 'Swiss German' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '', '', 0, 'VK_Y'], + Digit1: ['1', '+', '¦', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '*', '#', '', 0, 'VK_3'], + Digit4: ['4', 'ç', '°', '', 0, 'VK_4'], + Digit5: ['5', '%', '§', '', 0, 'VK_5'], + Digit6: ['6', '&', '¬', '', 0, 'VK_6'], + Digit7: ['7', '/', '|', '', 0, 'VK_7'], + Digit8: ['8', '(', '¢', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '´', '', 0, 'VK_OEM_4'], + Equal: ['^', '`', '~', '', 0, 'VK_OEM_6'], + BracketLeft: ['ü', 'è', '[', '', 0, 'VK_OEM_1'], + BracketRight: ['¨', '!', ']', '', 0, 'VK_OEM_3'], + Backslash: ['$', '£', '}', '', 0, 'VK_OEM_8'], + Semicolon: ['ö', 'é', '', '', 0, 'VK_OEM_7'], + Quote: ['ä', 'à', '{', '', 0, 'VK_OEM_5'], + Backquote: ['§', '°', '', '', 0, 'VK_OEM_2'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '\\', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts new file mode 100644 index 0000000000..f5847cb243 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.German', lang: 'de', localizedName: 'German' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', '‹', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', '™', 0], + KeyE: ['e', 'E', '€', '‰', 0], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', 'Ì', 0], + KeyH: ['h', 'H', 'ª', 'Ó', 0], + KeyI: ['i', 'I', '⁄', 'Û', 0], + KeyJ: ['j', 'J', 'º', 'ı', 0], + KeyK: ['k', 'K', '∆', 'ˆ', 0], + KeyL: ['l', 'L', '@', 'fl', 0], + KeyM: ['m', 'M', 'µ', '˘', 0], + KeyN: ['n', 'N', '~', '›', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', '«', '»', 0], + KeyR: ['r', 'R', '®', '¸', 0], + KeyS: ['s', 'S', '‚', 'Í', 0], + KeyT: ['t', 'T', '†', '˝', 0], + KeyU: ['u', 'U', '¨', 'Á', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', 'Ù', 0], + KeyY: ['z', 'Z', 'Ω', 'ˇ', 0], + KeyZ: ['y', 'Y', '¥', '‡', 0], + Digit1: ['1', '!', '¡', '¬', 0], + Digit2: ['2', '"', '“', '”', 0], + Digit3: ['3', '§', '¶', '#', 0], + Digit4: ['4', '$', '¢', '£', 0], + Digit5: ['5', '%', '[', 'fi', 0], + Digit6: ['6', '&', ']', '^', 8], + Digit7: ['7', '/', '|', '\\', 0], + Digit8: ['8', '(', '{', '˜', 0], + Digit9: ['9', ')', '}', '·', 0], + Digit0: ['0', '=', '≠', '¯', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['ß', '?', '¿', '˙', 0], + Equal: ['´', '`', '\'', '˚', 3], + BracketLeft: ['ü', 'Ü', '•', '°', 0], + BracketRight: ['+', '*', '±', '', 0], + Backslash: ['#', '\'', '‘', '’', 0], + Semicolon: ['ö', 'Ö', 'œ', 'Œ', 0], + Quote: ['ä', 'Ä', 'æ', 'Æ', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '∞', '˛', 0], + Period: ['.', ':', '…', '÷', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', ',', '.', '.', 0], + IntlBackslash: ['^', '°', '„', '“', 1], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts new file mode 100644 index 0000000000..8a397dc600 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc104', layout: 'de', variant: '', options: '', rules: 'base' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'æ', 'Æ', 0], + KeyB: ['b', 'B', '“', '‘', 0], + KeyC: ['c', 'C', '¢', '©', 0], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '€', '€', 0], + KeyF: ['f', 'F', 'đ', 'ª', 0], + KeyG: ['g', 'G', 'ŋ', 'Ŋ', 0], + KeyH: ['h', 'H', 'ħ', 'Ħ', 0], + KeyI: ['i', 'I', '→', 'ı', 0], + KeyJ: ['j', 'J', '̣', '̇', 0], + KeyK: ['k', 'K', 'ĸ', '&', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: ['m', 'M', 'µ', 'º', 0], + KeyN: ['n', 'N', '”', '’', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'þ', 'Þ', 0], + KeyQ: ['q', 'Q', '@', 'Ω', 0], + KeyR: ['r', 'R', '¶', '®', 0], + KeyS: ['s', 'S', 'ſ', 'ẞ', 0], + KeyT: ['t', 'T', 'ŧ', 'Ŧ', 0], + KeyU: ['u', 'U', '↓', '↑', 0], + KeyV: ['v', 'V', '„', '‚', 0], + KeyW: ['w', 'W', 'ł', 'Ł', 0], + KeyX: ['x', 'X', '«', '‹', 0], + KeyY: ['z', 'Z', '←', '¥', 0], + KeyZ: ['y', 'Y', '»', '›', 0], + Digit1: ['1', '!', '¹', '¡', 0], + Digit2: ['2', '"', '²', '⅛', 0], + Digit3: ['3', '§', '³', '£', 0], + Digit4: ['4', '$', '¼', '¤', 0], + Digit5: ['5', '%', '½', '⅜', 0], + Digit6: ['6', '&', '¬', '⅝', 0], + Digit7: ['7', '/', '{', '⅞', 0], + Digit8: ['8', '(', '[', '™', 0], + Digit9: ['9', ')', ']', '±', 0], + Digit0: ['0', '=', '}', '°', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['ß', '?', '\\', '¿', 0], + Equal: ['́', '̀', '̧', '̨', 0], + BracketLeft: ['ü', 'Ü', '̈', '̊', 0], + BracketRight: ['+', '*', '~', '¯', 0], + Backslash: ['#', '\'', '’', '̆', 0], + Semicolon: ['ö', 'Ö', '̋', '̣', 0], + Quote: ['ä', 'Ä', '̂', '̌', 0], + Backquote: ['̂', '°', '′', '″', 0], + Comma: [',', ';', '·', '×', 0], + Period: ['.', ':', '…', '÷', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: ['', '', '', '', 0], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: ['/', '/', '/', '/', 0], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: [], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', ',', '', ',', 0], + IntlBackslash: ['<', '>', '|', '̱', 0], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: [], + NumpadParenRight: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: ['\r', '\r', '\r', '\r', 0], + MetaRight: ['.', '.', '.', '.', 0], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts new file mode 100644 index 0000000000..b19368a031 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000407', id: '', text: 'German' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '@', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '', '', 0, 'VK_Y'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '²', '', 0, 'VK_2'], + Digit3: ['3', '§', '³', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['ß', '?', '\\', 'ẞ', 0, 'VK_OEM_4'], + Equal: ['´', '`', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['ü', 'Ü', '', '', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', '~', '', 0, 'VK_OEM_PLUS'], + Backslash: ['#', '\'', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ö', 'Ö', '', '', 0, 'VK_OEM_3'], + Quote: ['ä', 'Ä', '', '', 0, 'VK_OEM_7'], + Backquote: ['^', '°', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '|', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts new file mode 100644 index 0000000000..aecbd29691 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000406', id: '', text: 'Danish' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '¤', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['+', '?', '', '', 0, 'VK_OEM_PLUS'], + Equal: ['´', '`', '|', '', 0, 'VK_OEM_4'], + BracketLeft: ['å', 'Å', '', '', 0, 'VK_OEM_6'], + BracketRight: ['¨', '^', '~', '', 0, 'VK_OEM_1'], + Backslash: ['\'', '*', '', '', 0, 'VK_OEM_2'], + Semicolon: ['æ', 'Æ', '', '', 0, 'VK_OEM_3'], + Quote: ['ø', 'Ø', '', '', 0, 'VK_OEM_7'], + Backquote: ['½', '§', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '\\', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts new file mode 100644 index 0000000000..919fe5ea0d --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000813', id: '', text: 'Belgian (Period)' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: [',', '?', '', '', 0, 'VK_OEM_COMMA'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['a', 'A', '', '', 0, 'VK_A'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['w', 'W', '', '', 0, 'VK_W'], + Digit1: ['&', '1', '|', '', 0, 'VK_1'], + Digit2: ['é', '2', '@', '', 0, 'VK_2'], + Digit3: ['"', '3', '#', '', 0, 'VK_3'], + Digit4: ['\'', '4', '{', '', 0, 'VK_4'], + Digit5: ['(', '5', '[', '', 0, 'VK_5'], + Digit6: ['§', '6', '^', '', 0, 'VK_6'], + Digit7: ['è', '7', '', '', 0, 'VK_7'], + Digit8: ['!', '8', '', '', 0, 'VK_8'], + Digit9: ['ç', '9', '{', '', 0, 'VK_9'], + Digit0: ['à', '0', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: [')', '°', '', '', 0, 'VK_OEM_4'], + Equal: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + BracketLeft: ['^', '¨', '[', '', 0, 'VK_OEM_6'], + BracketRight: ['$', '*', ']', '', 0, 'VK_OEM_1'], + Backslash: ['µ', '£', '`', '`', 0, 'VK_OEM_5'], + Semicolon: ['m', 'M', '', '', 0, 'VK_M'], + Quote: ['ù', '%', '´', '´', 0, 'VK_OEM_3'], + Backquote: ['²', '³', '', '', 0, 'VK_OEM_7'], + Comma: [';', '.', '', '', 0, 'VK_OEM_PERIOD'], + Period: [':', '/', '', '', 0, 'VK_OEM_2'], + Slash: ['=', '+', '~', '~', 0, 'VK_OEM_PLUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '\\', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts new file mode 100644 index 0000000000..661af5fee5 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.USExtended', lang: 'en', localizedName: 'ABC - Extended' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', '¯', '̄', 4], + KeyB: ['b', 'B', '˘', '̆', 4], + KeyC: ['c', 'C', '¸', '̧', 4], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '´', '́', 4], + KeyF: ['f', 'F', 'ƒ', '', 0], + KeyG: ['g', 'G', '©', '‸', 8], + KeyH: ['h', 'H', 'ˍ', '̱', 4], + KeyI: ['i', 'I', 'ʼ', '̛', 4], + KeyJ: ['j', 'J', '˝', '̋', 4], + KeyK: ['k', 'K', '˚', '̊', 4], + KeyL: ['l', 'L', '-', '̵', 4], + KeyM: ['m', 'M', '˛', '̨', 4], + KeyN: ['n', 'N', '˜', '̃', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', ',', '̦', 4], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', '', 0], + KeyT: ['t', 'T', 'þ', 'Þ', 0], + KeyU: ['u', 'U', '¨', '̈', 4], + KeyV: ['v', 'V', 'ˇ', '̌', 4], + KeyW: ['w', 'W', '˙', '̇', 4], + KeyX: ['x', 'X', '.', '̣', 4], + KeyY: ['y', 'Y', '¥', '', 0], + KeyZ: ['z', 'Z', 'ˀ', '̉', 4], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '§', '†', 0], + Digit6: ['6', '^', 'ˆ', '̂', 4], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', '№', 8], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '̀', 4], + Comma: [',', '<', '≤', '„', 0], + Period: ['.', '>', '≥', 'ʔ', 8], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts new file mode 100644 index 0000000000..2274e2ab64 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00004009', id: '', text: 'India' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'ā', 'Ā', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', 'ḍ', 'Ḍ', 0, 'VK_D'], + KeyE: ['e', 'E', 'ē', 'Ē', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', 'ṅ', 'Ṅ', 0, 'VK_G'], + KeyH: ['h', 'H', 'ḥ', 'Ḥ', 0, 'VK_H'], + KeyI: ['i', 'I', 'ī', 'Ī', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'l̥', 'L̥', 0, 'VK_L'], + KeyM: ['m', 'M', 'ṁ', 'Ṁ', 0, 'VK_M'], + KeyN: ['n', 'N', 'ṇ', 'Ṇ', 0, 'VK_N'], + KeyO: ['o', 'O', 'ō', 'Ō', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', 'æ', 'Æ', 0, 'VK_Q'], + KeyR: ['r', 'R', 'r̥', 'R̥', 0, 'VK_R'], + KeyS: ['s', 'S', 'ś', 'Ś', 0, 'VK_S'], + KeyT: ['t', 'T', 'ṭ', 'Ṭ', 0, 'VK_T'], + KeyU: ['u', 'U', 'ū', 'Ū', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', 'ṣ', 'Ṣ', 0, 'VK_X'], + KeyY: ['y', 'Y', 'ñ', 'Ñ', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '@', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '₹', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', 'ˆ', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '˘', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '-', 'ˍ', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '', '', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '~', 0, 'VK_OEM_3'], + Comma: [',', '<', ',', '<', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '.', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts new file mode 100644 index 0000000000..1bc49b2b8b --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.USInternational-PC', lang: 'en', localizedName: 'U.S. International - PC' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', 'ˆ', '§', 'fl', 2], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 3], + Backquote: ['`', '˜', '`', '`', 7], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts new file mode 100644 index 0000000000..a49db8bdaf --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00020409', id: '0001', text: 'United States-International' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'á', 'Á', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '©', '¢', 0, 'VK_C'], + KeyD: ['d', 'D', 'ð', 'Ð', 0, 'VK_D'], + KeyE: ['e', 'E', 'é', 'É', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', 'í', 'Í', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'ø', 'Ø', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', 'ñ', 'Ñ', 0, 'VK_N'], + KeyO: ['o', 'O', 'ó', 'Ó', 0, 'VK_O'], + KeyP: ['p', 'P', 'ö', 'Ö', 0, 'VK_P'], + KeyQ: ['q', 'Q', 'ä', 'Ä', 0, 'VK_Q'], + KeyR: ['r', 'R', '®', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'ß', '§', 0, 'VK_S'], + KeyT: ['t', 'T', 'þ', 'Þ', 0, 'VK_T'], + KeyU: ['u', 'U', 'ú', 'Ú', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', 'å', 'Å', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', 'ü', 'Ü', 0, 'VK_Y'], + KeyZ: ['z', 'Z', 'æ', 'Æ', 0, 'VK_Z'], + Digit1: ['1', '!', '¡', '¹', 0, 'VK_1'], + Digit2: ['2', '@', '²', '', 0, 'VK_2'], + Digit3: ['3', '#', '³', '', 0, 'VK_3'], + Digit4: ['4', '$', '¤', '£', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '^', '¼', '', 0, 'VK_6'], + Digit7: ['7', '&', '½', '', 0, 'VK_7'], + Digit8: ['8', '*', '¾', '', 0, 'VK_8'], + Digit9: ['9', '(', '‘', '', 0, 'VK_9'], + Digit0: ['0', ')', '’', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '¥', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '×', '÷', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '«', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '»', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '¬', '¦', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '¶', '°', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '´', '¨', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', 'ç', 'Ç', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '¿', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts new file mode 100644 index 0000000000..925559f424 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.British', lang: 'en', localizedName: 'British' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '‰', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', 'Ì', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', '^', 'È', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', '˜', 0], + KeyN: ['n', 'N', '~', 'ˆ', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', 'Â', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'Ê', 0], + KeyU: ['u', 'U', '¨', 'Ë', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', 'Ù', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', 'Û', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '€', '™', 0], + Digit3: ['3', '£', '#', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '^', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', 'Ÿ', 4], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '', '', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts new file mode 100644 index 0000000000..47fb8e477f --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000809', id: '', text: 'United Kingdom' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'á', 'Á', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', 'é', 'É', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', 'í', 'Í', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', 'ó', 'Ó', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', 'ú', 'Ú', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '£', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '€', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['#', '~', '\\', '|', 0, 'VK_OEM_7'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '@', '', '', 0, 'VK_OEM_3'], + Backquote: ['`', '¬', '¦', '', 0, 'VK_OEM_8'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.ts new file mode 100644 index 0000000000..9d8ea89ac1 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.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. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.US', lang: 'en', localizedName: 'U.S.', isUSStandard: true }, + secondaryLayouts: [ + { id: 'com.apple.keylayout.ABC', lang: 'en', localizedName: 'ABC' }, + { id: 'com.sogou.inputmethod.sogou.pinyin', lang: 'zh-Hans', localizedName: 'Pinyin - Simplified' }, + { id: 'com.apple.inputmethod.Kotoeri.Roman', lang: 'en', localizedName: 'Romaji' }, + { id: 'com.apple.inputmethod.Kotoeri.Japanese', lang: 'ja', localizedName: 'Hiragana' }, + { id: 'com.apple.keylayout.Australian', lang: 'en', localizedName: 'Australian' }, + { id: 'com.apple.keylayout.Canadian', lang: 'en', localizedName: 'Canadian English' }, + { id: 'com.apple.keylayout.Brazilian', lang: 'pt', localizedName: 'Brazilian' }, + ], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '^', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '`', 4], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.ts new file mode 100644 index 0000000000..6bda4615a1 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.ts @@ -0,0 +1,190 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc105', layout: 'us', variant: '', options: '', rules: 'evdev', isUSStandard: true }, + secondaryLayouts: [ + { model: 'pc105', layout: 'cn', variant: '', options: '', rules: 'evdev' }, + ], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'a', 'A', 0], + KeyB: ['b', 'B', 'b', 'B', 0], + KeyC: ['c', 'C', 'c', 'C', 0], + KeyD: ['d', 'D', 'd', 'D', 0], + KeyE: ['e', 'E', 'e', 'E', 0], + KeyF: ['f', 'F', 'f', 'F', 0], + KeyG: ['g', 'G', 'g', 'G', 0], + KeyH: ['h', 'H', 'h', 'H', 0], + KeyI: ['i', 'I', 'i', 'I', 0], + KeyJ: ['j', 'J', 'j', 'J', 0], + KeyK: ['k', 'K', 'k', 'K', 0], + KeyL: ['l', 'L', 'l', 'L', 0], + KeyM: ['m', 'M', 'm', 'M', 0], + KeyN: ['n', 'N', 'n', 'N', 0], + KeyO: ['o', 'O', 'o', 'O', 0], + KeyP: ['p', 'P', 'p', 'P', 0], + KeyQ: ['q', 'Q', 'q', 'Q', 0], + KeyR: ['r', 'R', 'r', 'R', 0], + KeyS: ['s', 'S', 's', 'S', 0], + KeyT: ['t', 'T', 't', 'T', 0], + KeyU: ['u', 'U', 'u', 'U', 0], + KeyV: ['v', 'V', 'v', 'V', 0], + KeyW: ['w', 'W', 'w', 'W', 0], + KeyX: ['x', 'X', 'x', 'X', 0], + KeyY: ['y', 'Y', 'y', 'Y', 0], + KeyZ: ['z', 'Z', 'z', 'Z', 0], + Digit1: ['1', '!', '1', '!', 0], + Digit2: ['2', '@', '2', '@', 0], + Digit3: ['3', '#', '3', '#', 0], + Digit4: ['4', '$', '4', '$', 0], + Digit5: ['5', '%', '5', '%', 0], + Digit6: ['6', '^', '6', '^', 0], + Digit7: ['7', '&', '7', '&', 0], + Digit8: ['8', '*', '8', '*', 0], + Digit9: ['9', '(', '9', '(', 0], + Digit0: ['0', ')', '0', ')', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '-', '_', 0], + Equal: ['=', '+', '=', '+', 0], + BracketLeft: ['[', '{', '[', '{', 0], + BracketRight: [']', '}', ']', '}', 0], + Backslash: ['\\', '|', '\\', '|', 0], + Semicolon: [';', ':', ';', ':', 0], + Quote: ['\'', '"', '\'', '"', 0], + Backquote: ['`', '~', '`', '~', 0], + Comma: [',', '<', ',', '<', 0], + Period: ['.', '>', '.', '>', 0], + Slash: ['/', '?', '/', '?', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: ['', '', '', '', 0], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: ['\r', '\r', '\r', '\r', 0], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', '.', '', '.', 0], + IntlBackslash: ['<', '>', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: ['.', '.', '.', '.', 0], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: ['(', '(', '(', '(', 0], + NumpadParenRight: [')', ')', ')', ')', 0], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } + +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts new file mode 100644 index 0000000000..02fe55f854 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000409', id: '', text: 'US', isUSStandard: true }, + secondaryLayouts: [ + { name: '00000804', id: '', text: 'Chinese (Simplified) - US Keyboard' }, + { name: '00000411', id: '', text: 'Japanese' }, + { name: '00000412', id: '', text: 'Korean' }, + { name: '00000404', id: '', text: 'Chinese (Traditional) - US Keyboard' } + ], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '@', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '', '', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts new file mode 100644 index 0000000000..32d103802c --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000080A', id: '', text: 'Latin American' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '@', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '', '', 0, 'VK_7'], + Digit8: ['8', '(', '', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '\\', '', 0, 'VK_OEM_4'], + Equal: ['¿', '¡', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['´', '¨', '', '', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', '~', '', 0, 'VK_OEM_PLUS'], + Backslash: ['}', ']', '`', '', 0, 'VK_OEM_2'], + Semicolon: ['ñ', 'Ñ', '', '', 0, 'VK_OEM_3'], + Quote: ['{', '[', '^', '', 0, 'VK_OEM_7'], + Backquote: ['|', '°', '¬', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts new file mode 100644 index 0000000000..f672b88114 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Spanish-ISO', lang: 'es', localizedName: 'Spanish - ISO' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', 'ß', '', 0], + KeyC: ['c', 'C', '©', ' ', 0], + KeyD: ['d', 'D', '∂', '∆', 0], + KeyE: ['e', 'E', '€', '€', 0], + KeyF: ['f', 'F', 'ƒ', 'fi', 0], + KeyG: ['g', 'G', '', 'fl', 0], + KeyH: ['h', 'H', '™', ' ', 0], + KeyI: ['i', 'I', ' ', ' ', 0], + KeyJ: ['j', 'J', '¶', '¯', 0], + KeyK: ['k', 'K', '§', 'ˇ', 0], + KeyL: ['l', 'L', ' ', '˘', 0], + KeyM: ['m', 'M', 'µ', '˚', 0], + KeyN: ['n', 'N', ' ', '˙', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', ' ', 0], + KeyS: ['s', 'S', '∫', ' ', 0], + KeyT: ['t', 'T', '†', '‡', 0], + KeyU: ['u', 'U', ' ', ' ', 0], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', 'æ', 'Æ', 0], + KeyX: ['x', 'X', '∑', '›', 0], + KeyY: ['y', 'Y', '¥', ' ', 0], + KeyZ: ['z', 'Z', 'Ω', '‹', 0], + Digit1: ['1', '!', '|', 'ı', 0], + Digit2: ['2', '"', '@', '˝', 0], + Digit3: ['3', '·', '#', '•', 0], + Digit4: ['4', '$', '¢', '£', 0], + Digit5: ['5', '%', '∞', '‰', 0], + Digit6: ['6', '&', '¬', ' ', 0], + Digit7: ['7', '/', '÷', '⁄', 0], + Digit8: ['8', '(', '“', '‘', 0], + Digit9: ['9', ')', '”', '’', 0], + Digit0: ['0', '=', '≠', '≈', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['\'', '?', '´', '¸', 0], + Equal: ['¡', '¿', '‚', '˛', 0], + BracketLeft: ['`', '^', '[', 'ˆ', 3], + BracketRight: ['+', '*', ']', '±', 0], + Backslash: ['ç', 'Ç', '}', '»', 0], + Semicolon: ['ñ', 'Ñ', '~', '˜', 4], + Quote: ['´', '¨', '{', '«', 3], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '„', '', 0], + Period: ['.', ':', '…', '…', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', ',', ',', ',', 0], + IntlBackslash: ['º', 'ª', '\\', '°', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts new file mode 100644 index 0000000000..9674c9fbd2 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc105', layout: 'es', variant: '', options: '', rules: 'evdev' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'æ', 'Æ', 0], + KeyB: ['b', 'B', '”', '’', 0], + KeyC: ['c', 'C', '¢', '©', 0], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '€', '¢', 0], + KeyF: ['f', 'F', 'đ', 'ª', 0], + KeyG: ['g', 'G', 'ŋ', 'Ŋ', 0], + KeyH: ['h', 'H', 'ħ', 'Ħ', 0], + KeyI: ['i', 'I', '→', 'ı', 0], + KeyJ: ['j', 'J', '̉', '̛', 0], + KeyK: ['k', 'K', 'ĸ', '&', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: ['m', 'M', 'µ', 'º', 0], + KeyN: ['n', 'N', 'n', 'N', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'þ', 'Þ', 0], + KeyQ: ['q', 'Q', '@', 'Ω', 0], + KeyR: ['r', 'R', '¶', '®', 0], + KeyS: ['s', 'S', 'ß', '§', 0], + KeyT: ['t', 'T', 'ŧ', 'Ŧ', 0], + KeyU: ['u', 'U', '↓', '↑', 0], + KeyV: ['v', 'V', '“', '‘', 0], + KeyW: ['w', 'W', 'ł', 'Ł', 0], + KeyX: ['x', 'X', '»', '>', 0], + KeyY: ['y', 'Y', '←', '¥', 0], + KeyZ: ['z', 'Z', '«', '<', 0], + Digit1: ['1', '!', '|', '¡', 0], + Digit2: ['2', '"', '@', '⅛', 0], + Digit3: ['3', '·', '#', '£', 0], + Digit4: ['4', '$', '~', '$', 0], + Digit5: ['5', '%', '½', '⅜', 0], + Digit6: ['6', '&', '¬', '⅝', 0], + Digit7: ['7', '/', '{', '⅞', 0], + Digit8: ['8', '(', '[', '™', 0], + Digit9: ['9', ')', ']', '±', 0], + Digit0: ['0', '=', '}', '°', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['\'', '?', '\\', '¿', 0], + Equal: ['¡', '¿', '̃', '~', 0], + BracketLeft: ['̀', '̂', '[', '̊', 0], + BracketRight: ['+', '*', ']', '̄', 0], + Backslash: ['ç', 'Ç', '}', '̆', 0], + Semicolon: ['ñ', 'Ñ', '~', '̋', 0], + Quote: ['́', '̈', '{', '{', 0], + Backquote: ['º', 'ª', '\\', '\\', 0], + Comma: [',', ';', '─', '×', 0], + Period: ['.', ':', '·', '÷', 0], + Slash: ['-', '_', '̣', '̇', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: ['', '', '', '', 0], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: ['\r', '\r', '\r', '\r', 0], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', '.', '', '.', 0], + IntlBackslash: ['<', '>', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: ['.', '.', '.', '.', 0], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: ['(', '(', '(', '(', 0], + NumpadParenRight: [')', ')', ')', ')', 0], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts new file mode 100644 index 0000000000..c05628151d --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000040A', id: '', text: 'Spanish' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '|', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '·', '#', '', 0, 'VK_3'], + Digit4: ['4', '$', '~', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '¬', '', 0, 'VK_6'], + Digit7: ['7', '/', '', '', 0, 'VK_7'], + Digit8: ['8', '(', '', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '', '', 0, 'VK_OEM_4'], + Equal: ['¡', '¿', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['`', '^', '[', '', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', ']', '', 0, 'VK_OEM_PLUS'], + Backslash: ['ç', 'Ç', '}', '', 0, 'VK_OEM_2'], + Semicolon: ['ñ', 'Ñ', '', '', 0, 'VK_OEM_3'], + Quote: ['´', '¨', '{', '', 0, 'VK_OEM_7'], + Backquote: ['º', 'ª', '\\', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts new file mode 100644 index 0000000000..a9a0bf0800 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.French', lang: 'fr', localizedName: 'French' }, + secondaryLayouts: [], + mapping: { + KeyA: ['q', 'Q', '‡', 'Ω', 0], + KeyB: ['b', 'B', 'ß', '∫', 0], + KeyC: ['c', 'C', '©', '¢', 0], + KeyD: ['d', 'D', '∂', '∆', 0], + KeyE: ['e', 'E', 'ê', 'Ê', 0], + KeyF: ['f', 'F', 'ƒ', '·', 0], + KeyG: ['g', 'G', 'fi', 'fl', 0], + KeyH: ['h', 'H', 'Ì', 'Î', 0], + KeyI: ['i', 'I', 'î', 'ï', 0], + KeyJ: ['j', 'J', 'Ï', 'Í', 0], + KeyK: ['k', 'K', 'È', 'Ë', 0], + KeyL: ['l', 'L', '¬', '|', 0], + KeyM: [',', '?', '∞', '¿', 0], + KeyN: ['n', 'N', '~', 'ı', 4], + KeyO: ['o', 'O', 'œ', 'Œ', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['a', 'A', 'æ', 'Æ', 0], + KeyR: ['r', 'R', '®', '‚', 0], + KeyS: ['s', 'S', 'Ò', '∑', 0], + KeyT: ['t', 'T', '†', '™', 0], + KeyU: ['u', 'U', 'º', 'ª', 0], + KeyV: ['v', 'V', '◊', '√', 0], + KeyW: ['z', 'Z', 'Â', 'Å', 0], + KeyX: ['x', 'X', '≈', '⁄', 0], + KeyY: ['y', 'Y', 'Ú', 'Ÿ', 0], + KeyZ: ['w', 'W', '‹', '›', 0], + Digit1: ['&', '1', '', '´', 8], + Digit2: ['é', '2', 'ë', '„', 0], + Digit3: ['"', '3', '“', '”', 0], + Digit4: ['\'', '4', '‘', '’', 0], + Digit5: ['(', '5', '{', '[', 0], + Digit6: ['§', '6', '¶', 'å', 0], + Digit7: ['è', '7', '«', '»', 0], + Digit8: ['!', '8', '¡', 'Û', 0], + Digit9: ['ç', '9', 'Ç', 'Á', 0], + Digit0: ['à', '0', 'ø', 'Ø', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: [')', '°', '}', ']', 0], + Equal: ['-', '_', '—', '–', 0], + BracketLeft: ['^', '¨', 'ô', 'Ô', 3], + BracketRight: ['$', '*', '€', '¥', 0], + Backslash: ['`', '£', '@', '#', 1], + Semicolon: ['m', 'M', 'µ', 'Ó', 0], + Quote: ['ù', '%', 'Ù', '‰', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [';', '.', '…', '•', 0], + Period: [':', '/', '÷', '\\', 0], + Slash: ['=', '+', '≠', '±', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', '.', 0], + IntlBackslash: ['@', '#', '•', 'Ÿ', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts new file mode 100644 index 0000000000..2ad79ae6a5 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc104', layout: 'fr', variant: '', options: '', rules: 'base' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['q', 'Q', '@', 'Ω', 0], + KeyB: ['b', 'B', '”', '’', 0], + KeyC: ['c', 'C', '¢', '©', 0], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '€', '¢', 0], + KeyF: ['f', 'F', 'đ', 'ª', 0], + KeyG: ['g', 'G', 'ŋ', 'Ŋ', 0], + KeyH: ['h', 'H', 'ħ', 'Ħ', 0], + KeyI: ['i', 'I', '→', 'ı', 0], + KeyJ: ['j', 'J', '̉', '̛', 0], + KeyK: ['k', 'K', 'ĸ', '&', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: [',', '?', '́', '̋', 0], + KeyN: ['n', 'N', 'n', 'N', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'þ', 'Þ', 0], + KeyQ: ['a', 'A', 'æ', 'Æ', 0], + KeyR: ['r', 'R', '¶', '®', 0], + KeyS: ['s', 'S', 'ß', '§', 0], + KeyT: ['t', 'T', 'ŧ', 'Ŧ', 0], + KeyU: ['u', 'U', '↓', '↑', 0], + KeyV: ['v', 'V', '“', '‘', 0], + KeyW: ['z', 'Z', '«', '<', 0], + KeyX: ['x', 'X', '»', '>', 0], + KeyY: ['y', 'Y', '←', '¥', 0], + KeyZ: ['w', 'W', 'ł', 'Ł', 0], + Digit1: ['&', '1', '¹', '¡', 0], + Digit2: ['é', '2', '~', '⅛', 0], + Digit3: ['"', '3', '#', '£', 0], + Digit4: ['\'', '4', '{', '$', 0], + Digit5: ['(', '5', '[', '⅜', 0], + Digit6: ['-', '6', '|', '⅝', 0], + Digit7: ['è', '7', '`', '⅞', 0], + Digit8: ['_', '8', '\\', '™', 0], + Digit9: ['ç', '9', '^', '±', 0], + Digit0: ['à', '0', '@', '°', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: [')', '°', ']', '¿', 0], + Equal: ['=', '+', '}', '̨', 0], + BracketLeft: ['̂', '̈', '̈', '̊', 0], + BracketRight: ['$', '£', '¤', '̄', 0], + Backslash: ['*', 'µ', '̀', '̆', 0], + Semicolon: ['m', 'M', 'µ', 'º', 0], + Quote: ['ù', '%', '̂', '̌', 0], + Backquote: ['²', '~', '¬', '¬', 0], + Comma: [';', '.', '─', '×', 0], + Period: [':', '/', '·', '÷', 0], + Slash: ['!', '§', '̣', '̇', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: ['', '', '', '', 0], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: ['/', '/', '/', '/', 0], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: [], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', '.', '', '.', 0], + IntlBackslash: ['<', '>', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: [], + NumpadParenRight: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: ['\r', '\r', '\r', '\r', 0], + MetaRight: ['.', '.', '.', '.', 0], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts new file mode 100644 index 0000000000..9b63990525 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000040C', id: '', text: 'French' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: [',', '?', '', '', 0, 'VK_OEM_COMMA'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['a', 'A', '', '', 0, 'VK_A'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['w', 'W', '', '', 0, 'VK_W'], + Digit1: ['&', '1', '', '', 0, 'VK_1'], + Digit2: ['é', '2', '~', '', 0, 'VK_2'], + Digit3: ['"', '3', '#', '', 0, 'VK_3'], + Digit4: ['\'', '4', '{', '', 0, 'VK_4'], + Digit5: ['(', '5', '[', '', 0, 'VK_5'], + Digit6: ['-', '6', '|', '', 0, 'VK_6'], + Digit7: ['è', '7', '`', '', 0, 'VK_7'], + Digit8: ['_', '8', '\\', '', 0, 'VK_8'], + Digit9: ['ç', '9', '^', '', 0, 'VK_9'], + Digit0: ['à', '0', '@', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: [')', '°', ']', '', 0, 'VK_OEM_4'], + Equal: ['=', '+', '}', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['^', '¨', '', '', 0, 'VK_OEM_6'], + BracketRight: ['$', '£', '¤', '', 0, 'VK_OEM_1'], + Backslash: ['*', 'µ', '', '', 0, 'VK_OEM_5'], + Semicolon: ['m', 'M', '', '', 0, 'VK_M'], + Quote: ['ù', '%', '', '', 0, 'VK_OEM_3'], + Backquote: ['²', '', '', '', 0, 'VK_OEM_7'], + Comma: [';', '.', '', '', 0, 'VK_OEM_PERIOD'], + Period: [':', '/', '', '', 0, 'VK_OEM_2'], + Slash: ['!', '§', '', '', 0, 'VK_OEM_8'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts new file mode 100644 index 0000000000..ee21c5a22b --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000040E', id: '', text: 'Hungarian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'ä', '', 0, 'VK_A'], + KeyB: ['b', 'B', '{', '', 0, 'VK_B'], + KeyC: ['c', 'C', '&', '', 0, 'VK_C'], + KeyD: ['d', 'D', 'Đ', '', 0, 'VK_D'], + KeyE: ['e', 'E', 'Ä', '', 0, 'VK_E'], + KeyF: ['f', 'F', '[', '', 0, 'VK_F'], + KeyG: ['g', 'G', ']', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', 'Í', '', 0, 'VK_I'], + KeyJ: ['j', 'J', 'í', '', 0, 'VK_J'], + KeyK: ['k', 'K', 'ł', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'Ł', '', 0, 'VK_L'], + KeyM: ['m', 'M', '<', '', 0, 'VK_M'], + KeyN: ['n', 'N', '}', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '\\', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'đ', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '€', '', 0, 'VK_U'], + KeyV: ['v', 'V', '@', '', 0, 'VK_V'], + KeyW: ['w', 'W', '|', '', 0, 'VK_W'], + KeyX: ['x', 'X', '#', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '>', '', 0, 'VK_Y'], + Digit1: ['1', '\'', '~', '', 0, 'VK_1'], + Digit2: ['2', '"', 'ˇ', '', 0, 'VK_2'], + Digit3: ['3', '+', '^', '', 0, 'VK_3'], + Digit4: ['4', '!', '˘', '', 0, 'VK_4'], + Digit5: ['5', '%', '°', '', 0, 'VK_5'], + Digit6: ['6', '/', '˛', '', 0, 'VK_6'], + Digit7: ['7', '=', '`', '', 0, 'VK_7'], + Digit8: ['8', '(', '˙', '', 0, 'VK_8'], + Digit9: ['9', ')', '´', '', 0, 'VK_9'], + Digit0: ['ö', 'Ö', '˝', '', 0, 'VK_OEM_3'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['ü', 'Ü', '¨', '', 0, 'VK_OEM_2'], + Equal: ['ó', 'Ó', '¸', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['ő', 'Ő', '÷', '', 0, 'VK_OEM_4'], + BracketRight: ['ú', 'Ú', '×', '', 0, 'VK_OEM_6'], + Backslash: ['ű', 'Ű', '¤', '', 0, 'VK_OEM_5'], + Semicolon: ['é', 'É', '$', '', 0, 'VK_OEM_1'], + Quote: ['á', 'Á', 'ß', '', 0, 'VK_OEM_7'], + Backquote: ['0', '§', '', '', 0, 'VK_0'], + Comma: [',', '?', ';', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '>', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '*', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['í', 'Í', '<', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts new file mode 100644 index 0000000000..00f1044217 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Italian-Pro', lang: 'it', localizedName: 'Italian' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'Í', 0], + KeyC: ['c', 'C', '©', 'Á', 0], + KeyD: ['d', 'D', '∂', '˘', 0], + KeyE: ['e', 'E', '€', 'È', 0], + KeyF: ['f', 'F', 'ƒ', '˙', 0], + KeyG: ['g', 'G', '∞', '˚', 0], + KeyH: ['h', 'H', '∆', '¸', 0], + KeyI: ['i', 'I', 'œ', 'Œ', 0], + KeyJ: ['j', 'J', 'ª', '˝', 0], + KeyK: ['k', 'K', 'º', '˛', 0], + KeyL: ['l', 'L', '¬', 'ˇ', 0], + KeyM: ['m', 'M', 'µ', 'Ú', 0], + KeyN: ['n', 'N', '˜', 'Ó', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', '„', '‚', 0], + KeyR: ['r', 'R', '®', 'Ì', 0], + KeyS: ['s', 'S', 'ß', '¯', 0], + KeyT: ['t', 'T', '™', 'Ò', 0], + KeyU: ['u', 'U', '¨', 'Ù', 4], + KeyV: ['v', 'V', '√', 'É', 0], + KeyW: ['w', 'W', 'Ω', 'À', 0], + KeyX: ['x', 'X', '†', '‡', 0], + KeyY: ['y', 'Y', 'æ', 'Æ', 0], + KeyZ: ['z', 'Z', '∑', ' ', 0], + Digit1: ['1', '!', '«', '»', 0], + Digit2: ['2', '"', '“', '”', 0], + Digit3: ['3', '£', '‘', '’', 0], + Digit4: ['4', '$', '¥', '¢', 0], + Digit5: ['5', '%', '~', '‰', 0], + Digit6: ['6', '&', '‹', '›', 0], + Digit7: ['7', '/', '÷', '⁄', 0], + Digit8: ['8', '(', '´', '', 4], + Digit9: ['9', ')', '`', ' ', 4], + Digit0: ['0', '=', '≠', '≈', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['\'', '?', '¡', '¿', 0], + Equal: ['ì', '^', 'ˆ', '±', 4], + BracketLeft: ['è', 'é', '[', '{', 0], + BracketRight: ['+', '*', ']', '}', 0], + Backslash: ['ù', '§', '¶', '◊', 0], + Semicolon: ['ò', 'ç', '@', 'Ç', 0], + Quote: ['à', '°', '#', '∞', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '…', ' ', 0], + Period: ['.', ':', '•', '·', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', '.', 0], + IntlBackslash: ['\\', '|', '`', 'ı', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts new file mode 100644 index 0000000000..b9526ee5da --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000410', id: '', text: 'Italian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '£', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '', '', 0, 'VK_7'], + Digit8: ['8', '(', '', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '', '', 0, 'VK_OEM_4'], + Equal: ['ì', '^', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['è', 'é', '[', '{', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', ']', '}', 0, 'VK_OEM_PLUS'], + Backslash: ['ù', '§', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ò', 'ç', '@', '', 0, 'VK_OEM_3'], + Quote: ['à', '°', '#', '', 0, 'VK_OEM_7'], + Backquote: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts new file mode 100644 index 0000000000..3b76a1aaa9 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.google.inputmethod.Japanese.Roman', lang: 'en', localizedName: 'Alphanumeric (Google)' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', '¯', '̄', 4], + KeyB: ['b', 'B', '˘', '̆', 4], + KeyC: ['c', 'C', '¸', '̧', 4], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '´', '́', 4], + KeyF: ['f', 'F', 'ƒ', '', 0], + KeyG: ['g', 'G', '©', '‸', 8], + KeyH: ['h', 'H', 'ˍ', '̱', 4], + KeyI: ['i', 'I', 'ʼ', '̛', 4], + KeyJ: ['j', 'J', '˝', '̋', 4], + KeyK: ['k', 'K', '˚', '̊', 4], + KeyL: ['l', 'L', '-', '̵', 4], + KeyM: ['m', 'M', '˛', '̨', 4], + KeyN: ['n', 'N', '˜', '̃', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', ',', '̦', 4], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', '', 0], + KeyT: ['t', 'T', 'þ', 'Þ', 0], + KeyU: ['u', 'U', '¨', '̈', 4], + KeyV: ['v', 'V', 'ˇ', '̌', 4], + KeyW: ['w', 'W', '˙', '̇', 4], + KeyX: ['x', 'X', '.', '̣', 4], + KeyY: ['y', 'Y', '¥', '', 0], + KeyZ: ['z', 'Z', 'ˀ', '̉', 4], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '§', '†', 0], + Digit6: ['6', '^', 'ˆ', '̂', 4], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', '№', 8], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '̀', 4], + Comma: [',', '<', '≤', '„', 0], + Period: ['.', '>', '≥', 'ʔ', 8], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts new file mode 100644 index 0000000000..46798076f8 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.inputmethod.Kotoeri.Japanese', lang: 'ja', localizedName: 'Hiragana' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '^', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '`', 4], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts new file mode 100644 index 0000000000..c4aaeb7750 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.inputmethod.Korean.2SetKorean', lang: 'ko', localizedName: '2-Set Korean' }, + secondaryLayouts: [], + mapping: { + KeyA: ['ㅁ', 'ㅁ', 'a', 'A', 0], + KeyB: ['ㅠ', 'ㅠ', 'b', 'B', 0], + KeyC: ['ㅊ', 'ㅊ', 'c', 'C', 0], + KeyD: ['ㅇ', 'ㅇ', 'd', 'D', 0], + KeyE: ['ㄷ', 'ㄸ', 'e', 'E', 0], + KeyF: ['ㄹ', 'ㄹ', 'f', 'F', 0], + KeyG: ['ㅎ', 'ㅎ', 'g', 'G', 0], + KeyH: ['ㅗ', 'ㅗ', 'h', 'H', 0], + KeyI: ['ㅑ', 'ㅑ', 'i', 'I', 0], + KeyJ: ['ㅓ', 'ㅓ', 'j', 'J', 0], + KeyK: ['ㅏ', 'ㅏ', 'k', 'K', 0], + KeyL: ['ㅣ', 'ㅣ', 'l', 'L', 0], + KeyM: ['ㅡ', 'ㅡ', 'm', 'M', 0], + KeyN: ['ㅜ', 'ㅜ', 'n', 'N', 0], + KeyO: ['ㅐ', 'ㅒ', 'o', 'O', 0], + KeyP: ['ㅔ', 'ㅖ', 'p', 'P', 0], + KeyQ: ['ㅂ', 'ㅃ', 'q', 'Q', 0], + KeyR: ['ㄱ', 'ㄲ', 'r', 'R', 0], + KeyS: ['ㄴ', 'ㄴ', 's', 'S', 0], + KeyT: ['ㅅ', 'ㅆ', 't', 'T', 0], + KeyU: ['ㅕ', 'ㅕ', 'u', 'U', 0], + KeyV: ['ㅍ', 'ㅍ', 'v', 'V', 0], + KeyW: ['ㅈ', 'ㅉ', 'w', 'W', 0], + KeyX: ['ㅌ', 'ㅌ', 'x', 'X', 0], + KeyY: ['ㅛ', 'ㅛ', 'y', 'Y', 0], + KeyZ: ['ㅋ', 'ㅋ', 'z', 'Z', 0], + Digit1: ['1', '!', '1', '!', 0], + Digit2: ['2', '@', '2', '@', 0], + Digit3: ['3', '#', '3', '#', 0], + Digit4: ['4', '$', '4', '$', 0], + Digit5: ['5', '%', '5', '%', 0], + Digit6: ['6', '^', '6', '^', 0], + Digit7: ['7', '&', '7', '&', 0], + Digit8: ['8', '*', '8', '*', 0], + Digit9: ['9', '(', '9', '(', 0], + Digit0: ['0', ')', '0', ')', 0], + Enter: [], + Escape: ['', '', '', '‌', 0], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '-', '_', 0], + Equal: ['=', '+', '=', '+', 0], + BracketLeft: ['[', '{', '[', '{', 0], + BracketRight: [']', '}', ']', '}', 0], + Backslash: ['\\', '|', '\\', '|', 0], + Semicolon: [';', ':', ';', ':', 0], + Quote: ['\'', '"', '\'', '"', 0], + Backquote: ['₩', '~', '`', '~', 0], + Comma: [',', '<', ',', '<', 0], + Period: ['.', '>', '.', '>', 0], + Slash: ['/', '?', '/', '?', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.ts new file mode 100644 index 0000000000..826e790aa0 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.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 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin'; // 15% +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin'; + +export { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.ts new file mode 100644 index 0000000000..affe520976 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.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 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux'; + +export { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.ts new file mode 100644 index 0000000000..1884cee996 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.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 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.win'; // 40% +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/it.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/no.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win'; + +export { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts new file mode 100644 index 0000000000..d84800c065 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000414', id: '', text: 'Norwegian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '¤', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['+', '?', '', '', 0, 'VK_OEM_PLUS'], + Equal: ['\\', '`', '´', '', 0, 'VK_OEM_4'], + BracketLeft: ['å', 'Å', '', '', 0, 'VK_OEM_6'], + BracketRight: ['¨', '^', '~', '', 0, 'VK_OEM_1'], + Backslash: ['\'', '*', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ø', 'Ø', '', '', 0, 'VK_OEM_3'], + Quote: ['æ', 'Æ', '', '', 0, 'VK_OEM_7'], + Backquote: ['|', '§', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts new file mode 100644 index 0000000000..7aecff5814 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.PolishPro', lang: 'pl', localizedName: 'Polish - Pro' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'ą', 'Ą', 0], + KeyB: ['b', 'B', 'ļ', 'ű', 0], + KeyC: ['c', 'C', 'ć', 'Ć', 0], + KeyD: ['d', 'D', '∂', 'Ž', 0], + KeyE: ['e', 'E', 'ę', 'Ę', 0], + KeyF: ['f', 'F', 'ń', 'ž', 0], + KeyG: ['g', 'G', '©', 'Ū', 0], + KeyH: ['h', 'H', 'ķ', 'Ó', 0], + KeyI: ['i', 'I', '^', 'ť', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', 'Ż', 'ū', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: ['m', 'M', 'Ķ', 'ų', 0], + KeyN: ['n', 'N', 'ń', 'Ń', 0], + KeyO: ['o', 'O', 'ó', 'Ó', 0], + KeyP: ['p', 'P', 'Ļ', 'ł', 0], + KeyQ: ['q', 'Q', 'Ō', 'ő', 0], + KeyR: ['r', 'R', '®', '£', 0], + KeyS: ['s', 'S', 'ś', 'Ś', 0], + KeyT: ['t', 'T', '†', 'ś', 0], + KeyU: ['u', 'U', '¨', 'Ť', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', 'ź', 'Ź', 0], + KeyY: ['y', 'Y', 'ī', 'Á', 0], + KeyZ: ['z', 'Z', 'ż', 'Ż', 0], + Digit1: ['1', '!', 'Ń', 'ŕ', 0], + Digit2: ['2', '@', '™', 'Ř', 0], + Digit3: ['3', '#', '€', '‹', 0], + Digit4: ['4', '$', 'ß', '›', 0], + Digit5: ['5', '%', 'į', 'ř', 0], + Digit6: ['6', '^', '§', 'Ŗ', 0], + Digit7: ['7', '&', '¶', 'ŗ', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'Ľ', 'Š', 0], + Digit0: ['0', ')', 'ľ', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', 'Ī', 0], + BracketLeft: ['[', '{', '„', '”', 0], + BracketRight: [']', '}', '‚', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'ĺ', 'ģ', 0], + Backquote: ['`', '~', '`', 'Ŕ', 4], + Comma: [',', '<', '≤', 'Ý', 0], + Period: ['.', '>', '≥', 'ý', 0], + Slash: ['/', '?', '÷', 'ņ', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '£', '¬', '¬', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts new file mode 100644 index 0000000000..0ba8ceff01 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000415', id: '', text: 'Polish (Programmers)' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'ą', 'Ą', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', 'ć', 'Ć', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', 'ę', 'Ę', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'ł', 'Ł', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', 'ń', 'Ń', 0, 'VK_N'], + KeyO: ['o', 'O', 'ó', 'Ó', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'ś', 'Ś', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '€', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', 'ź', 'Ź', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', 'ż', 'Ż', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '@', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '', '', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts new file mode 100644 index 0000000000..c0f24581d5 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000416', id: '', text: 'Portuguese (Brazilian ABNT)' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '₢', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '°', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '/', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '?', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '¹', '', 0, 'VK_1'], + Digit2: ['2', '@', '²', '', 0, 'VK_2'], + Digit3: ['3', '#', '³', '', 0, 'VK_3'], + Digit4: ['4', '$', '£', '', 0, 'VK_4'], + Digit5: ['5', '%', '¢', '', 0, 'VK_5'], + Digit6: ['6', '¨', '¬', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '§', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['´', '`', '', '', 0, 'VK_OEM_4'], + BracketRight: ['[', '{', 'ª', '', 0, 'VK_OEM_6'], + Backslash: [']', '}', 'º', '', 0, 'VK_OEM_5'], + Semicolon: ['ç', 'Ç', '', '', 0, 'VK_OEM_1'], + Quote: ['~', '^', '', '', 0, 'VK_OEM_7'], + Backquote: ['\'', '"', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: [';', ':', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: ['.', '.', '', '', 0, 'VK_ABNT_C2'], + IntlRo: ['/', '?', '°', '', 0, 'VK_ABNT_C1'], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts new file mode 100644 index 0000000000..7be853c8b1 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Brazilian-Pro', lang: 'pt' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', 'ˆ', '§', 'fl', 2], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 3], + Backquote: ['`', '˜', '`', '`', 7], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts new file mode 100644 index 0000000000..5009825331 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000816', id: '', text: 'Portuguese' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '$', '§', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '', '', 0, 'VK_OEM_4'], + Equal: ['«', '»', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['+', '*', '¨', '', 0, 'VK_OEM_PLUS'], + BracketRight: ['´', '`', ']', '', 0, 'VK_OEM_1'], + Backslash: ['~', '^', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ç', 'Ç', '', '', 0, 'VK_OEM_3'], + Quote: ['º', 'ª', '', '', 0, 'VK_OEM_7'], + Backquote: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts new file mode 100644 index 0000000000..749e8c11d9 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Russian', lang: 'ru', localizedName: 'Russian' }, + secondaryLayouts: [], + mapping: { + KeyA: ['ф', 'Ф', 'ƒ', 'ƒ', 0], + KeyB: ['и', 'И', 'и', 'И', 0], + KeyC: ['с', 'С', '≠', '≠', 0], + KeyD: ['в', 'В', 'ћ', 'Ћ', 0], + KeyE: ['у', 'У', 'ќ', 'Ќ', 0], + KeyF: ['а', 'А', '÷', '÷', 0], + KeyG: ['п', 'П', '©', '©', 0], + KeyH: ['р', 'Р', '₽', '₽', 0], + KeyI: ['ш', 'Ш', 'ѕ', 'Ѕ', 0], + KeyJ: ['о', 'О', '°', '•', 0], + KeyK: ['л', 'Л', 'љ', 'Љ', 0], + KeyL: ['д', 'Д', '∆', '∆', 0], + KeyM: ['ь', 'Ь', '~', '~', 0], + KeyN: ['т', 'Т', '™', '™', 0], + KeyO: ['щ', 'Щ', 'ў', 'Ў', 0], + KeyP: ['з', 'З', '‘', '’', 0], + KeyQ: ['й', 'Й', 'ј', 'Ј', 0], + KeyR: ['к', 'К', '®', '®', 0], + KeyS: ['ы', 'Ы', 'ы', 'Ы', 0], + KeyT: ['е', 'Е', '†', '†', 0], + KeyU: ['г', 'Г', 'ѓ', 'Ѓ', 0], + KeyV: ['м', 'М', 'µ', 'µ', 0], + KeyW: ['ц', 'Ц', 'џ', 'Џ', 0], + KeyX: ['ч', 'Ч', '≈', '≈', 0], + KeyY: ['н', 'Н', 'њ', 'Њ', 0], + KeyZ: ['я', 'Я', 'ђ', 'Ђ', 0], + Digit1: ['1', '!', '!', '|', 0], + Digit2: ['2', '"', '@', '"', 0], + Digit3: ['3', '№', '#', '£', 0], + Digit4: ['4', '%', '$', '€', 0], + Digit5: ['5', ':', '%', '∞', 0], + Digit6: ['6', ',', '^', '¬', 0], + Digit7: ['7', '.', '&', '¶', 0], + Digit8: ['8', ';', '*', '√', 0], + Digit9: ['9', '(', '{', '\'', 0], + Digit0: ['0', ')', '}', '`', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '»', '«', 0], + BracketLeft: ['х', 'Х', '“', '”', 0], + BracketRight: ['ъ', 'Ъ', 'ъ', 'Ъ', 0], + Backslash: ['ё', 'Ё', 'ё', 'Ё', 0], + Semicolon: ['ж', 'Ж', '…', '…', 0], + Quote: ['э', 'Э', 'э', 'Э', 0], + Backquote: [']', '[', ']', '[', 0], + Comma: ['б', 'Б', '≤', '<', 0], + Period: ['ю', 'Ю', '≥', '>', 0], + Slash: ['/', '?', '“', '„', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', ',', 0], + IntlBackslash: ['>', '<', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts new file mode 100644 index 0000000000..3b284dbe65 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc104', layout: 'ru', variant: ',', options: '', rules: 'base' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['ф', 'Ф', 'ф', 'Ф', 0], + KeyB: ['и', 'И', 'и', 'И', 0], + KeyC: ['с', 'С', 'с', 'С', 0], + KeyD: ['в', 'В', 'в', 'В', 0], + KeyE: ['у', 'У', 'у', 'У', 0], + KeyF: ['а', 'А', 'а', 'А', 0], + KeyG: ['п', 'П', 'п', 'П', 0], + KeyH: ['р', 'Р', 'р', 'Р', 0], + KeyI: ['ш', 'Ш', 'ш', 'Ш', 0], + KeyJ: ['о', 'О', 'о', 'О', 0], + KeyK: ['л', 'Л', 'л', 'Л', 0], + KeyL: ['д', 'Д', 'д', 'Д', 0], + KeyM: ['ь', 'Ь', 'ь', 'Ь', 0], + KeyN: ['т', 'Т', 'т', 'Т', 0], + KeyO: ['щ', 'Щ', 'щ', 'Щ', 0], + KeyP: ['з', 'З', 'з', 'З', 0], + KeyQ: ['й', 'Й', 'й', 'Й', 0], + KeyR: ['к', 'К', 'к', 'К', 0], + KeyS: ['ы', 'Ы', 'ы', 'Ы', 0], + KeyT: ['е', 'Е', 'е', 'Е', 0], + KeyU: ['г', 'Г', 'г', 'Г', 0], + KeyV: ['м', 'М', 'м', 'М', 0], + KeyW: ['ц', 'Ц', 'ц', 'Ц', 0], + KeyX: ['ч', 'Ч', 'ч', 'Ч', 0], + KeyY: ['н', 'Н', 'н', 'Н', 0], + KeyZ: ['я', 'Я', 'я', 'Я', 0], + Digit1: ['1', '!', '1', '!', 0], + Digit2: ['2', '"', '2', '"', 0], + Digit3: ['3', '№', '3', '№', 0], + Digit4: ['4', ';', '4', ';', 0], + Digit5: ['5', '%', '5', '%', 0], + Digit6: ['6', ':', '6', ':', 0], + Digit7: ['7', '?', '7', '?', 0], + Digit8: ['8', '*', '₽', '', 0], + Digit9: ['9', '(', '9', '(', 0], + Digit0: ['0', ')', '0', ')', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '-', '_', 0], + Equal: ['=', '+', '=', '+', 0], + BracketLeft: ['х', 'Х', 'х', 'Х', 0], + BracketRight: ['ъ', 'Ъ', 'ъ', 'Ъ', 0], + Backslash: ['\\', '/', '\\', '/', 0], + Semicolon: ['ж', 'Ж', 'ж', 'Ж', 0], + Quote: ['э', 'Э', 'э', 'Э', 0], + Backquote: ['ё', 'Ё', 'ё', 'Ё', 0], + Comma: ['б', 'Б', 'б', 'Б', 0], + Period: ['ю', 'Ю', 'ю', 'Ю', 0], + Slash: ['.', ',', '.', ',', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: ['', '', '', '', 0], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: ['/', '/', '/', '/', 0], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: [], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', ',', '', ',', 0], + IntlBackslash: ['/', '|', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: [], + NumpadParenRight: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: ['\r', '\r', '\r', '\r', 0], + MetaRight: ['.', '.', '.', '.', 0], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts new file mode 100644 index 0000000000..2bf016dec7 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000419', id: '', text: 'Russian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['ф', 'Ф', '', '', 0, 'VK_A'], + KeyB: ['и', 'И', '', '', 0, 'VK_B'], + KeyC: ['с', 'С', '', '', 0, 'VK_C'], + KeyD: ['в', 'В', '', '', 0, 'VK_D'], + KeyE: ['у', 'У', '', '', 0, 'VK_E'], + KeyF: ['а', 'А', '', '', 0, 'VK_F'], + KeyG: ['п', 'П', '', '', 0, 'VK_G'], + KeyH: ['р', 'Р', '', '', 0, 'VK_H'], + KeyI: ['ш', 'Ш', '', '', 0, 'VK_I'], + KeyJ: ['о', 'О', '', '', 0, 'VK_J'], + KeyK: ['л', 'Л', '', '', 0, 'VK_K'], + KeyL: ['д', 'Д', '', '', 0, 'VK_L'], + KeyM: ['ь', 'Ь', '', '', 0, 'VK_M'], + KeyN: ['т', 'Т', '', '', 0, 'VK_N'], + KeyO: ['щ', 'Щ', '', '', 0, 'VK_O'], + KeyP: ['з', 'З', '', '', 0, 'VK_P'], + KeyQ: ['й', 'Й', '', '', 0, 'VK_Q'], + KeyR: ['к', 'К', '', '', 0, 'VK_R'], + KeyS: ['ы', 'Ы', '', '', 0, 'VK_S'], + KeyT: ['е', 'Е', '', '', 0, 'VK_T'], + KeyU: ['г', 'Г', '', '', 0, 'VK_U'], + KeyV: ['м', 'М', '', '', 0, 'VK_V'], + KeyW: ['ц', 'Ц', '', '', 0, 'VK_W'], + KeyX: ['ч', 'Ч', '', '', 0, 'VK_X'], + KeyY: ['н', 'Н', '', '', 0, 'VK_Y'], + KeyZ: ['я', 'Я', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '№', '', '', 0, 'VK_3'], + Digit4: ['4', ';', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', ':', '', '', 0, 'VK_6'], + Digit7: ['7', '?', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '₽', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['х', 'Х', '', '', 0, 'VK_OEM_4'], + BracketRight: ['ъ', 'Ъ', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '/', '', '', 0, 'VK_OEM_5'], + Semicolon: ['ж', 'Ж', '', '', 0, 'VK_OEM_1'], + Quote: ['э', 'Э', '', '', 0, 'VK_OEM_7'], + Backquote: ['ё', 'Ё', '', '', 0, 'VK_OEM_3'], + Comma: ['б', 'Б', '', '', 0, 'VK_OEM_COMMA'], + Period: ['ю', 'Ю', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['.', ',', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '/', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts new file mode 100644 index 0000000000..26c60a6f52 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Swedish-Pro', lang: 'sv', localizedName: 'Swedish - Pro' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', '', '◊', 0], + KeyB: ['b', 'B', '›', '»', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', '∆', 0], + KeyE: ['e', 'E', 'é', 'É', 0], + KeyF: ['f', 'F', 'ƒ', '∫', 0], + KeyG: ['g', 'G', '¸', '¯', 0], + KeyH: ['h', 'H', '˛', '˘', 0], + KeyI: ['i', 'I', 'ı', 'ˆ', 0], + KeyJ: ['j', 'J', '√', '¬', 0], + KeyK: ['k', 'K', 'ª', 'º', 0], + KeyL: ['l', 'L', 'fi', 'fl', 0], + KeyM: ['m', 'M', '’', '”', 0], + KeyN: ['n', 'N', '‘', '“', 0], + KeyO: ['o', 'O', 'œ', 'Œ', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', '•', '°', 0], + KeyR: ['r', 'R', '®', '√', 0], + KeyS: ['s', 'S', 'ß', '∑', 0], + KeyT: ['t', 'T', '†', '‡', 0], + KeyU: ['u', 'U', 'ü', 'Ü', 0], + KeyV: ['v', 'V', '‹', '«', 0], + KeyW: ['w', 'W', 'Ω', '˝', 0], + KeyX: ['x', 'X', '≈', 'ˇ', 0], + KeyY: ['y', 'Y', 'µ', '˜', 0], + KeyZ: ['z', 'Z', '÷', '⁄', 0], + Digit1: ['1', '!', '©', '¡', 0], + Digit2: ['2', '"', '@', '”', 0], + Digit3: ['3', '#', '£', '¥', 0], + Digit4: ['4', '€', '$', '¢', 0], + Digit5: ['5', '%', '∞', '‰', 0], + Digit6: ['6', '&', '§', '¶', 0], + Digit7: ['7', '/', '|', '\\', 0], + Digit8: ['8', '(', '[', '{', 0], + Digit9: ['9', ')', ']', '}', 0], + Digit0: ['0', '=', '≈', '≠', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['+', '?', '±', '¿', 0], + Equal: ['´', '`', '´', '`', 3], + BracketLeft: ['å', 'Å', '˙', '˚', 0], + BracketRight: ['¨', '^', '~', '^', 7], + Backslash: ['\'', '*', '™', '’', 0], + Semicolon: ['ö', 'Ö', 'ø', 'Ø', 0], + Quote: ['ä', 'Ä', 'æ', 'Æ', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '‚', '„', 0], + Period: ['.', ':', '…', '·', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', '.', 0], + IntlBackslash: ['§', '°', '¶', '•', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts new file mode 100644 index 0000000000..64e58d676d --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000041D', id: '', text: 'Swedish' }, + secondaryLayouts: [ + { name: '0000040B', id: '', text: 'Finnish' } + ], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '¤', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['+', '?', '\\', '', 0, 'VK_OEM_PLUS'], + Equal: ['´', '`', '', '', 0, 'VK_OEM_4'], + BracketLeft: ['å', 'Å', '', '', 0, 'VK_OEM_6'], + BracketRight: ['¨', '^', '~', '', 0, 'VK_OEM_1'], + Backslash: ['\'', '*', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ö', 'Ö', '', '', 0, 'VK_OEM_3'], + Quote: ['ä', 'Ä', '', '', 0, 'VK_OEM_7'], + Backquote: ['§', '½', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '|', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts new file mode 100644 index 0000000000..53ff3d64a9 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000041E', id: '', text: 'Thai Kedmanee' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['ฟ', 'ฤ', '', '', 0, 'VK_A'], + KeyB: ['ิ', 'ฺ', '', '', 0, 'VK_B'], + KeyC: ['แ', 'ฉ', '', '', 0, 'VK_C'], + KeyD: ['ก', 'ฏ', '', '', 0, 'VK_D'], + KeyE: ['ำ', 'ฎ', '', '', 0, 'VK_E'], + KeyF: ['ด', 'โ', '', '', 0, 'VK_F'], + KeyG: ['เ', 'ฌ', '', '', 0, 'VK_G'], + KeyH: ['้', '็', '', '', 0, 'VK_H'], + KeyI: ['ร', 'ณ', '', '', 0, 'VK_I'], + KeyJ: ['่', '๋', '', '', 0, 'VK_J'], + KeyK: ['า', 'ษ', '', '', 0, 'VK_K'], + KeyL: ['ส', 'ศ', '', '', 0, 'VK_L'], + KeyM: ['ท', '?', '', '', 0, 'VK_M'], + KeyN: ['ื', '์', '', '', 0, 'VK_N'], + KeyO: ['น', 'ฯ', '', '', 0, 'VK_O'], + KeyP: ['ย', 'ญ', '', '', 0, 'VK_P'], + KeyQ: ['ๆ', '๐', '', '', 0, 'VK_Q'], + KeyR: ['พ', 'ฑ', '', '', 0, 'VK_R'], + KeyS: ['ห', 'ฆ', '', '', 0, 'VK_S'], + KeyT: ['ะ', 'ธ', '', '', 0, 'VK_T'], + KeyU: ['ี', '๊', '', '', 0, 'VK_U'], + KeyV: ['อ', 'ฮ', '', '', 0, 'VK_V'], + KeyW: ['ไ', '"', '', '', 0, 'VK_W'], + KeyX: ['ป', ')', '', '', 0, 'VK_X'], + KeyY: ['ั', 'ํ', '', '', 0, 'VK_Y'], + KeyZ: ['ผ', '(', '', '', 0, 'VK_Z'], + Digit1: ['ๅ', '+', '', '', 0, 'VK_1'], + Digit2: ['/', '๑', '', '', 0, 'VK_2'], + Digit3: ['-', '๒', '', '', 0, 'VK_3'], + Digit4: ['ภ', '๓', '', '', 0, 'VK_4'], + Digit5: ['ถ', '๔', '', '', 0, 'VK_5'], + Digit6: ['ุ', 'ู', '', '', 0, 'VK_6'], + Digit7: ['ึ', '฿', '', '', 0, 'VK_7'], + Digit8: ['ค', '๕', '', '', 0, 'VK_8'], + Digit9: ['ต', '๖', '', '', 0, 'VK_9'], + Digit0: ['จ', '๗', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['ข', '๘', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['ช', '๙', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['บ', 'ฐ', '', '', 0, 'VK_OEM_4'], + BracketRight: ['ล', ',', '', '', 0, 'VK_OEM_6'], + Backslash: ['ฃ', 'ฅ', '', '', 0, 'VK_OEM_5'], + Semicolon: ['ว', 'ซ', '', '', 0, 'VK_OEM_1'], + Quote: ['ง', '.', '', '', 0, 'VK_OEM_7'], + Backquote: ['_', '%', '', '', 0, 'VK_OEM_3'], + Comma: ['ม', 'ฒ', '', '', 0, 'VK_OEM_COMMA'], + Period: ['ใ', 'ฬ', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['ฝ', 'ฦ', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['ฃ', 'ฅ', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts new file mode 100644 index 0000000000..66a6cfbe22 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000041F', id: '', text: 'Turkish Q' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'æ', 'Æ', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['ı', 'I', 'i', 'İ', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '@', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'ß', '', 0, 'VK_S'], + KeyT: ['t', 'T', '₺', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '>', '', 0, 'VK_1'], + Digit2: ['2', '\'', '£', '', 0, 'VK_2'], + Digit3: ['3', '^', '#', '', 0, 'VK_3'], + Digit4: ['4', '+', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '½', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['*', '?', '\\', '', 0, 'VK_OEM_8'], + Equal: ['-', '_', '|', '', 0, 'VK_OEM_MINUS'], + BracketLeft: ['ğ', 'Ğ', '¨', '', 0, 'VK_OEM_4'], + BracketRight: ['ü', 'Ü', '~', '', 0, 'VK_OEM_6'], + Backslash: [',', ';', '`', '', 0, 'VK_OEM_COMMA'], + Semicolon: ['ş', 'Ş', '´', '', 0, 'VK_OEM_1'], + Quote: ['i', 'İ', '', '', 0, 'VK_OEM_7'], + Backquote: ['"', 'é', '<', '', 0, 'VK_OEM_3'], + Comma: ['ö', 'Ö', '', '', 0, 'VK_OEM_2'], + Period: ['ç', 'Ç', '', '', 0, 'VK_OEM_5'], + Slash: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '|', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts new file mode 100644 index 0000000000..323b77606c --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.inputmethod.SCIM.ITABC', lang: 'zh-Hans', localizedName: '搜狗拼音' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '¥', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['【', '「', '“', '”', 0], + BracketRight: ['】', '」', '‘', '’', 0], + Backslash: ['、', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['·', '~', '`', '`', 4], + Comma: [',', '《', '≤', '¯', 0], + Period: ['。', '》', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keymapService.ts b/src/vs/workbench/services/keybinding/browser/keymapService.ts new file mode 100644 index 0000000000..f59a51579e --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keymapService.ts @@ -0,0 +1,622 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IKeymapService, IKeyboardLayoutInfo, IKeyboardMapping, IWindowsKeyboardMapping, KeymapInfo, IRawMixedKeyboardMapping, getKeyboardLayoutId, IKeymapInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { IKeyboardMapper, CachedKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { OS, OperatingSystem, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { WindowsKeyboardMapper } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { parse } from 'vs/base/common/json'; +import * as objects from 'vs/base/common/objects'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; + +export class BrowserKeyboardMapperFactoryBase { + // keyboard mapper + protected _initialized: boolean; + protected _keyboardMapper: IKeyboardMapper | null; + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + // keymap infos + protected _keymapInfos: KeymapInfo[]; + protected _mru: KeymapInfo[]; + private _activeKeymapInfo: KeymapInfo | null; + + get activeKeymap(): KeymapInfo | null { + return this._activeKeymapInfo; + } + + get keymapInfos(): KeymapInfo[] { + return this._keymapInfos; + } + + get activeKeyboardLayout(): IKeyboardLayoutInfo | null { + if (!this._initialized) { + return null; + } + + return this._activeKeymapInfo && this._activeKeymapInfo.layout; + } + + get activeKeyMapping(): IKeyboardMapping | null { + if (!this._initialized) { + return null; + } + + return this._activeKeymapInfo && this._activeKeymapInfo.mapping; + } + + get keyboardLayouts(): IKeyboardLayoutInfo[] { + return this._keymapInfos.map(keymapInfo => keymapInfo.layout); + } + + protected constructor( + private _notificationService: INotificationService, + private _storageService: IStorageService, + private _commandService: ICommandService + ) { + this._keyboardMapper = null; + this._initialized = false; + this._keymapInfos = []; + this._mru = []; + this._activeKeymapInfo = null; + + if ((navigator).keyboard && (navigator).keyboard.addEventListener) { + (navigator).keyboard.addEventListener!('layoutchange', () => { + // Update user keyboard map settings + this._getBrowserKeyMapping().then((mapping: IKeyboardMapping | null) => { + if (this.isKeyMappingActive(mapping)) { + return; + } + + this.onKeyboardLayoutChanged(); + }); + }); + } + } + + registerKeyboardLayout(layout: KeymapInfo) { + this._keymapInfos.push(layout); + this._mru = this._keymapInfos; + } + + removeKeyboardLayout(layout: KeymapInfo): void { + let index = this._mru.indexOf(layout); + this._mru.splice(index, 1); + index = this._keymapInfos.indexOf(layout); + this._keymapInfos.splice(index, 1); + } + + getMatchedKeymapInfo(keyMapping: IKeyboardMapping | null): { result: KeymapInfo, score: number } | null { + if (!keyMapping) { + return null; + } + + let usStandard = this.getUSStandardLayout(); + + if (usStandard) { + let maxScore = usStandard.getScore(keyMapping); + if (maxScore === 0) { + return { + result: usStandard, + score: 0 + }; + } + + let result = usStandard; + for (let i = 0; i < this._mru.length; i++) { + let score = this._mru[i].getScore(keyMapping); + if (score > maxScore) { + if (score === 0) { + return { + result: this._mru[i], + score: 0 + }; + } + + maxScore = score; + result = this._mru[i]; + } + } + + return { + result, + score: maxScore + }; + } + + for (let i = 0; i < this._mru.length; i++) { + if (this._mru[i].fuzzyEqual(keyMapping)) { + return { + result: this._mru[i], + score: 0 + }; + } + } + + return null; + } + + getUSStandardLayout() { + const usStandardLayouts = this._mru.filter(layout => layout.layout.isUSStandard); + + if (usStandardLayouts.length) { + return usStandardLayouts[0]; + } + + return null; + } + + isKeyMappingActive(keymap: IKeyboardMapping | null) { + return this._activeKeymapInfo && keymap && this._activeKeymapInfo.fuzzyEqual(keymap); + } + + setUSKeyboardLayout() { + this._activeKeymapInfo = this.getUSStandardLayout(); + } + + setActiveKeyMapping(keymap: IKeyboardMapping | null) { + let matchedKeyboardLayout = this.getMatchedKeymapInfo(keymap); + if (matchedKeyboardLayout) { + let score = matchedKeyboardLayout.score; + + if (keymap && score < 0) { + const donotAskUpdateKey = 'missing.keyboardlayout.donotask'; + if (this._storageService.getBoolean(donotAskUpdateKey, StorageScope.GLOBAL)) { + return; + } + + // the keyboard layout doesn't actually match the key event or the keymap from chromium + this._notificationService.prompt( + Severity.Info, + nls.localize('missing.keyboardlayout', 'Fail to find matching keyboard layout'), + [{ + label: nls.localize('keyboardLayoutMissing.configure', "Configure"), + run: () => this._commandService.executeCommand('workbench.action.openKeyboardLayoutPicker') + }, { + label: nls.localize('neverAgain', "Don't Show Again"), + isSecondary: true, + run: () => this._storageService.store(donotAskUpdateKey, true, StorageScope.GLOBAL) + }] + ); + + return; + } + + if (!this._activeKeymapInfo) { + this._activeKeymapInfo = matchedKeyboardLayout.result; + } else if (keymap) { + if (matchedKeyboardLayout.result.getScore(keymap) > this._activeKeymapInfo.getScore(keymap)) { + this._activeKeymapInfo = matchedKeyboardLayout.result; + } + } + } + + if (!this._activeKeymapInfo) { + this._activeKeymapInfo = this.getUSStandardLayout(); + } + + if (!this._activeKeymapInfo) { + return; + } + + const index = this._mru.indexOf(this._activeKeymapInfo); + + this._mru.splice(index, 1); + this._mru.unshift(this._activeKeymapInfo); + + this._setKeyboardData(this._activeKeymapInfo); + } + + setActiveKeymapInfo(keymapInfo: KeymapInfo) { + this._activeKeymapInfo = keymapInfo; + + const index = this._mru.indexOf(this._activeKeymapInfo); + + if (index === 0) { + return; + } + + this._mru.splice(index, 1); + this._mru.unshift(this._activeKeymapInfo); + + this._setKeyboardData(this._activeKeymapInfo); + } + + public onKeyboardLayoutChanged(): void { + this._updateKeyboardLayoutAsync(this._initialized); + } + + private _updateKeyboardLayoutAsync(initialized: boolean, keyboardEvent?: IKeyboardEvent) { + if (!initialized) { + return; + } + + this._getBrowserKeyMapping(keyboardEvent).then(keyMap => { + // might be false positive + if (this.isKeyMappingActive(keyMap)) { + return; + } + this.setActiveKeyMapping(keyMap); + }); + } + + public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + if (!this._initialized) { + return new MacLinuxFallbackKeyboardMapper(OS); + } + if (dispatchConfig === DispatchConfig.KeyCode) { + // Forcefully set to use keyCode + return new MacLinuxFallbackKeyboardMapper(OS); + } + return this._keyboardMapper!; + } + + public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { + if (!this._initialized) { + return; + } + + let isCurrentKeyboard = this._validateCurrentKeyboardMapping(keyboardEvent); + + if (isCurrentKeyboard) { + return; + } + + this._updateKeyboardLayoutAsync(true, keyboardEvent); + } + + public setKeyboardLayout(layoutName: string) { + let matchedLayouts: KeymapInfo[] = this.keymapInfos.filter(keymapInfo => getKeyboardLayoutId(keymapInfo.layout) === layoutName); + + if (matchedLayouts.length > 0) { + this.setActiveKeymapInfo(matchedLayouts[0]); + } + } + + private _setKeyboardData(keymapInfo: KeymapInfo): void { + this._initialized = true; + + this._keyboardMapper = new CachedKeyboardMapper(BrowserKeyboardMapperFactory._createKeyboardMapper(keymapInfo)); + this._onDidChangeKeyboardMapper.fire(); + } + + private static _createKeyboardMapper(keymapInfo: KeymapInfo): IKeyboardMapper { + let rawMapping = keymapInfo.mapping; + const isUSStandard = !!keymapInfo.layout.isUSStandard; + if (OS === OperatingSystem.Windows) { + return new WindowsKeyboardMapper(isUSStandard, rawMapping); + } + if (Object.keys(rawMapping).length === 0) { + // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) + return new MacLinuxFallbackKeyboardMapper(OS); + } + + return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); + } + + //#region Browser API + private _validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): boolean { + if (!this._initialized) { + return true; + } + + const standardKeyboardEvent = keyboardEvent as StandardKeyboardEvent; + const currentKeymap = this._activeKeymapInfo; + if (!currentKeymap) { + return true; + } + + const mapping = currentKeymap.mapping[standardKeyboardEvent.code]; + + if (!mapping) { + return false; + } + + if (mapping.value === '') { + // we don't undetstand + if (keyboardEvent.ctrlKey || keyboardEvent.metaKey) { + setTimeout(() => { + this._getBrowserKeyMapping().then((keymap: IKeyboardMapping) => { + if (this.isKeyMappingActive(keymap)) { + return; + } + + this.onKeyboardLayoutChanged(); + }); + }, 350); + } + return true; + } + + const expectedValue = standardKeyboardEvent.altKey && standardKeyboardEvent.shiftKey ? mapping.withShiftAltGr : + standardKeyboardEvent.altKey ? mapping.withAltGr : + standardKeyboardEvent.shiftKey ? mapping.withShift : mapping.value; + + const isDead = (standardKeyboardEvent.altKey && standardKeyboardEvent.shiftKey && mapping.withShiftAltGrIsDeadKey) || + (standardKeyboardEvent.altKey && mapping.withAltGrIsDeadKey) || + (standardKeyboardEvent.shiftKey && mapping.withShiftIsDeadKey) || + mapping.valueIsDeadKey; + + if (isDead && standardKeyboardEvent.browserEvent.key !== 'Dead') { + return false; + } + + // TODO, this assumption is wrong as `browserEvent.key` doesn't necessarily equal expectedValue from real keymap + if (!isDead && standardKeyboardEvent.browserEvent.key !== expectedValue) { + return false; + } + + return true; + } + + private async _getBrowserKeyMapping(keyboardEvent?: IKeyboardEvent): Promise { + if ((navigator as any).keyboard) { + try { + return (navigator as any).keyboard.getLayoutMap().then((e: any) => { + let ret: IKeyboardMapping = {}; + for (let key of e) { + ret[key[0]] = { + 'value': key[1], + 'withShift': '', + 'withAltGr': '', + 'withShiftAltGr': '' + }; + } + + return ret; + + // const matchedKeyboardLayout = this.getMatchedKeymapInfo(ret); + + // if (matchedKeyboardLayout) { + // return matchedKeyboardLayout.result.mapping; + // } + + // return null; + }); + } catch { + // getLayoutMap can throw if invoked from a nested browsing context + } + } else if (keyboardEvent && !keyboardEvent.shiftKey && !keyboardEvent.altKey && !keyboardEvent.metaKey && !keyboardEvent.metaKey) { + let ret: IKeyboardMapping = {}; + const standardKeyboardEvent = keyboardEvent as StandardKeyboardEvent; + ret[standardKeyboardEvent.browserEvent.code] = { + 'value': standardKeyboardEvent.browserEvent.key, + 'withShift': '', + 'withAltGr': '', + 'withShiftAltGr': '' + }; + + const matchedKeyboardLayout = this.getMatchedKeymapInfo(ret); + + if (matchedKeyboardLayout) { + return ret; + } + + return null; + } + + return null; + } + + //#endregion +} + +export class BrowserKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase { + constructor(notificationService: INotificationService, storageService: IStorageService, commandService: ICommandService) { + super(notificationService, storageService, commandService); + + const platform = isWindows ? 'win' : isMacintosh ? 'darwin' : 'linux'; + + import('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.' + platform).then((m) => { + let keymapInfos: IKeymapInfo[] = m.KeyboardLayoutContribution.INSTANCE.layoutInfos; + this._keymapInfos.push(...keymapInfos.map(info => (new KeymapInfo(info.layout, info.secondaryLayouts, info.mapping, info.isUserKeyboardLayout)))); + this._mru = this._keymapInfos; + this._initialized = true; + this.onKeyboardLayoutChanged(); + }); + } +} + +class UserKeyboardLayout extends Disposable { + + private readonly reloadConfigurationScheduler: RunOnceScheduler; + protected readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private _keyboardLayout: KeymapInfo | null; + get keyboardLayout(): KeymapInfo | null { return this._keyboardLayout; } + + constructor( + private readonly keyboardLayoutResource: URI, + private readonly fileService: IFileService + ) { + super(); + + this._keyboardLayout = null; + + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => { + if (changed) { + this._onDidChange.fire(); + } + }), 50)); + + this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.keyboardLayoutResource))(() => this.reloadConfigurationScheduler.schedule())); + } + + async initialize(): Promise { + await this.reload(); + } + + private async reload(): Promise { + const existing = this._keyboardLayout; + try { + const content = await this.fileService.readFile(this.keyboardLayoutResource); + const value = parse(content.value.toString()); + const layoutInfo = value.layout; + const mappings = value.rawMapping; + this._keyboardLayout = KeymapInfo.createKeyboardLayoutFromDebugInfo(layoutInfo, mappings, true); + } catch (e) { + this._keyboardLayout = null; + } + + return existing ? !objects.equals(existing, this._keyboardLayout) : true; + } + +} + +class BrowserKeymapService extends Disposable implements IKeymapService { + public _serviceBrand: any; + + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + private _userKeyboardLayout: UserKeyboardLayout; + + private readonly layoutChangeListener = this._register(new MutableDisposable()); + private readonly _factory: BrowserKeyboardMapperFactory; + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @INotificationService notificationService: INotificationService, + @IStorageService storageService: IStorageService, + @ICommandService commandService: ICommandService, + @IConfigurationService private configurationService: IConfigurationService, + ) { + super(); + const keyboardConfig = configurationService.getValue<{ layout: string }>('keyboard'); + const layout = keyboardConfig.layout; + this._factory = new BrowserKeyboardMapperFactory(notificationService, storageService, commandService); + + this.registerKeyboardListener(); + + if (layout && layout !== 'autodetect') { + // set keyboard layout + this._factory.setKeyboardLayout(layout); + } + + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectedKeys.indexOf('keyboard.layout') >= 0) { + const keyboardConfig = configurationService.getValue<{ layout: string }>('keyboard'); + const layout = keyboardConfig.layout; + + if (layout === 'autodetect') { + this.registerKeyboardListener(); + this._factory.onKeyboardLayoutChanged(); + } else { + this._factory.setKeyboardLayout(layout); + this.layoutChangeListener.clear(); + } + } + })); + + this._userKeyboardLayout = new UserKeyboardLayout(environmentService.keyboardLayoutResource, fileService); + this._userKeyboardLayout.initialize().then(() => { + if (this._userKeyboardLayout.keyboardLayout) { + this._factory.registerKeyboardLayout(this._userKeyboardLayout.keyboardLayout); + + this.setUserKeyboardLayoutIfMatched(); + } + }); + + this._register(this._userKeyboardLayout.onDidChange(() => { + let userKeyboardLayouts = this._factory.keymapInfos.filter(layout => layout.isUserKeyboardLayout); + + if (userKeyboardLayouts.length) { + if (this._userKeyboardLayout.keyboardLayout) { + userKeyboardLayouts[0].update(this._userKeyboardLayout.keyboardLayout); + } else { + this._factory.removeKeyboardLayout(userKeyboardLayouts[0]); + } + } else { + if (this._userKeyboardLayout.keyboardLayout) { + this._factory.registerKeyboardLayout(this._userKeyboardLayout.keyboardLayout); + } + } + + this.setUserKeyboardLayoutIfMatched(); + })); + } + + setUserKeyboardLayoutIfMatched() { + const keyboardConfig = this.configurationService.getValue<{ layout: string }>('keyboard'); + const layout = keyboardConfig.layout; + + if (layout && this._userKeyboardLayout.keyboardLayout) { + if (getKeyboardLayoutId(this._userKeyboardLayout.keyboardLayout.layout) === layout && this._factory.activeKeymap) { + + if (!this._userKeyboardLayout.keyboardLayout.equal(this._factory.activeKeymap)) { + this._factory.setActiveKeymapInfo(this._userKeyboardLayout.keyboardLayout); + } + } + } + } + + registerKeyboardListener() { + this.layoutChangeListener.value = this._factory.onDidChangeKeyboardMapper(() => { + this._onDidChangeKeyboardMapper.fire(); + }); + } + + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + return this._factory.getKeyboardMapper(dispatchConfig); + } + + public getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null { + return this._factory.activeKeyboardLayout; + } + + public getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { + return this._factory.keyboardLayouts; + } + + public getRawKeyboardMapping(): IKeyboardMapping | null { + return this._factory.activeKeyMapping; + } + + public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { + this._factory.validateCurrentKeyboardMapping(keyboardEvent); + } +} + +registerSingleton(IKeymapService, BrowserKeymapService, true); + +// Configuration +const configurationRegistry = Registry.as(ConfigExtensions.Configuration); +const keyboardConfiguration: IConfigurationNode = { + 'id': 'keyboard', + 'order': 15, + 'type': 'object', + 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), + 'overridable': true, + 'properties': { + 'keyboard.layout': { + 'type': 'string', + 'default': 'autodetect', + 'description': nls.localize('keyboard.layout.config', "Control the keyboard layout used in web.") + } + } +}; + +configurationRegistry.registerConfiguration(keyboardConfiguration); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/common/dispatchConfig.ts b/src/vs/workbench/services/keybinding/common/dispatchConfig.ts new file mode 100644 index 0000000000..b462353e93 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/dispatchConfig.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export const enum DispatchConfig { + Code, + KeyCode +} + +export function getDispatchConfig(configurationService: IConfigurationService): DispatchConfig { + const keyboard = configurationService.getValue('keyboard'); + const r = (keyboard ? (keyboard).dispatch : null); + return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code); +} \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index eeffa3f7db..7d74b3ff00 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -44,7 +44,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding public _serviceBrand: any; private queue: Queue; - private resource: URI = URI.file(this.environmentService.appKeybindingsPath); + private resource: URI = this.environmentService.keybindingsResource; constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, @@ -186,7 +186,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } private asObject(key: string, command: string | null, when: string | undefined, negate: boolean): any { - const object = { key }; + const object: any = { key }; if (command) { object['command'] = negate ? `-${command}` : command; } @@ -210,7 +210,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding private resolveModelReference(): Promise> { return this.fileService.exists(this.resource) .then(exists => { - const EOL = this.configurationService.getValue<{}>('files', { overrideIdentifier: 'json' })['eol']; + const EOL = this.configurationService.getValue<{ eol: string }>('files', { overrideIdentifier: 'json' })['eol']; const result: Promise = exists ? Promise.resolve(null) : this.textFileService.write(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); return result.then(() => this.textModelResolverService.createModelReference(this.resource)); }); diff --git a/src/vs/workbench/services/keybinding/common/keymapInfo.ts b/src/vs/workbench/services/keybinding/common/keymapInfo.ts new file mode 100644 index 0000000000..d425d0f1b2 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/keymapInfo.ts @@ -0,0 +1,342 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { isWindows, isLinux } from 'vs/base/common/platform'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + + +export interface IWindowsKeyMapping { + vkey: string; + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface IWindowsKeyboardMapping { + [code: string]: IWindowsKeyMapping; +} +export interface ILinuxKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface ILinuxKeyboardMapping { + [code: string]: ILinuxKeyMapping; +} +export interface IMacKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; + valueIsDeadKey: boolean; + withShiftIsDeadKey: boolean; + withAltGrIsDeadKey: boolean; + withShiftAltGrIsDeadKey: boolean; +} +export interface IMacKeyboardMapping { + [code: string]: IMacKeyMapping; +} + +export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping; + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IWindowsKeyboardLayoutInfo { + name: string; + id: string; + text: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface ILinuxKeyboardLayoutInfo { + model: string; + layout: string; + variant: string; + options: string; + rules: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "localizedName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IMacKeyboardLayoutInfo { + id: string; + lang: string; + localizedName?: string; +} + +export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo) & { isUserKeyboardLayout?: boolean; isUSStandard?: true }; + +export const IKeymapService = createDecorator('keymapService'); + +export interface IKeymapService { + _serviceBrand: ServiceIdentifier; + onDidChangeKeyboardMapper: Event; + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper; + getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null; + getAllKeyboardLayouts(): IKeyboardLayoutInfo[]; + getRawKeyboardMapping(): IKeyboardMapping | null; + validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void; +} + +export function areKeyboardLayoutsEqual(a: IKeyboardLayoutInfo | null, b: IKeyboardLayoutInfo | null): boolean { + if (!a || !b) { + return false; + } + + if ((a).name && (b).name && (a).name === (b).name) { + return true; + } + + if ((a).id && (b).id && (a).id === (b).id) { + return true; + } + + if ((a).model && + (b).model && + (a).model === (b).model && + (a).layout === (b).layout + ) { + return true; + } + + return false; +} + +export function parseKeyboardLayoutDescription(layout: IKeyboardLayoutInfo | null): { label: string, description: string } { + if (!layout) { + return { label: '', description: '' }; + } + + if ((layout).name) { + // windows + let windowsLayout = layout; + return { + label: windowsLayout.text, + description: '' + }; + } + + if ((layout).id) { + let macLayout = layout; + if (macLayout.localizedName) { + return { + label: macLayout.localizedName, + description: '' + }; + } + + if (/^com\.apple\.keylayout\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^com\.apple\.keylayout\./, '').replace(/-/, ' '), + description: '' + }; + } + if (/^.*inputmethod\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^.*inputmethod\./, '').replace(/[-\.]/, ' '), + description: `Input Method (${macLayout.lang})` + }; + } + + return { + label: macLayout.lang, + description: '' + }; + } + + let linuxLayout = layout; + + return { + label: linuxLayout.layout, + description: '' + }; +} + +export function getKeyboardLayoutId(layout: IKeyboardLayoutInfo): string { + if ((layout).name) { + return (layout).name; + } + + if ((layout).id) { + return (layout).id; + } + + return (layout).layout; +} + +function deserializeMapping(serializedMapping: ISerializedMapping) { + let mapping = serializedMapping; + + let ret: { [key: string]: any } = {}; + for (let key in mapping) { + let result: (string | number)[] = mapping[key]; + if (result.length) { + let value = result[0]; + let withShift = result[1]; + let withAltGr = result[2]; + let withShiftAltGr = result[3]; + let mask = Number(result[4]); + let vkey = result.length === 6 ? result[5] : undefined; + ret[key] = { + 'value': value, + 'vkey': vkey, + 'withShift': withShift, + 'withAltGr': withAltGr, + 'withShiftAltGr': withShiftAltGr, + 'valueIsDeadKey': (mask & 1) > 0, + 'withShiftIsDeadKey': (mask & 2) > 0, + 'withAltGrIsDeadKey': (mask & 4) > 0, + 'withShiftAltGrIsDeadKey': (mask & 8) > 0 + }; + } else { + ret[key] = { + 'value': '', + 'valueIsDeadKey': false, + 'withShift': '', + 'withShiftIsDeadKey': false, + 'withAltGr': '', + 'withAltGrIsDeadKey': false, + 'withShiftAltGr': '', + 'withShiftAltGrIsDeadKey': false + }; + } + } + + return ret; +} + +export interface IRawMixedKeyboardMapping { + [key: string]: { + value: string, + withShift: string; + withAltGr: string; + withShiftAltGr: string; + valueIsDeadKey?: boolean; + withShiftIsDeadKey?: boolean; + withAltGrIsDeadKey?: boolean; + withShiftAltGrIsDeadKey?: boolean; + + }; +} + +interface ISerializedMapping { + [key: string]: (string | number)[]; +} + +export interface IKeymapInfo { + layout: IKeyboardLayoutInfo; + secondaryLayouts: IKeyboardLayoutInfo[]; + mapping: ISerializedMapping; + isUserKeyboardLayout?: boolean; +} + +export class KeymapInfo { + mapping: IRawMixedKeyboardMapping; + isUserKeyboardLayout: boolean; + + constructor(public layout: IKeyboardLayoutInfo, public secondaryLayouts: IKeyboardLayoutInfo[], keyboardMapping: ISerializedMapping, isUserKeyboardLayout?: boolean) { + this.mapping = deserializeMapping(keyboardMapping); + this.isUserKeyboardLayout = !!isUserKeyboardLayout; + this.layout.isUserKeyboardLayout = !!isUserKeyboardLayout; + } + + static createKeyboardLayoutFromDebugInfo(layout: IKeyboardLayoutInfo, value: IRawMixedKeyboardMapping, isUserKeyboardLayout?: boolean): KeymapInfo { + let keyboardLayoutInfo = new KeymapInfo(layout, [], {}, true); + keyboardLayoutInfo.mapping = value; + return keyboardLayoutInfo; + } + + update(other: KeymapInfo) { + this.layout = other.layout; + this.secondaryLayouts = other.secondaryLayouts; + this.mapping = other.mapping; + this.isUserKeyboardLayout = other.isUserKeyboardLayout; + this.layout.isUserKeyboardLayout = other.isUserKeyboardLayout; + } + + getScore(other: IRawMixedKeyboardMapping): number { + let score = 0; + for (let key in other) { + if (isWindows && (key === 'Backslash' || key === 'KeyQ')) { + // keymap from Chromium is probably wrong. + continue; + } + + if (isLinux && (key === 'Backspace' || key === 'Escape')) { + // native keymap doesn't align with keyboard event + continue; + } + + if (this.mapping[key] === undefined) { + score -= 1; + } + + let currentMapping = this.mapping[key]; + let otherMapping = other[key]; + + if (currentMapping.value !== otherMapping.value) { + score -= 1; + } + } + + return score; + } + + equal(other: KeymapInfo): boolean { + if (this.isUserKeyboardLayout !== other.isUserKeyboardLayout) { + return false; + } + + if (getKeyboardLayoutId(this.layout) !== getKeyboardLayoutId(other.layout)) { + return false; + } + + return this.fuzzyEqual(other.mapping); + } + + fuzzyEqual(other: IRawMixedKeyboardMapping): boolean { + for (let key in other) { + if (isWindows && (key === 'Backslash' || key === 'KeyQ')) { + // keymap from Chromium is probably wrong. + continue; + } + if (this.mapping[key] === undefined) { + return false; + } + + let currentMapping = this.mapping[key]; + let otherMapping = other[key]; + + if (currentMapping.value !== otherMapping.value) { + return false; + } + } + + return true; + } +} diff --git a/src/vs/workbench/electron-browser/actions/media/actions.css b/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts similarity index 57% rename from src/vs/workbench/electron-browser/actions/media/actions.css rename to src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts index 76334008ff..4efb45310f 100644 --- a/src/vs/workbench/electron-browser/actions/media/actions.css +++ b/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts @@ -3,11 +3,13 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.vs .action-remove-from-recently-opened { - background: url("remove.svg") center center no-repeat; -} +export interface IKeyboard { + getLayoutMap(): Promise; + lock(keyCodes?: string[]): Promise; + unlock(): void; + addEventListener?(type: string, listener: () => void): void; -.vs-dark .action-remove-from-recently-opened, -.hc-black .action-remove-from-recently-opened { - background: url("remove-dark.svg") center center no-repeat; -} \ No newline at end of file +} +export type INavigatorWithKeyboard = Navigator & { + keyboard: IKeyboard +}; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts b/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts new file mode 100644 index 0000000000..f0fd8c00aa --- /dev/null +++ b/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.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 nls from 'vs/nls'; +import { release } from 'os'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; + +const configurationRegistry = Registry.as(ConfigExtensions.Configuration); +const keyboardConfiguration: IConfigurationNode = { + 'id': 'keyboard', + 'order': 15, + 'type': 'object', + 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), + 'overridable': true, + 'properties': { + 'keyboard.touchbar.enabled': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."), + 'included': OS === OperatingSystem.Macintosh && parseFloat(release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) + } + } +}; + +configurationRegistry.registerConfiguration(keyboardConfiguration); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts new file mode 100644 index 0000000000..019e0a6404 --- /dev/null +++ b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nativeKeymap from 'native-keymap'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IKeymapService, IKeyboardLayoutInfo, IKeyboardMapping } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IKeyboardMapper, CachedKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { Emitter, Event } from 'vs/base/common/event'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { WindowsKeyboardMapper, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals, IMacLinuxKeyboardMapping } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +export class KeyboardMapperFactory { + public static readonly INSTANCE = new KeyboardMapperFactory(); + + private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo | null; + private _rawMapping: nativeKeymap.IKeyboardMapping | null; + private _keyboardMapper: IKeyboardMapper | null; + private _initialized: boolean; + + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + private constructor() { + this._layoutInfo = null; + this._rawMapping = null; + this._keyboardMapper = null; + this._initialized = false; + } + + public _onKeyboardLayoutChanged(): void { + if (this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + } + + public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + if (!this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + if (dispatchConfig === DispatchConfig.KeyCode) { + // Forcefully set to use keyCode + return new MacLinuxFallbackKeyboardMapper(OS); + } + return this._keyboardMapper!; + } + + public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo | null { + if (!this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + return this._layoutInfo; + } + + private static _isUSStandard(_kbInfo: nativeKeymap.IKeyboardLayoutInfo): boolean { + if (OS === OperatingSystem.Linux) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.layout === 'us'); + } + + if (OS === OperatingSystem.Macintosh) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.id === 'com.apple.keylayout.US'); + } + + if (OS === OperatingSystem.Windows) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.name === '00000409'); + } + + return false; + } + + public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping | null { + if (!this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + return this._rawMapping; + } + + private _setKeyboardData(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { + this._layoutInfo = layoutInfo; + + if (this._initialized && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { + // nothing to do... + return; + } + + this._initialized = true; + this._rawMapping = rawMapping; + this._keyboardMapper = new CachedKeyboardMapper( + KeyboardMapperFactory._createKeyboardMapper(this._layoutInfo, this._rawMapping) + ); + this._onDidChangeKeyboardMapper.fire(); + } + + private static _createKeyboardMapper(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { + const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo); + if (OS === OperatingSystem.Windows) { + return new WindowsKeyboardMapper(isUSStandard, rawMapping); + } + + if (Object.keys(rawMapping).length === 0) { + // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) + return new MacLinuxFallbackKeyboardMapper(OS); + } + + if (OS === OperatingSystem.Macintosh) { + const kbInfo = layoutInfo; + if (kbInfo.id === 'com.apple.keylayout.DVORAK-QWERTYCMD') { + // Use keyCode based dispatching for DVORAK - QWERTY ⌘ + return new MacLinuxFallbackKeyboardMapper(OS); + } + } + + return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); + } + + private static _equals(a: nativeKeymap.IKeyboardMapping | null, b: nativeKeymap.IKeyboardMapping | null): boolean { + if (OS === OperatingSystem.Windows) { + return windowsKeyboardMappingEquals(a, b); + } + + return macLinuxKeyboardMappingEquals(a, b); + } +} + +class NativeKeymapService extends Disposable implements IKeymapService { + public _serviceBrand: any; + + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + constructor() { + super(); + + this._register(KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { + this._onDidChangeKeyboardMapper.fire(); + })); + } + + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + return KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + } + + public getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null { + return KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(); + } + + getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { + return []; + } + + public getRawKeyboardMapping(): IKeyboardMapping | null { + return KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(); + } + + public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { + return; + } +} + +registerSingleton(IKeymapService, NativeKeymapService, true); diff --git a/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts new file mode 100644 index 0000000000..9d66817e23 --- /dev/null +++ b/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin'; // 15% +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin'; +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; +import { BrowserKeyboardMapperFactoryBase } from '../browser/keymapService'; +import { KeymapInfo, IKeymapInfo } from '../common/keymapInfo'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +class TestKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase { + constructor(notificationService: INotificationService, storageService: IStorageService, commandService: ICommandService) { + super(notificationService, storageService, commandService); + + let keymapInfos: IKeymapInfo[] = KeyboardLayoutContribution.INSTANCE.layoutInfos; + this._keymapInfos.push(...keymapInfos.map(info => (new KeymapInfo(info.layout, info.secondaryLayouts, info.mapping, info.isUserKeyboardLayout)))); + this._mru = this._keymapInfos; + this._initialized = true; + this.onKeyboardLayoutChanged(); + } +} + + +suite('keyboard layout loader', () => { + let instantiationService: TestInstantiationService = new TestInstantiationService(); + let notitifcationService = instantiationService.stub(INotificationService, {}); + let storageService = instantiationService.stub(IStorageService, {}); + let commandService = instantiationService.stub(ICommandService, {}); + let instance = new TestKeyboardMapperFactory(notitifcationService, storageService, commandService); + + test.skip('load default US keyboard layout', () => { + assert.notEqual(instance.activeKeyboardLayout, null); + assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); + }); + + test.skip('isKeyMappingActive', () => { + assert.equal(instance.isKeyMappingActive({ + KeyA: { + value: 'a', + valueIsDeadKey: false, + withShift: 'A', + withShiftIsDeadKey: false, + withAltGr: 'å', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Å', + withShiftAltGrIsDeadKey: false + } + }), true); + + assert.equal(instance.isKeyMappingActive({ + KeyA: { + value: 'a', + valueIsDeadKey: false, + withShift: 'A', + withShiftIsDeadKey: false, + withAltGr: 'å', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Å', + withShiftAltGrIsDeadKey: false + }, + KeyZ: { + value: 'z', + valueIsDeadKey: false, + withShift: 'Z', + withShiftIsDeadKey: false, + withAltGr: 'Ω', + withAltGrIsDeadKey: false, + withShiftAltGr: '¸', + withShiftAltGrIsDeadKey: false + } + }), true); + + assert.equal(instance.isKeyMappingActive({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + }, + }), false); + + }); + + test('Switch keymapping', () => { + instance.setActiveKeyMapping({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + } + }); + assert.equal(!!instance.activeKeyboardLayout!.isUSStandard, false); + assert.equal(instance.isKeyMappingActive({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + }, + }), true); + + instance.setUSKeyboardLayout(); + assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); + }); + + test('Switch keyboard layout info', () => { + instance.setKeyboardLayout('com.apple.keylayout.German'); + assert.equal(!!instance.activeKeyboardLayout!.isUSStandard, false); + assert.equal(instance.isKeyMappingActive({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + }, + }), true); + + instance.setUSKeyboardLayout(); + assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts index 7b50510857..2cc12ff2c2 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/keybindingEditing.test.ts @@ -40,10 +40,25 @@ import { KeybindingsEditingService } from 'vs/workbench/services/keybinding/comm import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { TestBackupFileService, TestContextService, TestEditorGroupsService, TestEditorService, TestLifecycleService, TestLogService, TestTextFileService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { TestBackupFileService, TestContextService, TestEditorGroupsService, TestEditorService, TestLifecycleService, TestTextFileService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { FileService } from 'vs/platform/files/common/fileService'; import { Schemas } from 'vs/base/common/network'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { URI } from 'vs/base/common/uri'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; + +class TestEnvironmentService extends WorkbenchEnvironmentService { + + constructor(private _appSettingsHome: URI) { + super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); + } + + get appSettingsHome() { return this._appSettingsHome; } + +} interface Modifiers { metaKey?: boolean; @@ -65,7 +80,8 @@ suite('KeybindingsEditing', () => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IEnvironmentService, { appKeybindingsPath: keybindingsFile, appSettingsPath: path.join(testDir, 'settings.json') }); + const environmentService = new TestEnvironmentService(URI.file(testDir)); + instantiationService.stub(IEnvironmentService, environmentService); instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); @@ -78,11 +94,13 @@ suite('KeybindingsEditing', () => { instantiationService.stub(IEditorService, new TestEditorService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModeService, ModeServiceImpl); - instantiationService.stub(ILogService, new TestLogService()); + instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(instantiationService.get(IConfigurationService))); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, diskFileSystemProvider, environmentService)); instantiationService.stub(IFileService, fileService); instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); @@ -143,16 +161,6 @@ suite('KeybindingsEditing', () => { .then(() => assert.deepEqual(getUserKeybindings(), expected)); }); - test('edit a default keybinding to a non existing keybindings file', () => { - keybindingsFile = path.join(testDir, 'nonExistingFile.json'); - instantiationService.get(IEnvironmentService).appKeybindingsPath = keybindingsFile; - testObject = instantiationService.createInstance(KeybindingsEditingService); - - const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; - return testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape }, command: 'a' }), 'alt+c', undefined) - .then(() => assert.deepEqual(getUserKeybindings(), expected)); - }); - test('edit a default keybinding to an empty array', () => { writeToKeybindingsFile(); const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 57ff084478..254c10a6ac 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -45,6 +45,21 @@ export interface IWorkbenchLayoutService extends ILayoutService { */ readonly onZenModeChange: Event; + /** + * Emits when fullscreen is enabled or disabled. + */ + readonly onFullscreenChange: Event; + + /** + * Emits when centered layout is enabled or disabled. + */ + readonly onCenteredLayoutChange: Event; + + /** + * Emit when panel position changes. + */ + readonly onPanelPositionChange: Event; + /** * Asks the part service if all parts have been fully restored. For editor part * this means that the contents of editors have loaded. @@ -123,6 +138,11 @@ export interface IWorkbenchLayoutService extends ILayoutService { */ setPanelPosition(position: Position): void; + /** + * Returns the element that is parent of the workbench element. + */ + getWorkbenchContainer(): HTMLElement; + /** * Returns the element that contains the workbench. */ diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 4621348a50..f477cb7281 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -3,15 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; import { INotificationsModel, NotificationsModel, ChoiceAction } from 'vs/workbench/common/notifications'; -import { dispose, Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IAction } from 'vs/base/common/actions'; export class NotificationService extends Disposable implements INotificationService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private _model: INotificationsModel = this._register(new NotificationsModel()); @@ -26,7 +28,7 @@ export class NotificationService extends Disposable implements INotificationServ return; } - this.model.notify({ severity: Severity.Info, message }); + this.model.addNotification({ severity: Severity.Info, message }); } warn(message: NotificationMessage | NotificationMessage[]): void { @@ -36,7 +38,7 @@ export class NotificationService extends Disposable implements INotificationServ return; } - this.model.notify({ severity: Severity.Warning, message }); + this.model.addNotification({ severity: Severity.Warning, message }); } error(message: NotificationMessage | NotificationMessage[]): void { @@ -46,37 +48,32 @@ export class NotificationService extends Disposable implements INotificationServ return; } - this.model.notify({ severity: Severity.Error, message }); + this.model.addNotification({ severity: Severity.Error, message }); } notify(notification: INotification): INotificationHandle { - return this.model.notify(notification); + return this.model.addNotification(notification); } prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { - const toDispose: IDisposable[] = []; + const toDispose = new DisposableStore(); let choiceClicked = false; let handle: INotificationHandle; // Convert choices into primary/secondary actions - const actions: INotificationActions = { primary: [], secondary: [] }; + const primaryActions: IAction[] = []; + const secondaryActions: IAction[] = []; choices.forEach((choice, index) => { const action = new ChoiceAction(`workbench.dialog.choice.${index}`, choice); if (!choice.isSecondary) { - if (!actions.primary) { - actions.primary = []; - } - actions.primary.push(action); + primaryActions.push(action); } else { - if (!actions.secondary) { - actions.secondary = []; - } - actions.secondary.push(action); + secondaryActions.push(action); } // React to action being clicked - toDispose.push(action.onDidRun(() => { + toDispose.add(action.onDidRun(() => { choiceClicked = true; // Close notification unless we are told to keep open @@ -85,16 +82,17 @@ export class NotificationService extends Disposable implements INotificationServ } })); - toDispose.push(action); + toDispose.add(action); }); // Show notification with actions + const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; handle = this.notify({ severity, message, actions, sticky: options && options.sticky, silent: options && options.silent }); Event.once(handle.onDidClose)(() => { // Cleanup when notification gets disposed - dispose(toDispose); + toDispose.dispose(); // Indicate cancellation to the outside if no action was executed if (options && typeof options.onCancel === 'function' && !choiceClicked) { @@ -104,6 +102,10 @@ export class NotificationService extends Disposable implements INotificationServ return handle; } + + status(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable { + return this.model.showStatusMessage(message, options); + } } registerSingleton(INotificationService, NotificationService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/services/output/common/outputChannelModel.ts index 73a4db073c..49dc1b0f95 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/services/output/common/outputChannelModel.ts @@ -51,10 +51,10 @@ export abstract class AsbtractOutputChannelModelService { export abstract class AbstractFileOutputChannelModel extends Disposable implements IOutputChannelModel { - protected _onDidAppendedContent = new Emitter(); + protected readonly _onDidAppendedContent = this._register(new Emitter()); readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; - protected _onDispose = new Emitter(); + protected readonly _onDispose = this._register(new Emitter()); readonly onDispose: Event = this._onDispose.event; protected modelUpdater: RunOnceScheduler; @@ -96,12 +96,11 @@ export abstract class AbstractFileOutputChannelModel extends Disposable implemen } else { this.model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); this.onModelCreated(this.model); - const disposables: IDisposable[] = []; - disposables.push(this.model.onWillDispose(() => { + const disposable = this.model.onWillDispose(() => { this.onModelWillDispose(this.model); this.model = null; - dispose(disposables); - })); + dispose(disposable); + }); } return this.model; } @@ -340,11 +339,10 @@ export class BufferredOutputChannel extends Disposable implements IOutputChannel private createModel(content: string): ITextModel { const model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); - const disposables: IDisposable[] = []; - disposables.push(model.onWillDispose(() => { + const disposable = model.onWillDispose(() => { this.model = null; - dispose(disposables); - })); + dispose(disposable); + }); return model; } diff --git a/src/vs/workbench/services/output/node/outputChannelModelService.ts b/src/vs/workbench/services/output/node/outputChannelModelService.ts index 5f885c141d..b297aa5881 100644 --- a/src/vs/workbench/services/output/node/outputChannelModelService.ts +++ b/src/vs/workbench/services/output/node/outputChannelModelService.ts @@ -169,10 +169,7 @@ class DelegatedOutputChannelModel extends Disposable implements IOutputChannelMo } catch (e) { // Do not crash if spdlog rotating logger cannot be loaded (workaround for https://github.com/Microsoft/vscode/issues/47883) this.logService.error(e); - /* __GDPR__ - "output.channel.creation.error" : {} - */ - this.telemetryService.publicLog('output.channel.creation.error'); + this.telemetryService.publicLog2('output.channel.creation.error'); outputChannelModel = this.instantiationService.createInstance(BufferredOutputChannel, modelUri, mimeType); } this._register(outputChannelModel); diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index dec7845307..a9a1d73645 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -8,6 +8,7 @@ import { IPanel } from 'vs/workbench/common/panel'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IProgressIndicator } from 'vs/platform/progress/common/progress'; export const IPanelService = createDecorator('panelService'); @@ -18,11 +19,11 @@ export interface IPanelIdentifier { } export interface IPanelService { + _serviceBrand: ServiceIdentifier; - onDidPanelOpen: Event<{ panel: IPanel, focus: boolean }>; - - onDidPanelClose: Event; + readonly onDidPanelOpen: Event<{ panel: IPanel, focus: boolean }>; + readonly onDidPanelClose: Event; /** * Opens a panel with the given identifier and pass keyboard focus to it if specified. @@ -35,7 +36,12 @@ export interface IPanelService { getActivePanel(): IPanel | null; /** - * Returns all built-in panels following the default order (Problems - Output - Debug Console - Terminal) + * Returns the panel by id. + */ + getPanel(id: string): IPanelIdentifier | undefined; + + /** + * Returns all built-in panels following the default order */ getPanels(): IPanelIdentifier[]; @@ -44,6 +50,11 @@ export interface IPanelService { */ getPinnedPanels(): IPanelIdentifier[]; + /** + * Returns the progress indicator for the panel bar. + */ + getProgressIndicator(id: string): IProgressIndicator | null; + /** * Show an activity in a panel. */ diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index ecb2429752..222c593f1a 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -265,15 +265,13 @@ export class PreferencesService extends Disposable implements IPreferencesServic } openGlobalKeybindingSettings(textual: boolean): Promise { - /* __GDPR__ - "openKeybindings" : { - "textual" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('openKeybindings', { textual }); + type OpenKeybindingsClassification = { + textual: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + }; + this.telemetryService.publicLog2<{ textual: boolean }, OpenKeybindingsClassification>('openKeybindings', { textual }); if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; - const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath); + const editableKeybindings = this.environmentService.keybindingsResource; const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor @@ -524,9 +522,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic switch (configurationTarget) { case ConfigurationTarget.USER: case ConfigurationTarget.USER_LOCAL: - return URI.file(this.environmentService.appSettingsPath); + return this.environmentService.settingsResource; case ConfigurationTarget.USER_REMOTE: - return URI.file(this.environmentService.appSettingsPath); + return this.environmentService.settingsResource; case ConfigurationTarget.WORKSPACE: if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return null; diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index 4c805f68f6..d90e393457 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -160,7 +160,7 @@ export class KeybindingsEditorModel extends EditorModel { return result; } - resolve(editorActionsLabels: { [id: string]: string; }): Promise { + resolve(editorActionsLabels: Map): Promise { const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); this._keybindingItemsSortedByPrecedence = []; @@ -209,9 +209,9 @@ export class KeybindingsEditorModel extends EditorModel { return a.command.localeCompare(b.command); } - private static toKeybindingEntry(command: string, keybindingItem: ResolvedKeybindingItem, workbenchActionsRegistry: IWorkbenchActionRegistry, editorActions: { [id: string]: string; }): IKeybindingItem { - const menuCommand = MenuRegistry.getCommand(command); - const editorActionLabel = editorActions[command]; + private static toKeybindingEntry(command: string, keybindingItem: ResolvedKeybindingItem, workbenchActionsRegistry: IWorkbenchActionRegistry, editorActions: Map): IKeybindingItem { + const menuCommand = MenuRegistry.getCommand(command)!; + const editorActionLabel = editorActions.get(command)!; return { keybinding: keybindingItem.resolvedKeybinding, keybindingItem, diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index bfb0c9f3d8..ce0283bfd7 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -25,6 +25,7 @@ export enum SettingValueType { Integer = 'integer', Number = 'number', Boolean = 'boolean', + ArrayOfString = 'array-of-string', Exclude = 'exclude', Complex = 'complex', NullableInteger = 'nullable-integer', @@ -61,6 +62,7 @@ export interface ISetting { scope?: ConfigurationScope; type?: string | string[]; + arrayItemType?: string; enum?: string[]; enumDescriptions?: string[]; enumDescriptionsAreMarkdown?: boolean; diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 51d96a9f7c..25dd38669d 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -22,7 +22,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorModel } from 'vs/workbench/common/editor'; import { IFilterMetadata, IFilterResult, IGroupFilter, IKeybindingsEditorModel, ISearchResultGroup, ISetting, ISettingMatch, ISettingMatcher, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, isArray } from 'vs/base/common/types'; export const nullRange: IRange = { startLineNumber: -1, startColumn: -1, endLineNumber: -1, endColumn: -1 }; export function isNullRange(range: IRange): boolean { return range.startLineNumber === -1 && range.startColumn === -1 && range.endLineNumber === -1 && range.endColumn === -1; } @@ -613,6 +613,10 @@ export class DefaultSettings extends Disposable { const value = prop.default; const description = (prop.description || prop.markdownDescription || '').split('\n'); const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; + const listItemType = prop.type === 'array' && prop.items && !isArray(prop.items) && prop.items.type && !isArray(prop.items.type) + ? prop.items.type + : undefined; + result.push({ key, value, @@ -625,6 +629,7 @@ export class DefaultSettings extends Disposable { overrides, scope: prop.scope, type: prop.type, + arrayItemType: listItemType, enum: prop.enum, enumDescriptions: prop.enumDescriptions || prop.markdownEnumDescriptions, enumDescriptionsAreMarkdown: !prop.enumDescriptions, diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index 3fcd994409..b7fc5de557 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -56,7 +56,7 @@ suite('KeybindingsEditorModel test', () => { aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }) ); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('')); assertKeybindingItems(actuals, expected); }); @@ -67,7 +67,7 @@ suite('KeybindingsEditorModel test', () => { aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }) ); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('').slice(0, 2), true); assertKeybindingItems(actuals, expected); }); @@ -80,7 +80,7 @@ suite('KeybindingsEditorModel test', () => { ); const expected = [keybindings[2], keybindings[0], keybindings[1]]; - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('')); assertKeybindingItems(actuals, expected); }); @@ -93,7 +93,7 @@ suite('KeybindingsEditorModel test', () => { ); const expected = [keybindings[1], keybindings[0]]; - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('')); assertKeybindingItems(actuals, expected); }); @@ -113,7 +113,7 @@ suite('KeybindingsEditorModel test', () => { instantiationService.stub(IKeybindingService, 'getKeybindings', () => keybindings); instantiationService.stub(IKeybindingService, 'getDefaultKeybindings', () => keybindings); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('')); assertKeybindingItems(actuals, expected); }); @@ -131,7 +131,7 @@ suite('KeybindingsEditorModel test', () => { registerCommandWithTitle(keybindings[3].command!, 'Same Title'); const expected = [keybindings[3], keybindings[1], keybindings[0], keybindings[2]]; - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('')); assertKeybindingItems(actuals, expected); }); @@ -143,7 +143,7 @@ suite('KeybindingsEditorModel test', () => { aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Backspace } }) ); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actuals = asResolvedKeybindingItems(testObject.fetch('', true)); assertKeybindingItems(actuals, expected); }); @@ -152,7 +152,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('')[0]; assert.equal(actual.keybindingItem.command, expected.command); assert.equal(actual.keybindingItem.commandLabel, ''); @@ -166,7 +166,7 @@ suite('KeybindingsEditorModel test', () => { prepareKeybindingService(expected); registerCommandWithTitle(expected.command!, 'Some Title'); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('')[0]; assert.equal(actual.keybindingItem.command, expected.command); assert.equal(actual.keybindingItem.commandLabel, 'Some Title'); @@ -179,7 +179,7 @@ suite('KeybindingsEditorModel test', () => { CommandsRegistry.registerCommand('command_without_keybinding', () => { }); prepareKeybindingService(); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('').filter(element => element.keybindingItem.command === 'command_without_keybinding')[0]; assert.equal(actual.keybindingItem.command, 'command_without_keybinding'); assert.equal(actual.keybindingItem.commandLabel, ''); @@ -193,7 +193,7 @@ suite('KeybindingsEditorModel test', () => { registerCommandWithTitle(id, 'some title'); prepareKeybindingService(); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('').filter(element => element.keybindingItem.command === id)[0]; assert.equal(actual.keybindingItem.command, id); assert.equal(actual.keybindingItem.commandLabel, 'some title'); @@ -207,7 +207,7 @@ suite('KeybindingsEditorModel test', () => { registerCommandWithTitle(id, 'some title'); prepareKeybindingService(); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('workbench action view size').filter(element => element.keybindingItem.command === id)[0]; assert.ok(actual); }); @@ -217,7 +217,7 @@ suite('KeybindingsEditorModel test', () => { registerCommandWithTitle(id, 'Increase view size'); prepareKeybindingService(); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('increase size').filter(element => element.keybindingItem.command === id)[0]; assert.ok(actual); }); @@ -227,7 +227,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('default').filter(element => element.keybindingItem.command === command)[0]; assert.ok(actual); }); @@ -237,7 +237,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: false }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('user').filter(element => element.keybindingItem.command === command)[0]; assert.ok(actual); }); @@ -247,7 +247,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: true }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('@source: default').filter(element => element.keybindingItem.command === command)[0]; assert.ok(actual); }); @@ -257,7 +257,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: false }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('@source: user').filter(element => element.keybindingItem.command === command)[0]; assert.ok(actual); }); @@ -267,7 +267,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('when context').filter(element => element.keybindingItem.command === command)[0]; assert.ok(actual); }); @@ -279,7 +279,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('cmd').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true }); @@ -293,7 +293,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('meta').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true }); @@ -307,7 +307,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('command').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true }); @@ -321,7 +321,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('windows').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true }); @@ -333,7 +333,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('alt').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { altKey: true }); @@ -345,7 +345,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('option').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { altKey: true }); @@ -357,7 +357,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('ctrl').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { ctrlKey: true }); @@ -369,7 +369,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('control').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { ctrlKey: true }); @@ -381,7 +381,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('shift').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { shiftKey: true }); @@ -393,7 +393,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('arrow').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { keyCode: true }); @@ -405,7 +405,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('alt right').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { altKey: true, keyCode: true }); @@ -417,7 +417,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('right alt').filter(element => element.keybindingItem.command === command); assert.equal(0, actual.length); }); @@ -428,7 +428,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true, metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('alt cmd esc').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { altKey: true, metaKey: true, keyCode: true }); @@ -441,7 +441,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true, shiftKey: true, keyCode: true }); @@ -454,7 +454,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true, shiftKey: true, keyCode: true }); @@ -467,7 +467,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('cmd del').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { metaKey: true }); @@ -480,7 +480,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.UpArrow }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('cmd shift esc del').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { shiftKey: true, metaKey: true, keyCode: true }); @@ -492,7 +492,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"ctrl c"').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { ctrlKey: true, keyCode: true }); @@ -504,7 +504,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"shift meta escape ctrl c"').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { shiftKey: true, metaKey: true, keyCode: true }); @@ -517,7 +517,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.UpArrow }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"cmd shift esc del"').filter(element => element.keybindingItem.command === command); assert.equal(0, actual.length); }); @@ -527,7 +527,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"control+c"').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { ctrlKey: true, keyCode: true }); @@ -539,7 +539,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"shift+meta+escape ctrl+c"').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { shiftKey: true, metaKey: true, keyCode: true }); @@ -551,7 +551,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Space, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Backspace, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"ctrl+space"').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); }); @@ -562,7 +562,7 @@ suite('KeybindingsEditorModel test', () => { const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.DownArrow } }); prepareKeybindingService(expected, aResolvedKeybindingItem({ command: 'down', firstPart: { keyCode: KeyCode.Escape } })); - await testObject.resolve({}); + await testObject.resolve(new Map()); const actual = testObject.fetch('"down"').filter(element => element.keybindingItem.command === command); assert.equal(1, actual.length); assert.deepEqual(actual[0].keybindingMatches!.firstPart, { keyCode: true }); diff --git a/src/vs/workbench/services/progress/browser/editorProgressService.ts b/src/vs/workbench/services/progress/browser/editorProgressService.ts new file mode 100644 index 0000000000..693be8fe64 --- /dev/null +++ b/src/vs/workbench/services/progress/browser/editorProgressService.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 { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { ProgressBarIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; + +export class EditorProgressService extends ProgressBarIndicator { + + _serviceBrand: ServiceIdentifier; +} diff --git a/src/vs/workbench/services/progress/browser/media/progressService2.css b/src/vs/workbench/services/progress/browser/media/progressService.css similarity index 85% rename from src/vs/workbench/services/progress/browser/media/progressService2.css rename to src/vs/workbench/services/progress/browser/media/progressService.css index e4516338b5..c7e5960f2b 100644 --- a/src/vs/workbench/services/progress/browser/media/progressService2.css +++ b/src/vs/workbench/services/progress/browser/media/progressService.css @@ -3,11 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .part.statusbar > .statusbar-item.progress { +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.progress { padding-left: 5px; } -.monaco-workbench .part.statusbar > .statusbar-item.progress .spinner-container { +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.progress .spinner-container { padding-right: 5px; } diff --git a/src/vs/workbench/services/progress/browser/progressIndicator.ts b/src/vs/workbench/services/progress/browser/progressIndicator.ts new file mode 100644 index 0000000000..6f2f66f51f --- /dev/null +++ b/src/vs/workbench/services/progress/browser/progressIndicator.ts @@ -0,0 +1,300 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { isUndefinedOrNull } from 'vs/base/common/types'; +import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IProgressRunner, IProgressIndicator } from 'vs/platform/progress/common/progress'; + +export class ProgressBarIndicator implements IProgressIndicator { + + constructor(private progressbar: ProgressBar) { } + + show(infinite: true, delay?: number): IProgressRunner; + show(total: number, delay?: number): IProgressRunner; + show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { + if (typeof infiniteOrTotal === 'boolean') { + this.progressbar.infinite().show(delay); + } else { + this.progressbar.total(infiniteOrTotal).show(delay); + } + + return { + total: (total: number) => { + this.progressbar.total(total); + }, + + worked: (worked: number) => { + if (this.progressbar.hasTotal()) { + this.progressbar.worked(worked); + } else { + this.progressbar.infinite().show(); + } + }, + + done: () => { + this.progressbar.stop().hide(); + } + }; + } + + async showWhile(promise: Promise, delay?: number): Promise { + try { + this.progressbar.infinite().show(delay); + + await promise; + } catch (error) { + // ignore + } finally { + this.progressbar.stop().hide(); + } + } +} + +namespace ProgressIndicatorState { + + export const enum Type { + None, + Done, + Infinite, + While, + Work + } + + export const None = new class { readonly type = Type.None; }; + export const Done = new class { readonly type = Type.Done; }; + export const Infinite = new class { readonly type = Type.Infinite; }; + + export class While { + readonly type = Type.While; + + constructor( + readonly whilePromise: Promise, + readonly whileStart: number, + readonly whileDelay: number, + ) { } + } + + export class Work { + readonly type = Type.Work; + + constructor( + readonly total: number | undefined, + readonly worked: number | undefined + ) { } + } + + export type State = + typeof None + | typeof Done + | typeof Infinite + | While + | Work; +} + +export abstract class CompositeScope extends Disposable { + + constructor( + private viewletService: IViewletService, + private panelService: IPanelService, + private scopeId: string + ) { + super(); + + this.registerListeners(); + } + + registerListeners(): void { + this._register(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId()))); + this._register(this.panelService.onDidPanelOpen(({ panel }) => this.onScopeOpened(panel.getId()))); + + this._register(this.viewletService.onDidViewletClose(viewlet => this.onScopeClosed(viewlet.getId()))); + this._register(this.panelService.onDidPanelClose(panel => this.onScopeClosed(panel.getId()))); + } + + private onScopeClosed(scopeId: string) { + if (scopeId === this.scopeId) { + this.onScopeDeactivated(); + } + } + + private onScopeOpened(scopeId: string) { + if (scopeId === this.scopeId) { + this.onScopeActivated(); + } + } + + abstract onScopeActivated(): void; + + abstract onScopeDeactivated(): void; +} + +export class CompositeProgressIndicator extends CompositeScope implements IProgressIndicator { + private isActive: boolean; + private progressbar: ProgressBar; + private progressState: ProgressIndicatorState.State = ProgressIndicatorState.None; + + constructor( + progressbar: ProgressBar, + scopeId: string, + isActive: boolean, + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService + ) { + super(viewletService, panelService, scopeId); + + this.progressbar = progressbar; + this.isActive = isActive || isUndefinedOrNull(scopeId); // If service is unscoped, enable by default + } + + onScopeDeactivated(): void { + this.isActive = false; + } + + onScopeActivated(): void { + this.isActive = true; + + // Return early if progress state indicates that progress is done + if (this.progressState.type === ProgressIndicatorState.Done.type) { + return; + } + + // Replay Infinite Progress from Promise + if (this.progressState.type === ProgressIndicatorState.Type.While) { + let delay: number | undefined; + if (this.progressState.whileDelay > 0) { + const remainingDelay = this.progressState.whileDelay - (Date.now() - this.progressState.whileStart); + if (remainingDelay > 0) { + delay = remainingDelay; + } + } + + this.doShowWhile(delay); + } + + // Replay Infinite Progress + else if (this.progressState.type === ProgressIndicatorState.Type.Infinite) { + this.progressbar.infinite().show(); + } + + // Replay Finite Progress (Total & Worked) + else if (this.progressState.type === ProgressIndicatorState.Type.Work) { + if (this.progressState.total) { + this.progressbar.total(this.progressState.total).show(); + } + + if (this.progressState.worked) { + this.progressbar.worked(this.progressState.worked).show(); + } + } + } + + show(infinite: true, delay?: number): IProgressRunner; + show(total: number, delay?: number): IProgressRunner; + show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { + + // Sort out Arguments + if (typeof infiniteOrTotal === 'boolean') { + this.progressState = ProgressIndicatorState.Infinite; + } else { + this.progressState = new ProgressIndicatorState.Work(infiniteOrTotal, undefined); + } + + // Active: Show Progress + if (this.isActive) { + + // Infinite: Start Progressbar and Show after Delay + if (this.progressState.type === ProgressIndicatorState.Type.Infinite) { + this.progressbar.infinite().show(delay); + } + + // Finite: Start Progressbar and Show after Delay + else if (this.progressState.type === ProgressIndicatorState.Type.Work && typeof this.progressState.total === 'number') { + this.progressbar.total(this.progressState.total).show(delay); + } + } + + return { + total: (total: number) => { + this.progressState = new ProgressIndicatorState.Work( + total, + this.progressState.type === ProgressIndicatorState.Type.Work ? this.progressState.worked : undefined); + + if (this.isActive) { + this.progressbar.total(total); + } + }, + + worked: (worked: number) => { + + // Verify first that we are either not active or the progressbar has a total set + if (!this.isActive || this.progressbar.hasTotal()) { + this.progressState = new ProgressIndicatorState.Work( + this.progressState.type === ProgressIndicatorState.Type.Work ? this.progressState.total : undefined, + this.progressState.type === ProgressIndicatorState.Type.Work && typeof this.progressState.worked === 'number' ? this.progressState.worked + worked : worked); + + if (this.isActive) { + this.progressbar.worked(worked); + } + } + + // Otherwise the progress bar does not support worked(), we fallback to infinite() progress + else { + this.progressState = ProgressIndicatorState.Infinite; + this.progressbar.infinite().show(); + } + }, + + done: () => { + this.progressState = ProgressIndicatorState.Done; + + if (this.isActive) { + this.progressbar.stop().hide(); + } + } + }; + } + + async showWhile(promise: Promise, delay?: number): Promise { + + // Join with existing running promise to ensure progress is accurate + if (this.progressState.type === ProgressIndicatorState.Type.While) { + promise = Promise.all([promise, this.progressState.whilePromise]); + } + + // Keep Promise in State + this.progressState = new ProgressIndicatorState.While(promise, delay || 0, Date.now()); + + try { + this.doShowWhile(delay); + + await promise; + } catch (error) { + // ignore + } finally { + + // If this is not the last promise in the list of joined promises, skip this + if (this.progressState.type !== ProgressIndicatorState.Type.While || this.progressState.whilePromise === promise) { + + // The while promise is either null or equal the promise we last hooked on + this.progressState = ProgressIndicatorState.None; + + if (this.isActive) { + this.progressbar.stop().hide(); + } + } + } + } + + private doShowWhile(delay?: number): void { + + // Show Progress when active + if (this.isActive) { + this.progressbar.infinite().show(delay); + } + } +} diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index f5f09c13ab..efd7602c15 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -3,294 +3,381 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import * as types from 'vs/base/common/types'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import 'vs/css!./media/progressService'; + +import { localize } from 'vs/nls'; +import { IDisposable, dispose, DisposableStore, MutableDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions, IProgressRunner, IProgressIndicator } from 'vs/platform/progress/common/progress'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { timeout } from 'vs/base/common/async'; +import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; +import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; +import { Action } from 'vs/base/common/actions'; +import { Event } from 'vs/base/common/event'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; +import { attachDialogStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { EventHelper } from 'vs/base/browser/dom'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +export class ProgressService extends Disposable implements IProgressService { -namespace ProgressState { - export const enum Type { - None, - Done, - Infinite, - While, - Work - } + _serviceBrand: ServiceIdentifier; - export const None = new class { readonly type = Type.None; }; - export const Done = new class { readonly type = Type.Done; }; - export const Infinite = new class { readonly type = Type.Infinite; }; - - export class While { - public readonly type = Type.While; - constructor( - public readonly whilePromise: Promise, - public readonly whileStart: number, - public readonly whileDelay: number, - ) { } - } - - export class Work { - public readonly type = Type.Work; - constructor( - public readonly total: number | undefined, - public readonly worked: number | undefined - ) { } - } - - export type State = - typeof None - | typeof Done - | typeof Infinite - | While - | Work; -} - -export abstract class ScopedService extends Disposable { - - constructor(private viewletService: IViewletService, private panelService: IPanelService, private scopeId: string) { - super(); - - this.registerListeners(); - } - - registerListeners(): void { - this._register(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId()))); - this._register(this.panelService.onDidPanelOpen(({ panel }) => this.onScopeOpened(panel.getId()))); - - this._register(this.viewletService.onDidViewletClose(viewlet => this.onScopeClosed(viewlet.getId()))); - this._register(this.panelService.onDidPanelClose(panel => this.onScopeClosed(panel.getId()))); - } - - private onScopeClosed(scopeId: string) { - if (scopeId === this.scopeId) { - this.onScopeDeactivated(); - } - } - - private onScopeOpened(scopeId: string) { - if (scopeId === this.scopeId) { - this.onScopeActivated(); - } - } - - abstract onScopeActivated(): void; - - abstract onScopeDeactivated(): void; -} - -export class ScopedProgressService extends ScopedService implements IProgressService { - _serviceBrand: any; - private isActive: boolean; - private progressbar: ProgressBar; - private progressState: ProgressState.State = ProgressState.None; + private readonly stack: [IProgressOptions, Progress][] = []; + private readonly globalStatusEntry = this._register(new MutableDisposable()); constructor( - progressbar: ProgressBar, - scopeId: string, - isActive: boolean, - @IViewletService viewletService: IViewletService, - @IPanelService panelService: IPanelService + @IActivityService private readonly activityService: IActivityService, + @IViewletService private readonly viewletService: IViewletService, + @IPanelService private readonly panelService: IPanelService, + @INotificationService private readonly notificationService: INotificationService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @ILayoutService private readonly layoutService: ILayoutService, + @IThemeService private readonly themeService: IThemeService, + @IKeybindingService private readonly keybindingService: IKeybindingService ) { - super(viewletService, panelService, scopeId); - - this.progressbar = progressbar; - this.isActive = isActive || types.isUndefinedOrNull(scopeId); // If service is unscoped, enable by default + super(); } - onScopeDeactivated(): void { - this.isActive = false; - } + withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: () => void): Promise { + const { location } = options; + if (typeof location === 'string') { + if (this.viewletService.getProgressIndicator(location)) { + return this.withViewletProgress(location, task, { ...options, location }); + } - onScopeActivated(): void { - this.isActive = true; + if (this.panelService.getProgressIndicator(location)) { + return this.withPanelProgress(location, task, { ...options, location }); + } - // Return early if progress state indicates that progress is done - if (this.progressState.type === ProgressState.Done.type) { - return; + return Promise.reject(new Error(`Bad progress location: ${location}`)); } - // Replay Infinite Progress from Promise - if (this.progressState.type === ProgressState.Type.While) { - let delay: number | undefined; - if (this.progressState.whileDelay > 0) { - const remainingDelay = this.progressState.whileDelay - (Date.now() - this.progressState.whileStart); - if (remainingDelay > 0) { - delay = remainingDelay; + switch (location) { + case ProgressLocation.Notification: + return this.withNotificationProgress({ ...options, location }, task, onDidCancel); + case ProgressLocation.Window: + return this.withWindowProgress(options, task); + case ProgressLocation.Explorer: + return this.withViewletProgress('workbench.view.explorer', task, { ...options, location }); + case ProgressLocation.Scm: + return this.withViewletProgress('workbench.view.scm', task, { ...options, location }); + case ProgressLocation.Extensions: + return this.withViewletProgress('workbench.view.extensions', task, { ...options, location }); + case ProgressLocation.Dialog: + return this.withDialogProgress(options, task, onDidCancel); + default: + return Promise.reject(new Error(`Bad progress location: ${location}`)); + } + } + + private withWindowProgress(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise): Promise { + const task: [IProgressOptions, Progress] = [options, new Progress(() => this.updateWindowProgress())]; + + const promise = callback(task[1]); + + let delayHandle: any = setTimeout(() => { + delayHandle = undefined; + this.stack.unshift(task); + this.updateWindowProgress(); + + // show progress for at least 150ms + Promise.all([ + timeout(150), + promise + ]).finally(() => { + const idx = this.stack.indexOf(task); + this.stack.splice(idx, 1); + this.updateWindowProgress(); + }); + }, 150); + + // cancel delay if promise finishes below 150ms + return promise.finally(() => clearTimeout(delayHandle)); + } + + private updateWindowProgress(idx: number = 0) { + this.globalStatusEntry.clear(); + + if (idx < this.stack.length) { + const [options, progress] = this.stack[idx]; + + let progressTitle = options.title; + let progressMessage = progress.value && progress.value.message; + let text: string; + let title: string; + + if (progressTitle && progressMessage) { + // : <message> + text = localize('progress.text2', "{0}: {1}", progressTitle, progressMessage); + title = options.source ? localize('progress.title3', "[{0}] {1}: {2}", options.source, progressTitle, progressMessage) : text; + + } else if (progressTitle) { + // <title> + text = progressTitle; + title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressTitle) : text; + + } else if (progressMessage) { + // <message> + text = progressMessage; + title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressMessage) : text; + + } else { + // no title, no message -> no progress. try with next on stack + this.updateWindowProgress(idx + 1); + return; + } + + this.globalStatusEntry.value = this.statusbarService.addEntry({ + text: `$(sync~spin) ${text}`, + tooltip: title + }, 'status.progress', localize('status.progress', "Progress Message"), StatusbarAlignment.LEFT); + } + } + + private withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { + const toDispose = new DisposableStore(); + + const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { + if (!message) { + return undefined; // we need a message at least + } + + const primaryActions = options.primaryActions ? Array.from(options.primaryActions) : []; + const secondaryActions = options.secondaryActions ? Array.from(options.secondaryActions) : []; + if (options.cancellable) { + const cancelAction = new class extends Action { + constructor() { + super('progress.cancel', localize('cancel', "Cancel"), undefined, true); + } + + run(): Promise<any> { + if (typeof onDidCancel === 'function') { + onDidCancel(); + } + + return Promise.resolve(undefined); + } + }; + toDispose.add(cancelAction); + + primaryActions.push(cancelAction); + } + + const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; + const handle = this.notificationService.notify({ + severity: Severity.Info, + message, + source: options.source, + actions + }); + + updateProgress(handle, increment); + + Event.once(handle.onDidClose)(() => { + toDispose.dispose(); + }); + + return handle; + }; + + const updateProgress = (notification: INotificationHandle, increment?: number): void => { + if (typeof increment === 'number' && increment >= 0) { + notification.progress.total(100); // always percentage based + notification.progress.worked(increment); + } else { + notification.progress.infinite(); + } + }; + + let handle: INotificationHandle | undefined; + const updateNotification = (message?: string, increment?: number): void => { + if (!handle) { + handle = createNotification(message, increment); + } else { + if (typeof message === 'string') { + let newMessage: string; + if (typeof options.title === 'string') { + newMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932) + } else { + newMessage = message; + } + + handle.updateMessage(newMessage); + } + + if (typeof increment === 'number') { + updateProgress(handle, increment); } } + }; - this.doShowWhile(delay); - } + // Show initially + updateNotification(options.title); - // Replay Infinite Progress - else if (this.progressState.type === ProgressState.Type.Infinite) { - this.progressbar.infinite().show(); - } - - // Replay Finite Progress (Total & Worked) - else if (this.progressState.type === ProgressState.Type.Work) { - if (this.progressState.total) { - this.progressbar.total(this.progressState.total).show(); + // Update based on progress + const promise = callback({ + report: progress => { + updateNotification(progress.message, progress.increment); } + }); - if (this.progressState.worked) { - this.progressbar.worked(this.progressState.worked).show(); + // Show progress for at least 800ms and then hide once done or canceled + Promise.all([timeout(800), promise]).finally(() => { + if (handle) { + handle.close(); } - } + }); + + return promise; } - show(infinite: true, delay?: number): IProgressRunner; - show(total: number, delay?: number): IProgressRunner; - show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { - // Sort out Arguments - if (typeof infiniteOrTotal === 'boolean') { - this.progressState = ProgressState.Infinite; - } else { - this.progressState = new ProgressState.Work(infiniteOrTotal, undefined); - } + private withViewletProgress<P extends Promise<R>, R = unknown>(viewletId: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P { - // Active: Show Progress - if (this.isActive) { + // show in viewlet + const promise = this.withCompositeProgress(this.viewletService.getProgressIndicator(viewletId), task, options); - // Infinite: Start Progressbar and Show after Delay - if (this.progressState.type === ProgressState.Type.Infinite) { - this.progressbar.infinite().show(delay); - } + // show activity bar + let activityProgress: IDisposable; + let delayHandle: any = setTimeout(() => { + delayHandle = undefined; - // Finite: Start Progressbar and Show after Delay - else if (this.progressState.type === ProgressState.Type.Work && typeof this.progressState.total === 'number') { - this.progressbar.total(this.progressState.total).show(delay); - } - } + const handle = this.activityService.showActivity( + viewletId, + new ProgressBadge(() => ''), + 'progress-badge', + 100 + ); - return { - total: (total: number) => { - this.progressState = new ProgressState.Work( - total, - this.progressState.type === ProgressState.Type.Work ? this.progressState.worked : undefined); - - if (this.isActive) { - this.progressbar.total(total); - } - }, - - worked: (worked: number) => { - - // Verify first that we are either not active or the progressbar has a total set - if (!this.isActive || this.progressbar.hasTotal()) { - this.progressState = new ProgressState.Work( - this.progressState.type === ProgressState.Type.Work ? this.progressState.total : undefined, - this.progressState.type === ProgressState.Type.Work && typeof this.progressState.worked === 'number' ? this.progressState.worked + worked : worked); - - if (this.isActive) { - this.progressbar.worked(worked); + const startTimeVisible = Date.now(); + const minTimeVisible = 300; + activityProgress = { + dispose() { + const d = Date.now() - startTimeVisible; + if (d < minTimeVisible) { + // should at least show for Nms + setTimeout(() => handle.dispose(), minTimeVisible - d); + } else { + // shown long enough + handle.dispose(); } } + }; + }, options.delay || 300); - // Otherwise the progress bar does not support worked(), we fallback to infinite() progress - else { - this.progressState = ProgressState.Infinite; - this.progressbar.infinite().show(); + promise.finally(() => { + clearTimeout(delayHandle); + dispose(activityProgress); + }); + + return promise; + } + + private withPanelProgress<P extends Promise<R>, R = unknown>(panelid: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P { + + // show in panel + return this.withCompositeProgress(this.panelService.getProgressIndicator(panelid), task, options); + } + + private withCompositeProgress<P extends Promise<R>, R = unknown>(progressIndicator: IProgressIndicator | null, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P { + let progressRunner: IProgressRunner | undefined = undefined; + + const promise = task({ + report: progress => { + if (!progressRunner) { + return; } - }, - done: () => { - this.progressState = ProgressState.Done; - - if (this.isActive) { - this.progressbar.stop().hide(); + if (typeof progress.increment === 'number') { + progressRunner.worked(progress.increment); } + + if (typeof progress.total === 'number') { + progressRunner.total(progress.total); + } + } + }); + + if (progressIndicator) { + if (typeof options.total === 'number') { + progressRunner = progressIndicator.show(options.total, options.delay); + promise.catch(() => undefined /* ignore */).finally(() => progressRunner ? progressRunner.done() : undefined); + } else { + progressIndicator.showWhile(promise, options.delay); + } + } + + return promise; + } + + private withDialogProgress<P extends Promise<R>, R = unknown>(options: IProgressOptions, task: (progress: IProgress<IProgressStep>) => P, onDidCancel?: () => void): P { + const disposables = new DisposableStore(); + const allowableCommands = [ + 'workbench.action.quit', + 'workbench.action.reloadWindow' + ]; + + let dialog: Dialog; + + const createDialog = (message: string) => { + dialog = new Dialog( + this.layoutService.container, + message, + [options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")], + { + type: 'pending', + keyEventProcessor: (event: StandardKeyboardEvent) => { + const resolved = this.keybindingService.softDispatch(event, this.layoutService.container); + if (resolved && resolved.commandId) { + if (allowableCommands.indexOf(resolved.commandId) === -1) { + EventHelper.stop(event, true); + } + } + } + } + ); + + disposables.add(dialog); + disposables.add(attachDialogStyler(dialog, this.themeService)); + + dialog.show().then(() => { + if (typeof onDidCancel === 'function') { + onDidCancel(); + } + + dispose(dialog); + }); + + return dialog; + }; + + const updateDialog = (message?: string) => { + if (message && !dialog) { + dialog = createDialog(message); + } else if (message) { + dialog.updateMessage(message); } }; - } - async showWhile(promise: Promise<any>, delay?: number): Promise<void> { - - // Join with existing running promise to ensure progress is accurate - if (this.progressState.type === ProgressState.Type.While) { - promise = Promise.all([promise, this.progressState.whilePromise]); - } - - // Keep Promise in State - this.progressState = new ProgressState.While(promise, delay || 0, Date.now()); - - try { - this.doShowWhile(delay); - - await promise; - } catch (error) { - // ignore - } finally { - - // If this is not the last promise in the list of joined promises, skip this - if (this.progressState.type !== ProgressState.Type.While || this.progressState.whilePromise === promise) { - - // The while promise is either null or equal the promise we last hooked on - this.progressState = ProgressState.None; - - if (this.isActive) { - this.progressbar.stop().hide(); - } + const promise = task({ + report: progress => { + updateDialog(progress.message); } - } - } + }); - private doShowWhile(delay?: number): void { + promise.finally(() => { + dispose(disposables); + }); - // Show Progress when active - if (this.isActive) { - this.progressbar.infinite().show(delay); - } + return promise; } } -export class ProgressService implements IProgressService { - - _serviceBrand: any; - - constructor(private progressbar: ProgressBar) { } - - show(infinite: true, delay?: number): IProgressRunner; - show(total: number, delay?: number): IProgressRunner; - show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { - if (typeof infiniteOrTotal === 'boolean') { - this.progressbar.infinite().show(delay); - } else { - this.progressbar.total(infiniteOrTotal).show(delay); - } - - return { - total: (total: number) => { - this.progressbar.total(total); - }, - - worked: (worked: number) => { - if (this.progressbar.hasTotal()) { - this.progressbar.worked(worked); - } else { - this.progressbar.infinite().show(); - } - }, - - done: () => { - this.progressbar.stop().hide(); - } - }; - } - - async showWhile(promise: Promise<any>, delay?: number): Promise<void> { - try { - this.progressbar.infinite().show(delay); - - await promise; - } catch (error) { - // ignore - } finally { - this.progressbar.stop().hide(); - } - } -} +registerSingleton(IProgressService, ProgressService, true); diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts deleted file mode 100644 index 1e516d069a..0000000000 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ /dev/null @@ -1,344 +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 'vs/css!./media/progressService2'; - -import { localize } from 'vs/nls'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress, IProgressNotificationOptions } from 'vs/platform/progress/common/progress'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { timeout } from 'vs/base/common/async'; -import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; -import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; -import { Action } from 'vs/base/common/actions'; -import { Event } from 'vs/base/common/event'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; -import { attachDialogStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { EventHelper } from 'vs/base/browser/dom'; - -export class ProgressService2 implements IProgressService2 { - - _serviceBrand: any; - - private readonly _stack: [IProgressOptions, Progress<IProgressStep>][] = []; - private _globalStatusEntry: IDisposable; - - constructor( - @IActivityService private readonly _activityBar: IActivityService, - @IViewletService private readonly _viewletService: IViewletService, - @INotificationService private readonly _notificationService: INotificationService, - @IStatusbarService private readonly _statusbarService: IStatusbarService, - @ILayoutService private readonly _layoutService: ILayoutService, - @IThemeService private readonly _themeService: IThemeService, - @IKeybindingService private readonly _keybindingService: IKeybindingService - ) { } - - withProgress<R = unknown>(options: IProgressOptions, task: (progress: IProgress<IProgressStep>) => Promise<R>, onDidCancel?: () => void): Promise<R> { - - const { location } = options; - if (typeof location === 'string') { - const viewlet = this._viewletService.getViewlet(location); - if (viewlet) { - return this._withViewletProgress(location, task); - } - return Promise.reject(new Error(`Bad progress location: ${location}`)); - } - - switch (location) { - case ProgressLocation.Notification: - return this._withNotificationProgress({ ...options, location: ProgressLocation.Notification }, task, onDidCancel); - case ProgressLocation.Window: - return this._withWindowProgress(options, task); - case ProgressLocation.Explorer: - return this._withViewletProgress('workbench.view.explorer', task); - case ProgressLocation.Scm: - return this._withViewletProgress('workbench.view.scm', task); - case ProgressLocation.Extensions: - return this._withViewletProgress('workbench.view.extensions', task); - case ProgressLocation.Dialog: - return this._withDialogProgress(options, task, onDidCancel); - default: - return Promise.reject(new Error(`Bad progress location: ${location}`)); - } - } - - private _withWindowProgress<R = unknown>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise<R>): Promise<R> { - - const task: [IProgressOptions, Progress<IProgressStep>] = [options, new Progress<IProgressStep>(() => this._updateWindowProgress())]; - - const promise = callback(task[1]); - - let delayHandle: any = setTimeout(() => { - delayHandle = undefined; - this._stack.unshift(task); - this._updateWindowProgress(); - - // show progress for at least 150ms - Promise.all([ - timeout(150), - promise - ]).finally(() => { - const idx = this._stack.indexOf(task); - this._stack.splice(idx, 1); - this._updateWindowProgress(); - }); - - }, 150); - - // cancel delay if promise finishes below 150ms - return promise.finally(() => clearTimeout(delayHandle)); - } - - private _updateWindowProgress(idx: number = 0) { - - dispose(this._globalStatusEntry); - - if (idx < this._stack.length) { - - const [options, progress] = this._stack[idx]; - - let progressTitle = options.title; - let progressMessage = progress.value && progress.value.message; - let text: string; - let title: string; - - if (progressTitle && progressMessage) { - // <title>: <message> - text = localize('progress.text2', "{0}: {1}", progressTitle, progressMessage); - title = options.source ? localize('progress.title3', "[{0}] {1}: {2}", options.source, progressTitle, progressMessage) : text; - - } else if (progressTitle) { - // <title> - text = progressTitle; - title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressTitle) : text; - - } else if (progressMessage) { - // <message> - text = progressMessage; - title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressMessage) : text; - - } else { - // no title, no message -> no progress. try with next on stack - this._updateWindowProgress(idx + 1); - return; - } - - this._globalStatusEntry = this._statusbarService.addEntry({ - text: `$(sync~spin) ${text}`, - tooltip: title - }, StatusbarAlignment.LEFT); - } - } - - private _withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { - const toDispose: IDisposable[] = []; - - const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { - if (!message) { - return undefined; // we need a message at least - } - - const actions: INotificationActions = { primary: options.primaryActions || [], secondary: options.secondaryActions || [] }; - if (options.cancellable) { - const cancelAction = new class extends Action { - constructor() { - super('progress.cancel', localize('cancel', "Cancel"), undefined, true); - } - - run(): Promise<any> { - if (typeof onDidCancel === 'function') { - onDidCancel(); - } - - return Promise.resolve(undefined); - } - }; - toDispose.push(cancelAction); - - actions.primary!.push(cancelAction); - } - - const handle = this._notificationService.notify({ - severity: Severity.Info, - message, - source: options.source, - actions - }); - - updateProgress(handle, increment); - - Event.once(handle.onDidClose)(() => { - dispose(toDispose); - }); - - return handle; - }; - - const updateProgress = (notification: INotificationHandle, increment?: number): void => { - if (typeof increment === 'number' && increment >= 0) { - notification.progress.total(100); // always percentage based - notification.progress.worked(increment); - } else { - notification.progress.infinite(); - } - }; - - let handle: INotificationHandle | undefined; - const updateNotification = (message?: string, increment?: number): void => { - if (!handle) { - handle = createNotification(message, increment); - } else { - if (typeof message === 'string') { - let newMessage: string; - if (typeof options.title === 'string') { - newMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932) - } else { - newMessage = message; - } - - handle.updateMessage(newMessage); - } - - if (typeof increment === 'number') { - updateProgress(handle, increment); - } - } - }; - - // Show initially - updateNotification(options.title); - - // Update based on progress - const p = callback({ - report: progress => { - updateNotification(progress.message, progress.increment); - } - }); - - // Show progress for at least 800ms and then hide once done or canceled - Promise.all([timeout(800), p]).finally(() => { - if (handle) { - handle.close(); - } - }); - - return p; - } - - private _withViewletProgress<P extends Promise<R>, R = unknown>(viewletId: string, task: (progress: IProgress<{ message?: string }>) => P): P { - - const promise = task(emptyProgress); - - // show in viewlet - const viewletProgress = this._viewletService.getProgressIndicator(viewletId); - if (viewletProgress) { - viewletProgress.showWhile(promise); - } - - // show activity bar - let activityProgress: IDisposable; - let delayHandle: any = setTimeout(() => { - delayHandle = undefined; - const handle = this._activityBar.showActivity( - viewletId, - new ProgressBadge(() => ''), - 'progress-badge', - 100 - ); - const startTimeVisible = Date.now(); - const minTimeVisible = 300; - activityProgress = { - dispose() { - const d = Date.now() - startTimeVisible; - if (d < minTimeVisible) { - // should at least show for Nms - setTimeout(() => handle.dispose(), minTimeVisible - d); - } else { - // shown long enough - handle.dispose(); - } - } - }; - }, 300); - - const onDone = () => { - clearTimeout(delayHandle); - dispose(activityProgress); - }; - - promise.then(onDone, onDone); - return promise; - } - - private _withDialogProgress<P extends Promise<R>, R = unknown>(options: IProgressOptions, task: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { - const disposables: IDisposable[] = []; - const allowableCommands = [ - 'workbench.action.quit', - 'workbench.action.reloadWindow' - ]; - - let dialog: Dialog; - - const createDialog = (message: string) => { - dialog = new Dialog( - this._layoutService.container, - message, - [options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")], - { - type: 'pending', - keyEventProcessor: (event: StandardKeyboardEvent) => { - const resolved = this._keybindingService.softDispatch(event, this._layoutService.container); - if (resolved && resolved.commandId) { - if (allowableCommands.indexOf(resolved.commandId) === -1) { - EventHelper.stop(event, true); - } - } - } - } - ); - - disposables.push(dialog); - disposables.push(attachDialogStyler(dialog, this._themeService)); - - dialog.show().then(() => { - if (typeof onDidCancel === 'function') { - onDidCancel(); - } - - dispose(dialog); - }); - - return dialog; - }; - - const updateDialog = (message?: string) => { - if (message && !dialog) { - dialog = createDialog(message); - } else if (message) { - dialog.updateMessage(message); - } - }; - - const p = task({ - report: progress => { - updateDialog(progress.message); - } - }); - - p.finally(() => { - dispose(disposables); - }); - - return p; - } -} - -registerSingleton(IProgressService2, ProgressService2, true); diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressIndicator.test.ts similarity index 67% rename from src/vs/workbench/services/progress/test/progressService.test.ts rename to src/vs/workbench/services/progress/test/progressIndicator.test.ts index f0dc396eae..14109b1b8f 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressIndicator.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { IAction, IActionViewItem } from 'vs/base/common/actions'; import { IEditorControl } from 'vs/workbench/common/editor'; -import { ScopedProgressService, ScopedService } from 'vs/workbench/services/progress/browser/progressService'; +import { CompositeScope, CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewlet } from 'vs/workbench/common/viewlet'; @@ -16,106 +16,55 @@ class TestViewlet implements IViewlet { constructor(private id: string) { } - getId(): string { - return this.id; - } - - /** - * Returns the name of this composite to show in the title area. - */ - getTitle(): string { - return this.id; - } - - /** - * Returns the primary actions of the composite. - */ - getActions(): IAction[] { - return []; - } - - /** - * Returns the secondary actions of the composite. - */ - getSecondaryActions(): IAction[] { - return []; - } - - /** - * Returns an array of actions to show in the context menu of the composite - */ - public getContextMenuActions(): IAction[] { - return []; - } - - /** - * Returns the action item for a specific action. - */ - getActionViewItem(action: IAction): IActionViewItem { - return null!; - } - - /** - * Returns the underlying control of this composite. - */ - getControl(): IEditorControl { - return null!; - } - - /** - * Asks the underlying control to focus. - */ - focus(): void { - } - - getOptimalWidth(): number { - return 10; - } + getId(): string { return this.id; } + getTitle(): string { return this.id; } + getActions(): IAction[] { return []; } + getSecondaryActions(): IAction[] { return []; } + getContextMenuActions(): IAction[] { return []; } + getActionViewItem(action: IAction): IActionViewItem { return null!; } + getControl(): IEditorControl { return null!; } + focus(): void { } + getOptimalWidth(): number { return 10; } } -class TestScopedService extends ScopedService { - public isActive: boolean; +class TestCompositeScope extends CompositeScope { + isActive: boolean; constructor(viewletService: IViewletService, panelService: IPanelService, scopeId: string) { super(viewletService, panelService, scopeId); } - public onScopeActivated() { - this.isActive = true; - } - public onScopeDeactivated() { - this.isActive = false; - } + onScopeActivated() { this.isActive = true; } + onScopeDeactivated() { this.isActive = false; } } class TestProgressBar { - public fTotal: number; - public fWorked: number; - public fInfinite: boolean; - public fDone: boolean; + fTotal: number; + fWorked: number; + fInfinite: boolean; + fDone: boolean; - constructor() { - } + constructor() { } - public infinite() { + infinite() { this.fDone = null!; this.fInfinite = true; return this; } - public total(total: number) { + total(total: number) { this.fDone = null!; this.fTotal = total; return this; } - public hasTotal() { + hasTotal() { return !!this.fTotal; } - public worked(worked: number) { + worked(worked: number) { this.fDone = null!; if (this.fWorked) { @@ -127,7 +76,7 @@ class TestProgressBar { return this; } - public done() { + done() { this.fDone = true; this.fInfinite = null!; @@ -137,25 +86,21 @@ class TestProgressBar { return this; } - public stop() { + stop() { return this.done(); } - public show(): void { + show(): void { } - } - - public hide(): void { - - } + hide(): void { } } -suite('Progress Service', () => { +suite('Progress Indicator', () => { - test('ScopedService', () => { + test('CompositeScope', () => { let viewletService = new TestViewletService(); let panelService = new TestPanelService(); - let service = new TestScopedService(viewletService, panelService, 'test.scopeId'); + let service = new TestCompositeScope(viewletService, panelService, 'test.scopeId'); const testViewlet = new TestViewlet('test.scopeId'); assert(!service.isActive); @@ -167,11 +112,11 @@ suite('Progress Service', () => { }); - test('WorkbenchProgressService', async () => { + test('CompositeProgressIndicator', async () => { let testProgressBar = new TestProgressBar(); let viewletService = new TestViewletService(); let panelService = new TestPanelService(); - let service = new ScopedProgressService((<any>testProgressBar), 'test.scopeId', true, viewletService, panelService); + let service = new CompositeProgressIndicator((<any>testProgressBar), 'test.scopeId', true, viewletService, panelService); // Active: Show (Infinite) let fn = service.show(true); diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index 39c809d537..892669d578 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -3,25 +3,27 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentConnection } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; import { IProductService } from 'vs/platform/product/common/product'; import { browserWebSocketFactory } from 'vs/platform/remote/browser/browserWebSocketFactory'; +import { ISignService } from 'vs/platform/sign/common/sign'; export class RemoteAgentService extends AbstractRemoteAgentService { private readonly _connection: IRemoteAgentConnection | null = null; constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IProductService productService: IProductService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService signService: ISignService ) { super(environmentService); - const authority = document.location.host; - this._connection = this._register(new RemoteAgentConnection(authority, productService.commit, browserWebSocketFactory, environmentService, remoteAuthorityResolverService)); + + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, browserWebSocketFactory, environmentService, remoteAuthorityResolverService, signService)); } getConnection(): IRemoteAgentConnection | null { diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 79420d8dc8..0db97264ba 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -19,6 +19,7 @@ import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/r import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService'; import { Emitter } from 'vs/base/common/event'; +import { ISignService } from 'vs/platform/sign/common/sign'; export abstract class AbstractRemoteAgentService extends Disposable implements IRemoteAgentService { @@ -84,7 +85,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon private readonly _commit: string | undefined, private readonly _webSocketFactory: IWebSocketFactory, private readonly _environmentService: IEnvironmentService, - private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService + private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + private readonly _signService: ISignService ) { super(); this.remoteAuthority = remoteAuthority; @@ -122,7 +124,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon const { host, port } = await this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority); return { host, port }; } - } + }, + signService: this._signService }; const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`)); this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e))); diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index aaac649ac3..9f300dda8e 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -10,6 +10,7 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import product from 'vs/platform/product/node/product'; import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; +import { ISignService } from 'vs/platform/sign/common/sign'; export class RemoteAgentService extends AbstractRemoteAgentService { @@ -17,11 +18,12 @@ export class RemoteAgentService extends AbstractRemoteAgentService { constructor({ remoteAuthority }: IWindowConfiguration, @IEnvironmentService environmentService: IEnvironmentService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService signService: ISignService ) { super(environmentService); if (remoteAuthority) { - this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeWebSocketFactory, environmentService, remoteAuthorityResolverService)); + this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeWebSocketFactory, environmentService, remoteAuthorityResolverService, signService)); } } diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/workbench/services/remote/node/tunnelService.ts index 0c7042fd25..ed6caeb7e5 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/workbench/services/remote/node/tunnelService.ts @@ -13,6 +13,7 @@ import { connectRemoteAgentTunnel, IConnectionOptions } from 'vs/platform/remote import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; +import { ISignService } from 'vs/platform/sign/common/sign'; export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemotePort: number): Promise<RemoteTunnel> { const tunnel = new NodeRemoteTunnel(options, tunnelRemotePort); @@ -88,6 +89,7 @@ export class TunnelService implements ITunnelService { public constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService private readonly signService: ISignService ) { } @@ -106,7 +108,8 @@ export class TunnelService implements ITunnelService { const { host, port } = await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority); return { host, port }; } - } + }, + signService: this.signService }; return createRemoteTunnel(options, remotePort); } diff --git a/src/vs/workbench/services/request/browser/requestService.ts b/src/vs/workbench/services/request/browser/requestService.ts new file mode 100644 index 0000000000..20b2b7b2b6 --- /dev/null +++ b/src/vs/workbench/services/request/browser/requestService.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRequestOptions, IRequestContext } from 'vs/platform/request/common/request'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ILogService } from 'vs/platform/log/common/log'; +import { RequestChannelClient } from 'vs/platform/request/common/requestIpc'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { RequestService as BrowserRequestService } from 'vs/platform/request/browser/requestService'; + +export class RequestService extends BrowserRequestService { + + private readonly remoteRequestChannel: RequestChannelClient | null; + + constructor( + private readonly requestHandler: ((options: IRequestOptions) => Promise<IRequestContext>) | undefined, + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IConfigurationService configurationService: IConfigurationService, + @ILogService logService: ILogService + ) { + super(configurationService, logService); + const connection = remoteAgentService.getConnection(); + this.remoteRequestChannel = connection ? new RequestChannelClient(connection.getChannel('request')) : null; + } + + async request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> { + if (this.requestHandler) { + return this.requestHandler(options); + } + try { + const context = await super.request(options, token); + if (this.remoteRequestChannel && context.res.statusCode === 405) { + return this.remoteRequestChannel.request(options, token); + } + return context; + } catch (error) { + if (this.remoteRequestChannel) { + const result = await this.remoteRequestChannel.request(options, token); + return result; + } + throw error; + } + } + +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts new file mode 100644 index 0000000000..1279e20e9a --- /dev/null +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -0,0 +1,455 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { keys, ResourceMap, values } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { URI as uri } from 'vs/base/common/uri'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, isFileMatch, isProgressMessage } from 'vs/workbench/services/search/common/search'; +import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class SearchService extends Disposable implements ISearchService { + _serviceBrand: any; + + protected diskSearch: ISearchResultProvider; + private readonly fileSearchProviders = new Map<string, ISearchResultProvider>(); + private readonly textSearchProviders = new Map<string, ISearchResultProvider>(); + + constructor( + private readonly modelService: IModelService, + private readonly untitledEditorService: IUntitledEditorService, + private readonly editorService: IEditorService, + private readonly telemetryService: ITelemetryService, + private readonly logService: ILogService, + private readonly extensionService: IExtensionService, + private readonly fileService: IFileService + ) { + super(); + } + + registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable { + let list: Map<string, ISearchResultProvider>; + if (type === SearchProviderType.file) { + list = this.fileSearchProviders; + } else if (type === SearchProviderType.text) { + list = this.textSearchProviders; + } else { + throw new Error('Unknown SearchProviderType'); + } + + list.set(scheme, provider); + + return toDisposable(() => { + list.delete(scheme); + }); + } + + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { + // Get local results from dirty/untitled + const localResults = this.getLocalResults(query); + + if (onProgress) { + arrays.coalesce(localResults.values()).forEach(onProgress); + } + + const onProviderProgress = (progress: ISearchProgressItem) => { + if (isFileMatch(progress)) { + // Match + if (!localResults.has(progress.resource) && onProgress) { // don't override local results + onProgress(progress); + } + } else if (onProgress) { + // Progress + onProgress(<IProgressMessage>progress); + } + + if (isProgressMessage(progress)) { + this.logService.debug('SearchService#search', progress.message); + } + }; + + return this.doSearch(query, token, onProviderProgress); + } + + fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete> { + return this.doSearch(query, token); + } + + private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { + this.logService.trace('SearchService#search', JSON.stringify(query)); + + const schemesInQuery = this.getSchemesInQuery(query); + + const providerActivations: Promise<any>[] = [Promise.resolve(null)]; + schemesInQuery.forEach(scheme => providerActivations.push(this.extensionService.activateByEvent(`onSearch:${scheme}`))); + providerActivations.push(this.extensionService.activateByEvent('onSearch:file')); + + const providerPromise = Promise.all(providerActivations) + .then(() => this.extensionService.whenInstalledExtensionsRegistered()) + .then(() => { + // Cancel faster if search was canceled while waiting for extensions + if (token && token.isCancellationRequested) { + return Promise.reject(canceled()); + } + + const progressCallback = (item: ISearchProgressItem) => { + if (token && token.isCancellationRequested) { + return; + } + + if (onProgress) { + onProgress(item); + } + }; + + return this.searchWithProviders(query, progressCallback, token); + }) + .then(completes => { + completes = arrays.coalesce(completes); + if (!completes.length) { + return { + limitHit: false, + results: [] + }; + } + + return <ISearchComplete>{ + limitHit: completes[0] && completes[0].limitHit, + stats: completes[0].stats, + results: arrays.flatten(completes.map((c: ISearchComplete) => c.results)) + }; + }); + + return new Promise((resolve, reject) => { + if (token) { + token.onCancellationRequested(() => { + reject(canceled()); + }); + } + + providerPromise.then(resolve, reject); + }); + } + + private getSchemesInQuery(query: ISearchQuery): Set<string> { + const schemes = new Set<string>(); + if (query.folderQueries) { + query.folderQueries.forEach(fq => schemes.add(fq.folder.scheme)); + } + + if (query.extraFileResources) { + query.extraFileResources.forEach(extraFile => schemes.add(extraFile.scheme)); + } + + return schemes; + } + + private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void, token?: CancellationToken) { + const e2eSW = StopWatch.create(false); + + const diskSearchQueries: IFolderQuery[] = []; + const searchPs: Promise<ISearchComplete>[] = []; + + const fqs = this.groupFolderQueriesByScheme(query); + keys(fqs).forEach(scheme => { + const schemeFQs = fqs.get(scheme)!; + const provider = query.type === QueryType.File ? + this.fileSearchProviders.get(scheme) : + this.textSearchProviders.get(scheme); + + if (!provider && scheme === 'file') { + diskSearchQueries.push(...schemeFQs); + } else if (!provider) { + console.warn('No search provider registered for scheme: ' + scheme); + } else { + const oneSchemeQuery: ISearchQuery = { + ...query, + ...{ + folderQueries: schemeFQs + } + }; + + searchPs.push(query.type === QueryType.File ? + provider.fileSearch(<IFileQuery>oneSchemeQuery, token) : + provider.textSearch(<ITextQuery>oneSchemeQuery, onProviderProgress, token)); + } + }); + + const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === Schemas.file); + + if (diskSearchQueries.length || diskSearchExtraFileResources) { + const diskSearchQuery: ISearchQuery = { + ...query, + ...{ + folderQueries: diskSearchQueries + }, + extraFileResources: diskSearchExtraFileResources + }; + + + if (this.diskSearch) { + searchPs.push(diskSearchQuery.type === QueryType.File ? + this.diskSearch.fileSearch(diskSearchQuery, token) : + this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); + } + } + + return Promise.all(searchPs).then(completes => { + const endToEndTime = e2eSW.elapsed(); + this.logService.trace(`SearchService#search: ${endToEndTime}ms`); + completes.forEach(complete => { + this.sendTelemetry(query, endToEndTime, complete); + }); + return completes; + }, err => { + const endToEndTime = e2eSW.elapsed(); + this.logService.trace(`SearchService#search: ${endToEndTime}ms`); + const searchError = deserializeSearchError(err.message); + this.sendTelemetry(query, endToEndTime, undefined, searchError); + + throw searchError; + }); + } + + private groupFolderQueriesByScheme(query: ISearchQuery): Map<string, IFolderQuery[]> { + const queries = new Map<string, IFolderQuery[]>(); + + query.folderQueries.forEach(fq => { + const schemeFQs = queries.get(fq.folder.scheme) || []; + schemeFQs.push(fq); + + queries.set(fq.folder.scheme, schemeFQs); + }); + + return queries; + } + + private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: SearchError): void { + const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); + const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); + const scheme = fileSchemeOnly ? 'file' : + otherSchemeOnly ? 'other' : + 'mixed'; + + if (query.type === QueryType.File && complete && complete.stats) { + const fileSearchStats = complete.stats as IFileSearchStats; + if (fileSearchStats.fromCache) { + const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; + + type CachedSearchCompleteClassifcation = { + reason?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + resultCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + workspaceFolderCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + endToEndTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + sortingTime?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cacheWasResolved: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + cacheLookupTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cacheFilterTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cacheEntryCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + scheme: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + type CachedSearchCompleteEvent = { + reason?: string; + resultCount: number; + workspaceFolderCount: number; + type: 'fileSearchProvider' | 'searchProcess'; + endToEndTime: number; + sortingTime?: number; + cacheWasResolved: boolean; + cacheLookupTime: number; + cacheFilterTime: number; + cacheEntryCount: number; + scheme: string; + }; + this.telemetryService.publicLog2<CachedSearchCompleteEvent, CachedSearchCompleteClassifcation>('cachedSearchComplete', { + reason: query._reason, + resultCount: fileSearchStats.resultCount, + workspaceFolderCount: query.folderQueries.length, + type: fileSearchStats.type, + endToEndTime: endToEndTime, + sortingTime: fileSearchStats.sortingTime, + cacheWasResolved: cacheStats.cacheWasResolved, + cacheLookupTime: cacheStats.cacheLookupTime, + cacheFilterTime: cacheStats.cacheFilterTime, + cacheEntryCount: cacheStats.cacheEntryCount, + scheme + }); + } else { + const searchEngineStats: ISearchEngineStats = fileSearchStats.detailStats as ISearchEngineStats; + + type SearchCompleteClassification = { + reason?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + resultCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + workspaceFolderCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + endToEndTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + sortingTime?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + fileWalkTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + directoriesWalked: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + filesWalked: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cmdTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + cmdResultCount?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + scheme: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + type SearchCompleteEvent = { + reason?: string; + resultCount: number; + workspaceFolderCount: number; + type: 'fileSearchProvider' | 'searchProcess'; + endToEndTime: number; + sortingTime?: number; + fileWalkTime: number + directoriesWalked: number; + filesWalked: number; + cmdTime: number; + cmdResultCount?: number; + scheme: string; + + }; + + this.telemetryService.publicLog2<SearchCompleteEvent, SearchCompleteClassification>('searchComplete', { + reason: query._reason, + resultCount: fileSearchStats.resultCount, + workspaceFolderCount: query.folderQueries.length, + type: fileSearchStats.type, + endToEndTime: endToEndTime, + sortingTime: fileSearchStats.sortingTime, + fileWalkTime: searchEngineStats.fileWalkTime, + directoriesWalked: searchEngineStats.directoriesWalked, + filesWalked: searchEngineStats.filesWalked, + cmdTime: searchEngineStats.cmdTime, + cmdResultCount: searchEngineStats.cmdResultCount, + scheme + }); + } + } else if (query.type === QueryType.Text) { + let errorType: string | undefined; + if (err) { + errorType = err.code === SearchErrorCode.regexParseError ? 'regex' : + err.code === SearchErrorCode.unknownEncoding ? 'encoding' : + err.code === SearchErrorCode.globParseError ? 'glob' : + err.code === SearchErrorCode.invalidLiteral ? 'literal' : + err.code === SearchErrorCode.other ? 'other' : + 'unknown'; + } + + type TextSearchCompleteClassification = { + reason?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + workspaceFolderCount: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + endToEndTime: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true }; + scheme: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + error?: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + usePCRE2: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + type TextSearchCompleteEvent = { + reason?: string; + workspaceFolderCount: number; + endToEndTime: number; + scheme: string; + error?: string; + usePCRE2: boolean; + }; + this.telemetryService.publicLog2<TextSearchCompleteEvent, TextSearchCompleteClassification>('textSearchComplete', { + reason: query._reason, + workspaceFolderCount: query.folderQueries.length, + endToEndTime: endToEndTime, + scheme, + error: errorType, + usePCRE2: !!query.usePCRE2 + }); + } + } + + private getLocalResults(query: ITextQuery): ResourceMap<IFileMatch | null> { + const localResults = new ResourceMap<IFileMatch | null>(); + + if (query.type === QueryType.Text) { + const models = this.modelService.getModels(); + models.forEach((model) => { + const resource = model.uri; + if (!resource) { + return; + } + + if (!this.editorService.isOpen({ resource })) { + return; + } + + // Support untitled files + if (resource.scheme === Schemas.untitled) { + if (!this.untitledEditorService.exists(resource)) { + return; + } + } + + // Block walkthrough, webview, etc. + else if (!this.fileService.canHandleResource(resource)) { + return; + } + + if (!this.matches(resource, query)) { + return; // respect user filters + } + + // Use editor API to find matches + const matches = model.findMatches(query.contentPattern.pattern, false, !!query.contentPattern.isRegExp, !!query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators! : null, false, query.maxResults); + if (matches.length) { + const fileMatch = new FileMatch(resource); + localResults.set(resource, fileMatch); + + const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); + fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); + } else { + localResults.set(resource, null); + } + }); + } + + return localResults; + } + + private matches(resource: uri, query: ITextQuery): boolean { + return pathIncludedInQuery(query, resource.fsPath); + } + + clearCache(cacheKey: string): Promise<void> { + const clearPs = [ + this.diskSearch, + ...values(this.fileSearchProviders) + ].map(provider => provider && provider.clearCache(cacheKey)); + + return Promise.all(clearPs) + .then(() => { }); + } +} + +export class RemoteSearchService extends SearchService { + constructor( + @IModelService modelService: IModelService, + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IEditorService editorService: IEditorService, + @ITelemetryService telemetryService: ITelemetryService, + @ILogService logService: ILogService, + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService + ) { + super(modelService, untitledEditorService, editorService, telemetryService, logService, extensionService, fileService); + } +} + +registerSingleton(ISearchService, RemoteSearchService, true); diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index ebac0b2291..649e6a2d39 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -121,9 +121,10 @@ export class RipgrepTextSearchEngine { * "failed" when a fatal error was produced. */ export function rgErrorMsgForDisplay(msg: string): Maybe<SearchError> { - const firstLine = msg.split('\n')[0].trim(); + const lines = msg.split('\n'); + const firstLine = lines[0].trim(); - if (startsWith(firstLine, 'regex parse error')) { + if (lines.some(l => startsWith(l, 'regex parse error'))) { return new SearchError('Regex parse error', SearchErrorCode.regexParseError); } @@ -209,7 +210,7 @@ export class RipgrepParser extends EventEmitter { newlineIdx = dataStr.indexOf('\n', prevIdx); } - this.remainder = dataStr.substring(prevIdx).trim(); + this.remainder = dataStr.substring(prevIdx); } private handleLine(outputLine: string): void { @@ -409,24 +410,25 @@ function getRgArgs(query: TextSearchQuery, options: TextSearchOptions): string[] if ((<IExtendedExtensionSearchOptions>options).usePCRE2) { args.push('--pcre2'); - - if (query.isRegExp) { - pattern = unicodeEscapesToPCRE2(pattern); - } } + if (query.isRegExp) { + pattern = unicodeEscapesToPCRE2(pattern); + } + + // Allow $ to match /r/n + args.push('--crlf'); + let searchPatternAfterDoubleDashes: Maybe<string>; if (query.isWordMatch) { const regexp = createRegExp(pattern, !!query.isRegExp, { wholeWord: query.isWordMatch }); const regexpStr = regexp.source.replace(/\\\//g, '/'); // RegExp.source arbitrarily returns escaped slashes. Search and destroy. args.push('--regexp', regexpStr); } else if (query.isRegExp) { - let fixedRegexpQuery = fixRegexEndingPattern(query.pattern); - fixedRegexpQuery = fixRegexNewline(fixedRegexpQuery); + let fixedRegexpQuery = fixRegexNewline(query.pattern); fixedRegexpQuery = fixNewline(fixedRegexpQuery); - fixedRegexpQuery = fixRegexCRMatchingNonWordClass(fixedRegexpQuery, !!query.isMultiline); - fixedRegexpQuery = fixRegexCRMatchingWhitespaceClass(fixedRegexpQuery, !!query.isMultiline); args.push('--regexp', fixedRegexpQuery); + args.push('--auto-hybrid-regex'); } else { searchPatternAfterDoubleDashes = pattern; args.push('--fixed-strings'); @@ -508,32 +510,12 @@ export interface IRgSubmatch { export type IRgBytesOrText = { bytes: string } | { text: string }; -export function fixRegexEndingPattern(pattern: string): string { - // Replace an unescaped $ at the end of the pattern with \r?$ - // Match $ preceeded by none or even number of literal \ - return pattern.match(/([^\\]|^)(\\\\)*\$$/) ? - pattern.replace(/\$$/, '\\r?$') : - pattern; -} - export function fixRegexNewline(pattern: string): string { // Replace an unescaped $ at the end of the pattern with \r?$ // Match $ preceeded by none or even number of literal \ return pattern.replace(/([^\\]|^)(\\\\)*\\n/g, '$1$2\\r?\\n'); } -export function fixRegexCRMatchingWhitespaceClass(pattern: string, isMultiline: boolean): string { - return isMultiline ? - pattern.replace(/([^\\]|^)((?:\\\\)*)\\s/g, '$1$2(\\r?\\n|[^\\S\\r])') : - pattern.replace(/([^\\]|^)((?:\\\\)*)\\s/g, '$1$2[ \\t\\f]'); -} - -export function fixRegexCRMatchingNonWordClass(pattern: string, isMultiline: boolean): string { - return isMultiline ? - pattern.replace(/([^\\]|^)((?:\\\\)*)\\W/g, '$1$2(\\r?\\n|[^\\w\\r])') : - pattern.replace(/([^\\]|^)((?:\\\\)*)\\W/g, '$1$2[^\\w\\r]'); -} - export function fixNewline(pattern: string): string { return pattern.replace(/\n/g, '\\r?\\n'); } diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index e39184d324..01eb296d96 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -4,416 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import { getPathFromAmdModule } from 'vs/base/common/amd'; -import * as arrays from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { keys, ResourceMap, values } from 'vs/base/common/map'; -import { Schemas } from 'vs/base/common/network'; -import { StopWatch } from 'vs/base/common/stopwatch'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI as uri } from 'vs/base/common/uri'; -import * as pfs from 'vs/base/node/pfs'; import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, isFileMatch, isProgressMessage } from 'vs/workbench/services/search/common/search'; -import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; -import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { FileMatch, IFileMatch, IFileQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchResultProvider, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, ISearchService } from 'vs/workbench/services/search/common/search'; import { SearchChannelClient } from './searchIpc'; +import { SearchService } from 'vs/workbench/services/search/common/searchService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class SearchService extends Disposable implements ISearchService { - _serviceBrand: any; - - private diskSearch: DiskSearch; - private readonly fileSearchProviders = new Map<string, ISearchResultProvider>(); - private readonly textSearchProviders = new Map<string, ISearchResultProvider>(); - +export class LocalSearchService extends SearchService { constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IModelService private readonly modelService: IModelService, - @IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService, - @IEditorService private readonly editorService: IEditorService, - @IEnvironmentService environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILogService private readonly logService: ILogService, - @IExtensionService private readonly extensionService: IExtensionService, - @IFileService private readonly fileService: IFileService + @IModelService modelService: IModelService, + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IEditorService editorService: IEditorService, + @ITelemetryService telemetryService: ITelemetryService, + @ILogService logService: ILogService, + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService, + @IEnvironmentService readonly environmentService: IEnvironmentService, + @IInstantiationService readonly instantiationService: IInstantiationService ) { - super(); - this.diskSearch = this.instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, environmentService.debugSearch); - } + super(modelService, untitledEditorService, editorService, telemetryService, logService, extensionService, fileService); - registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable { - let list: Map<string, ISearchResultProvider>; - if (type === SearchProviderType.file) { - list = this.fileSearchProviders; - } else if (type === SearchProviderType.text) { - list = this.textSearchProviders; - } else { - throw new Error('Unknown SearchProviderType'); - } - list.set(scheme, provider); - - return toDisposable(() => { - list.delete(scheme); - }); - } - - textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { - // Get local results from dirty/untitled - const localResults = this.getLocalResults(query); - - if (onProgress) { - arrays.coalesce(localResults.values()).forEach(onProgress); - } - - const onProviderProgress = (progress: ISearchProgressItem) => { - if (isFileMatch(progress)) { - // Match - if (!localResults.has(progress.resource) && onProgress) { // don't override local results - onProgress(progress); - } - } else if (onProgress) { - // Progress - onProgress(<IProgressMessage>progress); - } - - if (isProgressMessage(progress)) { - this.logService.debug('SearchService#search', progress.message); - } - }; - - return this.doSearch(query, token, onProviderProgress); - } - - fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete> { - return this.doSearch(query, token); - } - - private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { - this.logService.trace('SearchService#search', JSON.stringify(query)); - - const schemesInQuery = this.getSchemesInQuery(query); - - const providerActivations: Promise<any>[] = [Promise.resolve(null)]; - schemesInQuery.forEach(scheme => providerActivations.push(this.extensionService.activateByEvent(`onSearch:${scheme}`))); - providerActivations.push(this.extensionService.activateByEvent('onSearch:file')); - - const providerPromise = Promise.all(providerActivations) - .then(() => this.extensionService.whenInstalledExtensionsRegistered()) - .then(() => { - // Cancel faster if search was canceled while waiting for extensions - if (token && token.isCancellationRequested) { - return Promise.reject(canceled()); - } - - const progressCallback = (item: ISearchProgressItem) => { - if (token && token.isCancellationRequested) { - return; - } - - if (onProgress) { - onProgress(item); - } - }; - - return this.searchWithProviders(query, progressCallback, token); - }) - .then(completes => { - completes = arrays.coalesce(completes); - if (!completes.length) { - return { - limitHit: false, - results: [] - }; - } - - return <ISearchComplete>{ - limitHit: completes[0] && completes[0].limitHit, - stats: completes[0].stats, - results: arrays.flatten(completes.map((c: ISearchComplete) => c.results)) - }; - }); - - return new Promise((resolve, reject) => { - if (token) { - token.onCancellationRequested(() => { - reject(canceled()); - }); - } - - providerPromise.then(resolve, reject); - }); - } - - private getSchemesInQuery(query: ISearchQuery): Set<string> { - const schemes = new Set<string>(); - if (query.folderQueries) { - query.folderQueries.forEach(fq => schemes.add(fq.folder.scheme)); - } - - if (query.extraFileResources) { - query.extraFileResources.forEach(extraFile => schemes.add(extraFile.scheme)); - } - - return schemes; - } - - private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void, token?: CancellationToken) { - const e2eSW = StopWatch.create(false); - - const diskSearchQueries: IFolderQuery[] = []; - const searchPs: Promise<ISearchComplete>[] = []; - - const fqs = this.groupFolderQueriesByScheme(query); - keys(fqs).forEach(scheme => { - const schemeFQs = fqs.get(scheme)!; - const provider = query.type === QueryType.File ? - this.fileSearchProviders.get(scheme) : - this.textSearchProviders.get(scheme); - - if (!provider && scheme === 'file') { - diskSearchQueries.push(...schemeFQs); - } else if (!provider) { - console.warn('No search provider registered for scheme: ' + scheme); - } else { - const oneSchemeQuery: ISearchQuery = { - ...query, - ...{ - folderQueries: schemeFQs - } - }; - - searchPs.push(query.type === QueryType.File ? - provider.fileSearch(<IFileQuery>oneSchemeQuery, token) : - provider.textSearch(<ITextQuery>oneSchemeQuery, onProviderProgress, token)); - } - }); - - const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === Schemas.file); - - if (diskSearchQueries.length || diskSearchExtraFileResources) { - const diskSearchQuery: ISearchQuery = { - ...query, - ...{ - folderQueries: diskSearchQueries - }, - extraFileResources: diskSearchExtraFileResources - }; - - searchPs.push(diskSearchQuery.type === QueryType.File ? - this.diskSearch.fileSearch(diskSearchQuery, token) : - this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); - } - - return Promise.all(searchPs).then(completes => { - const endToEndTime = e2eSW.elapsed(); - this.logService.trace(`SearchService#search: ${endToEndTime}ms`); - completes.forEach(complete => { - this.sendTelemetry(query, endToEndTime, complete); - }); - return completes; - }, err => { - const endToEndTime = e2eSW.elapsed(); - this.logService.trace(`SearchService#search: ${endToEndTime}ms`); - const searchError = deserializeSearchError(err.message); - this.sendTelemetry(query, endToEndTime, undefined, searchError); - - throw searchError; - }); - } - - private groupFolderQueriesByScheme(query: ISearchQuery): Map<string, IFolderQuery[]> { - const queries = new Map<string, IFolderQuery[]>(); - - query.folderQueries.forEach(fq => { - const schemeFQs = queries.get(fq.folder.scheme) || []; - schemeFQs.push(fq); - - queries.set(fq.folder.scheme, schemeFQs); - }); - - return queries; - } - - private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: SearchError): void { - const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); - const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); - const scheme = fileSchemeOnly ? 'file' : - otherSchemeOnly ? 'other' : - 'mixed'; - - if (query.type === QueryType.File && complete && complete.stats) { - const fileSearchStats = complete.stats as IFileSearchStats; - if (fileSearchStats.fromCache) { - const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; - - /* __GDPR__ - "cachedSearchComplete" : { - "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheWasResolved" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "cacheLookupTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheFilterTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheEntryCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('cachedSearchComplete', { - reason: query._reason, - resultCount: fileSearchStats.resultCount, - workspaceFolderCount: query.folderQueries.length, - type: fileSearchStats.type, - endToEndTime: endToEndTime, - sortingTime: fileSearchStats.sortingTime, - cacheWasResolved: cacheStats.cacheWasResolved, - cacheLookupTime: cacheStats.cacheLookupTime, - cacheFilterTime: cacheStats.cacheFilterTime, - cacheEntryCount: cacheStats.cacheEntryCount, - scheme - }); - } else { - const searchEngineStats: ISearchEngineStats = fileSearchStats.detailStats as ISearchEngineStats; - - /* __GDPR__ - "searchComplete" : { - "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "fileWalkTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "directoriesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('searchComplete', { - reason: query._reason, - resultCount: fileSearchStats.resultCount, - workspaceFolderCount: query.folderQueries.length, - type: fileSearchStats.type, - endToEndTime: endToEndTime, - sortingTime: fileSearchStats.sortingTime, - fileWalkTime: searchEngineStats.fileWalkTime, - directoriesWalked: searchEngineStats.directoriesWalked, - filesWalked: searchEngineStats.filesWalked, - cmdTime: searchEngineStats.cmdTime, - cmdResultCount: searchEngineStats.cmdResultCount, - scheme - }); - } - } else if (query.type === QueryType.Text) { - let errorType: string | undefined; - if (err) { - errorType = err.code === SearchErrorCode.regexParseError ? 'regex' : - err.code === SearchErrorCode.unknownEncoding ? 'encoding' : - err.code === SearchErrorCode.globParseError ? 'glob' : - err.code === SearchErrorCode.invalidLiteral ? 'literal' : - err.code === SearchErrorCode.other ? 'other' : - 'unknown'; - } - - /* __GDPR__ - "textSearchComplete" : { - "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "error" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "usePCRE2" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('textSearchComplete', { - reason: query._reason, - workspaceFolderCount: query.folderQueries.length, - endToEndTime: endToEndTime, - scheme, - error: errorType, - usePCRE2: !!query.usePCRE2 - }); - } - } - - private getLocalResults(query: ITextQuery): ResourceMap<IFileMatch | null> { - const localResults = new ResourceMap<IFileMatch | null>(); - - if (query.type === QueryType.Text) { - const models = this.modelService.getModels(); - models.forEach((model) => { - const resource = model.uri; - if (!resource) { - return; - } - - if (!this.editorService.isOpen({ resource })) { - return; - } - - // Support untitled files - if (resource.scheme === Schemas.untitled) { - if (!this.untitledEditorService.exists(resource)) { - return; - } - } - - // Block walkthrough, webview, etc. - else if (!this.fileService.canHandleResource(resource)) { - return; - } - - if (!this.matches(resource, query)) { - return; // respect user filters - } - - // Use editor API to find matches - const matches = model.findMatches(query.contentPattern.pattern, false, !!query.contentPattern.isRegExp, !!query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators! : null, false, query.maxResults); - if (matches.length) { - const fileMatch = new FileMatch(resource); - localResults.set(resource, fileMatch); - - const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); - fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); - } else { - localResults.set(resource, null); - } - }); - } - - return localResults; - } - - private matches(resource: uri, query: ITextQuery): boolean { - return pathIncludedInQuery(query, resource.fsPath); - } - - clearCache(cacheKey: string): Promise<void> { - const clearPs = [ - this.diskSearch, - ...values(this.fileSearchProviders) - ].map(provider => provider && provider.clearCache(cacheKey)); - - return Promise.all(clearPs) - .then(() => { }); + this.diskSearch = instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, environmentService.debugSearch); } } @@ -425,6 +53,7 @@ export class DiskSearch implements ISearchResultProvider { searchDebug: IDebugParams | undefined, @ILogService private readonly logService: ILogService, @IConfigurationService private readonly configService: IConfigurationService, + @IFileService private readonly fileService: IFileService ) { const timeout = this.configService.getValue<ISearchConfiguration>().search.maintainFileSearchCache ? Number.MAX_VALUE : @@ -465,7 +94,7 @@ export class DiskSearch implements ISearchResultProvider { textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise<ISearchComplete> { const folderQueries = query.folderQueries || []; - return Promise.all(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.all(folderQueries.map(q => this.fileService.exists(q.folder))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); @@ -480,7 +109,7 @@ export class DiskSearch implements ISearchResultProvider { fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete> { const folderQueries = query.folderQueries || []; - return Promise.all(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.all(folderQueries.map(q => this.fileService.exists(q.folder))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); @@ -575,4 +204,4 @@ export class DiskSearch implements ISearchResultProvider { } } -registerSingleton(ISearchService, SearchService, true); +registerSingleton(ISearchService, LocalSearchService, true); diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts index 68e966f693..96713436e5 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { fixRegexCRMatchingNonWordClass, fixRegexCRMatchingWhitespaceClass, fixRegexEndingPattern, fixRegexNewline, IRgMatch, IRgMessage, RipgrepParser, unicodeEscapesToPCRE2, fixNewline } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; +import { fixRegexNewline, IRgMatch, IRgMessage, RipgrepParser, unicodeEscapesToPCRE2, fixNewline } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; import { Range, TextSearchResult } from 'vs/workbench/services/search/common/searchExtTypes'; suite('RipgrepTextSearchEngine', () => { @@ -24,70 +24,6 @@ suite('RipgrepTextSearchEngine', () => { assert.equal(unicodeEscapesToPCRE2(''), ''); }); - test('fixRegexEndingPattern', () => { - function testFixRegexEndingPattern([input, expectedResult]: string[]): void { - assert.equal(fixRegexEndingPattern(input), expectedResult); - } - - [ - ['foo', 'foo'], - ['', ''], - ['^foo.*bar\\s+', '^foo.*bar\\s+'], - ['foo$', 'foo\\r?$'], - ['$', '\\r?$'], - ['foo\\$', 'foo\\$'], - ['foo\\\\$', 'foo\\\\\\r?$'], - ].forEach(testFixRegexEndingPattern); - }); - - test('fixRegexCRMatchingWhitespaceClass', () => { - function testFixRegexCRMatchingWhitespaceClass([inputReg, isMultiline, testStr, shouldMatch]: [string, boolean, string, boolean]): void { - const fixed = fixRegexCRMatchingWhitespaceClass(inputReg, isMultiline); - const reg = new RegExp(fixed); - assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); - } - - [ - ['foo', false, 'foo', true], - - ['foo\\s', false, 'foo\r\n', false], - ['foo\\sabc', true, 'foo\r\nabc', true], - - ['foo\\s', false, 'foo\n', false], - ['foo\\s', true, 'foo\n', true], - - ['foo\\s\\n', true, 'foo\r\n', false], - ['foo\\r\\s', true, 'foo\r\n', true], - - ['foo\\s+abc', true, 'foo \r\nabc', true], - ['foo\\s+abc', false, 'foo \t abc', true], - ].forEach(testFixRegexCRMatchingWhitespaceClass); - }); - - test('fixRegexCRMatchingNonWordClass', () => { - function testRegexCRMatchingNonWordClass([inputReg, isMultiline, testStr, shouldMatch]: [string, boolean, string, boolean]): void { - const fixed = fixRegexCRMatchingNonWordClass(inputReg, isMultiline); - const reg = new RegExp(fixed); - assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); - } - - [ - ['foo', false, 'foo', true], - - ['foo\\W', false, 'foo\r\n', false], - ['foo\\Wabc', true, 'foo\r\nabc', true], - - ['foo\\W', false, 'foo\n', true], - ['foo\\W', true, 'foo\n', true], - - ['foo\\W\\n', true, 'foo\r\n', false], - ['foo\\r\\W', true, 'foo\r\n', true], - - ['foo\\W+abc', true, 'foo \r\nabc', true], - ['foo\\W+abc', false, 'foo .-\t abc', true], - ].forEach(testRegexCRMatchingNonWordClass); - }); - test('fixRegexNewline', () => { function testFixRegexNewline([inputReg, testStr, shouldMatch]: [string, string, boolean]): void { const fixed = fixRegexNewline(inputReg); @@ -223,15 +159,16 @@ suite('RipgrepTextSearchEngine', () => { test('chopped-up input chunks', () => { const dataStrs = [ - makeRgMatch('file1.js', 'foobar', 4, [{ start: 3, end: 6 }]), + makeRgMatch('file1.js', 'foo bar', 4, [{ start: 3, end: 7 }]), makeRgMatch('app/file2.js', 'foobar', 4, [{ start: 3, end: 6 }]), makeRgMatch('app2/file3.js', 'foobar', 4, [{ start: 3, end: 6 }]), ]; + const dataStr0Space = dataStrs[0].indexOf(' '); testParser( [ - dataStrs[0].substring(0, 5), - dataStrs[0].substring(5), + dataStrs[0].substring(0, dataStr0Space + 1), + dataStrs[0].substring(dataStr0Space + 1), '\n', dataStrs[1].trim(), '\n' + dataStrs[2].substring(0, 25), @@ -240,11 +177,11 @@ suite('RipgrepTextSearchEngine', () => { [ { preview: { - text: 'foobar', - matches: [new Range(0, 3, 0, 6)] + text: 'foo bar', + matches: [new Range(0, 3, 0, 7)] }, uri: joinPath(TEST_FOLDER, 'file1.js'), - ranges: [new Range(3, 3, 3, 6)] + ranges: [new Range(3, 3, 3, 7)] }, { preview: { diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index fdef99b135..e30e448957 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -378,7 +378,7 @@ suite('Search-integration', function () { folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'foo' }, includePattern: { - '***': true + '{{}': true } }; @@ -386,26 +386,10 @@ suite('Search-integration', function () { throw new Error('expected fail'); }, err => { const searchError = deserializeSearchError(err.message); - assert.equal(searchError.message, 'Error parsing glob \'***\': invalid use of **; must be one path component'); + assert.equal(searchError.message, 'Error parsing glob \'/{{}\': nested alternate groups are not allowed'); assert.equal(searchError.code, SearchErrorCode.globParseError); }); }); - - test('invalid literal', () => { - const config: ITextQuery = { - type: QueryType.Text, - folderQueries: ROOT_FOLDER_QUERY, - contentPattern: { pattern: 'foo\nbar', isRegExp: true } - }; - - return doSearchTest(config, 0).then(() => { - throw new Error('expected fail'); - }, err => { - const searchError = deserializeSearchError(err.message); - assert.equal(searchError.message, 'The literal \'"\\n"\' is not allowed in a regex'); - assert.equal(searchError.code, SearchErrorCode.invalidLiteral); - }); - }); }); }); diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index 6e25f6ffd3..419c2f0423 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -16,6 +16,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; import { TelemetryService as BaseTelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; export class TelemetryService extends Disposable implements ITelemetryService { @@ -37,7 +38,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), piiPaths: [environmentService.appRoot, environmentService.extensionsPath] }; @@ -59,6 +60,10 @@ export class TelemetryService extends Disposable implements ITelemetryService { return this.impl.publicLog(eventName, data, anonymizeFilePaths); } + publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>, anonymizeFilePaths?: boolean) { + return this.publicLog(eventName, data as ITelemetryData, anonymizeFilePaths); + } + getTelemetryInfo(): Promise<ITelemetryInfo> { return this.impl.getTelemetryInfo(); } diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts new file mode 100644 index 0000000000..b926836a32 --- /dev/null +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -0,0 +1,433 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import * as dom from 'vs/base/browser/dom'; +import { Color } from 'vs/base/common/color'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import * as resources from 'vs/base/common/resources'; +import * as types from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationRegistry, StandardTokenType, LanguageIdentifier } from 'vs/editor/common/modes'; +import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; +import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ITMSyntaxExtensionPoint, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars'; +import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; +import { ITokenColorizationRule, IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IGrammar, StackElement, IOnigLib, IRawTheme } from 'vscode-textmate'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IValidGrammarDefinition, IValidEmbeddedLanguagesMap, IValidTokenTypeMap } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; +import { TMGrammarFactory } from 'vs/workbench/services/textMate/common/TMGrammarFactory'; + +export abstract class AbstractTextMateService extends Disposable implements ITextMateService { + public _serviceBrand: any; + + private readonly _onDidEncounterLanguage: Emitter<LanguageId> = this._register(new Emitter<LanguageId>()); + public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; + + private readonly _styleElement: HTMLStyleElement; + private readonly _createdModes: string[]; + private readonly _encounteredLanguages: boolean[]; + + private _grammarDefinitions: IValidGrammarDefinition[] | null; + private _grammarFactory: TMGrammarFactory | null; + private _tokenizersRegistrations: IDisposable[]; + private _currentTokenColors: ITokenColorizationRule[] | null; + + constructor( + @IModeService private readonly _modeService: IModeService, + @IWorkbenchThemeService private readonly _themeService: IWorkbenchThemeService, + @IFileService protected readonly _fileService: IFileService, + @INotificationService private readonly _notificationService: INotificationService, + @ILogService private readonly _logService: ILogService, + @IConfigurationService private readonly _configurationService: IConfigurationService + ) { + super(); + this._styleElement = dom.createStyleSheet(); + this._styleElement.className = 'vscode-tokens-styles'; + this._createdModes = []; + this._encounteredLanguages = []; + + this._grammarDefinitions = null; + this._grammarFactory = null; + this._tokenizersRegistrations = []; + + grammarsExtPoint.setHandler((extensions) => { + this._grammarDefinitions = null; + if (this._grammarFactory) { + this._grammarFactory.dispose(); + this._grammarFactory = null; + this._onDidDisposeGrammarFactory(); + } + this._tokenizersRegistrations = dispose(this._tokenizersRegistrations); + + this._grammarDefinitions = []; + for (const extension of extensions) { + const grammars = extension.value; + for (const grammar of grammars) { + if (!this._validateGrammarExtensionPoint(extension.description.extensionLocation, grammar, extension.collector)) { + continue; + } + const grammarLocation = resources.joinPath(extension.description.extensionLocation, grammar.path); + + const embeddedLanguages: IValidEmbeddedLanguagesMap = Object.create(null); + if (grammar.embeddedLanguages) { + let scopes = Object.keys(grammar.embeddedLanguages); + for (let i = 0, len = scopes.length; i < len; i++) { + let scope = scopes[i]; + let language = grammar.embeddedLanguages[scope]; + if (typeof language !== 'string') { + // never hurts to be too careful + continue; + } + let languageIdentifier = this._modeService.getLanguageIdentifier(language); + if (languageIdentifier) { + embeddedLanguages[scope] = languageIdentifier.id; + } + } + } + + const tokenTypes: IValidTokenTypeMap = Object.create(null); + if (grammar.tokenTypes) { + const scopes = Object.keys(grammar.tokenTypes); + for (const scope of scopes) { + const tokenType = grammar.tokenTypes[scope]; + switch (tokenType) { + case 'string': + tokenTypes[scope] = StandardTokenType.String; + break; + case 'other': + tokenTypes[scope] = StandardTokenType.Other; + break; + case 'comment': + tokenTypes[scope] = StandardTokenType.Comment; + break; + } + } + } + + let languageIdentifier: LanguageIdentifier | null = null; + if (grammar.language) { + languageIdentifier = this._modeService.getLanguageIdentifier(grammar.language); + } + + this._grammarDefinitions.push({ + location: grammarLocation, + language: languageIdentifier ? languageIdentifier.id : undefined, + scopeName: grammar.scopeName, + embeddedLanguages: embeddedLanguages, + tokenTypes: tokenTypes, + injectTo: grammar.injectTo, + }); + } + } + + for (const createMode of this._createdModes) { + this._registerDefinitionIfAvailable(createMode); + } + }); + + this._register(this._themeService.onDidColorThemeChange(() => { + if (this._grammarFactory) { + this._updateTheme(this._grammarFactory, this._themeService.getColorTheme(), false); + } + })); + + // Generate some color map until the grammar registry is loaded + let colorTheme = this._themeService.getColorTheme(); + let defaultForeground: Color = Color.transparent; + let defaultBackground: Color = Color.transparent; + for (let i = 0, len = colorTheme.tokenColors.length; i < len; i++) { + let rule = colorTheme.tokenColors[i]; + if (!rule.scope && rule.settings) { + if (rule.settings.foreground) { + defaultForeground = Color.fromHex(rule.settings.foreground); + } + if (rule.settings.background) { + defaultBackground = Color.fromHex(rule.settings.background); + } + } + } + TokenizationRegistry.setColorMap([null!, defaultForeground, defaultBackground]); + + this._modeService.onDidCreateMode((mode) => { + let modeId = mode.getId(); + this._createdModes.push(modeId); + this._registerDefinitionIfAvailable(modeId); + }); + } + + private _canCreateGrammarFactory(): boolean { + // Check if extension point is ready + return (this._grammarDefinitions ? true : false); + } + + private async _getOrCreateGrammarFactory(): Promise<TMGrammarFactory> { + if (this._grammarFactory) { + return this._grammarFactory; + } + + const vscodeTextmate = await this._loadVSCodeTextmate(); + + // Avoid duplicate instantiations + if (this._grammarFactory) { + return this._grammarFactory; + } + + this._grammarFactory = new TMGrammarFactory({ + logTrace: (msg: string) => this._logService.trace(msg), + logError: (msg: string, err: any) => this._logService.error(msg, err), + readFile: async (resource: URI) => { + const content = await this._fileService.readFile(resource); + return content.value.toString(); + } + }, this._grammarDefinitions || [], vscodeTextmate, this._loadOnigLib()); + this._onDidCreateGrammarFactory(this._grammarDefinitions || []); + + this._updateTheme(this._grammarFactory, this._themeService.getColorTheme(), true); + + return this._grammarFactory; + } + + private async _registerDefinitionIfAvailable(modeId: string): Promise<void> { + const languageIdentifier = this._modeService.getLanguageIdentifier(modeId); + if (!languageIdentifier) { + return; + } + const languageId = languageIdentifier.id; + try { + if (!this._canCreateGrammarFactory()) { + return; + } + const grammarFactory = await this._getOrCreateGrammarFactory(); + if (grammarFactory.has(languageId)) { + const promise = grammarFactory.createGrammar(languageId).then((r) => { + const tokenization = new TMTokenization(r.grammar, r.initialState, r.containsEmbeddedLanguages); + tokenization.onDidEncounterLanguage((languageId) => { + if (!this._encounteredLanguages[languageId]) { + this._encounteredLanguages[languageId] = true; + this._onDidEncounterLanguage.fire(languageId); + } + }); + return new TMTokenizationSupport(r.languageId, tokenization, this._notificationService, this._configurationService); + }, e => { + onUnexpectedError(e); + return null; + }); + this._tokenizersRegistrations.push(TokenizationRegistry.registerPromise(modeId, promise)); + } + } catch (err) { + onUnexpectedError(err); + } + } + + private static _toColorMap(colorMap: string[]): Color[] { + let result: Color[] = [null!]; + for (let i = 1, len = colorMap.length; i < len; i++) { + result[i] = Color.fromHex(colorMap[i]); + } + return result; + } + + private _updateTheme(grammarFactory: TMGrammarFactory, colorTheme: IColorTheme, forceUpdate: boolean): void { + if (!forceUpdate && AbstractTextMateService.equalsTokenRules(this._currentTokenColors, colorTheme.tokenColors)) { + return; + } + this._currentTokenColors = colorTheme.tokenColors; + this._doUpdateTheme(grammarFactory, { name: colorTheme.label, settings: colorTheme.tokenColors }); + } + + protected _doUpdateTheme(grammarFactory: TMGrammarFactory, theme: IRawTheme): void { + grammarFactory.setTheme(theme); + let colorMap = AbstractTextMateService._toColorMap(grammarFactory.getColorMap()); + let cssRules = generateTokensCSSForColorMap(colorMap); + this._styleElement.innerHTML = cssRules; + TokenizationRegistry.setColorMap(colorMap); + } + + private static equalsTokenRules(a: ITokenColorizationRule[] | null, b: ITokenColorizationRule[] | null): boolean { + if (!b || !a || b.length !== a.length) { + return false; + } + for (let i = b.length - 1; i >= 0; i--) { + let r1 = b[i]; + let r2 = a[i]; + if (r1.scope !== r2.scope) { + return false; + } + let s1 = r1.settings; + let s2 = r2.settings; + if (s1 && s2) { + if (s1.fontStyle !== s2.fontStyle || s1.foreground !== s2.foreground || s1.background !== s2.background) { + return false; + } + } else if (!s1 || !s2) { + return false; + } + } + return true; + } + + private _validateGrammarExtensionPoint(extensionLocation: URI, syntax: ITMSyntaxExtensionPoint, collector: ExtensionMessageCollector): boolean { + if (syntax.language && ((typeof syntax.language !== 'string') || !this._modeService.isRegisteredMode(syntax.language))) { + collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", grammarsExtPoint.name, String(syntax.language))); + return false; + } + if (!syntax.scopeName || (typeof syntax.scopeName !== 'string')) { + collector.error(nls.localize('invalid.scopeName', "Expected string in `contributes.{0}.scopeName`. Provided value: {1}", grammarsExtPoint.name, String(syntax.scopeName))); + return false; + } + if (!syntax.path || (typeof syntax.path !== 'string')) { + collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", grammarsExtPoint.name, String(syntax.path))); + return false; + } + if (syntax.injectTo && (!Array.isArray(syntax.injectTo) || syntax.injectTo.some(scope => typeof scope !== 'string'))) { + collector.error(nls.localize('invalid.injectTo', "Invalid value in `contributes.{0}.injectTo`. Must be an array of language scope names. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.injectTo))); + return false; + } + if (syntax.embeddedLanguages && !types.isObject(syntax.embeddedLanguages)) { + collector.error(nls.localize('invalid.embeddedLanguages', "Invalid value in `contributes.{0}.embeddedLanguages`. Must be an object map from scope name to language. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.embeddedLanguages))); + return false; + } + + if (syntax.tokenTypes && !types.isObject(syntax.tokenTypes)) { + collector.error(nls.localize('invalid.tokenTypes', "Invalid value in `contributes.{0}.tokenTypes`. Must be an object map from scope name to token type. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.tokenTypes))); + return false; + } + + const grammarLocation = resources.joinPath(extensionLocation, syntax.path); + if (!resources.isEqualOrParent(grammarLocation, extensionLocation)) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", grammarsExtPoint.name, grammarLocation.path, extensionLocation.path)); + } + return true; + } + + public async createGrammar(modeId: string): Promise<IGrammar> { + const grammarFactory = await this._getOrCreateGrammarFactory(); + const { grammar } = await grammarFactory.createGrammar(this._modeService.getLanguageIdentifier(modeId)!.id); + return grammar; + } + + protected _onDidCreateGrammarFactory(grammarDefinitions: IValidGrammarDefinition[]): void { + } + + protected _onDidDisposeGrammarFactory(): void { + } + + protected abstract _loadVSCodeTextmate(): Promise<typeof import('vscode-textmate')>; + protected abstract _loadOnigLib(): Promise<IOnigLib> | undefined; +} + +class TMTokenizationSupport implements ITokenizationSupport { + private readonly _languageId: LanguageId; + private readonly _actual: TMTokenization; + private _tokenizationWarningAlreadyShown: boolean; + private _maxTokenizationLineLength: number; + + constructor( + languageId: LanguageId, + actual: TMTokenization, + @INotificationService private readonly _notificationService: INotificationService, + @IConfigurationService private readonly _configurationService: IConfigurationService + ) { + this._languageId = languageId; + this._actual = actual; + this._tokenizationWarningAlreadyShown = false; + this._maxTokenizationLineLength = this._configurationService.getValue<number>('editor.maxTokenizationLineLength'); + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.maxTokenizationLineLength')) { + this._maxTokenizationLineLength = this._configurationService.getValue<number>('editor.maxTokenizationLineLength'); + } + }); + } + + getInitialState(): IState { + return this._actual.getInitialState(); + } + + tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult { + throw new Error('Not supported!'); + } + + tokenize2(line: string, state: StackElement, offsetDelta: number): TokenizationResult2 { + if (offsetDelta !== 0) { + throw new Error('Unexpected: offsetDelta should be 0.'); + } + + // Do not attempt to tokenize if a line is too long + if (line.length >= this._maxTokenizationLineLength) { + if (!this._tokenizationWarningAlreadyShown) { + this._tokenizationWarningAlreadyShown = true; + this._notificationService.warn(nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. The length of a long line can be configured via `editor.maxTokenizationLineLength`.")); + } + console.log(`Line (${line.substr(0, 15)}...): longer than ${this._maxTokenizationLineLength} characters, tokenization skipped.`); + return nullTokenize2(this._languageId, line, state, offsetDelta); + } + + return this._actual.tokenize2(line, state); + } +} + +class TMTokenization extends Disposable { + + private readonly _grammar: IGrammar; + private readonly _containsEmbeddedLanguages: boolean; + private readonly _seenLanguages: boolean[]; + private readonly _initialState: StackElement; + + private readonly _onDidEncounterLanguage: Emitter<LanguageId> = this._register(new Emitter<LanguageId>()); + public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; + + constructor(grammar: IGrammar, initialState: StackElement, containsEmbeddedLanguages: boolean) { + super(); + this._grammar = grammar; + this._initialState = initialState; + this._containsEmbeddedLanguages = containsEmbeddedLanguages; + this._seenLanguages = []; + } + + public getInitialState(): IState { + return this._initialState; + } + + public tokenize2(line: string, state: StackElement): TokenizationResult2 { + let textMateResult = this._grammar.tokenizeLine2(line, state); + + if (this._containsEmbeddedLanguages) { + let seenLanguages = this._seenLanguages; + let tokens = textMateResult.tokens; + + // Must check if any of the embedded languages was hit + for (let i = 0, len = (tokens.length >>> 1); i < len; i++) { + let metadata = tokens[(i << 1) + 1]; + let languageId = TokenMetadata.getLanguageId(metadata); + + if (!seenLanguages[languageId]) { + seenLanguages[languageId] = true; + this._onDidEncounterLanguage.fire(languageId); + } + } + } + + let endState: StackElement; + // try to save an object if possible + if (state.equals(textMateResult.ruleStack)) { + endState = state; + } else { + endState = textMateResult.ruleStack; + + } + + return new TokenizationResult2(textMateResult.tokens, endState); + } +} diff --git a/src/vs/workbench/services/textMate/browser/textMateService.ts b/src/vs/workbench/services/textMate/browser/textMateService.ts new file mode 100644 index 0000000000..b9c475e0eb --- /dev/null +++ b/src/vs/workbench/services/textMate/browser/textMateService.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { AbstractTextMateService } from 'vs/workbench/services/textMate/browser/abstractTextMateService'; +import * as vscodeTextmate from 'vscode-textmate'; +import * as onigasm from 'onigasm-umd'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export class TextMateService extends AbstractTextMateService { + + constructor( + @IModeService modeService: IModeService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IFileService fileService: IFileService, + @INotificationService notificationService: INotificationService, + @ILogService logService: ILogService, + @IConfigurationService configurationService: IConfigurationService + ) { + super(modeService, themeService, fileService, notificationService, logService, configurationService); + } + + protected _loadVSCodeTextmate(): Promise<typeof import('vscode-textmate')> { + return import('vscode-textmate'); + } + + protected _loadOnigLib(): Promise<vscodeTextmate.IOnigLib> | undefined { + return loadOnigasm(); + } +} + +let onigasmPromise: Promise<vscodeTextmate.IOnigLib> | null = null; +async function loadOnigasm(): Promise<vscodeTextmate.IOnigLib> { + if (!onigasmPromise) { + onigasmPromise = doLoadOnigasm(); + } + return onigasmPromise; +} + +async function doLoadOnigasm(): Promise<vscodeTextmate.IOnigLib> { + const wasmBytes = await loadOnigasmWASM(); + await onigasm.loadWASM(wasmBytes); + return { + createOnigScanner(patterns: string[]) { return new onigasm.OnigScanner(patterns); }, + createOnigString(s: string) { return new onigasm.OnigString(s); } + }; +} + +async function loadOnigasmWASM(): Promise<ArrayBuffer> { + const wasmPath = require.toUrl('onigasm-umd/../onigasm.wasm'); + const response = await fetch(wasmPath); + const bytes = await response.arrayBuffer(); + return bytes; +} + +registerSingleton(ITextMateService, TextMateService); diff --git a/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts new file mode 100644 index 0000000000..a51b45def6 --- /dev/null +++ b/src/vs/workbench/services/textMate/common/TMGrammarFactory.ts @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { URI } from 'vs/base/common/uri'; +import { LanguageId } from 'vs/editor/common/modes'; +import { IGrammar, Registry, StackElement, IRawTheme, IOnigLib } from 'vscode-textmate'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { TMScopeRegistry, IValidGrammarDefinition, IValidEmbeddedLanguagesMap } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; + +interface ITMGrammarFactoryHost { + logTrace(msg: string): void; + logError(msg: string, err: any): void; + readFile(resource: URI): Promise<string>; +} + +export interface ICreateGrammarResult { + languageId: LanguageId; + grammar: IGrammar; + initialState: StackElement; + containsEmbeddedLanguages: boolean; +} + +export class TMGrammarFactory extends Disposable { + + private readonly _host: ITMGrammarFactoryHost; + private readonly _initialState: StackElement; + private readonly _scopeRegistry: TMScopeRegistry; + private readonly _injections: { [scopeName: string]: string[]; }; + private readonly _injectedEmbeddedLanguages: { [scopeName: string]: IValidEmbeddedLanguagesMap[]; }; + private readonly _languageToScope2: string[]; + private readonly _grammarRegistry: Registry; + + constructor(host: ITMGrammarFactoryHost, grammarDefinitions: IValidGrammarDefinition[], vscodeTextmate: typeof import('vscode-textmate'), onigLib: Promise<IOnigLib> | undefined) { + super(); + this._host = host; + this._initialState = vscodeTextmate.INITIAL; + this._scopeRegistry = this._register(new TMScopeRegistry()); + this._injections = {}; + this._injectedEmbeddedLanguages = {}; + this._languageToScope2 = []; + this._grammarRegistry = new vscodeTextmate.Registry({ + getOnigLib: (typeof onigLib === 'undefined' ? undefined : () => onigLib), + loadGrammar: async (scopeName: string) => { + const grammarDefinition = this._scopeRegistry.getGrammarDefinition(scopeName); + if (!grammarDefinition) { + this._host.logTrace(`No grammar found for scope ${scopeName}`); + return null; + } + const location = grammarDefinition.location; + try { + const content = await this._host.readFile(location); + return vscodeTextmate.parseRawGrammar(content, location.path); + } catch (e) { + this._host.logError(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); + return null; + } + }, + getInjections: (scopeName: string) => { + const scopeParts = scopeName.split('.'); + let injections: string[] = []; + for (let i = 1; i <= scopeParts.length; i++) { + const subScopeName = scopeParts.slice(0, i).join('.'); + injections = [...injections, ...(this._injections[subScopeName] || [])]; + } + return injections; + } + }); + + for (const validGrammar of grammarDefinitions) { + this._scopeRegistry.register(validGrammar); + + if (validGrammar.injectTo) { + for (let injectScope of validGrammar.injectTo) { + let injections = this._injections[injectScope]; + if (!injections) { + this._injections[injectScope] = injections = []; + } + injections.push(validGrammar.scopeName); + } + + if (validGrammar.embeddedLanguages) { + for (let injectScope of validGrammar.injectTo) { + let injectedEmbeddedLanguages = this._injectedEmbeddedLanguages[injectScope]; + if (!injectedEmbeddedLanguages) { + this._injectedEmbeddedLanguages[injectScope] = injectedEmbeddedLanguages = []; + } + injectedEmbeddedLanguages.push(validGrammar.embeddedLanguages); + } + } + } + + if (validGrammar.language) { + this._languageToScope2[validGrammar.language] = validGrammar.scopeName; + } + } + } + + public has(languageId: LanguageId): boolean { + return this._languageToScope2[languageId] ? true : false; + } + + public setTheme(theme: IRawTheme): void { + this._grammarRegistry.setTheme(theme); + } + + public getColorMap(): string[] { + return this._grammarRegistry.getColorMap(); + } + + public async createGrammar(languageId: LanguageId): Promise<ICreateGrammarResult> { + const scopeName = this._languageToScope2[languageId]; + if (typeof scopeName !== 'string') { + // No TM grammar defined + return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + } + + const grammarDefinition = this._scopeRegistry.getGrammarDefinition(scopeName); + if (!grammarDefinition) { + // No TM grammar defined + return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + } + + let embeddedLanguages = grammarDefinition.embeddedLanguages; + if (this._injectedEmbeddedLanguages[scopeName]) { + const injectedEmbeddedLanguages = this._injectedEmbeddedLanguages[scopeName]; + for (const injected of injectedEmbeddedLanguages) { + for (const scope of Object.keys(injected)) { + embeddedLanguages[scope] = injected[scope]; + } + } + } + + const containsEmbeddedLanguages = (Object.keys(embeddedLanguages).length > 0); + + const grammar = await this._grammarRegistry.loadGrammarWithConfiguration(scopeName, languageId, { embeddedLanguages, tokenTypes: <any>grammarDefinition.tokenTypes }); + + return { + languageId: languageId, + grammar: grammar, + initialState: this._initialState, + containsEmbeddedLanguages: containsEmbeddedLanguages + }; + } +} diff --git a/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts b/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts new file mode 100644 index 0000000000..cb711b40ab --- /dev/null +++ b/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as resources from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { StandardTokenType, LanguageId } from 'vs/editor/common/modes'; + +export interface IValidGrammarDefinition { + location: URI; + language?: LanguageId; + scopeName: string; + embeddedLanguages: IValidEmbeddedLanguagesMap; + tokenTypes: IValidTokenTypeMap; + injectTo?: string[]; +} + +export interface IValidTokenTypeMap { + [selector: string]: StandardTokenType; +} + +export interface IValidEmbeddedLanguagesMap { + [scopeName: string]: LanguageId; +} + +export class TMScopeRegistry extends Disposable { + + private _scopeNameToLanguageRegistration: { [scopeName: string]: IValidGrammarDefinition; }; + + constructor() { + super(); + this.reset(); + } + + public reset(): void { + this._scopeNameToLanguageRegistration = Object.create(null); + } + + public register(def: IValidGrammarDefinition): void { + if (this._scopeNameToLanguageRegistration[def.scopeName]) { + const existingRegistration = this._scopeNameToLanguageRegistration[def.scopeName]; + if (!resources.isEqual(existingRegistration.location, def.location)) { + console.warn( + `Overwriting grammar scope name to file mapping for scope ${def.scopeName}.\n` + + `Old grammar file: ${existingRegistration.location.toString()}.\n` + + `New grammar file: ${def.location.toString()}` + ); + } + } + this._scopeNameToLanguageRegistration[def.scopeName] = def; + } + + public getGrammarDefinition(scopeName: string): IValidGrammarDefinition | null { + return this._scopeNameToLanguageRegistration[scopeName] || null; + } +} diff --git a/src/vs/workbench/services/textMate/electron-browser/cgmanifest.json b/src/vs/workbench/services/textMate/common/cgmanifest.json similarity index 100% rename from src/vs/workbench/services/textMate/electron-browser/cgmanifest.json rename to src/vs/workbench/services/textMate/common/cgmanifest.json diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index 0d8a3b7769..db7166180a 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -3,512 +3,193 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as dom from 'vs/base/browser/dom'; -import { Color } from 'vs/base/common/color'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; -import * as resources from 'vs/base/common/resources'; -import * as types from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; -import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; -import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes'; -import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; -import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IFileService } from 'vs/platform/files/common/files'; -import { ILogService } from 'vs/platform/log/common/log'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars'; import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; -import { ITokenColorizationRule, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, IGrammar, ITokenTypeMap, Registry, StackElement, StandardTokenType } from 'vscode-textmate'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { AbstractTextMateService } from 'vs/workbench/services/textMate/browser/abstractTextMateService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { createWebWorker, MonacoWebWorker } from 'vs/editor/common/services/webWorker'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IOnigLib } from 'vscode-textmate'; +import { IValidGrammarDefinition } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; +import { TextMateWorker } from 'vs/workbench/services/textMate/electron-browser/textMateWorker'; +import { ITextModel } from 'vs/editor/common/model'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { UriComponents, URI } from 'vs/base/common/uri'; -export class TMScopeRegistry { +const RUN_TEXTMATE_IN_WORKER = false; - private _scopeNameToLanguageRegistration: { [scopeName: string]: TMLanguageRegistration; }; - private _encounteredLanguages: boolean[]; +class ModelWorkerTextMateTokenizer extends Disposable { - private readonly _onDidEncounterLanguage = new Emitter<LanguageId>(); - public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; + private readonly _worker: TextMateWorker; + private readonly _model: ITextModel; + private _isSynced: boolean; - constructor() { - this.reset(); - } + constructor(worker: TextMateWorker, model: ITextModel) { + super(); + this._worker = worker; + this._model = model; + this._isSynced = false; - public reset(): void { - this._scopeNameToLanguageRegistration = Object.create(null); - this._encounteredLanguages = []; - } + this._register(this._model.onDidChangeAttached(() => this._onDidChangeAttached())); + this._onDidChangeAttached(); - public register(scopeName: string, grammarLocation: URI, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: TokenTypesContribution): void { - if (this._scopeNameToLanguageRegistration[scopeName]) { - const existingRegistration = this._scopeNameToLanguageRegistration[scopeName]; - if (!resources.isEqual(existingRegistration.grammarLocation, grammarLocation)) { - console.warn( - `Overwriting grammar scope name to file mapping for scope ${scopeName}.\n` + - `Old grammar file: ${existingRegistration.grammarLocation.toString()}.\n` + - `New grammar file: ${grammarLocation.toString()}` - ); + this._register(this._model.onDidChangeContent((e) => { + if (this._isSynced) { + this._worker.acceptModelChanged(this._model.uri.toString(), e); } - } - this._scopeNameToLanguageRegistration[scopeName] = new TMLanguageRegistration(scopeName, grammarLocation, embeddedLanguages, tokenTypes); - } + })); - public getLanguageRegistration(scopeName: string): TMLanguageRegistration { - return this._scopeNameToLanguageRegistration[scopeName] || null; - } - - public getGrammarLocation(scopeName: string): URI | null { - let data = this.getLanguageRegistration(scopeName); - return data ? data.grammarLocation : null; - } - - /** - * To be called when tokenization found/hit an embedded language. - */ - public onEncounteredLanguage(languageId: LanguageId): void { - if (!this._encounteredLanguages[languageId]) { - this._encounteredLanguages[languageId] = true; - this._onDidEncounterLanguage.fire(languageId); - } - } -} - -export class TMLanguageRegistration { - _topLevelScopeNameDataBrand: void; - - readonly scopeName: string; - readonly grammarLocation: URI; - readonly embeddedLanguages: IEmbeddedLanguagesMap; - readonly tokenTypes: ITokenTypeMap; - - constructor(scopeName: string, grammarLocation: URI, embeddedLanguages: IEmbeddedLanguagesMap | undefined, tokenTypes: TokenTypesContribution | undefined) { - this.scopeName = scopeName; - this.grammarLocation = grammarLocation; - - // embeddedLanguages handling - this.embeddedLanguages = Object.create(null); - - if (embeddedLanguages) { - // If embeddedLanguages are configured, fill in `this._embeddedLanguages` - let scopes = Object.keys(embeddedLanguages); - for (let i = 0, len = scopes.length; i < len; i++) { - let scope = scopes[i]; - let language = embeddedLanguages[scope]; - if (typeof language !== 'string') { - // never hurts to be too careful - continue; - } - this.embeddedLanguages[scope] = language; + this._register(this._model.onDidChangeLanguage((e) => { + if (this._isSynced) { + this._worker.acceptModelLanguageChanged(this._model.uri.toString(), this._model.getLanguageIdentifier().id); } - } + })); + } - this.tokenTypes = Object.create(null); - if (tokenTypes) { - // If tokenTypes is configured, fill in `this._tokenTypes` - const scopes = Object.keys(tokenTypes); - for (const scope of scopes) { - const tokenType = tokenTypes[scope]; - switch (tokenType) { - case 'string': - this.tokenTypes[scope] = StandardTokenType.String; - break; - case 'other': - this.tokenTypes[scope] = StandardTokenType.Other; - break; - case 'comment': - this.tokenTypes[scope] = StandardTokenType.Comment; - break; - } + private _onDidChangeAttached(): void { + if (this._model.isAttachedToEditor()) { + if (!this._isSynced) { + this._beginSync(); + } + } else { + if (this._isSynced) { + this._endSync(); } } } + + private _beginSync(): void { + this._isSynced = true; + this._worker.acceptNewModel({ + uri: this._model.uri, + versionId: this._model.getVersionId(), + lines: this._model.getLinesContent(), + EOL: this._model.getEOL(), + languageId: this._model.getLanguageIdentifier().id, + }); + } + + private _endSync(): void { + this._isSynced = false; + this._worker.acceptRemovedModel(this._model.uri.toString()); + } + + public dispose() { + super.dispose(); + this._endSync(); + } } -interface ICreateGrammarResult { - languageId: LanguageId; - grammar: IGrammar; - initialState: StackElement; - containsEmbeddedLanguages: boolean; +export class TextMateWorkerHost { + + constructor(@IFileService private readonly _fileService: IFileService) { + } + + async readFile(_resource: UriComponents): Promise<string> { + const resource = URI.revive(_resource); + const content = await this._fileService.readFile(resource); + return content.value.toString(); + } } -export class TextMateService extends Disposable implements ITextMateService { - public _serviceBrand: any; +export class TextMateService extends AbstractTextMateService { - private readonly _onDidEncounterLanguage: Emitter<LanguageId> = this._register(new Emitter<LanguageId>()); - public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; - - private readonly _styleElement: HTMLStyleElement; - private readonly _createdModes: string[]; - - private _scopeRegistry: TMScopeRegistry; - private _injections: { [scopeName: string]: string[]; }; - private _injectedEmbeddedLanguages: { [scopeName: string]: IEmbeddedLanguagesMap[]; }; - private _languageToScope: Map<string, string>; - private _grammarRegistry: Promise<[Registry, StackElement]> | null; - private _tokenizersRegistrations: IDisposable[]; - private _currentTokenColors: ITokenColorizationRule[] | null; - private _themeListener: IDisposable | null; + private _worker: MonacoWebWorker<TextMateWorker> | null; + private _workerProxy: TextMateWorker | null; + private _tokenizers: { [uri: string]: ModelWorkerTextMateTokenizer; }; constructor( - @IModeService private readonly _modeService: IModeService, - @IWorkbenchThemeService private readonly _themeService: IWorkbenchThemeService, - @IFileService private readonly _fileService: IFileService, - @INotificationService private readonly _notificationService: INotificationService, - @ILogService private readonly _logService: ILogService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IModeService modeService: IModeService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IFileService fileService: IFileService, + @INotificationService notificationService: INotificationService, + @ILogService logService: ILogService, + @IConfigurationService configurationService: IConfigurationService, + @IModelService private readonly _modelService: IModelService, ) { - super(); - this._styleElement = dom.createStyleSheet(); - this._styleElement.className = 'vscode-tokens-styles'; - this._createdModes = []; - this._scopeRegistry = new TMScopeRegistry(); - this._scopeRegistry.onDidEncounterLanguage((language) => this._onDidEncounterLanguage.fire(language)); - this._injections = {}; - this._injectedEmbeddedLanguages = {}; - this._languageToScope = new Map<string, string>(); - this._grammarRegistry = null; - this._tokenizersRegistrations = []; - this._currentTokenColors = null; - this._themeListener = null; - - grammarsExtPoint.setHandler((extensions) => { - this._scopeRegistry.reset(); - this._injections = {}; - this._injectedEmbeddedLanguages = {}; - this._languageToScope = new Map<string, string>(); - this._grammarRegistry = null; - this._tokenizersRegistrations = dispose(this._tokenizersRegistrations); - this._currentTokenColors = null; - if (this._themeListener) { - this._themeListener.dispose(); - this._themeListener = null; - } - - for (const extension of extensions) { - let grammars = extension.value; - for (const grammar of grammars) { - this._handleGrammarExtensionPointUser(extension.description.extensionLocation, grammar, extension.collector); - } - } - - for (const createMode of this._createdModes) { - this._registerDefinitionIfAvailable(createMode); - } - }); - - // Generate some color map until the grammar registry is loaded - let colorTheme = this._themeService.getColorTheme(); - let defaultForeground: Color = Color.transparent; - let defaultBackground: Color = Color.transparent; - for (let i = 0, len = colorTheme.tokenColors.length; i < len; i++) { - let rule = colorTheme.tokenColors[i]; - if (!rule.scope && rule.settings) { - if (rule.settings.foreground) { - defaultForeground = Color.fromHex(rule.settings.foreground); - } - if (rule.settings.background) { - defaultBackground = Color.fromHex(rule.settings.background); - } - } - } - TokenizationRegistry.setColorMap([null!, defaultForeground, defaultBackground]); - - this._modeService.onDidCreateMode((mode) => { - let modeId = mode.getId(); - this._createdModes.push(modeId); - this._registerDefinitionIfAvailable(modeId); - }); + super(modeService, themeService, fileService, notificationService, logService, configurationService); + this._worker = null; + this._workerProxy = null; + this._tokenizers = Object.create(null); + this._register(this._modelService.onModelAdded(model => this._onModelAdded(model))); + this._register(this._modelService.onModelRemoved(model => this._onModelRemoved(model))); + this._modelService.getModels().forEach((model) => this._onModelAdded(model)); } - private _registerDefinitionIfAvailable(modeId: string): void { - if (this._languageToScope.has(modeId)) { - const promise = this._createGrammar(modeId).then((r) => { - return new TMTokenization(this._scopeRegistry, r.languageId, r.grammar, r.initialState, r.containsEmbeddedLanguages, this._notificationService, this._configurationService); - }, e => { - onUnexpectedError(e); - return null; + private _onModelAdded(model: ITextModel): void { + if (!this._workerProxy) { + return; + } + if (model.isTooLargeForSyncing()) { + return; + } + const key = model.uri.toString(); + const tokenizer = new ModelWorkerTextMateTokenizer(this._workerProxy, model); + this._tokenizers[key] = tokenizer; + } + + private _onModelRemoved(model: ITextModel): void { + const key = model.uri.toString(); + if (this._tokenizers[key]) { + this._tokenizers[key].dispose(); + delete this._tokenizers[key]; + } + } + + protected _loadVSCodeTextmate(): Promise<typeof import('vscode-textmate')> { + return import('vscode-textmate'); + } + + protected _loadOnigLib(): Promise<IOnigLib> | undefined { + return undefined; + } + + protected _onDidCreateGrammarFactory(grammarDefinitions: IValidGrammarDefinition[]): void { + this._killWorker(); + + if (RUN_TEXTMATE_IN_WORKER) { + const workerHost = new TextMateWorkerHost(this._fileService); + const worker = createWebWorker<TextMateWorker>(this._modelService, { + createData: { + grammarDefinitions + }, + label: 'textMateWorker', + moduleId: 'vs/workbench/services/textMate/electron-browser/textMateWorker', + host: workerHost }); - this._tokenizersRegistrations.push(TokenizationRegistry.registerPromise(modeId, promise)); - } - } - private async _createGrammarRegistry(): Promise<[Registry, StackElement]> { - const { Registry, INITIAL, parseRawGrammar } = await import('vscode-textmate'); - const grammarRegistry = new Registry({ - loadGrammar: async (scopeName: string) => { - const location = this._scopeRegistry.getGrammarLocation(scopeName); - if (!location) { - this._logService.trace(`No grammar found for scope ${scopeName}`); - return null; + this._worker = worker; + worker.getProxy().then((proxy) => { + if (this._worker !== worker) { + // disposed in the meantime + return; } - try { - const content = await this._fileService.readFile(location); - return parseRawGrammar(content.value.toString(), location.path); - } catch (e) { - this._logService.error(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); - return null; - } - }, - getInjections: (scopeName: string) => { - const scopeParts = scopeName.split('.'); - let injections: string[] = []; - for (let i = 1; i <= scopeParts.length; i++) { - const subScopeName = scopeParts.slice(0, i).join('.'); - injections = [...injections, ...(this._injections[subScopeName] || [])]; - } - return injections; - } - }); - this._updateTheme(grammarRegistry); - this._themeListener = this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); - return <[Registry, StackElement]>[grammarRegistry, INITIAL]; - } - - private _getOrCreateGrammarRegistry(): Promise<[Registry, StackElement]> { - if (!this._grammarRegistry) { - this._grammarRegistry = this._createGrammarRegistry(); - } - return this._grammarRegistry; - } - - private static _toColorMap(colorMap: string[]): Color[] { - let result: Color[] = [null!]; - for (let i = 1, len = colorMap.length; i < len; i++) { - result[i] = Color.fromHex(colorMap[i]); - } - return result; - } - - private _updateTheme(grammarRegistry: Registry): void { - let colorTheme = this._themeService.getColorTheme(); - if (!this.compareTokenRules(colorTheme.tokenColors)) { - return; - } - grammarRegistry.setTheme({ name: colorTheme.label, settings: colorTheme.tokenColors }); - let colorMap = TextMateService._toColorMap(grammarRegistry.getColorMap()); - let cssRules = generateTokensCSSForColorMap(colorMap); - this._styleElement.innerHTML = cssRules; - TokenizationRegistry.setColorMap(colorMap); - } - - private compareTokenRules(newRules: ITokenColorizationRule[]): boolean { - let currRules = this._currentTokenColors; - this._currentTokenColors = newRules; - if (!newRules || !currRules || newRules.length !== currRules.length) { - return true; - } - for (let i = newRules.length - 1; i >= 0; i--) { - let r1 = newRules[i]; - let r2 = currRules[i]; - if (r1.scope !== r2.scope) { - return true; - } - let s1 = r1.settings; - let s2 = r2.settings; - if (s1 && s2) { - if (s1.fontStyle !== s2.fontStyle || s1.foreground !== s2.foreground || s1.background !== s2.background) { - return true; - } - } else if (!s1 || !s2) { - return true; - } - } - return false; - } - - private _handleGrammarExtensionPointUser(extensionLocation: URI, syntax: ITMSyntaxExtensionPoint, collector: ExtensionMessageCollector): void { - if (syntax.language && ((typeof syntax.language !== 'string') || !this._modeService.isRegisteredMode(syntax.language))) { - collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", grammarsExtPoint.name, String(syntax.language))); - return; - } - if (!syntax.scopeName || (typeof syntax.scopeName !== 'string')) { - collector.error(nls.localize('invalid.scopeName', "Expected string in `contributes.{0}.scopeName`. Provided value: {1}", grammarsExtPoint.name, String(syntax.scopeName))); - return; - } - if (!syntax.path || (typeof syntax.path !== 'string')) { - collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", grammarsExtPoint.name, String(syntax.path))); - return; - } - if (syntax.injectTo && (!Array.isArray(syntax.injectTo) || syntax.injectTo.some(scope => typeof scope !== 'string'))) { - collector.error(nls.localize('invalid.injectTo', "Invalid value in `contributes.{0}.injectTo`. Must be an array of language scope names. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.injectTo))); - return; - } - if (syntax.embeddedLanguages && !types.isObject(syntax.embeddedLanguages)) { - collector.error(nls.localize('invalid.embeddedLanguages', "Invalid value in `contributes.{0}.embeddedLanguages`. Must be an object map from scope name to language. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.embeddedLanguages))); - return; - } - - if (syntax.tokenTypes && !types.isObject(syntax.tokenTypes)) { - collector.error(nls.localize('invalid.tokenTypes', "Invalid value in `contributes.{0}.tokenTypes`. Must be an object map from scope name to token type. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.tokenTypes))); - return; - } - - const grammarLocation = resources.joinPath(extensionLocation, syntax.path); - if (!resources.isEqualOrParent(grammarLocation, extensionLocation)) { - collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", grammarsExtPoint.name, grammarLocation.path, extensionLocation.path)); - } - - this._scopeRegistry.register(syntax.scopeName, grammarLocation, syntax.embeddedLanguages, syntax.tokenTypes); - - if (syntax.injectTo) { - for (let injectScope of syntax.injectTo) { - let injections = this._injections[injectScope]; - if (!injections) { - this._injections[injectScope] = injections = []; - } - injections.push(syntax.scopeName); - } - - if (syntax.embeddedLanguages) { - for (let injectScope of syntax.injectTo) { - let injectedEmbeddedLanguages = this._injectedEmbeddedLanguages[injectScope]; - if (!injectedEmbeddedLanguages) { - this._injectedEmbeddedLanguages[injectScope] = injectedEmbeddedLanguages = []; - } - injectedEmbeddedLanguages.push(syntax.embeddedLanguages); - } - } - } - - let modeId = syntax.language; - if (modeId) { - this._languageToScope.set(modeId, syntax.scopeName); + this._workerProxy = proxy; + this._modelService.getModels().forEach((model) => this._onModelAdded(model)); + }); } } - private _resolveEmbeddedLanguages(embeddedLanguages: IEmbeddedLanguagesMap): IEmbeddedLanguagesMap2 { - let scopes = Object.keys(embeddedLanguages); - let result: IEmbeddedLanguagesMap2 = Object.create(null); - for (let i = 0, len = scopes.length; i < len; i++) { - let scope = scopes[i]; - let language = embeddedLanguages[scope]; - let languageIdentifier = this._modeService.getLanguageIdentifier(language); - if (languageIdentifier) { - result[scope] = languageIdentifier.id; - } - } - return result; + protected _onDidDisposeGrammarFactory(): void { + this._killWorker(); } - public async createGrammar(modeId: string): Promise<IGrammar> { - const { grammar } = await this._createGrammar(modeId); - return grammar; - } - - private async _createGrammar(modeId: string): Promise<ICreateGrammarResult> { - const scopeName = this._languageToScope.get(modeId); - if (typeof scopeName !== 'string') { - // No TM grammar defined - return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + private _killWorker(): void { + for (let key of Object.keys(this._tokenizers)) { + this._tokenizers[key].dispose(); } - const languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); - if (!languageRegistration) { - // No TM grammar defined - return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + this._tokenizers = Object.create(null); + + if (this._worker) { + this._worker.dispose(); + this._worker = null; } - let embeddedLanguages = this._resolveEmbeddedLanguages(languageRegistration.embeddedLanguages); - let rawInjectedEmbeddedLanguages = this._injectedEmbeddedLanguages[scopeName]; - if (rawInjectedEmbeddedLanguages) { - let injectedEmbeddedLanguages: IEmbeddedLanguagesMap2[] = rawInjectedEmbeddedLanguages.map(this._resolveEmbeddedLanguages.bind(this)); - for (const injected of injectedEmbeddedLanguages) { - for (const scope of Object.keys(injected)) { - embeddedLanguages[scope] = injected[scope]; - } - } - } - - let languageId = this._modeService.getLanguageIdentifier(modeId)!.id; - let containsEmbeddedLanguages = (Object.keys(embeddedLanguages).length > 0); - - const [grammarRegistry, initialState] = await this._getOrCreateGrammarRegistry(); - const grammar = await grammarRegistry.loadGrammarWithConfiguration(scopeName, languageId, { embeddedLanguages, tokenTypes: languageRegistration.tokenTypes }); - return { - languageId: languageId, - grammar: grammar, - initialState: initialState, - containsEmbeddedLanguages: containsEmbeddedLanguages - }; - } -} - -class TMTokenization implements ITokenizationSupport { - - private readonly _scopeRegistry: TMScopeRegistry; - private readonly _languageId: LanguageId; - private readonly _grammar: IGrammar; - private readonly _containsEmbeddedLanguages: boolean; - private readonly _seenLanguages: boolean[]; - private readonly _initialState: StackElement; - private _maxTokenizationLineLength: number; - private _tokenizationWarningAlreadyShown: boolean; - - constructor(scopeRegistry: TMScopeRegistry, languageId: LanguageId, grammar: IGrammar, initialState: StackElement, containsEmbeddedLanguages: boolean, @INotificationService private readonly notificationService: INotificationService, @IConfigurationService readonly configurationService: IConfigurationService) { - this._scopeRegistry = scopeRegistry; - this._languageId = languageId; - this._grammar = grammar; - this._initialState = initialState; - this._containsEmbeddedLanguages = containsEmbeddedLanguages; - this._seenLanguages = []; - this._maxTokenizationLineLength = configurationService.getValue<number>('editor.maxTokenizationLineLength'); - } - - public getInitialState(): IState { - return this._initialState; - } - - public tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult { - throw new Error('Not supported!'); - } - - public tokenize2(line: string, state: StackElement, offsetDelta: number): TokenizationResult2 { - if (offsetDelta !== 0) { - throw new Error('Unexpected: offsetDelta should be 0.'); - } - - // Do not attempt to tokenize if a line is too long - if (line.length >= this._maxTokenizationLineLength) { - if (!this._tokenizationWarningAlreadyShown) { - this._tokenizationWarningAlreadyShown = true; - this.notificationService.warn(nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. The length of a long line can be configured via `editor.maxTokenizationLineLength`.")); - } - console.log(`Line (${line.substr(0, 15)}...): longer than ${this._maxTokenizationLineLength} characters, tokenization skipped.`); - return nullTokenize2(this._languageId, line, state, offsetDelta); - } - - let textMateResult = this._grammar.tokenizeLine2(line, state); - - if (this._containsEmbeddedLanguages) { - let seenLanguages = this._seenLanguages; - let tokens = textMateResult.tokens; - - // Must check if any of the embedded languages was hit - for (let i = 0, len = (tokens.length >>> 1); i < len; i++) { - let metadata = tokens[(i << 1) + 1]; - let languageId = TokenMetadata.getLanguageId(metadata); - - if (!seenLanguages[languageId]) { - seenLanguages[languageId] = true; - this._scopeRegistry.onEncounteredLanguage(languageId); - } - } - } - - let endState: StackElement; - // try to save an object if possible - if (state.equals(textMateResult.ruleStack)) { - endState = state; - } else { - endState = textMateResult.ruleStack; - - } - - return new TokenizationResult2(textMateResult.tokens, endState); + this._workerProxy = null; } } diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts new file mode 100644 index 0000000000..bd0b221be2 --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/textMateWorker.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkerContext } from 'vs/editor/common/services/editorSimpleWorker'; +import { UriComponents, URI } from 'vs/base/common/uri'; +import { LanguageId } from 'vs/editor/common/modes'; +import { IValidEmbeddedLanguagesMap, IValidTokenTypeMap, IValidGrammarDefinition } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; +import { TMGrammarFactory, ICreateGrammarResult } from 'vs/workbench/services/textMate/common/TMGrammarFactory'; +import { IModelChangedEvent, MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; +import { TextMateWorkerHost } from 'vs/workbench/services/textMate/electron-browser/textMateService'; + +export interface IValidGrammarDefinitionDTO { + location: UriComponents; + language?: LanguageId; + scopeName: string; + embeddedLanguages: IValidEmbeddedLanguagesMap; + tokenTypes: IValidTokenTypeMap; + injectTo?: string[]; +} + +export interface ICreateData { + grammarDefinitions: IValidGrammarDefinitionDTO[]; +} + +export interface IRawModelData { + uri: UriComponents; + versionId: number; + lines: string[]; + EOL: string; + languageId: LanguageId; +} + +class TextMateWorkerModel extends MirrorTextModel { + + private readonly _worker: TextMateWorker; + private _languageId: LanguageId; + + constructor(uri: URI, lines: string[], eol: string, versionId: number, worker: TextMateWorker, languageId: LanguageId) { + super(uri, lines, eol, versionId); + this._worker = worker; + this._languageId = languageId; + this._resetTokenization(); + } + + onLanguageId(languageId: LanguageId): void { + this._languageId = languageId; + this._resetTokenization(); + } + + private _resetTokenization(): void { + this._worker.getOrCreateGrammar(this._languageId).then((r) => { + console.log(r); + }); + } +} + +export class TextMateWorker { + + private readonly _host: TextMateWorkerHost; + private readonly _models: { [uri: string]: TextMateWorkerModel; }; + private readonly _grammarCache: Promise<ICreateGrammarResult>[]; + private readonly _grammarFactory: TMGrammarFactory; + + constructor(ctx: IWorkerContext<TextMateWorkerHost>, createData: ICreateData) { + this._host = ctx.host; + this._models = Object.create(null); + this._grammarCache = []; + const grammarDefinitions = createData.grammarDefinitions.map<IValidGrammarDefinition>((def) => { + return { + location: URI.revive(def.location), + language: def.language, + scopeName: def.scopeName, + embeddedLanguages: def.embeddedLanguages, + tokenTypes: def.tokenTypes, + injectTo: def.injectTo, + }; + }); + + let vscodeTextmate: typeof import('vscode-textmate'); + const globalDefine = (<any>self).define; + try { + (<any>self).define.amd = undefined; + vscodeTextmate = require.__$__nodeRequire('vscode-textmate'); + } catch (err) { + console.error(err); + return; + } finally { + (<any>self).define = globalDefine; + } + + this._grammarFactory = new TMGrammarFactory({ + logTrace: (msg: string) => console.log(msg), + logError: (msg: string, err: any) => console.error(msg, err), + readFile: (resource: URI) => this._host.readFile(resource) + }, grammarDefinitions, vscodeTextmate, undefined); + } + + public acceptNewModel(data: IRawModelData): void { + const uri = URI.revive(data.uri); + const key = uri.toString(); + this._models[key] = new TextMateWorkerModel(uri, data.lines, data.EOL, data.versionId, this, data.languageId); + } + + public acceptModelChanged(strURL: string, e: IModelChangedEvent): void { + this._models[strURL].onEvents(e); + } + + public acceptModelLanguageChanged(strURL: string, newLanguageId: LanguageId): void { + this._models[strURL].onLanguageId(newLanguageId); + } + + public acceptRemovedModel(strURL: string): void { + delete this._models[strURL]; + } + + public getOrCreateGrammar(languageId: LanguageId): Promise<ICreateGrammarResult> { + if (!this._grammarCache[languageId]) { + this._grammarCache[languageId] = this._grammarFactory.createGrammar(languageId); + } + return this._grammarCache[languageId]; + } +} + +export function create(ctx: IWorkerContext<TextMateWorkerHost>, createData: ICreateData): TextMateWorker { + return new TextMateWorker(ctx, createData); +} diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 63ef1904ad..7e7d7a77ab 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -6,6 +6,8 @@ import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; import { ITextFileService, IResourceEncodings, IResourceEncoding } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; export class BrowserTextFileService extends TextFileService { @@ -14,6 +16,42 @@ export class BrowserTextFileService extends TextFileService { return { encoding: 'utf8', hasBOM: false }; } }; + + protected beforeShutdown(reason: ShutdownReason): boolean { + // Web: we cannot perform long running in the shutdown phase + // As such we need to check sync if there are any dirty files + // that have not been backed up yet and then prevent the shutdown + // if that is the case. + return this.doBeforeShutdownSync(reason); + } + + private doBeforeShutdownSync(reason: ShutdownReason): boolean { + const dirtyResources = this.getDirty(); + if (!dirtyResources.length) { + return false; // no dirty: no veto + } + + if (!this.isHotExitEnabled) { + return true; // dirty without backup: veto + } + + for (const dirtyResource of dirtyResources) { + let hasBackup = false; + + if (this.fileService.canHandleResource(dirtyResource)) { + const model = this.models.get(dirtyResource); + hasBackup = !!(model && model.hasBackup()); + } else if (dirtyResource.scheme === Schemas.untitled) { + hasBackup = this.untitledEditorService.hasBackup(dirtyResource); + } + + if (!hasBackup) { + return true; // dirty without backup: veto + } + } + + return false; // dirty with backups: no veto + } } registerSingleton(ITextFileService, BrowserTextFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 8a07bf7270..520b3e7f99 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { join } from 'vs/base/common/path'; import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { guessMimeTypes } from 'vs/base/common/mime'; @@ -25,10 +24,9 @@ import { RunOnceScheduler, timeout } from 'vs/base/common/async'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { hash } from 'vs/base/common/hash'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { isLinux } from 'vs/base/common/platform'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { isEqual, isEqualOrParent, extname, basename } from 'vs/base/common/resources'; +import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; @@ -39,6 +37,22 @@ export interface IBackupMetaData { orphaned: boolean; } +type FileTelemetryDataFragment = { + mimeType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + ext: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + path: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + reason?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + whitelistedjson?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + +type TelemetryData = { + mimeType: string; + ext: string; + path: number; + reason?: number; + whitelistedjson?: string; +}; + /** * The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk. */ @@ -56,10 +70,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil static setSaveParticipant(handler: ISaveParticipant | null): void { TextFileEditorModel.saveParticipant = handler; } private readonly _onDidContentChange: Emitter<StateChange> = this._register(new Emitter<StateChange>()); - get onDidContentChange(): Event<StateChange> { return this._onDidContentChange.event; } + readonly onDidContentChange: Event<StateChange> = this._onDidContentChange.event; private readonly _onDidStateChange: Emitter<StateChange> = this._register(new Emitter<StateChange>()); - get onDidStateChange(): Event<StateChange> { return this._onDidStateChange.event; } + readonly onDidStateChange: Event<StateChange> = this._onDidStateChange.event; private resource: URI; @@ -76,7 +90,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private autoSaveAfterMillies?: number; private autoSaveAfterMilliesEnabled: boolean; - private autoSaveDisposable?: IDisposable; + private readonly autoSaveDisposable = this._register(new MutableDisposable()); private saveSequentializer: SaveSequentializer; private lastSaveAttemptTime: number; @@ -144,7 +158,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private onFileChanges(e: FileChangesEvent): void { + private async onFileChanges(e: FileChangesEvent): Promise<void> { let fileEventImpactsModel = false; let newInOrphanModeGuess: boolean | undefined; @@ -167,28 +181,25 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } if (fileEventImpactsModel && this.inOrphanMode !== newInOrphanModeGuess) { - let checkOrphanedPromise: Promise<boolean>; + let newInOrphanModeValidated: boolean = false; if (newInOrphanModeGuess) { // We have received reports of users seeing delete events even though the file still // exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665). // Since we do not want to mark the model as orphaned, we have to check if the // file is really gone and not just a faulty file event. - checkOrphanedPromise = timeout(100).then(() => { - if (this.disposed) { - return true; - } + await timeout(100); - return this.fileService.exists(this.resource).then(exists => !exists); - }); - } else { - checkOrphanedPromise = Promise.resolve(false); + if (this.disposed) { + newInOrphanModeValidated = true; + } else { + const exists = await this.fileService.exists(this.resource); + newInOrphanModeValidated = !exists; + } } - checkOrphanedPromise.then(newInOrphanModeValidated => { - if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) { - this.setOrphaned(newInOrphanModeValidated); - } - }); + if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) { + this.setOrphaned(newInOrphanModeValidated); + } } } @@ -239,40 +250,38 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.backupFileService.backupResource<IBackupMetaData>(target, this.createSnapshot(), this.versionId, meta); } + } - return Promise.resolve(); + hasBackup(): boolean { + return this.backupFileService.hasBackupSync(this.resource, this.versionId); } async revert(soft?: boolean): Promise<void> { if (!this.isResolved()) { - return Promise.resolve(undefined); + return; } // Cancel any running auto-save - this.cancelPendingAutoSave(); + this.autoSaveDisposable.clear(); // Unset flags const undo = this.setDirty(false); - let loadPromise: Promise<unknown>; - if (soft) { - loadPromise = Promise.resolve(); - } else { - loadPromise = this.load({ forceReadFromDisk: true }); + // Force read from disk unless reverting soft + if (!soft) { + try { + await this.load({ forceReadFromDisk: true }); + } catch (error) { + + // Set flags back to previous values, we are still dirty if revert failed + undo(); + + throw error; + } } - try { - await loadPromise; - - // Emit file change event - this._onDidStateChange.fire(StateChange.REVERTED); - } catch (error) { - - // Set flags back to previous values, we are still dirty if revert failed - undo(); - - return Promise.reject(error); - } + // Emit file change event + this._onDidStateChange.fire(StateChange.REVERTED); } async load(options?: ILoadOptions): Promise<ITextFileEditorModel> { @@ -437,21 +446,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype const settingsType = this.getTypeIfSettings(); if (settingsType) { - /* __GDPR__ - "settingsRead" : { - "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data + type SettingsReadClassification = { + settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + + this.telemetryService.publicLog2<{ settingsType: string }, SettingsReadClassification>('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data } else { - /* __GDPR__ - "fileGet" : { - "${include}": [ - "${FileTelemetryData}" - ] - } - */ - this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); + type FileGetClassification = {} & FileTelemetryDataFragment; + + this.telemetryService.publicLog2<TelemetryData, FileGetClassification>('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); } return this; @@ -577,7 +580,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace(`doAutoSave() - enter for versionId ${versionId}`, this.resource); // Cancel any currently running auto saves to make this the one that succeeds - this.cancelPendingAutoSave(); + this.autoSaveDisposable.clear(); // Create new save timer and store it for disposal as needed const handle = setTimeout(() => { @@ -588,25 +591,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } }, this.autoSaveAfterMillies); - this.autoSaveDisposable = toDisposable(() => clearTimeout(handle)); + this.autoSaveDisposable.value = toDisposable(() => clearTimeout(handle)); } - private cancelPendingAutoSave(): void { - if (this.autoSaveDisposable) { - this.autoSaveDisposable.dispose(); - this.autoSaveDisposable = undefined; - } - } - - save(options: ISaveOptions = Object.create(null)): Promise<void> { + async save(options: ISaveOptions = Object.create(null)): Promise<void> { if (!this.isResolved()) { - return Promise.resolve(undefined); + return; } this.logService.trace('save() - enter', this.resource); // Cancel any currently running auto saves to make this the one that succeeds - this.cancelPendingAutoSave(); + this.autoSaveDisposable.clear(); return this.doSave(this.versionId, options); } @@ -626,7 +622,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.saveSequentializer.hasPendingSave(versionId)) { this.logService.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource); - return this.saveSequentializer.pendingSave || Promise.resolve(undefined); + return this.saveSequentializer.pendingSave || Promise.resolve(); } // Return early if not dirty (unless forced) or version changed meanwhile @@ -639,7 +635,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if ((!options.force && !this.dirty) || versionId !== this.versionId) { this.logService.trace(`doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource); - return Promise.resolve(undefined); + return Promise.resolve(); } // Return if currently saving by storing this save request as the next save that should happen. @@ -750,21 +746,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Telemetry const settingsType = this.getTypeIfSettings(); if (settingsType) { - /* __GDPR__ - "settingsWritten" : { - "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data + type SettingsWrittenClassification = { + settingsType: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data } else { - /* __GDPR__ - "filePUT" : { - "${include}": [ - "${FileTelemetryData}" - ] - } - */ - this.telemetryService.publicLog('filePUT', this.getTelemetryData(options.reason)); + type FilePutClassfication = {} & FileTelemetryDataFragment; + this.telemetryService.publicLog2<TelemetryData, FilePutClassfication>('filePUT', this.getTelemetryData(options.reason)); } }, error => { this.logService.error(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource); @@ -792,22 +780,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Check for global settings file - if (isEqual(this.resource, URI.file(this.environmentService.appSettingsPath), !isLinux)) { + if (isEqual(this.resource, this.environmentService.settingsResource)) { return 'global-settings'; } // Check for keybindings file - if (isEqual(this.resource, URI.file(this.environmentService.appKeybindingsPath), !isLinux)) { + if (isEqual(this.resource, this.environmentService.keybindingsResource)) { return 'keybindings'; } // Check for locale file - if (isEqual(this.resource, URI.file(join(this.environmentService.appSettingsHome, 'locale.json')), !isLinux)) { + if (isEqual(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'locale.json'))) { return 'locale'; } // Check for snippets - if (isEqualOrParent(this.resource, URI.file(join(this.environmentService.appSettingsHome, 'snippets')))) { + if (isEqualOrParent(this.resource, joinPath(this.environmentService.userRoamingDataHome, 'snippets'))) { return 'snippets'; } @@ -827,30 +815,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return ''; } - private getTelemetryData(reason: number | undefined): object { + private getTelemetryData(reason: number | undefined): TelemetryData { const ext = extname(this.resource); const fileName = basename(this.resource); const path = this.resource.scheme === Schemas.file ? this.resource.fsPath : this.resource.path; const telemetryData = { - mimeType: guessMimeTypes(path).join(', '), + mimeType: guessMimeTypes(this.resource).join(', '), ext, path: hash(path), - reason + reason, + whitelistedjson: undefined as string | undefined }; if (ext === '.json' && TextFileEditorModel.WHITELIST_JSON.indexOf(fileName) > -1) { telemetryData['whitelistedjson'] = fileName; } - /* __GDPR__FRAGMENT__ - "FileTelemetryData" : { - "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "whitelistedjson": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ return telemetryData; } @@ -1050,8 +1030,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.inOrphanMode = false; this.inErrorMode = false; - this.cancelPendingAutoSave(); - super.dispose(); } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 6e5b3b6048..38e42ee73c 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -15,28 +15,28 @@ import { ResourceMap } from 'vs/base/common/map'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { private readonly _onModelDisposed: Emitter<URI> = this._register(new Emitter<URI>()); - get onModelDisposed(): Event<URI> { return this._onModelDisposed.event; } + readonly onModelDisposed: Event<URI> = this._onModelDisposed.event; private readonly _onModelContentChanged: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelContentChanged(): Event<TextFileModelChangeEvent> { return this._onModelContentChanged.event; } + readonly onModelContentChanged: Event<TextFileModelChangeEvent> = this._onModelContentChanged.event; private readonly _onModelDirty: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelDirty(): Event<TextFileModelChangeEvent> { return this._onModelDirty.event; } + readonly onModelDirty: Event<TextFileModelChangeEvent> = this._onModelDirty.event; private readonly _onModelSaveError: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelSaveError(): Event<TextFileModelChangeEvent> { return this._onModelSaveError.event; } + readonly onModelSaveError: Event<TextFileModelChangeEvent> = this._onModelSaveError.event; private readonly _onModelSaved: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelSaved(): Event<TextFileModelChangeEvent> { return this._onModelSaved.event; } + readonly onModelSaved: Event<TextFileModelChangeEvent> = this._onModelSaved.event; private readonly _onModelReverted: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelReverted(): Event<TextFileModelChangeEvent> { return this._onModelReverted.event; } + readonly onModelReverted: Event<TextFileModelChangeEvent> = this._onModelReverted.event; private readonly _onModelEncodingChanged: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelEncodingChanged(): Event<TextFileModelChangeEvent> { return this._onModelEncodingChanged.event; } + readonly onModelEncodingChanged: Event<TextFileModelChangeEvent> = this._onModelEncodingChanged.event; private readonly _onModelOrphanedChanged: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>()); - get onModelOrphanedChanged(): Event<TextFileModelChangeEvent> { return this._onModelOrphanedChanged.event; } + readonly onModelOrphanedChanged: Event<TextFileModelChangeEvent> = this._onModelOrphanedChanged.event; private _onModelsDirtyEvent: Event<TextFileModelChangeEvent[]>; private _onModelsSaveError: Event<TextFileModelChangeEvent[]>; diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index f36e3b0221..a722a4e6dc 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -31,7 +31,6 @@ import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream import { IModelService } from 'vs/editor/common/services/modelService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { isEqualOrParent, isEqual, joinPath, dirname, extname, basename, toLocalResource } from 'vs/base/common/resources'; -import { posix } from 'vs/base/common/path'; import { getConfirmMessage, IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation } from 'vs/platform/dialogs/common/dialogs'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -50,13 +49,13 @@ export abstract class TextFileService extends Disposable implements ITextFileSer _serviceBrand: ServiceIdentifier<any>; private readonly _onAutoSaveConfigurationChange: Emitter<IAutoSaveConfiguration> = this._register(new Emitter<IAutoSaveConfiguration>()); - get onAutoSaveConfigurationChange(): Event<IAutoSaveConfiguration> { return this._onAutoSaveConfigurationChange.event; } + readonly onAutoSaveConfigurationChange: Event<IAutoSaveConfiguration> = this._onAutoSaveConfigurationChange.event; private readonly _onFilesAssociationChange: Emitter<void> = this._register(new Emitter<void>()); - get onFilesAssociationChange(): Event<void> { return this._onFilesAssociationChange.event; } + readonly onFilesAssociationChange: Event<void> = this._onFilesAssociationChange.event; private readonly _onWillMove = this._register(new Emitter<IWillMoveEvent>()); - get onWillMove(): Event<IWillMoveEvent> { return this._onWillMove.event; } + readonly onWillMove: Event<IWillMoveEvent> = this._onWillMove.event; private _models: TextFileEditorModelManager; get models(): ITextFileEditorModelManager { return this._models; } @@ -73,7 +72,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer constructor( @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IFileService protected readonly fileService: IFileService, - @IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService, + @IUntitledEditorService protected readonly untitledEditorService: IUntitledEditorService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IInstantiationService protected instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -119,7 +118,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer })); } - private beforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> { + protected beforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> { // Dirty files need treatment on shutdown const dirty = this.getDirty(); @@ -152,7 +151,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // If hot exit is enabled, backup dirty files and allow to exit without confirmation if (this.isHotExitEnabled) { - return this.backupBeforeShutdown(dirty, this.models, reason).then(didBackup => { + return this.backupBeforeShutdown(dirty, reason).then(didBackup => { if (didBackup) { return this.noVeto({ cleanUpBackups: false }); // no veto and no backup cleanup (since backup was successful) } @@ -171,7 +170,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return this.confirmBeforeShutdown(); } - private async backupBeforeShutdown(dirtyToBackup: URI[], textFileEditorModelManager: ITextFileEditorModelManager, reason: ShutdownReason): Promise<boolean> { + private async backupBeforeShutdown(dirtyToBackup: URI[], reason: ShutdownReason): Promise<boolean> { const windowCount = await this.windowsService.getWindowCount(); // When quit is requested skip the confirm callback and attempt to backup all workspaces. @@ -212,24 +211,24 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return false; } - await this.backupAll(dirtyToBackup, textFileEditorModelManager); + await this.backupAll(dirtyToBackup); return true; } - private backupAll(dirtyToBackup: URI[], textFileEditorModelManager: ITextFileEditorModelManager): Promise<void> { + private backupAll(dirtyToBackup: URI[]): Promise<void> { // split up between files and untitled const filesToBackup: ITextFileEditorModel[] = []; const untitledToBackup: URI[] = []; - dirtyToBackup.forEach(s => { - if (this.fileService.canHandleResource(s)) { - const model = textFileEditorModelManager.get(s); + dirtyToBackup.forEach(dirty => { + if (this.fileService.canHandleResource(dirty)) { + const model = this.models.get(dirty); if (model) { filesToBackup.push(model); } - } else if (s.scheme === Schemas.untitled) { - untitledToBackup.push(s); + } else if (dirty.scheme === Schemas.untitled) { + untitledToBackup.push(dirty); } }); @@ -436,14 +435,14 @@ export abstract class TextFileService extends Disposable implements ITextFileSer } async delete(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> { - const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource, !platform.isLinux /* ignorecase */)); + const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource)); await this.revertAll(dirtyFiles, { soft: true }); return this.fileService.del(resource, options); } - async move(source: URI, target: URI, overwrite?: boolean): Promise<void> { + async move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> { const waitForPromises: Promise<unknown>[] = []; // Event @@ -467,7 +466,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer } // Handle dirty source models if existing (if source URI is a folder, this can be multiple) - const dirtySourceModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), source, !platform.isLinux /* ignorecase */)); + const dirtySourceModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), source)); const dirtyTargetModelUris: URI[] = []; if (dirtySourceModels.length) { await Promise.all(dirtySourceModels.map(async sourceModel => { @@ -475,7 +474,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer let targetModelResource: URI; // If the source is the actual model, just use target as new resource - if (isEqual(sourceModelResource, source, !platform.isLinux /* ignorecase */)) { + if (isEqual(sourceModelResource, source)) { targetModelResource = target; } @@ -498,10 +497,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // Rename to target try { - await this.fileService.move(source, target, overwrite); + const stat = await this.fileService.move(source, target, overwrite); // Load models that were dirty before await Promise.all(dirtyTargetModelUris.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))); + + return stat; } catch (error) { // In case of an error, discard any dirty target backups that were made @@ -536,7 +537,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer async confirmSave(resources?: URI[]): Promise<ConfirmResult> { if (this.environmentService.isExtensionDevelopment) { - return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assum we run interactive (e.g. tests) + return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests) } const resourcesToConfirm = this.getDirty(resources); @@ -591,11 +592,11 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // split up between files and untitled const filesToSave: URI[] = []; const untitledToSave: URI[] = []; - toSave.forEach(s => { - if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === Schemas.untitled) { - untitledToSave.push(s); + toSave.forEach(resourceToSave => { + if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && resourceToSave.scheme === Schemas.untitled) { + untitledToSave.push(resourceToSave); } else { - filesToSave.push(s); + filesToSave.push(resourceToSave); } }); @@ -646,18 +647,19 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return result; } - protected async promptForPath(resource: URI, defaultUri: URI): Promise<URI | undefined> { + protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> { // Help user to find a name for the file by opening it first await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true, } }); - return this.fileDialogService.showSaveDialog(this.getSaveDialogOptions(defaultUri)); + return this.fileDialogService.pickFileToSave(this.getSaveDialogOptions(defaultUri, availableFileSystems)); } - private getSaveDialogOptions(defaultUri: URI): ISaveDialogOptions { + private getSaveDialogOptions(defaultUri: URI, availableFileSystems?: string[]): ISaveDialogOptions { const options: ISaveDialogOptions = { defaultUri, - title: nls.localize('saveAsTitle', "Save As") + title: nls.localize('saveAsTitle', "Save As"), + availableFileSystems, }; // Filters are only enabled on Windows where they work properly @@ -763,7 +765,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer dialogPath = this.suggestFileName(resource); } - targetResource = await this.promptForPath(resource, dialogPath); + targetResource = await this.promptForPath(resource, dialogPath, options ? options.availableFileSystems : undefined); } if (!targetResource) { @@ -854,14 +856,15 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return false; } - // take over encoding, mode and model value from source model + // take over encoding, mode (only if more specific) and model value from source model targetModel.updatePreferredEncoding(sourceModel.getEncoding()); if (sourceModel.isResolved() && targetModel.isResolved()) { this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); - const mode = sourceModel.textEditorModel.getLanguageIdentifier(); - if (mode.language !== PLAINTEXT_MODE_ID) { - targetModel.textEditorModel.setMode(mode); // only use if more specific than plain/text + const sourceMode = sourceModel.textEditorModel.getLanguageIdentifier(); + const targetMode = targetModel.textEditorModel.getLanguageIdentifier(); + if (sourceMode.language !== PLAINTEXT_MODE_ID && targetMode.language === PLAINTEXT_MODE_ID) { + targetModel.textEditorModel.setMode(sourceMode); // only use if more specific than plain/text } } @@ -901,7 +904,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return joinPath(lastActiveFolder, untitledFileName); } - return schemeFilter === Schemas.file ? URI.file(untitledFileName) : URI.from({ scheme: schemeFilter, authority: remoteAuthority, path: posix.sep + untitledFileName }); + return untitledResource.with({ path: untitledFileName }); } async revert(resource: URI, options?: IRevertOptions): Promise<boolean> { diff --git a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts similarity index 100% rename from src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts rename to src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 8c46a858ac..4dc3f44ff5 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -125,7 +125,7 @@ export interface ITextFileService extends IDisposable { /** * Move a file. If the file is dirty, its contents will be preserved and restored. */ - move(source: URI, target: URI, overwrite?: boolean): Promise<void>; + move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>; /** * Brings up the confirm dialog to either save, don't save or cancel. @@ -136,12 +136,12 @@ export interface ITextFileService extends IDisposable { confirmSave(resources?: URI[]): Promise<ConfirmResult>; /** - * Convinient fast access to the current auto save mode. + * Convenient fast access to the current auto save mode. */ getAutoSaveMode(): AutoSaveMode; /** - * Convinient fast access to the raw configured auto save settings. + * Convenient fast access to the raw configured auto save settings. */ getAutoSaveConfiguration(): IAutoSaveConfiguration; } @@ -428,6 +428,7 @@ export interface ISaveOptions { overwriteEncoding?: boolean; skipSaveParticipants?: boolean; writeElevated?: boolean; + availableFileSystems?: string[]; } export interface ILoadOptions { @@ -467,6 +468,8 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport backup(target?: URI): Promise<void>; + hasBackup(): boolean; + isDirty(): boolean; isResolved(): this is IResolvedTextFileEditorModel; diff --git a/src/vs/workbench/services/textfile/node/textFileService.ts b/src/vs/workbench/services/textfile/node/textFileService.ts index d5b739af9c..c0e4ad7fde 100644 --- a/src/vs/workbench/services/textfile/node/textFileService.ts +++ b/src/vs/workbench/services/textfile/node/textFileService.ts @@ -13,7 +13,7 @@ import { IFileStatWithMetadata, ICreateFileOptions, FileOperationError, FileOper import { Schemas } from 'vs/base/common/network'; import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs'; import { join, dirname } from 'vs/base/common/path'; -import { isMacintosh, isLinux } from 'vs/base/common/platform'; +import { isMacintosh } from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -390,7 +390,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { const defaultEncodingOverrides: IEncodingOverride[] = []; // Global settings - defaultEncodingOverrides.push({ parent: URI.file(this.environmentService.appSettingsHome), encoding: UTF8 }); + defaultEncodingOverrides.push({ parent: this.environmentService.userRoamingDataHome, encoding: UTF8 }); // Workspace files defaultEncodingOverrides.push({ extension: WORKSPACE_EXTENSION, encoding: UTF8 }); @@ -490,7 +490,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { for (const override of this.encodingOverrides) { // check if the resource is child of encoding override path - if (override.parent && isEqualOrParent(resource, override.parent, !isLinux /* ignorecase */)) { + if (override.parent && isEqualOrParent(resource, override.parent)) { return override.encoding; } diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index 8c5fc900d9..118d976913 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -17,12 +17,12 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { Schemas } from 'vs/base/common/network'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { rimraf, RimRafMode, copy, readFile, exists } from 'vs/base/node/pfs'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { tmpdir } from 'os'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { generateUuid } from 'vs/base/common/uuid'; import { join, basename } from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -76,7 +76,7 @@ suite('Files - TextFileService i/o', () => { const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'textfileservice'); let accessor: ServiceAccessor; - let disposables: IDisposable[] = []; + const disposables = new DisposableStore(); let service: ITextFileService; let testDir: string; @@ -88,8 +88,8 @@ suite('Files - TextFileService i/o', () => { const fileService = new FileService(logService); const fileProvider = new DiskFileSystemProvider(logService); - disposables.push(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.push(fileProvider); + disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); + disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); @@ -108,7 +108,7 @@ suite('Files - TextFileService i/o', () => { (<TextFileEditorModelManager>accessor.textFileService.models).dispose(); accessor.untitledEditorService.revertAll(); - disposables = dispose(disposables); + disposables.clear(); await rimraf(parentDir, RimRafMode.MOVE); }); @@ -247,7 +247,10 @@ suite('Files - TextFileService i/o', () => { } test('write - use encoding (cp1252)', async () => { - await testEncodingKeepsData(URI.file(join(testDir, 'some_cp1252.txt')), 'cp1252', ['ObjectCount = LoadObjects("Öffentlicher Ordner");', '', 'Private = "Persönliche Information"', ''].join(isWindows ? '\r\n' : '\n')); + const filePath = join(testDir, 'some_cp1252.txt'); + const contents = await readFile(filePath, 'utf8'); + const eol = /\r\n/.test(contents) ? '\r\n' : '\n'; + await testEncodingKeepsData(URI.file(filePath), 'cp1252', ['ObjectCount = LoadObjects("Öffentlicher Ordner");', '', 'Private = "Persönliche Information"', ''].join(eol)); }); test('write - use encoding (shiftjis)', async () => { diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 340bb88cf9..987f060544 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -151,10 +151,10 @@ export class TextModelResolverService implements ITextModelService { const cachedModel = this.modelService.getModel(resource); if (!cachedModel) { - return Promise.reject(new Error('Cant resolve inmemory resource')); + throw new Error('Cant resolve inmemory resource'); } - return Promise.resolve(new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource) as IResolvedTextEditorModel)); + return new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource) as IResolvedTextEditorModel); } const ref = this.resourceModelCollection.acquire(resource.toString()); diff --git a/src/vs/workbench/services/themes/common/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts similarity index 97% rename from src/vs/workbench/services/themes/common/fileIconThemeData.ts rename to src/vs/workbench/services/themes/browser/fileIconThemeData.ts index 6472b581a7..fb849ae2cc 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -11,6 +11,7 @@ import * as Json from 'vs/base/common/json'; import { ExtensionData, IThemeExtensionPoint, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IFileService } from 'vs/platform/files/common/files'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; +import { asDomUri } from 'vs/base/browser/dom'; export class FileIconThemeData implements IFileIconTheme { id: string; @@ -118,7 +119,7 @@ export class FileIconThemeData implements IFileIconTheme { case 'hidesExplorerArrows': case 'hasFolderIcons': case 'watch': - theme[key] = data[key]; + (theme as any)[key] = data[key]; break; case 'location': theme.location = URI.revive(data.location); @@ -331,7 +332,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i let fonts = iconThemeDocument.fonts; if (Array.isArray(fonts)) { fonts.forEach(font => { - let src = font.src.map(l => `url('${resolvePath(l.path)}') format('${l.format}')`).join(', '); + let src = font.src.map(l => `url('${asDomUri(resolvePath(l.path))}') format('${l.format}')`).join(', '); cssRules.push(`@font-face { src: ${src}; font-family: '${font.id}'; font-weight: ${font.weight}; font-style: ${font.style}; }`); }); cssRules.push(`.show-file-icons .file-icon::before, .show-file-icons .folder-icon::before, .show-file-icons .rootfolder-icon::before { font-family: '${fonts[0].id}'; font-size: ${fonts[0].size || '150%'}}`); @@ -342,7 +343,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i let definition = iconThemeDocument.iconDefinitions[defId]; if (definition) { if (definition.iconPath) { - cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${resolvePath(definition.iconPath)}"); }`); + cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${asDomUri(resolvePath(definition.iconPath))}"); }`); } if (definition.fontCharacter || definition.fontColor) { let body = ''; @@ -366,5 +367,5 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i return result; } function escapeCSS(str: string) { - return window['CSS'].escape(str); + return (<any>window)['CSS'].escape(str); } diff --git a/src/vs/workbench/services/themes/common/fileIconThemeStore.ts b/src/vs/workbench/services/themes/browser/fileIconThemeStore.ts similarity index 94% rename from src/vs/workbench/services/themes/common/fileIconThemeStore.ts rename to src/vs/workbench/services/themes/browser/fileIconThemeStore.ts index 0816834bbc..f1dded774b 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeStore.ts @@ -11,8 +11,9 @@ import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/serv import { ExtensionData, IThemeExtensionPoint } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Event, Emitter } from 'vs/base/common/event'; -import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData'; +import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData'; import { URI } from 'vs/base/common/uri'; +import { Disposable } from 'vs/base/common/lifecycle'; const iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>({ extensionPoint: 'iconThemes', @@ -46,16 +47,16 @@ export interface FileIconThemeChangeEvent { added: FileIconThemeData[]; } -export class FileIconThemeStore { +export class FileIconThemeStore extends Disposable { private knownIconThemes: FileIconThemeData[]; - private readonly onDidChangeEmitter: Emitter<FileIconThemeChangeEvent>; - public get onDidChange(): Event<FileIconThemeChangeEvent> { return this.onDidChangeEmitter.event; } + private readonly onDidChangeEmitter = this._register(new Emitter<FileIconThemeChangeEvent>()); + readonly onDidChange: Event<FileIconThemeChangeEvent> = this.onDidChangeEmitter.event; constructor(@IExtensionService private readonly extensionService: IExtensionService) { + super(); this.knownIconThemes = []; - this.onDidChangeEmitter = new Emitter<FileIconThemeChangeEvent>(); this.initialize(); } @@ -167,5 +168,4 @@ export class FileIconThemeStore { return this.knownIconThemes; }); } - } diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index efda47b66a..7c083825c6 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -19,8 +19,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ColorThemeStore } from 'vs/workbench/services/themes/common/colorThemeStore'; -import { FileIconThemeStore } from 'vs/workbench/services/themes/common/fileIconThemeStore'; -import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData'; +import { FileIconThemeStore } from 'vs/workbench/services/themes/browser/fileIconThemeStore'; +import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData'; import { removeClasses, addClasses } from 'vs/base/browser/dom'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; @@ -31,6 +31,7 @@ import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSetting import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; // implementation // {{SQL CARBON EDIT}} @@ -45,6 +46,7 @@ const defaultThemeExtensionId = 'sql-theme-carbon'; const oldDefaultThemeExtensionId = 'vscode-theme-colorful-defaults'; const DEFAULT_ICON_THEME_SETTING_VALUE = 'vs-seti'; +const DEFAULT_ICON_THEME_ID = 'vscode.vscode-theme-seti-vs-seti'; const fileIconsEnabledClass = 'file-icons-enabled'; const colorThemeRulesClassName = 'contributedColorTheme'; @@ -97,10 +99,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService ) { - this.container = document.body; + this.container = layoutService.getWorkbenchContainer(); this.colorThemeStore = new ColorThemeStore(extensionService, ColorThemeData.createLoadedEmptyTheme(DEFAULT_THEME_ID, DEFAULT_THEME_SETTING_VALUE)); this.onFileIconThemeChange = new Emitter<IFileIconTheme>(); this.iconThemeStore = new FileIconThemeStore(extensionService); @@ -192,10 +195,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (!theme) { // current theme is no longer available prevFileIconId = this.currentIconTheme.id; - this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, 'auto'); + this.setFileIconTheme(DEFAULT_ICON_THEME_ID, 'auto'); } else { // restore color - if (this.currentIconTheme.id === DEFAULT_ICON_THEME_SETTING_VALUE && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { + if (this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { this.setFileIconTheme(prevFileIconId, 'auto'); prevFileIconId = undefined; } @@ -271,7 +274,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (devThemes.length) { return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY); } else { - return this.setFileIconTheme(theme && theme.id || DEFAULT_ICON_THEME_SETTING_VALUE, undefined); + return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); } }); }), @@ -294,7 +297,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let iconThemeSetting = this.configurationService.getValue<string | null>(ICON_THEME_SETTING); if (iconThemeSetting !== this.currentIconTheme.settingsId) { this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { - this.setFileIconTheme(theme && theme.id || DEFAULT_ICON_THEME_SETTING_VALUE, undefined); + this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); }); } } @@ -373,29 +376,26 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private updateDynamicCSSRules(themeData: ITheme) { - let cssRules: string[] = []; - let hasRule: { [rule: string]: boolean } = {}; - let ruleCollector = { + const cssRules = new Set<string>(); + const ruleCollector = { addRule: (rule: string) => { - if (!hasRule[rule]) { - cssRules.push(rule); - hasRule[rule] = true; + if (!cssRules.has(rule)) { + cssRules.add(rule); } } }; themingRegistry.getThemingParticipants().forEach(p => p(themeData, ruleCollector, this.environmentService)); - _applyRules(cssRules.join('\n'), colorThemeRulesClassName); + _applyRules([...cssRules].join('\n'), colorThemeRulesClassName); } private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined | 'auto', silent = false): Promise<IColorTheme | null> { - if (this.container) { - if (this.currentColorTheme) { - removeClasses(this.container, this.currentColorTheme.id); - } else { - removeClasses(this.container, VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME); - } - addClasses(this.container, newTheme.id); + if (this.currentColorTheme) { + removeClasses(this.container, this.currentColorTheme.id); + } else { + removeClasses(this.container, VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME); } + addClasses(this.container, newTheme.id); + this.currentColorTheme = newTheme; if (!this.themingParticipantChangeListener) { this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); @@ -439,16 +439,21 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (themeData) { let key = themeType + themeData.extensionId; if (!this.themeExtensionsActivated.get(key)) { - /* __GDPR__ - "activatePlugin" : { - "id" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "themeId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('activatePlugin', { + type ActivatePluginClassification = { + id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + themeId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + }; + type ActivatePluginEvent = { + id: string; + name: string; + isBuiltin: boolean; + publisherDisplayName: string; + themeId: string; + }; + this.telemetryService.publicLog2<ActivatePluginEvent, ActivatePluginClassification>('activatePlugin', { id: themeData.extensionId, name: themeData.extensionName, isBuiltin: themeData.extensionIsBuiltin, @@ -481,7 +486,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.doSetFileIconTheme(newIconTheme); // remember theme data for a quick restore - if (newIconTheme.isLoaded && newIconTheme.location && !getRemoteAuthority(newIconTheme.location)) { + if (newIconTheme.isLoaded && (!newIconTheme.location || !getRemoteAuthority(newIconTheme.location))) { this.storageService.store(PERSISTED_ICON_THEME_STORAGE_KEY, newIconTheme.toStorageData(), StorageScope.GLOBAL); } @@ -510,12 +515,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private doSetFileIconTheme(iconThemeData: FileIconThemeData): void { this.currentIconTheme = iconThemeData; - if (this.container) { - if (iconThemeData.id) { - addClasses(this.container, fileIconsEnabledClass); - } else { - removeClasses(this.container, fileIconsEnabledClass); - } + if (iconThemeData.id) { + addClasses(this.container, fileIconsEnabledClass); + } else { + removeClasses(this.container, fileIconsEnabledClass); } if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) { @@ -572,12 +575,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private getBaseThemeFromContainer() { - if (this.container) { - for (let i = this.container.classList.length - 1; i >= 0; i--) { - const item = document.body.classList.item(i); - if (item === VS_LIGHT_THEME || item === VS_DARK_THEME || item === VS_HC_THEME) { - return item; - } + for (let i = this.container.classList.length - 1; i >= 0; i--) { + const item = this.container.classList.item(i); + if (item === VS_LIGHT_THEME || item === VS_DARK_THEME || item === VS_HC_THEME) { + return item; } } return VS_DARK_THEME; @@ -694,4 +695,4 @@ const tokenColorCustomizationConfiguration: IConfigurationNode = { }; configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration); -registerSingleton(IWorkbenchThemeService, WorkbenchThemeService); \ No newline at end of file +registerSingleton(IWorkbenchThemeService, WorkbenchThemeService); diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index ad6b3a828a..9c10c7e162 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -23,7 +23,7 @@ import { startsWith } from 'vs/base/common/strings'; let colorRegistry = Registry.as<IColorRegistry>(Extensions.ColorContribution); -const tokenGroupToScopesMap: { [setting: string]: string[] } = { +const tokenGroupToScopesMap = { comments: ['comment'], strings: ['string'], keywords: ['keyword - keyword.operator', 'keyword.control', 'storage', 'storage.type'], @@ -146,10 +146,11 @@ export class ColorThemeData implements IColorTheme { // Put the general customizations such as comments, strings, etc. first so that // they can be overridden by specific customizations like "string.interpolated" for (let tokenGroup in tokenGroupToScopesMap) { - let value = customTokenColors[tokenGroup]; + const group = <keyof typeof tokenGroupToScopesMap>tokenGroup; // TS doesn't type 'tokenGroup' properly + let value = customTokenColors[group]; if (value) { let settings = typeof value === 'string' ? { foreground: value } : value; - let scopes = tokenGroupToScopesMap[tokenGroup]; + let scopes = tokenGroupToScopesMap[group]; for (let scope of scopes) { this.customTokenColors.push({ scope, settings }); } @@ -186,7 +187,7 @@ export class ColorThemeData implements IColorTheme { } toStorageData() { - let colorMapData = {}; + let colorMapData: { [key: string]: string } = {}; for (let key in this.colorMap) { colorMapData[key] = Color.Format.CSS.formatHexA(this.colorMap[key], true); } @@ -251,7 +252,7 @@ export class ColorThemeData implements IColorTheme { break; case 'themeTokenColors': case 'id': case 'label': case 'settingsId': case 'extensionData': case 'watch': - theme[key] = data[key]; + (theme as any)[key] = data[key]; break; } } diff --git a/src/vs/workbench/services/themes/common/colorThemeStore.ts b/src/vs/workbench/services/themes/common/colorThemeStore.ts index f44d48c350..41049a335d 100644 --- a/src/vs/workbench/services/themes/common/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/common/colorThemeStore.ts @@ -53,17 +53,15 @@ export interface ColorThemeChangeEvent { export class ColorThemeStore { private extensionsColorThemes: ColorThemeData[]; - private readonly onDidChangeEmitter: Emitter<ColorThemeChangeEvent>; - public get onDidChange(): Event<ColorThemeChangeEvent> { return this.onDidChangeEmitter.event; } + private readonly onDidChangeEmitter = new Emitter<ColorThemeChangeEvent>(); + public readonly onDidChange: Event<ColorThemeChangeEvent> = this.onDidChangeEmitter.event; constructor(@IExtensionService private readonly extensionService: IExtensionService, defaultTheme: ColorThemeData) { this.extensionsColorThemes = [defaultTheme]; - this.onDidChangeEmitter = new Emitter<ColorThemeChangeEvent>(); this.initialize(); } - private initialize() { themesExtPoint.setHandler((extensions, delta) => { const previousIds: { [key: string]: boolean } = {}; diff --git a/src/vs/workbench/services/themes/common/plistParser.ts b/src/vs/workbench/services/themes/common/plistParser.ts index ab25827796..1961e2862e 100644 --- a/src/vs/workbench/services/themes/common/plistParser.ts +++ b/src/vs/workbench/services/themes/common/plistParser.ts @@ -143,7 +143,7 @@ function _parse(content: string, filename: string | null, locationKeyName: strin if (curKey === null) { return fail('missing <key>'); } - let newDict = {}; + let newDict: { [key: string]: any } = {}; if (locationKeyName !== null) { newDict[locationKeyName] = { filename: filename, @@ -168,7 +168,7 @@ function _parse(content: string, filename: string | null, locationKeyName: strin const arrState = { enterDict: function () { - let newDict = {}; + let newDict: { [key: string]: any } = {}; if (locationKeyName !== null) { newDict[locationKeyName] = { filename: filename, diff --git a/src/vs/workbench/services/themes/common/themeCompatibility.ts b/src/vs/workbench/services/themes/common/themeCompatibility.ts index 4dd11378a5..43e406a5af 100644 --- a/src/vs/workbench/services/themes/common/themeCompatibility.ts +++ b/src/vs/workbench/services/themes/common/themeCompatibility.ts @@ -26,7 +26,8 @@ export function convertSettings(oldSettings: ITokenColorizationRule[], resultRul if (!settings) { rule.settings = {}; } else { - for (let key in settings) { + for (const settingKey in settings) { + const key = <keyof typeof settings>settingKey; let mappings = settingToColorIdMapping[key]; if (mappings) { let colorHex = settings[key]; diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 9567a2144b..aeccfd9c22 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -69,6 +69,7 @@ export interface IColorCustomizations { } export interface ITokenColorCustomizations { + [groupIdOrThemeSettingsId: string]: string | ITokenColorizationSetting | ITokenColorCustomizations | undefined | ITokenColorizationRule[]; comments?: string | ITokenColorizationSetting; strings?: string | ITokenColorizationSetting; numbers?: string | ITokenColorizationSetting; @@ -103,5 +104,6 @@ export interface IThemeExtensionPoint { label?: string; description?: string; path: string; + uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME; _watch: boolean; // unsupported options to watch location } \ No newline at end of file diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index fd947df735..1f2dbf365d 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -12,7 +12,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IUpdateService } from 'vs/platform/update/common/update'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -23,14 +22,12 @@ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessi /* __GDPR__FRAGMENT__ "IMemoryInfo" : { "workingSetSize" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "peakWorkingSetSize": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "privateBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "sharedBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ export interface IMemoryInfo { readonly workingSetSize: number; - readonly peakWorkingSetSize: number; readonly privateBytes: number; readonly sharedBytes: number; } @@ -211,7 +208,7 @@ export interface IStartupMetrics { readonly ellapsedWorkspaceServiceInit: number; /** - * The time it took to load the main-bundle of the workbench, e.g `workbench.main.js`. + * The time it took to load the main-bundle of the workbench, e.g. `workbench.main.js`. * * * Happens in the renderer-process * * Measured with the `willLoadWorkbenchMain` and `didLoadWorkbenchMain` performance marks. @@ -355,7 +352,13 @@ class TimerService implements ITimerService { release = os.release(); arch = os.arch(); loadavg = os.loadavg(); - meminfo = process.getProcessMemoryInfo(); + + const processMemoryInfo = await process.getProcessMemoryInfo(); + meminfo = { + workingSetSize: processMemoryInfo.residentSet, + privateBytes: processMemoryInfo.private, + sharedBytes: processMemoryInfo.shared + }; isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); @@ -428,16 +431,19 @@ export function didUseCachedData(): boolean { if (!Boolean((<any>global).require.getConfig().nodeCachedData)) { return false; } - // whenever cached data is produced or rejected a onNodeCachedData-callback is invoked. That callback - // stores data in the `MonacoEnvironment.onNodeCachedData` global. See: - // https://github.com/Microsoft/vscode/blob/efe424dfe76a492eab032343e2fa4cfe639939f0/src/vs/workbench/electron-browser/bootstrap/index.js#L299 - if (isNonEmptyArray(MonacoEnvironment.onNodeCachedData)) { - return false; + // There are loader events that signal if cached data was missing, rejected, + // or used. The former two mean no cached data. + let cachedDataFound = 0; + for (const event of require.getStats()) { + switch (event.type) { + case LoaderEventType.CachedDataRejected: + return false; + case LoaderEventType.CachedDataFound: + cachedDataFound += 1; + break; + } } - return true; + return cachedDataFound > 0; } -declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; -declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; - //#endregion diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts index e57ae79388..cbfe97396e 100644 --- a/src/vs/workbench/services/untitled/common/untitledEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -72,6 +72,11 @@ export interface IUntitledEditorService { */ isDirty(resource: URI): boolean; + /** + * Find out if a backup with the provided resource exists and has a backup on disk. + */ + hasBackup(resource: URI): boolean; + /** * Reverts the untitled resources if found. */ @@ -119,16 +124,16 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor private mapResourceToAssociatedFilePath = new ResourceMap<boolean>(); private readonly _onDidChangeContent: Emitter<URI> = this._register(new Emitter<URI>()); - get onDidChangeContent(): Event<URI> { return this._onDidChangeContent.event; } + readonly onDidChangeContent: Event<URI> = this._onDidChangeContent.event; private readonly _onDidChangeDirty: Emitter<URI> = this._register(new Emitter<URI>()); - get onDidChangeDirty(): Event<URI> { return this._onDidChangeDirty.event; } + readonly onDidChangeDirty: Event<URI> = this._onDidChangeDirty.event; private readonly _onDidChangeEncoding: Emitter<URI> = this._register(new Emitter<URI>()); - get onDidChangeEncoding(): Event<URI> { return this._onDidChangeEncoding.event; } + readonly onDidChangeEncoding: Event<URI> = this._onDidChangeEncoding.event; private readonly _onDidDisposeModel: Emitter<URI> = this._register(new Emitter<URI>()); - get onDidDisposeModel(): Event<URI> { return this._onDidDisposeModel.event; } + readonly onDidDisposeModel: Event<URI> = this._onDidDisposeModel.event; constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -177,6 +182,12 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor return input ? input.isDirty() : false; } + hasBackup(resource: URI): boolean { + const input = this.get(resource); + + return input ? input.hasBackup() : false; + } + getDirty(resources?: URI[]): URI[] { let inputs: UntitledEditorInput[]; if (resources) { @@ -241,21 +252,10 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor const input = this.instantiationService.createInstance(UntitledEditorInput, untitledResource, hasAssociatedFilePath, mode, initialValue, encoding); - const contentListener = input.onDidModelChangeContent(() => { - this._onDidChangeContent.fire(untitledResource); - }); - - const dirtyListener = input.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(untitledResource); - }); - - const encodingListener = input.onDidModelChangeEncoding(() => { - this._onDidChangeEncoding.fire(untitledResource); - }); - - const disposeListener = input.onDispose(() => { - this._onDidDisposeModel.fire(untitledResource); - }); + const contentListener = input.onDidModelChangeContent(() => this._onDidChangeContent.fire(untitledResource)); + const dirtyListener = input.onDidChangeDirty(() => this._onDidChangeDirty.fire(untitledResource)); + const encodingListener = input.onDidModelChangeEncoding(() => this._onDidChangeEncoding.fire(untitledResource)); + const disposeListener = input.onDispose(() => this._onDidDisposeModel.fire(untitledResource)); // Remove from cache on dispose const onceDispose = Event.once(input.onDispose); diff --git a/src/vs/workbench/services/userData/common/fileUserDataProvider.ts b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts new file mode 100644 index 0000000000..dafc8adbe6 --- /dev/null +++ b/src/vs/workbench/services/userData/common/fileUserDataProvider.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileWriteOptions, FileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithOpenReadWriteCloseCapability, FileOpenOptions, hasReadWriteCapability, hasOpenReadWriteCloseCapability } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import * as resources from 'vs/base/common/resources'; +import { startsWith } from 'vs/base/common/strings'; +import { BACKUPS } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability { + + readonly capabilities: FileSystemProviderCapabilities = this.fileSystemProvider.capabilities; + readonly onDidChangeCapabilities: Event<void> = Event.None; + + private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>()); + readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event; + + private readonly userDataHome: URI; + + constructor( + private readonly fileSystemUserDataHome: URI, + private readonly fileSystemBackupsHome: URI, + private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, + environmentService: IWorkbenchEnvironmentService + ) { + super(); + + this.userDataHome = environmentService.userRoamingDataHome; + + // Assumption: This path always exists + this._register(this.fileSystemProvider.watch(this.fileSystemUserDataHome, { recursive: false, excludes: [] })); + this._register(this.fileSystemProvider.onDidChangeFile(e => this.handleFileChanges(e))); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + return this.fileSystemProvider.watch(this.toFileSystemResource(resource), opts); + } + + stat(resource: URI): Promise<IStat> { + return this.fileSystemProvider.stat(this.toFileSystemResource(resource)); + } + + mkdir(resource: URI): Promise<void> { + return this.fileSystemProvider.mkdir(this.toFileSystemResource(resource)); + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { + return this.fileSystemProvider.rename(this.toFileSystemResource(from), this.toFileSystemResource(to), opts); + } + + readFile(resource: URI): Promise<Uint8Array> { + if (hasReadWriteCapability(this.fileSystemProvider)) { + return this.fileSystemProvider.readFile(this.toFileSystemResource(resource)); + } + throw new Error('not supported'); + } + + readdir(resource: URI): Promise<[string, FileType][]> { + return this.fileSystemProvider.readdir(this.toFileSystemResource(resource)); + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> { + if (hasReadWriteCapability(this.fileSystemProvider)) { + return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts); + } + throw new Error('not supported'); + } + + open(resource: URI, opts: FileOpenOptions): Promise<number> { + if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) { + return this.fileSystemProvider.open(this.toFileSystemResource(resource), opts); + } + throw new Error('not supported'); + } + + close(fd: number): Promise<void> { + if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) { + return this.fileSystemProvider.close(fd); + } + throw new Error('not supported'); + } + + read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { + if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) { + return this.fileSystemProvider.read(fd, pos, data, offset, length); + } + throw new Error('not supported'); + } + + write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { + if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) { + return this.fileSystemProvider.write(fd, pos, data, offset, length); + } + throw new Error('not supported'); + } + + delete(resource: URI, opts: FileDeleteOptions): Promise<void> { + return this.fileSystemProvider.delete(this.toFileSystemResource(resource), opts); + } + + private handleFileChanges(changes: IFileChange[]): void { + const userDataChanges: IFileChange[] = []; + for (const change of changes) { + const userDataResource = this.toUserDataResource(change.resource); + if (userDataResource) { + userDataChanges.push({ + resource: userDataResource, + type: change.type + }); + } + } + if (userDataChanges.length) { + this._onDidChangeFile.fire(userDataChanges); + } + } + + private toFileSystemResource(userDataResource: URI): URI { + const relativePath = resources.relativePath(this.userDataHome, userDataResource)!; + if (startsWith(relativePath, BACKUPS)) { + return resources.joinPath(resources.dirname(this.fileSystemBackupsHome), relativePath); + } + return resources.joinPath(this.fileSystemUserDataHome, relativePath); + } + + private toUserDataResource(fileSystemResource: URI): URI | null { + if (resources.isEqualOrParent(fileSystemResource, this.fileSystemUserDataHome)) { + const relativePath = resources.relativePath(this.fileSystemUserDataHome, fileSystemResource); + return relativePath ? resources.joinPath(this.userDataHome, relativePath) : this.userDataHome; + } + if (resources.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) { + const relativePath = resources.relativePath(this.fileSystemBackupsHome, fileSystemResource); + return relativePath ? resources.joinPath(this.userDataHome, BACKUPS, relativePath) : resources.joinPath(this.userDataHome, BACKUPS); + } + return null; + } + +} \ No newline at end of file diff --git a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts new file mode 100644 index 0000000000..7704241748 --- /dev/null +++ b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts @@ -0,0 +1,232 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import * as resources from 'vs/base/common/resources'; +import { FileChangeType, IFileSystemProvider, FileType, IWatchOptions, IStat, FileSystemProviderErrorCode, FileSystemProviderError, FileWriteOptions, IFileChange, FileDeleteOptions, FileSystemProviderCapabilities, FileOverwriteOptions } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; + +class File implements IStat { + + type: FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + data?: Uint8Array; + + constructor(name: string) { + this.type = FileType.File; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + } +} + +class Directory implements IStat { + + type: FileType; + ctime: number; + mtime: number; + size: number; + + name: string; + entries: Map<string, File | Directory>; + + constructor(name: string) { + this.type = FileType.Directory; + this.ctime = Date.now(); + this.mtime = Date.now(); + this.size = 0; + this.name = name; + this.entries = new Map(); + } +} + +export type Entry = File | Directory; + +export class InMemoryUserDataProvider extends Disposable implements IFileSystemProvider { + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities: Event<void> = Event.None; + + root = new Directory(''); + + // --- manage file metadata + + async stat(resource: URI): Promise<IStat> { + return this._lookup(resource, false); + } + + async readdir(resource: URI): Promise<[string, FileType][]> { + const entry = this._lookupAsDirectory(resource, false); + let result: [string, FileType][] = []; + for (const [name, child] of entry.entries) { + result.push([name, child.type]); + } + return result; + } + + // --- manage file contents + + async readFile(resource: URI): Promise<Uint8Array> { + const data = this._lookupAsFile(resource, false).data; + if (data) { + return data; + } + throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); + } + + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> { + let basename = resources.basename(resource); + let parent = this._lookupParentDirectory(resource); + let entry = parent.entries.get(basename); + if (entry instanceof Directory) { + throw new FileSystemProviderError('file is directory', FileSystemProviderErrorCode.FileIsADirectory); + } + if (!entry && !opts.create) { + throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); + } + if (entry && opts.create && !opts.overwrite) { + throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists); + } + if (!entry) { + entry = new File(basename); + parent.entries.set(basename, entry); + this._fireSoon({ type: FileChangeType.ADDED, resource }); + } + entry.mtime = Date.now(); + entry.size = content.byteLength; + entry.data = content; + + this._fireSoon({ type: FileChangeType.UPDATED, resource }); + } + + // --- manage files/folders + + async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { + if (!opts.overwrite && this._lookup(to, true)) { + throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists); + } + + let entry = this._lookup(from, false); + let oldParent = this._lookupParentDirectory(from); + + let newParent = this._lookupParentDirectory(to); + let newName = resources.basename(to); + + oldParent.entries.delete(entry.name); + entry.name = newName; + newParent.entries.set(newName, entry); + + this._fireSoon( + { type: FileChangeType.DELETED, resource: from }, + { type: FileChangeType.ADDED, resource: to } + ); + } + + async delete(resource: URI, opts: FileDeleteOptions): Promise<void> { + let dirname = resources.dirname(resource); + let basename = resources.basename(resource); + let parent = this._lookupAsDirectory(dirname, false); + if (!parent.entries.has(basename)) { + throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); + } + parent.entries.delete(basename); + parent.mtime = Date.now(); + parent.size -= 1; + this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED }); + } + + async mkdir(resource: URI): Promise<void> { + let basename = resources.basename(resource); + let dirname = resources.dirname(resource); + let parent = this._lookupAsDirectory(dirname, false); + + let entry = new Directory(basename); + parent.entries.set(entry.name, entry); + parent.mtime = Date.now(); + parent.size += 1; + this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { type: FileChangeType.ADDED, resource }); + } + + // --- lookup + + private _lookup(uri: URI, silent: false): Entry; + private _lookup(uri: URI, silent: boolean): Entry | undefined; + private _lookup(uri: URI, silent: boolean): Entry | undefined { + let parts = uri.path.split('/'); + let entry: Entry = this.root; + for (const part of parts) { + if (!part) { + continue; + } + let child: Entry | undefined; + if (entry instanceof Directory) { + child = entry.entries.get(part); + } + if (!child) { + if (!silent) { + throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); + } else { + return undefined; + } + } + entry = child; + } + return entry; + } + + private _lookupAsDirectory(uri: URI, silent: boolean): Directory { + let entry = this._lookup(uri, silent); + if (entry instanceof Directory) { + return entry; + } + throw new FileSystemProviderError('file not a directory', FileSystemProviderErrorCode.FileNotADirectory); + } + + private _lookupAsFile(uri: URI, silent: boolean): File { + let entry = this._lookup(uri, silent); + if (entry instanceof File) { + return entry; + } + throw new FileSystemProviderError('file is a directory', FileSystemProviderErrorCode.FileIsADirectory); + } + + private _lookupParentDirectory(uri: URI): Directory { + const dirname = resources.dirname(uri); + return this._lookupAsDirectory(dirname, false); + } + + // --- manage file events + + private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>()); + readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event; + + private _bufferedChanges: IFileChange[] = []; + private _fireSoonHandle?: NodeJS.Timer; + + + watch(resource: URI, opts: IWatchOptions): IDisposable { + // ignore, fires for all changes... + return Disposable.None; + } + + private _fireSoon(...changes: IFileChange[]): void { + this._bufferedChanges.push(...changes); + + if (this._fireSoonHandle) { + clearTimeout(this._fireSoonHandle); + } + + this._fireSoonHandle = setTimeout(() => { + this._onDidChangeFile.fire(this._bufferedChanges); + this._bufferedChanges.length = 0; + }, 5); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts new file mode 100644 index 0000000000..305077d071 --- /dev/null +++ b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts @@ -0,0 +1,478 @@ +/*--------------------------------------------------------------------------------------------- + * 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 os from 'os'; +import * as path from 'vs/base/common/path'; +import * as uuid from 'vs/base/common/uuid'; +import * as pfs from 'vs/base/node/pfs'; +import { IFileService, FileChangeType, IFileChange, IFileSystemProviderWithFileReadWriteCapability, IStat, FileType, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; +import { joinPath, dirname } from 'vs/base/common/resources'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; +import { BACKUPS } from 'vs/platform/environment/common/environment'; +import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; +import { Emitter, Event } from 'vs/base/common/event'; +import { timeout } from 'vs/base/common/async'; + +suite('FileUserDataProvider', () => { + + let testObject: IFileService; + let rootPath: string; + let userDataPath: string; + let backupsPath: string; + let userDataResource: URI; + const disposables = new DisposableStore(); + + setup(async () => { + const logService = new NullLogService(); + testObject = new FileService(logService); + disposables.add(testObject); + + const diskFileSystemProvider = new DiskFileSystemProvider(logService); + disposables.add(diskFileSystemProvider); + disposables.add(testObject.registerProvider(Schemas.file, diskFileSystemProvider)); + + rootPath = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid()); + userDataPath = path.join(rootPath, 'user'); + backupsPath = path.join(rootPath, BACKUPS); + userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData }); + await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]); + + const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: 'workspaceId' }); + environmentService.userRoamingDataHome = userDataResource; + + const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService); + disposables.add(userDataFileSystemProvider); + disposables.add(testObject.registerProvider(Schemas.userData, userDataFileSystemProvider)); + }); + + teardown(async () => { + disposables.clear(); + await pfs.rimraf(rootPath, pfs.RimRafMode.MOVE); + }); + + test('exists return false when file does not exist', async () => { + const exists = await testObject.exists(joinPath(userDataResource, 'settings.json')); + assert.equal(exists, false); + }); + + test('read file throws error if not exist', async () => { + try { + await testObject.readFile(joinPath(userDataResource, 'settings.json')); + assert.fail('Should fail since file does not exist'); + } catch (e) { } + }); + + test('read existing file', async () => { + await pfs.writeFile(path.join(userDataPath, 'settings.json'), '{}'); + const result = await testObject.readFile(joinPath(userDataResource, 'settings.json')); + assert.equal(result.value, '{}'); + }); + + test('create file', async () => { + const resource = joinPath(userDataResource, 'settings.json'); + const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json')); + assert.equal(actual2, '{}'); + }); + + test('write file creates the file if not exist', async () => { + const resource = joinPath(userDataResource, 'settings.json'); + const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json')); + assert.equal(actual2, '{}'); + }); + + test('write to existing file', async () => { + const resource = joinPath(userDataResource, 'settings.json'); + await pfs.writeFile(path.join(userDataPath, 'settings.json'), '{}'); + const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json')); + assert.equal(actual2, '{a:1}'); + }); + + test('delete file', async () => { + await pfs.writeFile(path.join(userDataPath, 'settings.json'), ''); + await testObject.del(joinPath(userDataResource, 'settings.json')); + const result = await pfs.exists(path.join(userDataPath, 'settings.json')); + assert.equal(false, result); + }); + + test('resolve file', async () => { + await pfs.writeFile(path.join(userDataPath, 'settings.json'), ''); + const result = await testObject.resolve(joinPath(userDataResource, 'settings.json')); + assert.ok(!result.isDirectory); + assert.ok(result.children === undefined); + }); + + test('exists return false for folder that does not exist', async () => { + const exists = await testObject.exists(joinPath(userDataResource, 'snippets')); + assert.equal(exists, false); + }); + + test('exists return true for folder that exists', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + const exists = await testObject.exists(joinPath(userDataResource, 'snippets')); + assert.equal(exists, true); + }); + + test('read file throws error for folder', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + try { + await testObject.readFile(joinPath(userDataResource, 'snippets')); + assert.fail('Should fail since read file is not supported for folders'); + } catch (e) { } + }); + + test('read file under folder', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); + const resource = joinPath(userDataResource, 'snippets/settings.json'); + const actual = await testObject.readFile(resource); + assert.equal(actual.resource.toString(), resource.toString()); + assert.equal(actual.value, '{}'); + }); + + test('read file under sub folder', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets', 'java')); + await pfs.writeFile(path.join(userDataPath, 'snippets', 'java', 'settings.json'), '{}'); + const resource = joinPath(userDataResource, 'snippets/java/settings.json'); + const actual = await testObject.readFile(resource); + assert.equal(actual.resource.toString(), resource.toString()); + assert.equal(actual.value, '{}'); + }); + + test('create file under folder that exists', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + const resource = joinPath(userDataResource, 'snippets/settings.json'); + const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual2 = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); + assert.equal(actual2, '{}'); + }); + + test('create file under folder that does not exist', async () => { + const resource = joinPath(userDataResource, 'snippets/settings.json'); + const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual2 = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); + assert.equal(actual2, '{}'); + }); + + test('write to not existing file under container that exists', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + const resource = joinPath(userDataResource, 'snippets/settings.json'); + const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); + assert.equal(actual, '{}'); + }); + + test('write to not existing file under container that does not exists', async () => { + const resource = joinPath(userDataResource, 'snippets/settings.json'); + const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); + assert.equal(actual, '{}'); + }); + + test('write to existing file under container', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); + const resource = joinPath(userDataResource, 'snippets/settings.json'); + const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json')); + assert.equal(actual.toString(), '{a:1}'); + }); + + test('write file under sub container', async () => { + const resource = joinPath(userDataResource, 'snippets/java/settings.json'); + const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); + assert.equal(actual1.resource.toString(), resource.toString()); + const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'java', 'settings.json')); + assert.equal(actual, '{}'); + }); + + test('delete throws error for folder that does not exist', async () => { + try { + await testObject.del(joinPath(userDataResource, 'snippets')); + assert.fail('Should fail the folder does not exist'); + } catch (e) { } + }); + + test('delete not existing file under container that exists', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + try { + await testObject.del(joinPath(userDataResource, 'snippets/settings.json')); + assert.fail('Should fail since file does not exist'); + } catch (e) { } + }); + + test('delete not existing file under container that does not exists', async () => { + try { + await testObject.del(joinPath(userDataResource, 'snippets/settings.json')); + assert.fail('Should fail since file does not exist'); + } catch (e) { } + }); + + test('delete existing file under folder', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); + await testObject.del(joinPath(userDataResource, 'snippets/settings.json')); + const exists = await pfs.exists(path.join(userDataPath, 'snippets', 'settings.json')); + assert.equal(exists, false); + }); + + test('resolve folder', async () => { + await pfs.mkdirp(path.join(userDataPath, 'snippets')); + await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}'); + const result = await testObject.resolve(joinPath(userDataResource, 'snippets')); + assert.ok(result.isDirectory); + assert.ok(result.children !== undefined); + assert.equal(result.children!.length, 1); + assert.equal(result.children![0].resource.toString(), joinPath(userDataResource, 'snippets/settings.json').toString()); + }); + + test('read backup file', async () => { + await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}'); + const result = await testObject.readFile(joinPath(userDataResource, `${BACKUPS}/backup.json`)); + assert.equal(result.value, '{}'); + }); + + test('create backup file', async () => { + await testObject.createFile(joinPath(userDataResource, `${BACKUPS}/backup.json`), VSBuffer.fromString('{}')); + const result = await pfs.readFile(path.join(backupsPath, 'backup.json')); + assert.equal(result, '{}'); + }); + + test('write backup file', async () => { + await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}'); + await testObject.writeFile(joinPath(userDataResource, `${BACKUPS}/backup.json`), VSBuffer.fromString('{a:1}')); + const result = await pfs.readFile(path.join(backupsPath, 'backup.json')); + assert.equal(result, '{a:1}'); + }); + + test('resolve backups folder', async () => { + await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}'); + const result = await testObject.resolve(joinPath(userDataResource, BACKUPS)); + assert.ok(result.isDirectory); + assert.ok(result.children !== undefined); + assert.equal(result.children!.length, 1); + assert.equal(result.children![0].resource.toString(), joinPath(userDataResource, `${BACKUPS}/backup.json`).toString()); + }); +}); + +class TestFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability { + + constructor(readonly onDidChangeFile: Event<IFileChange[]>) { } + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + + readonly onDidChangeCapabilities: Event<void> = Event.None; + + watch(): IDisposable { return Disposable.None; } + + stat(): Promise<IStat> { throw new Error('Not Supported'); } + + mkdir(resource: URI): Promise<void> { throw new Error('Not Supported'); } + + rename(): Promise<void> { throw new Error('Not Supported'); } + + readFile(resource: URI): Promise<Uint8Array> { throw new Error('Not Supported'); } + + readdir(resource: URI): Promise<[string, FileType][]> { throw new Error('Not Supported'); } + + writeFile(): Promise<void> { throw new Error('Not Supported'); } + + delete(): Promise<void> { throw new Error('Not Supported'); } + +} + +suite('FileUserDataProvider - Watching', () => { + + let testObject: IFileService; + let localBackupsResource: URI; + let localUserDataResource: URI; + let userDataResource: URI; + const disposables = new DisposableStore(); + + const fileEventEmitter: Emitter<IFileChange[]> = new Emitter<IFileChange[]>(); + disposables.add(fileEventEmitter); + + setup(() => { + + const rootPath = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid()); + const userDataPath = path.join(rootPath, 'user'); + const backupsPath = path.join(rootPath, BACKUPS); + localBackupsResource = URI.file(backupsPath); + localUserDataResource = URI.file(userDataPath); + userDataResource = localUserDataResource.with({ scheme: Schemas.userData }); + + const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: 'workspaceId' }); + environmentService.userRoamingDataHome = userDataResource; + + const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService); + disposables.add(userDataFileSystemProvider); + + testObject = new FileService(new NullLogService()); + disposables.add(testObject); + disposables.add(testObject.registerProvider(Schemas.userData, userDataFileSystemProvider)); + }); + + teardown(() => { + disposables.clear(); + }); + + test('file added change event', done => { + const expected = joinPath(userDataResource, 'settings.json'); + const target = joinPath(localUserDataResource, 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.ADDED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.ADDED + }]); + }); + + test('file updated change event', done => { + const expected = joinPath(userDataResource, 'settings.json'); + const target = joinPath(localUserDataResource, 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.UPDATED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.UPDATED + }]); + }); + + test('file deleted change event', done => { + const expected = joinPath(userDataResource, 'settings.json'); + const target = joinPath(localUserDataResource, 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.DELETED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.DELETED + }]); + }); + + test('file under folder created change event', done => { + const expected = joinPath(userDataResource, 'snippets', 'settings.json'); + const target = joinPath(localUserDataResource, 'snippets', 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.ADDED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.ADDED + }]); + }); + + test('file under folder updated change event', done => { + const expected = joinPath(userDataResource, 'snippets', 'settings.json'); + const target = joinPath(localUserDataResource, 'snippets', 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.UPDATED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.UPDATED + }]); + }); + + test('file under folder deleted change event', done => { + const expected = joinPath(userDataResource, 'snippets', 'settings.json'); + const target = joinPath(localUserDataResource, 'snippets', 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.DELETED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.DELETED + }]); + }); + + test('event is not triggered if file is not under user data', async () => { + const target = joinPath(dirname(localUserDataResource), 'settings.json'); + let triggered = false; + testObject.onFileChanges(() => triggered = true); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.DELETED + }]); + await timeout(0); + if (triggered) { + assert.fail('event should not be triggered'); + } + }); + + test('backup file created change event', done => { + const expected = joinPath(userDataResource, BACKUPS, 'settings.json'); + const target = joinPath(localBackupsResource, 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.ADDED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.ADDED + }]); + }); + + test('backup file update change event', done => { + const expected = joinPath(userDataResource, BACKUPS, 'settings.json'); + const target = joinPath(localBackupsResource, 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.UPDATED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.UPDATED + }]); + }); + + test('backup file delete change event', done => { + const expected = joinPath(userDataResource, BACKUPS, 'settings.json'); + const target = joinPath(localBackupsResource, 'settings.json'); + testObject.onFileChanges(e => { + if (e.contains(expected, FileChangeType.DELETED)) { + done(); + } + }); + fileEventEmitter.fire([{ + resource: target, + type: FileChangeType.DELETED + }]); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index 39624a7d04..2684916550 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -7,17 +7,18 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IProgressIndicator } from 'vs/platform/progress/common/progress'; export const IViewletService = createDecorator<IViewletService>('viewletService'); export interface IViewletService { + _serviceBrand: ServiceIdentifier<any>; - onDidViewletRegister: Event<ViewletDescriptor>; - onDidViewletDeregister: Event<ViewletDescriptor>; - onDidViewletOpen: Event<IViewlet>; - onDidViewletClose: Event<IViewlet>; + readonly onDidViewletRegister: Event<ViewletDescriptor>; + readonly onDidViewletDeregister: Event<ViewletDescriptor>; + readonly onDidViewletOpen: Event<IViewlet>; + readonly onDidViewletClose: Event<IViewlet>; /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. @@ -47,7 +48,7 @@ export interface IViewletService { /** * Returns the progress indicator for the side bar. */ - getProgressIndicator(id: string): IProgressService | null; + getProgressIndicator(id: string): IProgressIndicator | null; /** * Hide the active viewlet. diff --git a/src/vs/workbench/services/window/electron-browser/windowService.ts b/src/vs/workbench/services/window/electron-browser/windowService.ts index 8cc4fc0d9d..1f05a6c6f7 100644 --- a/src/vs/workbench/services/window/electron-browser/windowService.ts +++ b/src/vs/workbench/services/window/electron-browser/windowService.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions, IOpenSettings, IURIToOpen, isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; -import { IRecentlyOpened } from 'vs/platform/history/common/history'; +import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { URI } from 'vs/base/common/uri'; @@ -109,7 +109,7 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.closeWindow(this.windowId); } - toggleFullScreen(): Promise<void> { + toggleFullScreen(target?: HTMLElement): Promise<void> { return this.windowsService.toggleFullScreen(this.windowId); } @@ -121,6 +121,14 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.getRecentlyOpened(this.windowId); } + addRecentlyOpened(recents: IRecent[]): Promise<void> { + return this.windowsService.addRecentlyOpened(recents); + } + + removeFromRecentlyOpened(paths: URI[]): Promise<void> { + return this.windowsService.removeFromRecentlyOpened(paths); + } + focusWindow(): Promise<void> { return this.windowsService.focusWindow(this.windowId); } diff --git a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts index 5d1f94806c..d6019f344e 100644 --- a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts @@ -16,8 +16,8 @@ import { StorageService } from 'vs/platform/storage/node/storageService'; import { ConfigurationScope, IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { IBackupFileService, toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform'; @@ -32,10 +32,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export class WorkspaceEditingService implements IWorkspaceEditingService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier<IWorkspaceEditingService>; constructor( @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @@ -57,84 +58,93 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @ILifecycleService readonly lifecycleService: ILifecycleService, @ILabelService readonly labelService: ILabelService ) { + this.registerListeners(); + } - lifecycleService.onBeforeShutdown(async e => { + private registerListeners(): void { + this.lifecycleService.onBeforeShutdown(async e => { const saveOperation = this.saveUntitedBeforeShutdown(e.reason); if (saveOperation) { e.veto(saveOperation); } }); - } - private saveUntitedBeforeShutdown(reason: ShutdownReason): Promise<boolean> | undefined { + private async saveUntitedBeforeShutdown(reason: ShutdownReason): Promise<boolean> { if (reason !== ShutdownReason.LOAD && reason !== ShutdownReason.CLOSE) { - return undefined; // only interested when window is closing or loading + return false; // only interested when window is closing or loading } const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); if (!workspaceIdentifier || !isEqualOrParent(workspaceIdentifier.configPath, this.environmentService.untitledWorkspacesHome)) { - return undefined; // only care about untitled workspaces to ask for saving + return false; // only care about untitled workspaces to ask for saving } - return this.windowsService.getWindowCount().then(windowCount => { - if (reason === ShutdownReason.CLOSE && !isMacintosh && windowCount === 1) { - return false; // Windows/Linux: quits when last window is closed, so do not ask then - } - enum ConfirmResult { - SAVE, - DONT_SAVE, - CANCEL - } + const windowCount = await this.windowsService.getWindowCount(); - const save = { label: mnemonicButtonLabel(nls.localize('save', "Save")), result: ConfirmResult.SAVE }; - const dontSave = { label: mnemonicButtonLabel(nls.localize('doNotSave', "Don't Save")), result: ConfirmResult.DONT_SAVE }; - const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; + if (reason === ShutdownReason.CLOSE && !isMacintosh && windowCount === 1) { + return false; // Windows/Linux: quits when last window is closed, so do not ask then + } - const buttons: { label: string; result: ConfirmResult; }[] = []; - if (isWindows) { - buttons.push(save, dontSave, cancel); - } else if (isLinux) { - buttons.push(dontSave, cancel, save); - } else { - buttons.push(save, cancel, dontSave); - } + enum ConfirmResult { + SAVE, + DONT_SAVE, + CANCEL + } - const message = nls.localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"); - const detail = nls.localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."); - const cancelId = buttons.indexOf(cancel); + const save = { label: mnemonicButtonLabel(nls.localize('save', "Save")), result: ConfirmResult.SAVE }; + const dontSave = { label: mnemonicButtonLabel(nls.localize('doNotSave', "Don't Save")), result: ConfirmResult.DONT_SAVE }; + const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; - return this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }).then(res => { - switch (buttons[res].result) { + const buttons: { label: string; result: ConfirmResult; }[] = []; + if (isWindows) { + buttons.push(save, dontSave, cancel); + } else if (isLinux) { + buttons.push(dontSave, cancel, save); + } else { + buttons.push(save, cancel, dontSave); + } - // Cancel: veto unload - case ConfirmResult.CANCEL: - return true; + const message = nls.localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"); + const detail = nls.localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."); + const cancelId = buttons.indexOf(cancel); - // Don't Save: delete workspace - case ConfirmResult.DONT_SAVE: - this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); - return false; + const res = await this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }); - // Save: save workspace, but do not veto unload - case ConfirmResult.SAVE: { - return this.pickNewWorkspacePath().then(newWorkspacePath => { - if (newWorkspacePath) { - return this.saveWorkspaceAs(workspaceIdentifier, newWorkspacePath).then(_ => { - return this.workspaceService.getWorkspaceIdentifier(newWorkspacePath).then(newWorkspaceIdentifier => { - const label = this.labelService.getWorkspaceLabel(newWorkspaceIdentifier, { verbose: true }); - this.windowsService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]); - this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); - return false; - }); - }, () => false); - } - return true; // keep veto if no target was provided - }); - } + switch (buttons[res].result) { + + // Cancel: veto unload + case ConfirmResult.CANCEL: + return true; + + // Don't Save: delete workspace + case ConfirmResult.DONT_SAVE: + this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); + return false; + + // Save: save workspace, but do not veto unload if path provided + case ConfirmResult.SAVE: { + const newWorkspacePath = await this.pickNewWorkspacePath(); + if (!newWorkspacePath) { + return true; // keep veto if no target was provided } - }); - }); + + try { + await this.saveWorkspaceAs(workspaceIdentifier, newWorkspacePath); + + const newWorkspaceIdentifier = await this.workspaceService.getWorkspaceIdentifier(newWorkspacePath); + + const label = this.labelService.getWorkspaceLabel(newWorkspaceIdentifier, { verbose: true }); + this.windowService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]); + + this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); + } catch (error) { + // ignore + } + + return false; + } + } } pickNewWorkspacePath(): Promise<URI | undefined> { @@ -209,6 +219,13 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { private async doAddFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number, donotNotifyError: boolean = false): Promise<void> { const state = this.contextService.getWorkbenchState(); + if (this.environmentService.configuration.remoteAuthority) { + // Do not allow workspace folders with scheme different than the current remote scheme + const schemas = this.contextService.getWorkspace().folders.map(f => f.uri.scheme); + if (schemas.length && foldersToAdd.some(f => schemas.indexOf(f.uri.scheme) === -1)) { + return Promise.reject(new Error(nls.localize('differentSchemeRoots', "Workspace folders from different providers are not allowed in the same workspace."))); + } + } // If we are in no-workspace or single-folder workspace, adding folders has to // enter a workspace. @@ -218,7 +235,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { newWorkspaceFolders = distinct(newWorkspaceFolders, folder => getComparisonKey(folder.uri)); if (state === WorkbenchState.EMPTY && newWorkspaceFolders.length === 0 || state === WorkbenchState.FOLDER && newWorkspaceFolders.length === 1) { - return Promise.resolve(); // return if the operation is a no-op for the current state + return; // return if the operation is a no-op for the current state } return this.createAndEnterWorkspace(newWorkspaceFolders); @@ -267,7 +284,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { async createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise<void> { if (path && !await this.isValidTargetWorkspacePath(path)) { - return Promise.reject(null); + return; } const remoteAuthority = this.environmentService.configuration.remoteAuthority; const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders, remoteAuthority); @@ -281,11 +298,11 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { async saveAndEnterWorkspace(path: URI): Promise<void> { if (!await this.isValidTargetWorkspacePath(path)) { - return Promise.reject(null); + return; } const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); if (!workspaceIdentifier) { - return Promise.reject(null); + return; } await this.saveWorkspaceAs(workspaceIdentifier, path); @@ -318,7 +335,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { // Return early if target is same as source if (isEqual(configPathURI, targetConfigPathURI)) { - return Promise.resolve(null); + return; } // Read the contents of the workspace file, update it to new location and save it. @@ -327,18 +344,17 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { await this.textFileService.create(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); } - private handleWorkspaceConfigurationEditingError(error: JSONEditingError): Promise<void> { + private handleWorkspaceConfigurationEditingError(error: JSONEditingError): void { switch (error.code) { case JSONEditingErrorCode.ERROR_INVALID_FILE: this.onInvalidWorkspaceConfigurationFileError(); - return Promise.resolve(); + break; case JSONEditingErrorCode.ERROR_FILE_DIRTY: this.onWorkspaceConfigurationFileDirtyError(); - return Promise.resolve(); + break; + default: + this.notificationService.error(error.message); } - this.notificationService.error(error.message); - - return Promise.resolve(); } private onInvalidWorkspaceConfigurationFileError(): void { @@ -362,7 +378,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { async enterWorkspace(path: URI): Promise<void> { if (!!this.environmentService.extensionTestsLocationURI) { - return Promise.reject(new Error('Entering a new workspace is not possible in tests.')); + throw new Error('Entering a new workspace is not possible in tests.'); } const workspace = await this.workspaceService.getWorkspaceIdentifier(path); @@ -382,7 +398,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { await this.migrateStorage(result.workspace); // Reinitialize backup service if (this.backupFileService instanceof BackupFileService) { - this.backupFileService.initialize(result.backupPath!); + this.backupFileService.initialize(toBackupWorkspaceResource(result.backupPath!, this.environmentService)); } } @@ -409,7 +425,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { private doCopyWorkspaceSettings(toWorkspace: IWorkspaceIdentifier, filter?: (config: IConfigurationPropertySchema) => boolean): Promise<void> { const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties(); - const targetWorkspaceConfiguration = {}; + const targetWorkspaceConfiguration: any = {}; for (const key of this.configurationService.keys().workspace) { if (configurationProperties[key]) { if (filter && !filter(configurationProperties[key])) { diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index c667845604..ae67eb8b4c 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -127,7 +127,7 @@ suite('Workbench base editor', () => { let oldEditorsCnt = EditorRegistry.getEditors().length; let oldInputCnt = (<any>EditorRegistry).getEditorInputs().length; - EditorRegistry.registerEditor(d1, new SyncDescriptor(MyInput)); + EditorRegistry.registerEditor(d1, [new SyncDescriptor(MyInput)]); EditorRegistry.registerEditor(d2, [new SyncDescriptor(MyInput), new SyncDescriptor(MyOtherInput)]); assert.equal(EditorRegistry.getEditors().length, oldEditorsCnt + 2); @@ -148,8 +148,8 @@ suite('Workbench base editor', () => { let oldEditors = EditorRegistry.getEditors(); (<any>EditorRegistry).setEditors([]); - EditorRegistry.registerEditor(d2, new SyncDescriptor(ResourceEditorInput)); - EditorRegistry.registerEditor(d1, new SyncDescriptor(MyResourceInput)); + EditorRegistry.registerEditor(d2, [new SyncDescriptor(ResourceEditorInput)]); + EditorRegistry.registerEditor(d1, [new SyncDescriptor(MyResourceInput)]); let inst = new TestInstantiationService(); @@ -168,7 +168,7 @@ suite('Workbench base editor', () => { let oldEditors = EditorRegistry.getEditors(); (<any>EditorRegistry).setEditors([]); - EditorRegistry.registerEditor(d1, new SyncDescriptor(ResourceEditorInput)); + EditorRegistry.registerEditor(d1, [new SyncDescriptor(ResourceEditorInput)]); let inst = new TestInstantiationService(); diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index 95f31fe0b0..01b138d94e 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { NotificationsModel, NotificationViewItem, INotificationChangeEvent, NotificationChangeType, NotificationViewItemLabelKind } from 'vs/workbench/common/notifications'; +import { NotificationsModel, NotificationViewItem, INotificationChangeEvent, NotificationChangeType, NotificationViewItemLabelKind, IStatusMessageChangeEvent, StatusMessageChangeType } from 'vs/workbench/common/notifications'; import { Action } from 'vs/base/common/actions'; import { INotification, Severity } from 'vs/platform/notification/common/notification'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; @@ -132,9 +132,14 @@ suite('Notifications', () => { test('Model', () => { const model = new NotificationsModel(); - let lastEvent!: INotificationChangeEvent; + let lastNotificationEvent!: INotificationChangeEvent; model.onDidNotificationChange(e => { - lastEvent = e; + lastNotificationEvent = e; + }); + + let lastStatusMessageEvent!: IStatusMessageChangeEvent; + model.onDidStatusMessageChange(e => { + lastStatusMessageEvent = e; }); let item1: INotification = { severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } }; @@ -142,23 +147,23 @@ suite('Notifications', () => { let item2Duplicate: INotification = { severity: Severity.Warning, message: 'Warning Message', source: 'Some Source' }; let item3: INotification = { severity: Severity.Info, message: 'Info Message' }; - let item1Handle = model.notify(item1); - assert.equal(lastEvent.item.severity, item1.severity); - assert.equal(lastEvent.item.message.value, item1.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + let item1Handle = model.addNotification(item1); + assert.equal(lastNotificationEvent.item.severity, item1.severity); + assert.equal(lastNotificationEvent.item.message.value, item1.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); - let item2Handle = model.notify(item2); - assert.equal(lastEvent.item.severity, item2.severity); - assert.equal(lastEvent.item.message.value, item2.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + let item2Handle = model.addNotification(item2); + assert.equal(lastNotificationEvent.item.severity, item2.severity); + assert.equal(lastNotificationEvent.item.message.value, item2.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); - model.notify(item3); - assert.equal(lastEvent.item.severity, item3.severity); - assert.equal(lastEvent.item.message.value, item3.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + model.addNotification(item3); + assert.equal(lastNotificationEvent.item.severity, item3.severity); + assert.equal(lastNotificationEvent.item.message.value, item3.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); assert.equal(model.notifications.length, 3); @@ -170,29 +175,48 @@ suite('Notifications', () => { item1Handle.close(); assert.equal(called, 1); assert.equal(model.notifications.length, 2); - assert.equal(lastEvent.item.severity, item1.severity); - assert.equal(lastEvent.item.message.value, item1.message); - assert.equal(lastEvent.index, 2); - assert.equal(lastEvent.kind, NotificationChangeType.REMOVE); + assert.equal(lastNotificationEvent.item.severity, item1.severity); + assert.equal(lastNotificationEvent.item.message.value, item1.message); + assert.equal(lastNotificationEvent.index, 2); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.REMOVE); - model.notify(item2Duplicate); + model.addNotification(item2Duplicate); assert.equal(model.notifications.length, 2); - assert.equal(lastEvent.item.severity, item2Duplicate.severity); - assert.equal(lastEvent.item.message.value, item2Duplicate.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + assert.equal(lastNotificationEvent.item.severity, item2Duplicate.severity); + assert.equal(lastNotificationEvent.item.message.value, item2Duplicate.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); item2Handle.close(); assert.equal(model.notifications.length, 1); - assert.equal(lastEvent.item.severity, item2Duplicate.severity); - assert.equal(lastEvent.item.message.value, item2Duplicate.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.REMOVE); + assert.equal(lastNotificationEvent.item.severity, item2Duplicate.severity); + assert.equal(lastNotificationEvent.item.message.value, item2Duplicate.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.REMOVE); model.notifications[0].expand(); - assert.equal(lastEvent.item.severity, item3.severity); - assert.equal(lastEvent.item.message.value, item3.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.CHANGE); + assert.equal(lastNotificationEvent.item.severity, item3.severity); + assert.equal(lastNotificationEvent.item.message.value, item3.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.CHANGE); + + const disposable = model.showStatusMessage('Hello World'); + assert.equal(model.statusMessage!.message, 'Hello World'); + assert.equal(lastStatusMessageEvent.item.message, model.statusMessage!.message); + assert.equal(lastStatusMessageEvent.kind, StatusMessageChangeType.ADD); + disposable.dispose(); + assert.ok(!model.statusMessage); + assert.equal(lastStatusMessageEvent.kind, StatusMessageChangeType.REMOVE); + + let disposable2 = model.showStatusMessage('Hello World 2'); + const disposable3 = model.showStatusMessage('Hello World 3'); + + assert.equal(model.statusMessage!.message, 'Hello World 3'); + + disposable2.dispose(); + assert.equal(model.statusMessage!.message, 'Hello World 3'); + + disposable3.dispose(); + assert.ok(!model.statusMessage); }); }); \ No newline at end of file diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 458ac3380e..9e3a61156f 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -16,10 +16,8 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { IModelService } from 'vs/editor/common/services/modelService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { MainThreadLanguageFeatures } from 'vs/workbench/api/browser/mainThreadLanguageFeatures'; -import { IHeapService, NullHeapService } from 'vs/workbench/services/heap/common/heap'; import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -67,14 +65,14 @@ suite('ExtHostLanguageFeatureCommands', function () { { let instantiationService = new TestInstantiationService(); rpcProtocol = new TestRPCProtocol(); - instantiationService.stub(IHeapService, NullHeapService); instantiationService.stub(ICommandService, { _serviceBrand: undefined, executeCommand(id: string, args: any): any { - if (!CommandsRegistry.getCommands()[id]) { + const command = CommandsRegistry.getCommands().get(id); + if (!command) { return Promise.reject(new Error(id + ' NOT known')); } - let { handler } = CommandsRegistry.getCommands()[id]; + const { handler } = command; return Promise.resolve(instantiationService.invokeFunction(handler, args)); } }); @@ -109,9 +107,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments); - const heapService = new ExtHostHeapService(); - - commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService()); + commands = new ExtHostCommands(rpcProtocol, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostCommands, commands); rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); ExtHostApiCommands.register(commands); @@ -119,7 +115,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics, new NullLogService()); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); @@ -801,9 +797,9 @@ suite('ExtHostLanguageFeatureCommands', function () { })); await rpcProtocol.sync(); - let value = await commands.executeCommand<vscode.SelectionRange[][]>('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]); + let value = await commands.executeCommand<vscode.SelectionRange[]>('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]); assert.equal(value.length, 1); - assert.ok(value[0].length >= 2); + assert.ok(value[0].parent); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts index eda5b13f45..9bc2b444dc 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts @@ -26,7 +26,7 @@ suite('ExtHostCommands', function () { } }; - const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), undefined!, new NullLogService()); + const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), new NullLogService()); commands.registerCommand(true, 'foo', (): any => { }).dispose(); assert.equal(lastUnregister!, 'foo'); assert.equal(CommandsRegistry.getCommand('foo'), undefined); @@ -46,7 +46,7 @@ suite('ExtHostCommands', function () { } }; - const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), undefined!, new NullLogService()); + const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), new NullLogService()); const reg = commands.registerCommand(true, 'foo', (): any => { }); reg.dispose(); reg.dispose(); diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 92a05d2bbf..1df276538c 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -40,7 +40,7 @@ suite('ExtHostConfiguration', function () { user: new ConfigurationModel(contents), workspace: new ConfigurationModel(), folders: Object.create(null), - configurationScopes: {} + configurationScopes: [] }; } @@ -138,7 +138,7 @@ suite('ExtHostConfiguration', function () { testObject = all.getConfiguration('workbench'); actual = testObject.get('colorCustomizations')!; - delete actual['statusBar.foreground']; + actual['statusBar.foreground'] = undefined; assert.equal(actual['statusBar.foreground'], undefined); testObject = all.getConfiguration('workbench'); actual = testObject.get('colorCustomizations')!; @@ -278,7 +278,7 @@ suite('ExtHostConfiguration', function () { }, ['editor.wordWrap']), workspace: new ConfigurationModel({}, []), folders: Object.create(null), - configurationScopes: {} + configurationScopes: [] } ); @@ -326,7 +326,7 @@ suite('ExtHostConfiguration', function () { }, ['editor.wordWrap']), workspace, folders, - configurationScopes: {} + configurationScopes: [] } ); @@ -402,7 +402,7 @@ suite('ExtHostConfiguration', function () { }, ['editor.wordWrap']), workspace, folders, - configurationScopes: {} + configurationScopes: [] } ); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index 61f7175c09..61a5cba0e3 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -91,18 +91,18 @@ suite('ExtHostDiagnostics', () => { new Diagnostic(new Range(0, 0, 1, 1), 'message-2') ]); - let array = collection.get(URI.parse('foo:bar')); + let array = collection.get(URI.parse('foo:bar')) as Diagnostic[]; assert.throws(() => array.length = 0); assert.throws(() => array.pop()); assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); - collection.forEach((uri, array) => { + collection.forEach((uri, array: Diagnostic[]) => { assert.throws(() => array.length = 0); assert.throws(() => array.pop()); assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); }); - array = collection.get(URI.parse('foo:bar')); + array = collection.get(URI.parse('foo:bar')) as Diagnostic[]; assert.equal(array.length, 2); collection.dispose(); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index 6d8ce3da6b..4ccc72d92f 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -85,7 +85,7 @@ suite('ExtHostDocumentSaveParticipant', () => { sub.dispose(); assert.ok(event); - assert.throws(() => { event.document = null!; }); + assert.throws(() => { (event.document as any) = null!; }); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 86d1abc0b1..84265cfe4a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -18,7 +18,6 @@ import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguage import { MainThreadLanguageFeatures } from 'vs/workbench/api/browser/mainThreadLanguageFeatures'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; -import { IHeapService, NullHeapService } from 'vs/workbench/services/heap/common/heap'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; @@ -37,7 +36,6 @@ import { getDocumentFormattingEditsUntilResult, getDocumentRangeFormattingEditsU import { getLinks } from 'vs/editor/contrib/links/getLinks'; import { MainContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import * as vscode from 'vscode'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -81,7 +79,6 @@ suite('ExtHostLanguageFeatures', function () { { let instantiationService = new TestInstantiationService(); instantiationService.stub(IMarkerService, MarkerService); - instantiationService.stub(IHeapService, NullHeapService); inst = instantiationService; } @@ -102,16 +99,14 @@ suite('ExtHostLanguageFeatures', function () { const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments); - const heapService = new ExtHostHeapService(); - - const commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService()); + const commands = new ExtHostCommands(rpcProtocol, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostCommands, commands); rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); const diagnostics = new ExtHostDiagnostics(rpcProtocol); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics, new NullLogService()); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); @@ -194,7 +189,7 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); const value = await getCodeLensData(model, CancellationToken.None); - assert.equal(value.length, 1); + assert.equal(value.lenses.length, 1); }); test('CodeLens, do not resolve a resolved lens', async () => { @@ -212,8 +207,8 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); const value = await getCodeLensData(model, CancellationToken.None); - assert.equal(value.length, 1); - const data = value[0]; + assert.equal(value.lenses.length, 1); + const [data] = value.lenses; const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); assert.equal(symbol!.command!.id, 'id'); assert.equal(symbol!.command!.title, 'Title'); @@ -229,8 +224,8 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); const value = await getCodeLensData(model, CancellationToken.None); - assert.equal(value.length, 1); - let data = value[0]; + assert.equal(value.lenses.length, 1); + let [data] = value.lenses; const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); assert.equal(symbol!.command!.id, 'missing'); assert.equal(symbol!.command!.title, '!!MISSING: command!!'); @@ -1041,7 +1036,9 @@ suite('ExtHostLanguageFeatures', function () { disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider { provideDocumentLinks() { - return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; + const link = new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3')); + link.tooltip = 'tooltip'; + return [link]; } })); @@ -1051,6 +1048,7 @@ suite('ExtHostLanguageFeatures', function () { let [first] = links; assert.equal(first.url, 'foo:bar#3'); assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); + assert.equal(first.tooltip, 'tooltip'); }); test('Links, evil provider', async () => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index 102eede466..75e0ec6fdc 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -6,9 +6,11 @@ import * as assert from 'assert'; import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMessageService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; const emptyDialogService = new class implements IDialogService { _serviceBrand: 'dialogService'; @@ -30,7 +32,7 @@ const emptyCommandService: ICommandService = { }; const emptyNotificationService = new class implements INotificationService { - _serviceBrand: 'notificiationService'; + _serviceBrand: ServiceIdentifier<INotificationService>; notify(...args: any[]): never { throw new Error('not implemented'); } @@ -46,11 +48,13 @@ const emptyNotificationService = new class implements INotificationService { prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { throw new Error('not implemented'); } + status(message: string | Error, options?: IStatusMessageOptions): IDisposable { + return Disposable.None; + } }; class EmptyNotificationService implements INotificationService { - - _serviceBrand: any; + _serviceBrand: ServiceIdentifier<INotificationService>; constructor(private withNotify: (notification: INotification) => void) { } @@ -72,6 +76,9 @@ class EmptyNotificationService implements INotificationService { prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { throw new Error('not implemented'); } + status(message: string, options?: IStatusMessageOptions): IDisposable { + return Disposable.None; + } } suite('ExtHostMessageService', function () { diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index a1638d7cd3..7d72cc145a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { mapArrayOrNot } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError } from 'vs/base/common/errors'; -import { dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; @@ -16,12 +16,12 @@ import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/common/extHostTypes'; import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/workbench/services/search/common/search'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; -import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; import * as vscode from 'vscode'; +import { NullLogService } from 'vs/platform/log/common/log'; let rpcProtocol: TestRPCProtocol; let extHostSearch: ExtHostSearch; -let disposables: vscode.Disposable[] = []; +const disposables = new DisposableStore(); let mockMainThreadSearch: MockMainThreadSearch; class MockMainThreadSearch implements MainThreadSearchShape { @@ -63,12 +63,12 @@ export function extensionResultIsMatch(data: vscode.TextSearchResult): data is v suite('ExtHostSearch', () => { async function registerTestTextSearchProvider(provider: vscode.TextSearchProvider, scheme = 'file'): Promise<void> { - disposables.push(extHostSearch.registerTextSearchProvider(scheme, provider)); + disposables.add(extHostSearch.registerTextSearchProvider(scheme, provider)); await rpcProtocol.sync(); } async function registerTestFileSearchProvider(provider: vscode.FileSearchProvider, scheme = 'file'): Promise<void> { - disposables.push(extHostSearch.registerFileSearchProvider(scheme, provider)); + disposables.add(extHostSearch.registerFileSearchProvider(scheme, provider)); await rpcProtocol.sync(); } @@ -130,7 +130,7 @@ suite('ExtHostSearch', () => { rpcProtocol = new TestRPCProtocol(); mockMainThreadSearch = new MockMainThreadSearch(); - const logService = new TestLogService(); + const logService = new NullLogService(); rpcProtocol.set(MainContext.MainThreadSearch, mockMainThreadSearch); @@ -139,7 +139,7 @@ suite('ExtHostSearch', () => { }); teardown(() => { - dispose(disposables); + disposables.clear(); return rpcProtocol.sync(); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index 7b230b439a..ea19411591 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -11,7 +11,6 @@ import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { MainThreadTreeViewsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { TreeDataProvider, TreeItem } from 'vscode'; import { TestRPCProtocol } from './testRPCProtocol'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -72,7 +71,7 @@ suite('ExtHostTreeView', function () { rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); target = new RecordingShape(); - testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new ExtHostHeapService(), new NullLogService()), new NullLogService()); + testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new NullLogService()), new NullLogService()); onDidChangeTreeNode = new Emitter<{ key: string }>(); onDidChangeTreeNodeWithId = new Emitter<{ key: string }>(); testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 20e7cb6212..06f8131bb8 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -31,6 +31,7 @@ suite('ExtHostTypes', function () { scheme: 'file', path: '/path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), + _sep: isWindows ? 1 : undefined, }); assert.ok(uri.toString()); @@ -39,6 +40,7 @@ suite('ExtHostTypes', function () { scheme: 'file', path: '/path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), + _sep: isWindows ? 1 : undefined, external: 'file:///path/test.file' }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index 46474373c1..de23d0e3fa 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -4,20 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; +import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import * as vscode from 'vscode'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { URI } from 'vs/base/common/uri'; -suite('ExtHostWebview', function () { +suite('ExtHostWebview', () => { test('Cannot register multiple serializers for the same view type', async () => { const viewType = 'view.type'; const shape = createNoopMainThreadWebviews(); - const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape)); + const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { webviewCspSource: '', webviewResourceRoot: '' }); let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; @@ -46,11 +47,95 @@ suite('ExtHostWebview', function () { await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {}); assert.strictEqual(lastInvokedDeserializer, serializerB); }); + + test('toWebviewResource for desktop vscode-resource scheme', () => { + const shape = createNoopMainThreadWebviews(); + const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { + webviewCspSource: '', + webviewResourceRoot: 'vscode-resource:{{resource}}' + }); + const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); + + assert.strictEqual( + webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString(), + 'vscode-resource:/Users/codey/file.html', + 'Unix basic' + ); + + assert.strictEqual( + webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString(), + 'vscode-resource:/Users/codey/file.html#frag', + 'Unix should preserve fragment' + ); + + assert.strictEqual( + webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString(), + 'vscode-resource:/Users/codey/f%20ile.html', + 'Unix with encoding' + ); + + assert.strictEqual( + webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString(), + 'vscode-resource://localhost/Users/codey/file.html', + 'Unix should preserve authority' + ); + + assert.strictEqual( + webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString(), + 'vscode-resource:/c%3A/codey/file.txt', + 'Windows C drive' + ); + }); + + test('toWebviewResource for web endpoint', () => { + const shape = createNoopMainThreadWebviews(); + + const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { + webviewCspSource: '', + webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit{{resource}}` + }); + const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); + + function stripEndpointUuid(input: string) { + return input.replace(/^https:\/\/[^\.]+?\./, ''); + } + + assert.strictEqual( + stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString()), + 'webview.contoso.com/commit///Users/codey/file.html', + 'Unix basic' + ); + + assert.strictEqual( + stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString()), + 'webview.contoso.com/commit///Users/codey/file.html#frag', + 'Unix should preserve fragment' + ); + + assert.strictEqual( + stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString()), + 'webview.contoso.com/commit///Users/codey/f%20ile.html', + 'Unix with encoding' + ); + + assert.strictEqual( + stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString()), + 'webview.contoso.com/commit//localhost/Users/codey/file.html', + 'Unix should preserve authority' + ); + + assert.strictEqual( + stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString()), + 'webview.contoso.com/commit///c%3A/codey/file.txt', + 'Windows C drive' + ); + }); }); function createNoopMainThreadWebviews() { return new class extends mock<MainThreadWebviews>() { + $createWebviewPanel() { /* noop */ } $registerSerializer() { /* noop */ } $unregisterSerializer() { /* noop */ } }; diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index c9640a7423..532131bc72 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -62,7 +62,7 @@ suite('MainThreadEditors', () => { } move(source: URI, target: URI) { movedResources.set(source, target); - return Promise.resolve(undefined); + return Promise.resolve(Object.create(null)); } models = <any>{ onModelSaved: Event.None, diff --git a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts index 06685b8691..dd1efa6423 100644 --- a/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/workbench/test/electron-browser/colorRegistry.releaseTest.ts @@ -12,12 +12,15 @@ import { debugExceptionWidgetBackground } from 'vs/workbench/contrib/debug/brows import { debugToolBarBackground } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { buttonBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePage'; import { embeddedEditorBackground } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart'; -import { request, asText } from 'vs/base/node/request'; +import { asText } from 'vs/platform/request/common/request'; import * as pfs from 'vs/base/node/pfs'; import * as path from 'vs/base/common/path'; import * as assert from 'assert'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { RequestService } from 'vs/platform/request/node/requestService'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { NullLogService } from 'vs/platform/log/common/log'; interface ColorInfo { @@ -40,7 +43,7 @@ export const experimental: string[] = []; // 'settings.modifiedItemForeground', suite('Color Registry', function () { test('all colors documented', async function () { - const reqContext = await request({ url: 'https://raw.githubusercontent.com/Microsoft/vscode-docs/vnext/docs/getstarted/theme-color-reference.md' }, CancellationToken.None); + const reqContext = await new RequestService(new TestConfigurationService(), new NullLogService()).request({ url: 'https://raw.githubusercontent.com/Microsoft/vscode-docs/vnext/docs/getstarted/theme-color-reference.md' }, CancellationToken.None); const content = (await asText(reqContext))!; const expression = /\-\s*\`([\w\.]+)\`: (.*)/g; diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts index 5ed56444d2..1b8031cccf 100644 --- a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts @@ -26,9 +26,10 @@ import { Extensions, IQuickOpenRegistry } from 'vs/workbench/browser/quickopen'; import 'vs/workbench/contrib/search/browser/search.contribution'; // load contributions import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { LocalSearchService } from 'vs/workbench/services/search/node/searchService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { TestContextService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; namespace Timer { export interface ITimerEvent { @@ -78,7 +79,7 @@ suite.skip('QuickOpen performance (integration)', () => { [IEditorGroupsService, new TestEditorGroupsService()], [IEnvironmentService, TestEnvironmentService], [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], - [ISearchService, createSyncDescriptor(SearchService)] + [ISearchService, createSyncDescriptor(LocalSearchService)] )); const registry = Registry.as<IQuickOpenRegistry>(Extensions.Quickopen); @@ -172,6 +173,10 @@ class TestTelemetryService implements ITelemetryService { return Promise.resolve(undefined); } + public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) { + return this.publicLog(eventName, data as any); + } + public getTelemetryInfo(): Promise<ITelemetryInfo> { return Promise.resolve({ instanceId: 'someValue.instanceId', diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index be27e969ae..9f9ac6e88c 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -15,7 +15,7 @@ import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/serv import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import * as minimist from 'minimist'; import * as path from 'vs/base/common/path'; -import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { LocalSearchService } from 'vs/workbench/services/search/node/searchService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestEnvironmentService, TestContextService, TestEditorService, TestEditorGroupsService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -33,6 +33,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { NullLogService, ILogService } from 'vs/platform/log/common/log'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; declare var __dirname: string; @@ -68,7 +69,7 @@ suite.skip('TextSearch performance (integration)', () => { [IEditorGroupsService, new TestEditorGroupsService()], [IEnvironmentService, TestEnvironmentService], [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], - [ISearchService, createSyncDescriptor(SearchService)], + [ISearchService, createSyncDescriptor(LocalSearchService)], [ILogService, new NullLogService()] )); @@ -165,6 +166,10 @@ class TestTelemetryService implements ITelemetryService { return Promise.resolve(); } + public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) { + return this.publicLog(eventName, data as any); + } + public getTelemetryInfo(): Promise<ITelemetryInfo> { return Promise.resolve({ instanceId: 'someValue.instanceId', diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 325ad75f16..56b7d3cc8f 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -38,7 +38,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { IWindowsService, IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, MenuBarVisibility, IURIToOpen, IOpenSettings, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -64,14 +64,14 @@ import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { Dimension } from 'vs/base/browser/dom'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ILabelService } from 'vs/platform/label/common/label'; import { timeout } from 'vs/base/common/async'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletDescriptor, Viewlet } from 'vs/workbench/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isLinux, isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; import { Part } from 'vs/workbench/browser/part'; @@ -82,7 +82,7 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedPr import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; -import { BrowserTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; +import { NodeTextFileService } from 'vs/workbench/services/textfile/node/textFileService'; import { Schemas } from 'vs/base/common/network'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { @@ -177,7 +177,7 @@ export class TestContextService implements IWorkspaceContextService { } } -export class TestTextFileService extends BrowserTextFileService { +export class TestTextFileService extends NodeTextFileService { public cleanupBackupsBeforeShutdownCalled: boolean; private promptPath: URI; @@ -311,7 +311,7 @@ export function workbenchInstantiationService(): IInstantiationService { instantiationService.stub(ITextFileService, <ITextFileService>instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService)); instantiationService.stub(IThemeService, new TestThemeService()); - instantiationService.stub(ILogService, new TestLogService()); + instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService([new TestEditorGroup(0)])); instantiationService.stub(ILabelService, <ILabelService>instantiationService.createInstance(LabelService)); const editorService = new TestEditorService(); @@ -322,19 +322,6 @@ export function workbenchInstantiationService(): IInstantiationService { return instantiationService; } -export class TestLogService implements ILogService { - _serviceBrand: any; onDidChangeLogLevel: Event<LogLevel>; - getLevel(): LogLevel { return LogLevel.Info; } - setLevel(_level: LogLevel): void { } - trace(_message: string, ..._args: any[]): void { } - debug(_message: string, ..._args: any[]): void { } - info(_message: string, ..._args: any[]): void { } - warn(_message: string, ..._args: any[]): void { } - error(_message: string | Error, ..._args: any[]): void { } - critical(_message: string | Error, ..._args: any[]): void { } - dispose(): void { } -} - export class TestDecorationsService implements IDecorationsService { _serviceBrand: any; onDidChangeDecorations: Event<IResourceDecorationChangeEvent> = Event.None; @@ -439,6 +426,9 @@ export class TestFileDialogService implements IFileDialogService { public pickWorkspaceAndOpen(_options: IPickAndOpenOptions): Promise<any> { return Promise.resolve(0); } + public pickFileToSave(_options: ISaveDialogOptions): Promise<URI | undefined> { + return Promise.resolve(undefined); + } public showSaveDialog(_options: ISaveDialogOptions): Promise<URI | undefined> { return Promise.resolve(undefined); } @@ -456,6 +446,9 @@ export class TestLayoutService implements IWorkbenchLayoutService { container: HTMLElement = window.document.body; onZenModeChange: Event<boolean> = Event.None; + onCenteredLayoutChange: Event<boolean> = Event.None; + onFullscreenChange: Event<boolean> = Event.None; + onPanelPositionChange: Event<string> = Event.None; onLayout = Event.None; private _onTitleBarVisibilityChange = new Emitter<void>(); @@ -541,6 +534,8 @@ export class TestLayoutService implements IWorkbenchLayoutService { public addClass(_clazz: string): void { } public removeClass(_clazz: string): void { } + + public getWorkbenchContainer(): HTMLElement { throw new Error('not implemented'); } public getWorkbenchElement(): HTMLElement { throw new Error('not implemented'); } public toggleZenMode(): void { } @@ -617,6 +612,10 @@ export class TestPanelService implements IPanelService { return null!; } + public getPanel(id: string): any { + return activeViewlet; + } + public getPanels(): any[] { return []; } @@ -638,6 +637,10 @@ export class TestPanelService implements IPanelService { throw new Error('Method not implemented.'); } + public getProgressIndicator(id: string) { + return null!; + } + public hideActivePanel(): void { } public getLastActivePanelId(): string { @@ -700,11 +703,11 @@ export class TestEditorGroupsService implements IEditorGroupsService { throw new Error('not implemented'); } - getSize(_group: number | IEditorGroup): number { - return 100; + getSize(_group: number | IEditorGroup): { width: number, height: number } { + return { width: 100, height: 100 }; } - setSize(_group: number | IEditorGroup, _size: number): void { } + setSize(_group: number | IEditorGroup, _size: { width: number, height: number }): void { } arrangeGroups(_arrangement: GroupsArrangement): void { } @@ -1072,6 +1075,10 @@ export class TestBackupFileService implements IBackupFileService { return Promise.resolve(false); } + public hasBackupSync(resource: URI, versionId?: number): boolean { + return false; + } + public loadBackupResource(resource: URI): Promise<URI | undefined> { return this.hasBackup(resource).then(hasBackup => { if (hasBackup) { @@ -1217,6 +1224,14 @@ export class TestWindowService implements IWindowService { }); } + addRecentlyOpened(_recents: IRecent[]): Promise<void> { + return Promise.resolve(); + } + + removeFromRecentlyOpened(_paths: URI[]): Promise<void> { + return Promise.resolve(); + } + focusWindow(): Promise<void> { return Promise.resolve(); } @@ -1446,6 +1461,10 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } + openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> { + return Promise.resolve(); + } + getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { throw new Error('not implemented'); } @@ -1576,30 +1595,6 @@ export class TestSharedProcessService implements ISharedProcessService { registerChannel(channelName: string, channel: any): void { } } -export class NullFileSystemProvider implements IFileSystemProvider { - - capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.Readonly; - - onDidChangeCapabilities: Event<void> = Event.None; - onDidChangeFile: Event<IFileChange[]> = Event.None; - - constructor(private disposableFactory: () => IDisposable = () => Disposable.None) { } - - watch(resource: URI, opts: IWatchOptions): IDisposable { return this.disposableFactory(); } - stat(resource: URI): Promise<IStat> { return Promise.resolve(undefined!); } - mkdir(resource: URI): Promise<void> { return Promise.resolve(undefined!); } - readdir(resource: URI): Promise<[string, FileType][]> { return Promise.resolve(undefined!); } - delete(resource: URI, opts: FileDeleteOptions): Promise<void> { return Promise.resolve(undefined!); } - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { return Promise.resolve(undefined!); } - copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { return Promise.resolve(undefined!); } - readFile?(resource: URI): Promise<Uint8Array> { return Promise.resolve(undefined!); } - writeFile?(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> { return Promise.resolve(undefined!); } - open?(resource: URI, opts: FileOpenOptions): Promise<number> { return Promise.resolve(undefined!); } - close?(fd: number): Promise<void> { return Promise.resolve(undefined!); } - read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return Promise.resolve(undefined!); } - write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return Promise.resolve(undefined!); } -} - export class RemoteFileSystemProvider implements IFileSystemProvider { constructor(private readonly diskFileSystemProvider: IFileSystemProvider, private readonly remoteAuthority: string) { } diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index de94adc957..412c5d42fd 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -7,7 +7,8 @@ import 'vs/editor/editor.all'; -import 'vs/workbench/api/electron-browser/extensionHost.contribution'; +import 'vs/workbench/api/browser/extensionHost.contribution'; +import 'sql/workbench/api/electron-browser/extensionHost.contribution'; // {{SQL CARBON EDIT}} @anthonydresser add our extension contributions import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; @@ -20,6 +21,8 @@ import 'vs/workbench/electron-browser/main'; //#region --- workbench actions import 'vs/workbench/browser/actions/layoutActions'; +import 'vs/workbench/browser/actions/windowActions'; +import 'vs/workbench/browser/actions/developerActions'; import 'vs/workbench/browser/actions/listCommands'; import 'vs/workbench/browser/actions/navigationActions'; import 'vs/workbench/browser/parts/quickopen/quickOpenActions'; @@ -65,9 +68,9 @@ import { AccessibilityService } from 'vs/workbench/services/accessibility/node/a import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; -import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; -import { IRequestService } from 'vs/platform/request/node/request'; -import { RequestService } from 'vs/platform/request/electron-browser/requestService'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { RequestService } from 'vs/platform/request/browser/requestService'; import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; @@ -89,10 +92,8 @@ import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/workbench/services/remote/node/tunnelService'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; - -import 'vs/platform/remote/node/tunnelService'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/integrity/node/integrityService'; @@ -102,9 +103,8 @@ import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/decorations/browser/decorationsService'; import 'vs/workbench/services/search/node/searchService'; -import 'vs/workbench/services/progress/browser/progressService2'; +import 'vs/workbench/services/progress/browser/progressService'; import 'vs/workbench/services/editor/browser/codeEditorService'; -import 'vs/workbench/services/broadcast/electron-browser/broadcastService'; import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; import 'vs/workbench/services/preferences/browser/preferencesService'; import 'vs/workbench/services/output/node/outputChannelModelService'; @@ -113,14 +113,15 @@ import 'vs/workbench/services/textmodelResolver/common/textModelResolverService' import 'vs/workbench/services/textfile/node/textFileService'; import 'vs/workbench/services/dialogs/browser/fileDialogService'; import 'vs/workbench/services/dialogs/electron-browser/dialogService'; -import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/editor/browser/editorService'; import 'vs/workbench/services/history/browser/history'; import 'vs/workbench/services/activity/browser/activityService'; import 'vs/workbench/browser/parts/views/views'; -import 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; +import 'vs/workbench/services/keybinding/electron-browser/keybinding.contribution'; +import 'vs/workbench/services/keybinding/browser/keybindingService'; import 'vs/workbench/services/untitled/common/untitledEditorService'; -import 'vs/workbench/services/textfile/node/textResourcePropertiesService'; +import 'vs/workbench/services/textfile/common/textResourcePropertiesService'; import 'vs/workbench/services/mode/common/workbenchModeService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; @@ -132,10 +133,14 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; -import 'vs/workbench/services/heap/node/heap'; import 'vs/workbench/services/window/electron-browser/windowService'; import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; +import 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; + +registerSingleton(IBackupFileService, BackupFileService); registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); registerSingleton(IOpenerService, OpenerService, true); @@ -162,7 +167,7 @@ registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IURLService, RelayURLService); registerSingleton(ITunnelService, TunnelService, true); -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); +registerSingleton(ICredentialsService, KeytarCredentialsService, true); //#endregion @@ -179,7 +184,7 @@ import { IAngularEventingService } from 'sql/platform/angularEventing/common/ang import { AngularEventingService } from 'sql/platform/angularEventing/node/angularEventingService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { CapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesServiceImpl'; -import { ICredentialsService, CredentialsService } from 'sql/platform/credentials/common/credentialsService'; +import { ICredentialsService as sqlICredentialsService, CredentialsService } from 'sql/platform/credentials/common/credentialsService'; import { ISerializationService, SerializationService } from 'sql/platform/serialization/common/serializationService'; import { IMetadataService, MetadataService } from 'sql/platform/metadata/common/metadataService'; import { IObjectExplorerService, ObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService'; @@ -245,7 +250,7 @@ registerSingleton(ICapabilitiesService, CapabilitiesService); registerSingleton(IErrorMessageService, ErrorMessageService); registerSingleton(IConnectionDialogService, ConnectionDialogService); registerSingleton(IServerGroupController, ServerGroupController); -registerSingleton(ICredentialsService, CredentialsService); +registerSingleton(sqlICredentialsService, CredentialsService); registerSingleton(IResourceProviderService, ResourceProviderService); registerSingleton(IAccountManagementService, AccountManagementService); registerSingleton(IConnectionManagementService, ConnectionManagementService as any); @@ -289,6 +294,7 @@ import 'vs/workbench/browser/parts/statusbar/statusbarPart'; //#endregion + //#region --- workbench contributions // Workspace File Watching @@ -301,11 +307,15 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // Preferences -import 'vs/workbench/contrib/preferences/electron-browser/preferences.contribution'; +import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; +import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; +import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/browser/preferencesSearch'; +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; +import 'vs/workbench/contrib/logs/electron-browser/logs.contribution'; // Quick Open Handlers import 'vs/workbench/contrib/quickopen/browser/quickopen.contribution'; @@ -319,7 +329,7 @@ import 'vs/workbench/contrib/files/browser/files.contribution'; import 'vs/workbench/contrib/backup/common/backup.contribution'; // Stats -import 'vs/workbench/contrib/stats/node/stats.contribution'; +import 'vs/workbench/contrib/stats/electron-browser/stats.contribution'; // Rapid Render Splash import 'vs/workbench/contrib/splash/electron-browser/partsSplash.contribution'; @@ -333,14 +343,15 @@ import 'vs/workbench/contrib/search/browser/openAnythingHandler'; import 'vs/workbench/contrib/scm/browser/scm.contribution'; import 'vs/workbench/contrib/scm/browser/scmViewlet'; -// {{SQL CARBON EDIT}} +/* {{SQL CARBON EDIT}} // Debug -// import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; -// import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; -// import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; -// import 'vs/workbench/contrib/debug/browser/repl'; -// import 'vs/workbench/contrib/debug/browser/debugViewlet'; - +import 'vs/workbench/contrib/debug/browser/debug.contribution'; +import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; +import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +import 'vs/workbench/contrib/debug/browser/repl'; +import 'vs/workbench/contrib/debug/browser/debugViewlet'; +import 'vs/workbench/contrib/debug/node/debugHelperService'; +*/ // Markers import 'vs/workbench/contrib/markers/browser/markers.contribution'; @@ -357,7 +368,7 @@ import 'vs/workbench/contrib/webview/electron-browser/webview.contribution'; // Extensions Management import 'vs/workbench/contrib/extensions/electron-browser/extensions.contribution'; import 'vs/workbench/contrib/extensions/browser/extensionsQuickOpen'; -import 'vs/workbench/contrib/extensions/electron-browser/extensionsViewlet'; +import 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; // Output Panel import 'vs/workbench/contrib/output/browser/output.contribution'; @@ -372,11 +383,16 @@ import 'vs/workbench/contrib/terminal/browser/terminalPanel'; // Relauncher import 'vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution'; -// Tasks -import 'vs/workbench/contrib/tasks/electron-browser/task.contribution'; +// Tasks {{SQL CARBON EDIT}} remove tasks +// import 'vs/workbench/contrib/tasks/browser/task.contribution'; +// import { TaskService } from 'vs/workbench/contrib/tasks/electron-browser/taskService'; +// import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; +// registerSingleton(ITaskService, TaskService, true); // Remote -// import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; {{SQL CARBON EDIT}} @anthonydresser comment our remote +// {{SQL CARBON EDIT}} @anthonydresser comment our remote +// import 'vs/workbench/contrib/remote/common/remote.contribution'; +// import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; // Emmet // import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; {{SQL CARBON EDIT}} @anthonydresser comment our emmet @@ -386,7 +402,8 @@ import 'vs/workbench/contrib/codeEditor/browser/codeEditor.contribution'; import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution'; // Execution -import 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution'; +import 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; +import 'vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution'; // Snippets import 'vs/workbench/contrib/snippets/browser/snippets.contribution'; @@ -399,7 +416,7 @@ import 'vs/workbench/contrib/snippets/browser/tabCompletion'; import 'vs/workbench/contrib/format/browser/format.contribution'; // Send a Smile -import 'vs/workbench/contrib/feedback/electron-browser/feedback.contribution'; +import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; // Update import 'vs/workbench/contrib/update/electron-browser/update.contribution'; @@ -436,9 +453,6 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; -// Code Insets -import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; - // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; @@ -453,7 +467,6 @@ import 'sql/workbench/parts/dataExplorer/browser/dataExplorerExtensionPoint'; import 'sql/workbench/parts/dataExplorer/electron-browser/nodeActions.contribution'; import 'sql/platform/telemetry/telemetry.contribution'; -import 'sql/workbench/api/node/sqlExtHost.contribution'; import 'sql/workbench/parts/connection/browser/connection.contribution'; // query editor @@ -466,6 +479,9 @@ import 'sql/workbench/parts/editData/browser/editData.contribution'; // query plan editor import 'sql/workbench/parts/queryPlan/electron-browser/queryPlan.contribution'; +//acounts +import 'sql/workbench/parts/accounts/browser/accounts.contribution'; + import 'sql/workbench/parts/profiler/browser/profiler.contribution'; import 'sql/workbench/parts/profiler/browser/profilerActions.contribution'; import 'sql/workbench/parts/objectExplorer/common/serverGroup.contribution'; diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts new file mode 100644 index 0000000000..71f5499e79 --- /dev/null +++ b/src/vs/workbench/workbench.web.api.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/workbench/workbench.web.main'; +import { main } from 'vs/workbench/browser/web.main'; +import { UriComponents } from 'vs/base/common/uri'; +import { IFileSystemProvider } from 'vs/platform/files/common/files'; +import { IRequestOptions, IRequestContext } from 'vs/platform/request/common/request'; + +export interface IWorkbenchConstructionOptions { + + /** + * Experimental: the remote authority is the IP:PORT from where the workbench is served + * from. It is for example being used for the websocket connections as address. + */ + remoteAuthority: string; + + /** + * Experimental: An endpoint to serve iframe content ("webview") from. This is required + * to provide full security isolation from the workbench host. + */ + webviewEndpoint?: string; + + /** + * Experimental: An optional folder that is set as workspace context for the workbench. + */ + folderUri?: UriComponents; + + /** + * Experimental: An optional workspace that is set as workspace context for the workbench. + */ + workspaceUri?: UriComponents; + + /** + * Experimental: The userDataProvider is used to handle user specific application + * state like settings, keybindings, UI state (e.g. opened editors) and snippets. + */ + userDataProvider?: IFileSystemProvider; + + /** + * Experimental: Optional request handler to handle http requests. + * In case not provided, workbench uses <code>XMLHttpRequest</code>. + */ + requestHandler?: (requestOptions: IRequestOptions) => Promise<IRequestContext>; +} + +/** + * Experimental: Creates the workbench with the provided options in the provided container. + * + * @param domElement the container to create the workbench in + * @param options for setting up the workbench + */ +function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise<void> { + return main(domElement, options); +} + +export { + create +}; \ No newline at end of file diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 0e4a8d5f5b..69ec7383b1 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -7,9 +7,8 @@ import 'vs/editor/editor.all'; -// import 'vs/workbench/api/electron-browser/extensionHost.contribution'; +import 'vs/workbench/api/browser/extensionHost.contribution'; -// import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; import 'vs/workbench/browser/web.main'; @@ -20,6 +19,8 @@ import 'vs/workbench/browser/web.main'; //#region --- workbench actions import 'vs/workbench/browser/actions/layoutActions'; +import 'vs/workbench/browser/actions/windowActions'; +import 'vs/workbench/browser/actions/developerActions'; import 'vs/workbench/browser/actions/listCommands'; import 'vs/workbench/browser/actions/navigationActions'; import 'vs/workbench/browser/parts/quickopen/quickOpenActions'; @@ -52,8 +53,8 @@ import { IMarkerService } from 'vs/platform/markers/common/markers'; import { MarkerService } from 'vs/platform/markers/common/markerService'; // import { IDownloadService } from 'vs/platform/download/common/download'; // import { DownloadService } from 'vs/platform/download/node/downloadService'; -// import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -// import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { BrowserClipboardService } from 'vs/platform/clipboard/browser/clipboardService'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -62,19 +63,16 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; -// import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; -// import { IRequestService } from 'vs/platform/request/node/request'; -// import { RequestService } from 'vs/platform/request/electron-browser/requestService'; -// import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; -// import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; +import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycleService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { DialogService } from 'vs/platform/dialogs/browser/dialogService'; // import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; // import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; // import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -// import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; -// import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; // import { IProductService } from 'vs/platform/product/common/product'; // import { ProductService } from 'vs/platform/product/node/productService'; // import { IWindowsService } from 'vs/platform/windows/common/windows'; @@ -89,26 +87,21 @@ import { ContextViewService } from 'vs/platform/contextview/browser/contextViewS // import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; // import { IURLService } from 'vs/platform/url/common/url'; // import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; -import { IHeapService, NullHeapService } from 'vs/workbench/services/heap/common/heap'; -import { IBroadcastService, NullBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; - -import 'vs/workbench/browser/web.simpleservices'; -import 'vs/platform/dialogs/browser/dialogService'; - - +// import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +// import { TunnelService } from 'vs/workbench/services/remote/node/tunnelService'; +// import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +// import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; // import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; -// import 'vs/workbench/services/textMate/electron-browser/textMateService'; +import 'vs/workbench/services/textMate/browser/textMateService'; // import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; // import 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler'; import 'vs/workbench/services/decorations/browser/decorationsService'; -// import 'vs/workbench/services/search/node/searchService'; -import 'vs/workbench/services/progress/browser/progressService2'; +import 'vs/workbench/services/search/common/searchService'; +import 'vs/workbench/services/progress/browser/progressService'; import 'vs/workbench/services/editor/browser/codeEditorService'; -// import 'vs/workbench/services/broadcast/electron-browser/broadcastService'; +// import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; import 'vs/workbench/services/preferences/browser/preferencesService'; import 'vs/workbench/services/output/common/outputChannelModelService'; import 'vs/workbench/services/configuration/common/jsonEditingService'; @@ -116,31 +109,37 @@ import 'vs/workbench/services/textmodelResolver/common/textModelResolverService' import 'vs/workbench/services/textfile/browser/textFileService'; import 'vs/workbench/services/dialogs/browser/fileDialogService'; // import 'vs/workbench/services/dialogs/electron-browser/dialogService'; -// import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/editor/browser/editorService'; import 'vs/workbench/services/history/browser/history'; import 'vs/workbench/services/activity/browser/activityService'; import 'vs/workbench/browser/parts/views/views'; -// import 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import 'vs/workbench/services/keybinding/browser/keymapService'; +import 'vs/workbench/services/keybinding/browser/keybindingService'; import 'vs/workbench/services/untitled/common/untitledEditorService'; -// import 'vs/workbench/services/textfile/node/textResourcePropertiesService'; +import 'vs/workbench/services/textfile/common/textResourcePropertiesService'; import 'vs/workbench/services/mode/common/workbenchModeService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; // import 'vs/workbench/services/extensionManagement/node/extensionEnablementService'; -// import 'vs/workbench/services/extensions/electron-browser/extensionService'; +import 'vs/workbench/services/extensions/browser/extensionService'; // import 'vs/workbench/services/contextmenu/electron-browser/contextmenuService'; -// import 'vs/workbench/services/extensionManagement/node/multiExtensionManagement'; +// import 'vs/workbench/services/extensions/node/multiExtensionManagement'; import 'vs/workbench/services/label/common/labelService'; // import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; // import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; -// import 'vs/workbench/services/heap/node/heap'; // import 'vs/workbench/services/window/electron-browser/windowService'; // import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; +import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; +import 'vs/workbench/browser/web.simpleservices'; - +registerSingleton(IBackupFileService, BackupFileService); +registerSingleton(IDialogService, DialogService, true); registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); registerSingleton(IOpenerService, OpenerService, true); @@ -148,31 +147,29 @@ registerSingleton(IEditorWorkerService, EditorWorkerServiceImpl); registerSingleton(IMarkerDecorationsService, MarkerDecorationsService); registerSingleton(IMarkerService, MarkerService, true); // registerSingleton(IDownloadService, DownloadService, true); -// registerSingleton(IClipboardService, ClipboardService, true); +registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IContextKeyService, ContextKeyService); registerSingleton(IModelService, ModelServiceImpl, true); registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationService); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(IContextViewService, ContextViewService, true); -// registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); -// registerSingleton(IRequestService, RequestService, true); -// registerSingleton(ILifecycleService, LifecycleService); +registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); +registerSingleton(ILifecycleService, BrowserLifecycleService); // registerSingleton(ILocalizationsService, LocalizationsService); // registerSingleton(ISharedProcessService, SharedProcessService, true); -// registerSingleton(IProductService, ProductService, true); // registerSingleton(IWindowsService, WindowsService); // registerSingleton(IUpdateService, UpdateService); // registerSingleton(IIssueService, IssueService); // registerSingleton(IWorkspacesService, WorkspacesService); // registerSingleton(IMenubarService, MenubarService); // registerSingleton(IURLService, RelayURLService); -registerSingleton(IHeapService, NullHeapService); -registerSingleton(IBroadcastService, NullBroadcastService); +// registerSingleton(ITunnelService, TunnelService, true); +// registerSingleton(ICredentialsService, KeytarCredentialsService, true); registerSingleton(IContextMenuService, ContextMenuService); -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); //#endregion + //#region --- workbench parts import 'vs/workbench/browser/parts/quickinput/quickInput'; @@ -186,8 +183,12 @@ import 'vs/workbench/browser/parts/statusbar/statusbarPart'; //#endregion + //#region --- workbench contributions +// Resource Service Worker +import 'vs/workbench/contrib/resources/browser/resourceServiceWorkerClient'; + // Workspace File Watching import 'vs/workbench/services/files/common/workspaceWatcher'; @@ -198,8 +199,12 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; // import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // Preferences -// import 'vs/workbench/contrib/preferences/electron-browser/preferences.contribution'; +import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; +import 'vs/workbench/contrib/preferences/browser/keyboardLayoutPicker'; +import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; +import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/browser/preferencesSearch'; +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; @@ -216,7 +221,7 @@ import 'vs/workbench/contrib/files/browser/files.contribution'; import 'vs/workbench/contrib/backup/common/backup.contribution'; // Stats -// import 'vs/workbench/contrib/stats/node/stats.contribution'; +// import 'vs/workbench/contrib/stats/electron-browser/stats.contribution'; // Rapid Render Splash // import 'vs/workbench/contrib/splash/electron-browser/partsSplash.contribution'; @@ -231,45 +236,65 @@ import 'vs/workbench/contrib/scm/browser/scm.contribution'; import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug -// import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; -// import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; -// import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; -// import 'vs/workbench/contrib/debug/browser/repl'; -// import 'vs/workbench/contrib/debug/browser/debugViewlet'; -// import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; +import 'vs/workbench/contrib/debug/browser/debug.contribution'; +import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; +import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +import 'vs/workbench/contrib/debug/browser/repl'; +import 'vs/workbench/contrib/debug/browser/debugViewlet'; +import 'vs/workbench/contrib/debug/browser/debugHelperService'; // Markers import 'vs/workbench/contrib/markers/browser/markers.contribution'; // Comments -// import 'vs/workbench/contrib/comments/browser/comments.contribution'; +import 'vs/workbench/contrib/comments/browser/comments.contribution'; // URL Support import 'vs/workbench/contrib/url/common/url.contribution'; // Webview -// import 'vs/workbench/contrib/webview/electron-browser/webview.contribution'; +import 'vs/workbench/contrib/webview/browser/webview.contribution'; + +import { IWebviewService } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; +import { IWebviewEditorService, WebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +registerSingleton(IWebviewService, WebviewService, true); +registerSingleton(IWebviewEditorService, WebviewEditorService, true); // Extensions Management // import 'vs/workbench/contrib/extensions/electron-browser/extensions.contribution'; // import 'vs/workbench/contrib/extensions/browser/extensionsQuickOpen'; -// import 'vs/workbench/contrib/extensions/electron-browser/extensionsViewlet'; +// import 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; // Output Panel import 'vs/workbench/contrib/output/browser/output.contribution'; import 'vs/workbench/contrib/output/browser/outputPanel'; // Terminal -// import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; +import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; // import 'vs/workbench/contrib/terminal/electron-browser/terminal.contribution'; -// import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -// import 'vs/workbench/contrib/terminal/browser/terminalPanel'; +import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; +import 'vs/workbench/contrib/terminal/browser/terminalPanel'; + +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalNativeService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalNativeService } from 'vs/workbench/contrib/terminal/browser/terminalNativeService'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; +registerSingleton(ITerminalNativeService, TerminalNativeService, true); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); // Relauncher // import 'vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution'; // Tasks -// import 'vs/workbench/contrib/tasks/electron-browser/task.contribution'; +import 'vs/workbench/contrib/tasks/browser/task.contribution'; +import { TaskService } from 'vs/workbench/contrib/tasks/browser/taskService'; +import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; +registerSingleton(ITaskService, TaskService, true); + +// Remote +import 'vs/workbench/contrib/remote/common/remote.contribution'; +// import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; // Emmet import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; @@ -278,8 +303,8 @@ import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; import 'vs/workbench/contrib/codeEditor/browser/codeEditor.contribution'; // import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution'; -// Execution -// import 'vs/workbench/contrib/externalTerminal/electron-browser/externalTerminal.contribution'; +// External terminal +import 'vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution'; // Snippets import 'vs/workbench/contrib/snippets/browser/snippets.contribution'; @@ -292,7 +317,7 @@ import 'vs/workbench/contrib/snippets/browser/tabCompletion'; import 'vs/workbench/contrib/format/browser/format.contribution'; // Send a Smile -// import 'vs/workbench/contrib/feedback/electron-browser/feedback.contribution'; +// import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; // Update // import 'vs/workbench/contrib/update/electron-browser/update.contribution'; @@ -320,15 +345,15 @@ import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contributio import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay'; // import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; +// Call Hierarchy +import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution'; + // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments // import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; -// Code Insets -// import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; - // Issues // import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; diff --git a/test/electron/index.js b/test/electron/index.js index e138aa4fb6..9e5e46c278 100644 --- a/test/electron/index.js +++ b/test/electron/index.js @@ -100,20 +100,29 @@ function parseReporterOption(value) { app.on('ready', () => { + ipcMain.on('error', (_, err) => { + if (!argv.debug) { + console.error(err); + app.exit(1); + } + }); + const win = new BrowserWindow({ height: 600, width: 800, show: false, webPreferences: { backgroundThrottling: false, - webSecurity: false + nodeIntegration: true, + webSecurity: false, + webviewTag: true } }); win.webContents.on('did-finish-load', () => { if (argv.debug) { win.show(); - win.webContents.openDevTools({ mode: 'right' }); + win.webContents.openDevTools(); } win.webContents.send('run', argv); }); diff --git a/test/electron/renderer.js b/test/electron/renderer.js index 82e98d4379..92f8f0fc91 100644 --- a/test/electron/renderer.js +++ b/test/electron/renderer.js @@ -308,5 +308,12 @@ function runTests(opts) { ipcRenderer.on('run', (e, opts) => { initLoader(opts); - runTests(opts).catch(err => console.error(typeof err === 'string' ? err : JSON.stringify(err))); + runTests(opts).catch(err => { + if (typeof err !== 'string') { + err = JSON.stringify(err); + } + + console.error(err); + ipcRenderer.send('error', err); + }); }); diff --git a/test/smoke/README.md b/test/smoke/README.md index 3eed271d35..b5be966e51 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -15,11 +15,14 @@ yarn smoketest # Build yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --stable-build PATH_TO_LAST_STABLE_BUILD_PARENT_FOLDER + +# Remote +yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --remote ``` ### Run for a release -You must always run the smoketest version which matches the release you are testing. So, if you want to run the smoketest for a release build (eg `release/1.22`), you need that version of the smoke tests too: +You must always run the smoketest version which matches the release you are testing. So, if you want to run the smoketest for a release build (e.g. `release/1.22`), you need that version of the smoke tests too: ```bash git checkout release/1.22 diff --git a/test/smoke/package.json b/test/smoke/package.json index 00b04f8453..2d92a7a884 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -17,12 +17,12 @@ "@types/mkdirp": "0.5.1", "@types/mocha": "2.2.41", "@types/ncp": "2.0.1", - "@types/node": "8.0.33", + "@types/node": "^10.14.8", "@types/rimraf": "2.0.2", "@types/webdriverio": "4.6.1", "concurrently": "^3.5.1", "cpx": "^1.5.0", - "electron": "3.1.8", + "electron": "4.2.5", "htmlparser2": "^3.9.2", "mkdirp": "^0.5.1", "mocha": "^5.2.0", @@ -35,5 +35,8 @@ "tmp": "0.0.33", "typescript": "2.9.2", "watch": "^1.0.2" + }, + "dependencies": { + "vscode-uri": "^2.0.3" } } diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 7c60cabdfb..cf1dd45fbe 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -47,6 +47,10 @@ export class Application { return this.options.logger; } + get remote(): boolean { + return !!this.options.remote; + } + private _workspacePathOrFolder: string; get workspacePathOrFolder(): string { return this._workspacePathOrFolder; @@ -142,8 +146,12 @@ export class Application { await this.code.waitForWindowIds(ids => ids.length > 0); await this.code.waitForElement('.monaco-workbench'); + if (this.remote) { + await this.code.waitForElement('.monaco-workbench .statusbar-item[title="Editing on TestResolver"]'); + } + // wait a bit, since focus might be stolen off widgets - // as soon as they open (eg quick open) + // as soon as they open (e.g. quick open) await new Promise(c => setTimeout(c, 1000)); } } diff --git a/test/smoke/src/areas/editor/peek.ts b/test/smoke/src/areas/editor/peek.ts index baa00aff0f..1cb127456e 100644 --- a/test/smoke/src/areas/editor/peek.ts +++ b/test/smoke/src/areas/editor/peek.ts @@ -10,7 +10,7 @@ export class References { private static readonly REFERENCES_WIDGET = '.monaco-editor .zone-widget .zone-widget-container.peekview-widget.reference-zone-widget.results-loaded'; private static readonly REFERENCES_TITLE_FILE_NAME = `${References.REFERENCES_WIDGET} .head .peekview-title .filename`; private static readonly REFERENCES_TITLE_COUNT = `${References.REFERENCES_WIDGET} .head .peekview-title .meta`; - private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .reference`; + private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .highlight`; constructor(private code: Code) { } diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 5f1a5ac694..d664cd8108 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -20,6 +20,10 @@ export function setup() { await app.workbench.extensions.installExtension('michelkaporin.vscode-smoketest-check', 'vscode-smoketest-check'); await app.workbench.extensions.waitForExtensionsViewlet(); + + if (app.remote) { + await app.reload(); + } await app.workbench.quickopen.runCommand('Smoke Test Check'); await app.workbench.statusbar.waitForStatusbarText('smoke test', 'VS Code Smoke Test Check'); }); diff --git a/test/smoke/src/areas/git/git.test.ts b/test/smoke/src/areas/git/git.test.ts index 30ea90edaf..92a1850260 100644 --- a/test/smoke/src/areas/git/git.test.ts +++ b/test/smoke/src/areas/git/git.test.ts @@ -7,7 +7,7 @@ import * as cp from 'child_process'; import { Application } from '../../application'; const DIFF_EDITOR_LINE_INSERT = '.monaco-diff-editor .editor.modified .line-insert'; -const SYNC_STATUSBAR = 'div[id="workbench.parts.statusbar"] .statusbar-entry a[title$="Synchronize Changes"]'; +const SYNC_STATUSBAR = 'div[id="workbench.parts.statusbar"] .statusbar-item[title$="Synchronize Changes"]'; export function setup() { describe('Git', () => { diff --git a/test/smoke/src/areas/multiroot/multiroot.test.ts b/test/smoke/src/areas/multiroot/multiroot.test.ts index 996792effa..ca98e172aa 100644 --- a/test/smoke/src/areas/multiroot/multiroot.test.ts +++ b/test/smoke/src/areas/multiroot/multiroot.test.ts @@ -47,7 +47,8 @@ export function setup() { const app = this.app as Application; await app.workbench.quickopen.openQuickOpen('*.*'); - await app.workbench.quickopen.waitForQuickOpenElements(names => names.length === 6); + // TODO roblourens: Go to files finds welcome page: issue 74875 + await app.workbench.quickopen.waitForQuickOpenElements(names => names.length === 6 || names.length === 7); await app.workbench.quickopen.closeQuickOpen(); }); diff --git a/test/smoke/src/areas/problems/problems.ts b/test/smoke/src/areas/problems/problems.ts index 2c441a095e..77d6dcabf4 100644 --- a/test/smoke/src/areas/problems/problems.ts +++ b/test/smoke/src/areas/problems/problems.ts @@ -39,7 +39,7 @@ export class Problems { } public static getSelectorInProblemsView(problemType: ProblemSeverity): string { - let selector = problemType === ProblemSeverity.WARNING ? 'warning' : 'error'; + let selector = problemType === ProblemSeverity.WARNING ? 'severity-warning' : 'severity-error'; return `div[id="workbench.panel.markers"] .monaco-tl-contents .marker-icon.${selector}`; } diff --git a/test/smoke/src/areas/statusbar/statusbar.ts b/test/smoke/src/areas/statusbar/statusbar.ts index b3aecf0c4b..42da9019ac 100644 --- a/test/smoke/src/areas/statusbar/statusbar.ts +++ b/test/smoke/src/areas/statusbar/statusbar.ts @@ -38,7 +38,7 @@ export class StatusBar { } async waitForStatusbarText(title: string, text: string): Promise<void> { - await this.code.waitForTextContent(`${this.mainSelector} span[title="${title}"]`, text); + await this.code.waitForTextContent(`${this.mainSelector} .statusbar-item[title="${title}"]`, text); } private getSelector(element: StatusBarElement): string { @@ -48,17 +48,17 @@ export class StatusBar { case StatusBarElement.SYNC_STATUS: return `${this.mainSelector} ${this.leftSelector} .octicon.octicon-sync`; case StatusBarElement.PROBLEMS_STATUS: - return `${this.mainSelector} ${this.leftSelector} .task-statusbar-item[title="Problems"]`; + return `${this.mainSelector} ${this.leftSelector} .octicon.octicon-error`; case StatusBarElement.SELECTION_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-selection`; + return `${this.mainSelector} ${this.rightSelector}[title="Go to Line"]`; case StatusBarElement.INDENTATION_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-indentation`; + return `${this.mainSelector} ${this.rightSelector}[title="Select Indentation"]`; case StatusBarElement.ENCODING_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-encoding`; + return `${this.mainSelector} ${this.rightSelector}[title="Select Encoding"]`; case StatusBarElement.EOL_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-eol`; + return `${this.mainSelector} ${this.rightSelector}[title="Select End of Line Sequence"]`; case StatusBarElement.LANGUAGE_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-mode`; + return `${this.mainSelector} ${this.rightSelector}[title="Select Language Mode"]`; case StatusBarElement.FEEDBACK_ICON: return `${this.mainSelector} ${this.rightSelector} .monaco-dropdown.send-feedback`; default: diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 149a7ff773..3feda99159 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -37,7 +37,7 @@ export function setup() { await app.workbench.scm.waitForTitle(title => /quellcodeverwaltung/i.test(title)); await app.workbench.debug.openDebugViewlet(); - await app.workbench.debug.waitForTitle(title => /debuggen/i.test(title)); + await app.workbench.debug.waitForTitle(title => /debug/i.test(title)); await app.workbench.extensions.openExtensionsViewlet(); await app.workbench.extensions.waitForTitle(title => /erweiterungen/i.test(title)); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 6d683c4841..72dd61e255 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -164,7 +164,12 @@ async function setupRepository(): Promise<void> { console.log('*** Copying test project repository:', opts['test-repo']); rimraf.sync(workspacePath); // not platform friendly - cp.execSync(`cp -R "${opts['test-repo']}" "${workspacePath}"`); + if (process.platform === 'win32') { + cp.execSync(`xcopy /E "${opts['test-repo']}" "${workspacePath}"\\*`); + } else { + cp.execSync(`cp -R "${opts['test-repo']}" "${workspacePath}"`); + } + } else { if (!fs.existsSync(workspacePath)) { console.log('*** Cloning test project repository...'); diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index f6398446d1..1715804d3d 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -7,10 +7,12 @@ import * as path from 'path'; import * as cp from 'child_process'; import * as os from 'os'; import * as fs from 'fs'; +import * as mkdirp from 'mkdirp'; import { tmpName } from 'tmp'; import { IDriver, connect as connectDriver, IDisposable, IElement, Thenable } from './driver'; import { Logger } from '../logger'; import { ncp } from 'ncp'; +import { URI } from 'vscode-uri'; const repoPath = path.join(__dirname, '../../../..'); @@ -123,13 +125,12 @@ export async function spawn(options: SpawnOptions): Promise<Code> { '--driver', handle ]; + const env = process.env; + if (options.remote) { // Replace workspace path with URI - args.shift(); - args.push( - `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri`, - `vscode-remote://test+test${options.workspacePath}`, - ); + args[0] = `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(options.workspacePath).path}`; + if (codePath) { // running against a build: copy the test resolver extension const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver'); @@ -139,6 +140,9 @@ export async function spawn(options: SpawnOptions): Promise<Code> { } } args.push('--enable-proposed-api=vscode.vscode-test-resolver'); + const remoteDataDir = `${options.userDataDir}-server`; + mkdirp.sync(remoteDataDir); + env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; } if (!codePath) { @@ -157,7 +161,7 @@ export async function spawn(options: SpawnOptions): Promise<Code> { args.push(...options.extraArgs); } - const spawnOptions: cp.SpawnOptions = {}; + const spawnOptions: cp.SpawnOptions = { env }; const child = cp.spawn(electronPath, args, spawnOptions); diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index 5e1fd36aa4..4af77efee7 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -44,15 +44,15 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@8.0.33": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" - integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== +"@types/node@^10.12.18": + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== -"@types/node@^8.0.24": - version "8.10.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.23.tgz#e5ccfdafff42af5397c29669b6d7d65f7d629a00" - integrity sha512-aEp5ZTLr4mYhR9S85cJ+sEYkcsgFY10N1Si5m49iTAVzanZXOwp/pgw6ibFLKXxpflqm71aSWZCRtnTXXO56gA== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== "@types/rimraf@2.0.2": version "2.0.2" @@ -74,14 +74,6 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - ajv@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.3.0.tgz#4414ff74a50879c208ee5fdc826e32c303549eda" @@ -126,9 +118,9 @@ aproba@^1.0.3: integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== are-we-there-yet@~1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" - integrity sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0= + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== dependencies: delegates "^1.0.0" readable-stream "^2.0.6" @@ -140,11 +132,21 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: +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-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== +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= + array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" @@ -170,6 +172,11 @@ array-unique@^0.2.1: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -180,36 +187,31 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= +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= async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - integrity sha1-GdOGodntxufByF04iu28xW0zYC0= + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8= +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= -aws4@^1.2.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" - integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w== - aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" @@ -228,6 +230,19 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -236,29 +251,15 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" binary-extensions@^1.0.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" - integrity sha1-RqoXUftqL5PuXmibsQh9SxTGwgU= - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== bluebird@^2.9.34: version "2.11.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8= - dependencies: - hoek "2.x.x" - boom@4.x.x: version "4.3.1" resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" @@ -290,6 +291,22 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -300,6 +317,21 @@ builtin-modules@^1.0.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -350,6 +382,21 @@ chokidar@^1.6.0: optionalDependencies: fsevents "^1.0.0" +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -360,6 +407,14 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" @@ -382,6 +437,11 @@ commander@^2.8.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -415,10 +475,15 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + core-js@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" - integrity sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs= + version "2.6.9" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" + integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -447,13 +512,6 @@ crypt@~0.0.1: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g= - dependencies: - boom "2.x.x" - cryptiles@3.x.x: version "3.1.2" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" @@ -480,7 +538,7 @@ date-fns@^1.23.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" integrity sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw== -debug@2.6.9, debug@^2.1.3, debug@^2.2.0: +debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -494,7 +552,7 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" -debug@^3.0.0: +debug@^3.0.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -506,15 +564,37 @@ decamelize@^1.1.2: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-extend@~0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" - integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8= +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" delayed-stream@~1.0.0: version "1.0.0" @@ -596,12 +676,12 @@ electron-download@^4.1.0: semver "^5.4.1" sumchecker "^2.0.2" -electron@3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.8.tgz#01b0b147dfcca47967ff07dbf72bf5e96125a2ac" - integrity sha512-1MiFoMzxGaR0wDfwFE5Ydnuk6ry/4lKgF0c+NFyEItxM/WyEHNZPNjJAeKJ+M/0sevmZ+6W4syNZnQL5M3GgsQ== +electron@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/electron/-/electron-4.2.5.tgz#1d1432c38e2b2190318f7ca30897cdfdcf942e5a" + integrity sha512-P132MXzTtyn2ZaekhKi5JeHzmTAMuR/uQt4hrg3vfJV7fpncx9SL6UFwHAK1DU13iiyZJqqIziNUu+o8nODHsA== dependencies: - "@types/node" "^8.0.24" + "@types/node" "^10.12.18" electron-download "^4.1.0" extract-zip "^1.0.3" @@ -641,6 +721,19 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" @@ -648,7 +741,22 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -extend@~3.0.0, extend@~3.0.1: +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, 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" + +extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= @@ -660,6 +768,20 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + extract-zip@^1.0.3: version "1.6.6" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" @@ -698,16 +820,26 @@ filename-regex@^2.0.0: integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - integrity sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM= + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== dependencies: is-number "^2.1.0" isobject "^2.0.0" - randomatic "^1.1.3" + randomatic "^3.0.0" repeat-element "^1.1.2" repeat-string "^1.5.2" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" @@ -721,7 +853,7 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" -for-in@^1.0.1: +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= @@ -738,15 +870,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE= - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - form-data@~2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" @@ -756,6 +879,13 @@ form-data@~2.3.1: combined-stream "^1.0.5" mime-types "^2.1.12" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + fs-extra@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -765,37 +895,25 @@ fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + dependencies: + minipass "^2.2.1" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" - integrity sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q== + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.39" - -fstream-ignore@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" - integrity sha1-nDHa40dnAY/h0kmyTa2mfQktoQU= - dependencies: - fstream "^1.0.0" - inherits "2" - minimatch "^3.0.0" - -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" + nan "^2.12.1" + node-pre-gyp "^0.12.0" gauge@~2.7.3: version "2.7.4" @@ -816,6 +934,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -857,6 +980,11 @@ glob@7.1.2, glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" +graceful-fs@^4.1.11: + version "4.2.0" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" + integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== + graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -867,24 +995,11 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4= - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio= - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - har-validator@~5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" @@ -915,15 +1030,36 @@ has-unicode@^2.0.0: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= -hawk@3.1.3, hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ= +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" hawk@~6.0.2: version "6.0.2" @@ -940,11 +1076,6 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= - hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" @@ -967,15 +1098,6 @@ htmlparser2@^3.9.2: inherits "^2.0.1" readable-stream "^2.0.2" -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8= - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -985,6 +1107,20 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" @@ -1000,7 +1136,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -1010,6 +1146,20 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4= +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -1034,6 +1184,38 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -1046,11 +1228,18 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= +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@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" @@ -1070,6 +1259,11 @@ is-fullwidth-code-point@^1.0.0: dependencies: number-is-nan "^1.0.0" +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -1091,6 +1285,18 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-plain-object@^2.0.3, 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-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -1111,6 +1317,11 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -1128,6 +1339,11 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1148,13 +1364,6 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - 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" @@ -1182,7 +1391,7 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -kind-of@^3.0.2: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= @@ -1196,6 +1405,16 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -1225,11 +1444,28 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + md5@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" @@ -1279,16 +1515,30 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" integrity sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE= -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - mime-types@^2.1.12, mime-types@~2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" @@ -1296,14 +1546,7 @@ mime-types@^2.1.12, mime-types@~2.1.17: dependencies: mime-db "~1.30.0" -mime-types@~2.1.7: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -1320,6 +1563,29 @@ minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minipass@^2.2.1, minipass@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" @@ -1327,7 +1593,7 @@ mkdirp@0.5.0: dependencies: minimist "0.0.8" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -1380,32 +1646,57 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -nan@^2.3.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" - integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== +nan@^2.12.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" ncp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= -node-pre-gyp@^0.6.39: - version "0.6.39" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" - integrity sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ== +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== dependencies: detect-libc "^1.0.2" - hawk "3.1.3" mkdirp "^0.5.1" + needle "^2.2.1" nopt "^4.0.1" + npm-packlist "^1.1.6" npmlog "^4.0.2" - rc "^1.1.7" - request "2.81.0" + rc "^1.2.7" rimraf "^2.6.1" semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" + tar "^4" nopt@^4.0.1: version "4.0.1" @@ -1432,6 +1723,19 @@ normalize-path@^2.0.0, normalize-path@^2.0.1: dependencies: remove-trailing-separator "^1.0.1" +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" + integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -1460,7 +1764,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -oauth-sign@~0.8.1, oauth-sign@~0.8.2: +oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= @@ -1470,11 +1774,27 @@ object-assign@^4.0.1, object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + object-keys@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -1483,7 +1803,14 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -once@^1.3.0, once@^1.3.3: +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -1525,6 +1852,11 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -1542,10 +1874,10 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - integrity sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME= +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== path-type@^1.0.0: version "1.1.0" @@ -1561,11 +1893,6 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -1597,6 +1924,11 @@ portastic@^1.0.1: commander "^2.8.1" debug "^2.2.0" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" @@ -1616,9 +1948,9 @@ process-nextick-args@~1.0.6: integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + 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== progress-stream@^1.1.0: version "1.2.0" @@ -1633,35 +1965,21 @@ punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM= - qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== -randomatic@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - integrity sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how== +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" -rc@^1.1.7: - version "1.2.6" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" - integrity sha1-6xiYnG1PTxYsOZ953dKfODVWgJI= - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -rc@^1.2.1: +rc@^1.2.1, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -1701,7 +2019,7 @@ readable-stream@^2.0.2, readable-stream@^2.2.2: string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@^2.0.6, readable-stream@^2.1.4: +readable-stream@^2.0.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -1725,14 +2043,13 @@ readable-stream@~1.1.9: string_decoder "~0.10.x" readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg= + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" + graceful-fs "^4.1.11" + micromatch "^3.1.10" readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" redent@^1.0.0: version "1.0.0" @@ -1743,9 +2060,9 @@ redent@^1.0.0: strip-indent "^1.0.1" regenerator-runtime@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" - integrity sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A== + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== regex-cache@^0.4.2: version "0.4.4" @@ -1754,17 +2071,25 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + 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= repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -1776,34 +2101,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA= - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - request@^2.45.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" @@ -1832,14 +2129,24 @@ request@^2.45.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -resolve@^1.1.7: - version "1.7.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.0.tgz#2bdf5374811207285df0df652b78f118ab8f3c5e" - integrity sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA== - dependencies: - path-parse "^1.0.5" +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: +resolve@^1.1.7: + version "1.11.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" + integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== + dependencies: + path-parse "^1.0.6" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== @@ -1856,11 +2163,38 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== -"semver@2 || 3 || 4 || 5", semver@^5.3.0: +safe-buffer@^5.1.2: + 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-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +"semver@2 || 3 || 4 || 5": version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== +semver@^5.3.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + semver@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" @@ -1871,10 +2205,15 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" shell-quote@^1.6.1: version "1.6.1" @@ -1898,12 +2237,35 @@ single-line-log@^1.1.2: dependencies: string-width "^1.0.1" -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg= +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: - hoek "2.x.x" + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" sntp@2.x.x: version "2.1.0" @@ -1912,6 +2274,27 @@ sntp@2.x.x: dependencies: hoek "4.x.x" +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" @@ -1939,6 +2322,13 @@ speedometer@~0.1.2: resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" integrity sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0= +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + sshpk@^1.7.0: version "1.13.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" @@ -1954,7 +2344,15 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -string-width@^1.0.1, string-width@^1.0.2: +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= @@ -1963,6 +2361,14 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -1982,7 +2388,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringstream@~0.0.4, stringstream@~0.0.5: +stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg= @@ -2060,28 +2466,18 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -tar-pack@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" - integrity sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg== +tar@^4: + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== dependencies: - debug "^2.2.0" - fstream "^1.0.10" - fstream-ignore "^1.0.5" - once "^1.3.3" - readable-stream "^2.1.4" - rimraf "^2.5.1" - tar "^2.2.1" - uid-number "^0.0.6" - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.5" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" throttleit@0.0.2: version "0.0.2" @@ -2103,12 +2499,30 @@ tmp@0.0.33: dependencies: os-tmpdir "~1.0.2" -tough-cookie@~2.3.0: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA== +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: - punycode "^1.4.1" + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" tough-cookie@~2.3.3: version "2.3.3" @@ -2149,26 +2563,44 @@ typescript@2.9.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== -uid-number@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + 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= -uuid@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== - uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" @@ -2191,6 +2623,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vscode-uri@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.3.tgz#25e5f37f552fbee3cec7e5f80cef8469cefc6543" + integrity sha512-4D3DI3F4uRy09WNtDGD93H9q034OHImxiIcSq664Hq1Y1AScehlP3qqZyTkX/RWxeu0MRMHGkrxYqm2qlDF/aw== + watch@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/watch/-/watch-1.0.2.tgz#340a717bde765726fa0aa07d721e0147a551df0c" @@ -2200,11 +2637,11 @@ watch@^1.0.2: minimist "^1.2.0" wide-align@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" - integrity sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w== + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: - string-width "^1.0.2" + string-width "^1.0.2 || 2" wrappy@1: version "1.0.2" @@ -2223,6 +2660,11 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" +yallist@^3.0.0, yallist@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + yauzl@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" diff --git a/test/splitview/package.json b/test/splitview/package.json new file mode 100644 index 0000000000..d6ce0c3737 --- /dev/null +++ b/test/splitview/package.json @@ -0,0 +1,13 @@ +{ + "name": "splitview", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "devDependencies": { + "koa": "^2.5.1", + "koa-mount": "^3.0.0", + "koa-route": "^3.2.0", + "koa-static": "^5.0.0", + "mz": "^2.7.0" + } +} \ No newline at end of file diff --git a/test/splitview/public/index.html b/test/splitview/public/index.html new file mode 100644 index 0000000000..602682d6aa --- /dev/null +++ b/test/splitview/public/index.html @@ -0,0 +1,138 @@ +<html> + +<head> + <meta charset="utf-8"> + <title>Splitview + + + + +
+
+ + + + + + \ No newline at end of file diff --git a/test/splitview/server.js b/test/splitview/server.js new file mode 100644 index 0000000000..407e35c3f3 --- /dev/null +++ b/test/splitview/server.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +const fs = require('mz/fs'); +const path = require('path'); +const Koa = require('koa'); +const _ = require('koa-route'); +const serve = require('koa-static'); +const mount = require('koa-mount'); + +const app = new Koa(); + +app.use(serve('public')); +app.use(mount('/static', serve('../../out'))); + +app.listen(3000); +console.log('http://localhost:3000'); \ No newline at end of file diff --git a/test/splitview/yarn.lock b/test/splitview/yarn.lock new file mode 100644 index 0000000000..237201a684 --- /dev/null +++ b/test/splitview/yarn.lock @@ -0,0 +1,341 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +accepts@^1.2.2: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + +any-promise@^1.0.0, any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +content-disposition@~0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-type@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookies@~0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" + integrity sha1-fIphX1SBxhq58WyDNzG8uPZjuZs= + dependencies: + depd "~1.1.1" + keygrip "~1.0.2" + +debug@*, debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^2.6.1: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@^1.1.0, depd@~1.1.1, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +error-inject@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" + integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= + +escape-html@~1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +fresh@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +http-assert@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" + integrity sha1-oxpc+IyHPsu1eWkH1NbxMujAHko= + dependencies: + deep-equal "~1.0.1" + http-errors "~1.6.1" + +http-errors@^1.2.8, http-errors@^1.6.3, http-errors@~1.6.1, http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +is-generator-function@^1.0.3: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +keygrip@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" + integrity sha1-rTKXxVcGneqLz+ek+kkbdcXd65E= + +koa-compose@^3.0.0, koa-compose@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" + integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= + dependencies: + any-promise "^1.1.0" + +koa-compose@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-convert@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" + integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= + dependencies: + co "^4.6.0" + koa-compose "^3.0.0" + +koa-is-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" + integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= + +koa-mount@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-3.0.0.tgz#08cab3b83d31442ed8b7e75c54b1abeb922ec197" + integrity sha1-CMqzuD0xRC7Yt+dcVLGr65IuwZc= + dependencies: + debug "^2.6.1" + koa-compose "^3.2.1" + +koa-route@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce" + integrity sha1-dimLmaa8+p44yrb+XHmocz51i84= + dependencies: + debug "*" + methods "~1.1.0" + path-to-regexp "^1.2.0" + +koa-send@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" + integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== + dependencies: + debug "^3.1.0" + http-errors "^1.6.3" + mz "^2.7.0" + resolve-path "^1.4.0" + +koa-static@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" + integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== + dependencies: + debug "^3.1.0" + koa-send "^5.0.0" + +koa@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.1.tgz#79f8b95f8d72d04fe9a58a8da5ebd6d341103f9c" + integrity sha512-cchwbMeG2dv3E2xTAmheDAuvR53tPgJZN/Hf1h7bTzJLSPcFZp8/t5+bNKJ6GaQZoydhZQ+1GNruhKdj3lIrug== + dependencies: + accepts "^1.2.2" + content-disposition "~0.5.0" + content-type "^1.0.0" + cookies "~0.7.0" + debug "*" + delegates "^1.0.0" + depd "^1.1.0" + destroy "^1.0.3" + error-inject "~1.0.0" + escape-html "~1.0.1" + fresh "^0.5.2" + http-assert "^1.1.0" + http-errors "^1.2.8" + is-generator-function "^1.0.3" + koa-compose "^4.0.0" + koa-convert "^1.2.0" + koa-is-json "^1.0.0" + mime-types "^2.0.7" + on-finished "^2.1.0" + only "0.0.2" + parseurl "^1.3.0" + statuses "^1.2.0" + type-is "^1.5.5" + vary "^1.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +methods@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@^2.0.7, mime-types@~2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +on-finished@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +only@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= + +parseurl@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= + +path-is-absolute@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-to-regexp@^1.2.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= + dependencies: + isarray "0.0.1" + +resolve-path@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" + integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= + dependencies: + http-errors "~1.6.2" + path-is-absolute "1.0.1" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +"statuses@>= 1.4.0 < 2", statuses@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.0" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= + dependencies: + any-promise "^1.0.0" + +type-is@^1.5.5: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + +vary@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= diff --git a/yarn.lock b/yarn.lock index ab0d44422c..f1c206dace 100644 --- a/yarn.lock +++ b/yarn.lock @@ -102,10 +102,10 @@ dependencies: "@types/node" "*" -"@types/keytar@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.0.1.tgz#e2cf6405dc33861424e59b67516c66d2cf7bc21b" - integrity sha512-loKBID6UL4QjhD2scuvv6oAPlQ/WAY7aYTDyKlKo7fIgriLS8EZExqT567cHL5CY6si51MRoX1+r3mitD3eYrA== +"@types/keytar@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.0.tgz#ca24e6ee6d0df10c003aafe26e93113b8faf0d8e" + integrity sha512-cq/NkUUy6rpWD8n7PweNQQBpw2o0cf5v6fbkUVEpOB9VzzIvyPvSEId1/goIj+MciW2v1Lw5mRimKO01XgE9EA== "@types/minimist@^1.2.0": version "1.2.0" @@ -117,7 +117,7 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.39.tgz#f68d63db8b69c38e9558b4073525cf96c4f7a829" integrity sha1-9o1j24tpw46VWLQHNSXPlsT3qCk= -"@types/node@*", "@types/node@10.12.12", "@types/node@^10.11.7", "@types/node@^10.12.12": +"@types/node@*", "@types/node@^10.11.7", "@types/node@^10.12.12": version "10.12.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.12.tgz#e15a9d034d9210f00320ef718a50c4a799417c47" integrity sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A== @@ -359,26 +359,38 @@ acorn-dynamic-import@^3.0.0: dependencies: acorn "^5.0.0" +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= + dependencies: + acorn "^3.0.4" + acorn-jsx@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= + acorn@^5.0.0, acorn@^5.6.2: version "5.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" integrity sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ== +acorn@^5.5.0: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + acorn@^6.0.2: version "6.0.7" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.7.tgz#490180ce18337270232d9488a44be83d9afb7fd3" integrity sha512-HNJNgE60C9eOTgn974Tlp3dpLZdUr+SoxxDwPaY9J/kDNOLQTkaDgwBUXAF4SSsrAwD9RpdxuHK/EbuF+W9Ahw== -acorn@^6.0.7: - version "6.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" - integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== - agent-base@4, agent-base@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" @@ -393,6 +405,11 @@ agent-base@~4.2.0: dependencies: es6-promisify "^5.0.0" +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= + ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" @@ -408,7 +425,7 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^5.3.0: +ajv@^5.2.3, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= @@ -438,16 +455,6 @@ ajv@^6.5.3, ajv@^6.6.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.9.1: - version "6.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" - integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -895,6 +902,15 @@ azure-storage@^2.10.2: xml2js "0.2.8" xmlbuilder "^9.0.7" +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" @@ -978,11 +994,18 @@ binaryextensions@~1.0.0: resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-1.0.1.tgz#1e637488b35b58bda5f4774bf96a5212a8c90755" integrity sha1-HmN0iLNbWL2l9HdL+WpSEqjJB1U= -bindings@^1.2.1, bindings@^1.3.0: +bindings@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" integrity sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" @@ -1259,6 +1282,18 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= + callsites@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" @@ -1292,6 +1327,11 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" @@ -1374,6 +1414,11 @@ chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= + chardet@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" @@ -1562,6 +1607,15 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -1943,7 +1997,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^5.0.1: +cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= @@ -1952,7 +2006,7 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -2153,7 +2207,7 @@ debug@^4.0.1: dependencies: ms "^2.1.1" -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -2195,6 +2249,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8= + deep-is@~0.1.2, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -2363,13 +2422,6 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - documentdb@^1.5.1: version "1.14.1" resolved "https://registry.yarnpkg.com/documentdb/-/documentdb-1.14.1.tgz#1a4716c0b38a40daf375dc9a4b2a2beb4e26294a" @@ -2466,14 +2518,6 @@ each-props@^1.3.0: is-plain-object "^2.0.1" object.defaults "^1.1.0" -eachr@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eachr/-/eachr-3.2.0.tgz#2c35e43ea086516f7997cf80b7aa64d55a4a4484" - integrity sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ= - dependencies: - editions "^1.1.1" - typechecker "^4.3.0" - ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -2491,11 +2535,6 @@ ecstatic@^3.0.0: minimist "^1.1.0" url-join "^2.0.5" -editions@^1.1.1, editions@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" - integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg== - editorconfig@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.0.tgz#b6dd4a0b6b9e76ce48e066bdc15381aebb8804fd" @@ -2707,18 +2746,18 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" -eslint-scope@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" - integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA== +eslint-scope@^3.7.1: + version "3.7.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" + integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" + integrity sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -2733,47 +2772,49 @@ eslint-visitor-keys@^1.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== -eslint@>=4.18.2: - version "5.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== +eslint@^4.18.2: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" + ajv "^5.3.0" + babel-code-frame "^6.22.0" chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" + espree "^3.5.4" + esquery "^1.0.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^2.0.0" functional-red-black-tree "^1.0.1" glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" + globals "^11.0.1" + ignore "^3.3.3" imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" + lodash "^4.17.4" + minimatch "^3.0.2" mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" path-is-inside "^1.0.2" + pluralize "^7.0.0" progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" - text-table "^0.2.0" + strip-json-comments "~2.0.1" + table "4.0.2" + text-table "~0.2.0" eslint@^5.0.1: version "5.13.0" @@ -2817,6 +2858,14 @@ eslint@^5.0.1: table "^5.0.2" text-table "^0.2.0" +espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== + dependencies: + acorn "^5.5.0" + acorn-jsx "^3.0.0" + espree@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c" @@ -2826,15 +2875,6 @@ espree@^5.0.0: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== - dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - esprima@2.5.x: version "2.5.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.5.0.tgz#f387a46fd344c1b1a39baf8c20bfb43b6d0058cc" @@ -2855,7 +2895,7 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" integrity sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw== -esquery@^1.0.1: +esquery@^1.0.0, esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== @@ -2934,6 +2974,19 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -2961,10 +3014,10 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expand-template@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.1.tgz#981f188c0c3a87d2e28f559bc541426ff94f21dd" - integrity sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg== +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" @@ -3041,6 +3094,15 @@ extend@^3.0.2, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^2.0.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + external-editor@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6" @@ -3080,15 +3142,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-opts@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/extract-opts/-/extract-opts-3.3.1.tgz#5abbedc98c0d5202e3278727f9192d7e086c6be1" - integrity sha1-WrvtyYwNUgLjJ4cn+Rktfghsa+E= - dependencies: - eachr "^3.2.0" - editions "^1.1.1" - typechecker "^4.3.0" - extract-zip@^1.6.5: version "1.6.6" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" @@ -3160,6 +3213,13 @@ fd-slicer@~1.0.1: dependencies: pend "~1.2.0" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -3175,12 +3235,10 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== filename-regex@^2.0.0: version "2.0.0" @@ -3306,20 +3364,6 @@ flat-cache@^1.2.1: graceful-fs "^4.1.2" write "^0.2.1" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flatted@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" - integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== - flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" @@ -3449,9 +3493,9 @@ fs-extra@^2.0.0: jsonfile "^2.1.0" fs-extra@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.0.tgz#8cc3f47ce07ef7b3593a11b9fb245f7e34c041d6" - integrity sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ== + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" @@ -3537,16 +3581,16 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gc-signals@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/gc-signals/-/gc-signals-0.0.2.tgz#1cfa8a00adecaeeb93ea0dda72dad9e9f333e62f" - integrity sha512-Ghj4Co6x5bd3dvbAFuiDc6gN+BVK8ic8CBn70dXjzrtbC5hq4a+s4S6acEvftMP7LcQuHKN5v+30PGXhkCLoCQ== - get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" integrity sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U= +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" @@ -3557,19 +3601,18 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -getmac@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/getmac/-/getmac-1.4.1.tgz#cfefcb3ee7d7a73cba5292129cb100c19afbe17a" - integrity sha512-mQp+8D+grQX0gG8EJn6VfH0PxE56ZKNsTguOMxPShAiVk9lvH8Ey36eXepG705Ac1HCsvaSrQ/6bPHZ0++F/Mg== - dependencies: - editions "^1.3.4" - extract-opts "^3.2.0" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -3736,6 +3779,11 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +globals@^11.0.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^11.7.0: version "11.10.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.10.0.tgz#1e09776dffda5e01816b3bb4077c8b59c24eaa50" @@ -4343,20 +4391,27 @@ iconv-lite@0.4.19, iconv-lite@^0.4.19: resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== -iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== +iconv-lite@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" + integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.4.24: +iconv-lite@^0.4.17, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.4.22, iconv-lite@^0.4.4: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ieee754@^1.1.11, ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" @@ -4374,7 +4429,7 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^3.3.5: +ignore@^3.3.3, ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== @@ -4445,10 +4500,30 @@ ini@^1.3.4, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4= -innosetup-compiler@^5.5.60: - version "5.5.62" - resolved "https://registry.yarnpkg.com/innosetup-compiler/-/innosetup-compiler-5.5.62.tgz#fc12cd8d17cf75a2e3833b2754a5c2bc4f26cc4e" - integrity sha1-/BLNjRfPdaLjgzsnVKXCvE8mzE4= +innosetup@5.6.1: + version "5.6.1" + resolved "https://registry.yarnpkg.com/innosetup/-/innosetup-5.6.1.tgz#6e7031ba35b23e716e4f29686bc994052e0c278c" + integrity sha512-Eit24N3JR8O0Wpuq/dMWCl2r550eiNP2124SbdbwOob43x89WPGL/SGpZG5EPHu20kV2N+4TwvHwFIM8pFUJ0g== + +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" inquirer@^6.0.0: version "6.1.0" @@ -4488,25 +4563,6 @@ inquirer@^6.1.0: strip-ansi "^5.0.0" through "^2.3.6" -inquirer@^6.2.2: - version "6.4.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.4.0.tgz#6f9284047c4e48b76b169e46b3ae3b2171ce30a2" - integrity sha512-O3qJQ+fU/AI1K2y5/RjqefMEQTdJQf6sPTvyRA1bx6D634ADxcu97u6YOUciIeU2OWIuvpUsQs6Wx3Fdi3eFaQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.11" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - int64-buffer@^0.1.9: version "0.1.9" resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.9.tgz#9e039da043b24f78b196b283e04653ef5e990f61" @@ -4522,6 +4578,11 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -4793,6 +4854,11 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4978,6 +5044,11 @@ js-beautify@^1.8.9: mkdirp "~0.5.0" nopt "~4.0.1" +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4991,7 +5062,7 @@ js-yaml@3.x: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.11.0, js-yaml@^3.13.0: +js-yaml@^3.11.0, js-yaml@^3.13.0, js-yaml@^3.9.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -5128,13 +5199,13 @@ just-debounce@^1.0.0: resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= -keytar@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/keytar/-/keytar-4.2.1.tgz#8a06a6577fdf6373e0aa6b112277e63dec77fd12" - integrity sha1-igamV3/fY3PgqmsRInfmPex3/RI= +keytar@^4.11.0: + version "4.11.0" + resolved "https://registry.yarnpkg.com/keytar/-/keytar-4.11.0.tgz#891569045b287a0dabe69320e2381e059b02363f" + integrity sha512-cGn2xd4NY0yCBrU5zQ/lwIagP1UBOhUEemi6iSJU2gshN1RHkxHekSdLUji9IWNo5B1Va/iwXXWzGD2p8ziqfQ== dependencies: - nan "2.8.0" - prebuild-install "^2.4.1" + nan "2.14.0" + prebuild-install "5.3.0" kind-of@^1.1.0: version "1.1.0" @@ -5211,6 +5282,13 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + lcov-parse@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" @@ -5499,6 +5577,13 @@ mamacro@^0.0.3: resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5586,6 +5671,15 @@ mem@^1.1.0: dependencies: mimic-fn "^1.0.0" +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -5634,7 +5728,7 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^2.1.5, micromatch@^2.3.7: +micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= @@ -5719,6 +5813,11 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" integrity sha1-5md4PZLonb00KBi1IwudYqZyrRg= +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + mimic-response@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" @@ -5922,10 +6021,10 @@ mute-stream@0.0.7, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@2.8.0, nan@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" - integrity sha1-7XFfP+neArV6XmJS2QqWZ14fCFo= +nan@2.14.0, nan@^2.0.0, nan@^2.13.2, nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== nan@^2.10.0: version "2.11.0" @@ -5937,12 +6036,7 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== -nan@^2.13.2: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - -nan@^2.9.2, nan@~2.10.0: +nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== @@ -5964,26 +6058,26 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-build-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" + integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA== + native-is-elevated@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.2.1.tgz#70a2123a8575b9f624a3ef465d98cb74ae017385" integrity sha1-cKISOoV1ufYko+9GXZjLdK4Bc4U= -native-keymap@1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-1.2.5.tgz#1035a9417b9a9340cf8097763a43c76d588165a5" - integrity sha1-EDWpQXuak0DPgJd2OkPHbViBZaU= +native-keymap@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.0.0.tgz#7491ba8f9cc75bd6ada7e754dadb7716c793a3e3" + integrity sha512-KIlDZp0yKaHaGIkEVdlYN3QIaZICXwG1qh/oeBeQdM8TwAi90IAZlAD57qsNDkEvIJIzerCzb5jYYQAdHGBgYg== native-watchdog@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.0.0.tgz#97344e83cd6815a8c8e6c44a52e7be05832e65ca" integrity sha512-HKQATz5KLUMPyQQ/QaalzgTXaGz2plYPBxjyalaR4ECIu/UznXY8YJD+a9SLkkcvtxnJ8/zHLY3xik06vUZ7uA== -natives@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.6.tgz#a603b4a498ab77173612b9ea1acdec4d980f00bb" - integrity sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -6020,10 +6114,10 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" integrity sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA== -node-abi@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.1.tgz#7628c4d4ec4e9cd3764ceb3652f36b2e7f8d4923" - integrity sha512-pUlswqpHQ7zGPI9lGjZ4XDNIEUDbHxsltfIRb7dTnYdhgHWHOcB0MLZKLoCz6UMcGzSPG5wGl1HODZVQAUsH6w== +node-abi@^2.7.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.9.0.tgz#ae4075b298dab2d92dd1e22c48ccc7ffd7f06200" + integrity sha512-jmEOvv0eanWjhX8dX1pmjb7oJl1U1oR4FOh0b2GnvALwSYoOdU7sj+kLDSAyjo4pfC9aj/IxkloxdLJQhSSQBA== dependencies: semver "^5.4.1" @@ -6077,10 +6171,10 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-pty@0.9.0-beta9: - version "0.9.0-beta9" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0-beta9.tgz#75cffcf4026f543475c115f017ca7fe66cf6e7fe" - integrity sha512-h6e8jUikGSZwqt1JHmzT5Zi0fdUCultX/BWrS35suTaZNJm/YSJA2QDG9HTVoSA6dhRvtFoaGiBtgbX9uZKe6w== +node-pty@0.9.0-beta19: + version "0.9.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0-beta19.tgz#0fd381b2006f4665c4c2ee0509219e591842371a" + integrity sha512-MkKEvBnauGnzgXNr/oaoWQLVXm1gheIKZs4YQp8883ZiETmbEnpSvD0FU3bELcPXG5VFPRqIGsQJ4KUMBLzkPA== dependencies: nan "^2.13.2" @@ -6133,18 +6227,18 @@ normalize-path@^1.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" integrity sha1-MtDkcvkf80VwHBWoMRAY07CpA3k= -normalize-path@^2.0.0, normalize-path@^2.1.1: +normalize-path@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + integrity sha1-R4hqwWYnYNQmG32XnSQXCdPOP3o= + +normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" - integrity sha1-R4hqwWYnYNQmG32XnSQXCdPOP3o= - normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -6202,6 +6296,16 @@ npmlog@^4.0.1, npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" +nsfw@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/nsfw/-/nsfw-1.2.5.tgz#febe581af616f7b042f89df133abe62416c4c803" + integrity sha512-m3mwZUKXiCR69PDMLfAmKmiNzy0Oe9LhFE0DYZC5cc1htNj5Hyb1sAgglXhuaDkibFy22AVvPC5cCFB3A6mYIw== + dependencies: + fs-extra "^7.0.0" + lodash.isinteger "^4.0.4" + lodash.isundefined "^3.0.1" + nan "^2.0.0" + nth-check@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" @@ -6350,12 +6454,17 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -oniguruma@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.0.2.tgz#a5c922cf7066da1dbcc60f6385a90437a83f8d0b" - integrity sha512-zCsdNxTrrB4yVPMxhcIODGv1p4NVBu9WvsWnIGhMpu5djO4MQWXrC7YKjtza+OyoRqqgy27CqYWa1h5e2DDbig== +onigasm-umd@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" + integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw== + +oniguruma@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.2.0.tgz#c9a59c1ea7b9fe67e237a02e02139b638856f3af" + integrity sha512-bh+ZLdykY1sdIx8jBp2zpLbVFDBc3XmKH4Ceo2lijNaN1WhEqtnpqFlmtCbRuDB17nJ58RAUStVwfW8e8uEbnA== dependencies: - nan "^2.10.0" + nan "^2.14.0" opener@~1.4.0: version "1.4.3" @@ -6449,6 +6558,15 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -6477,11 +6595,21 @@ p-all@^1.0.0: dependencies: p-map "^1.0.0" +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + p-limit@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -6737,6 +6865,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picomatch@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" + integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -6801,6 +6934,11 @@ plugin-error@1.0.1, plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== + portfinder@^1.0.13: version "1.0.20" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" @@ -7064,22 +7202,23 @@ postcss@^7.0.5: source-map "^0.6.1" supports-color "^6.1.0" -prebuild-install@^2.4.1: - version "2.5.3" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.5.3.tgz#9f65f242782d370296353710e9bc843490c19f69" - integrity sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g== +prebuild-install@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.0.tgz#58b4d8344e03590990931ee088dd5401b03004c8" + integrity sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg== dependencies: detect-libc "^1.0.3" - expand-template "^1.0.2" + expand-template "^2.0.3" github-from-package "0.0.0" minimist "^1.2.0" mkdirp "^0.5.1" - node-abi "^2.2.0" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" noop-logger "^0.1.1" npmlog "^4.0.1" os-homedir "^1.0.1" pump "^2.0.1" - rc "^1.1.6" + rc "^1.2.7" simple-get "^2.7.0" tar-fs "^1.13.0" tunnel-agent "^0.6.0" @@ -7227,6 +7366,14 @@ pump@^2.0.0, pump@^2.0.1: end-of-stream "^1.1.0" once "^1.3.1" +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + pumpify@^1.3.3, pumpify@^1.3.5: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" @@ -7348,7 +7495,17 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" -rc@1.2.8, rc@^1.1.2, rc@^1.1.6, rc@^1.2.7: +rc@^1.1.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" + integrity sha1-2M6ctX6NZNnHut2YdsfDTL48cHc= + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7539,6 +7696,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -7691,6 +7853,19 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +require-uncached@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -7711,6 +7886,11 @@ resolve-dir@^1.0.0, resolve-dir@^1.0.1: expand-tilde "^2.0.0" global-modules "^1.0.0" +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -7772,7 +7952,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@2.6.3: +rimraf@2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -7820,6 +8000,18 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= + rxjs@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.0.tgz#a7db14ab157f9d7aac6a56e655e7a3860d39bf26" @@ -8128,7 +8320,14 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= -slice-ansi@^2.0.0, slice-ansi@^2.1.0: +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== + dependencies: + is-fullwidth-code-point "^2.0.0" + +slice-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== @@ -8259,14 +8458,14 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= -spdlog@0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.8.1.tgz#dfb3f3422ab3efe32be79e4769b95440ed72699f" - integrity sha512-W0s8IOXpn86md+8PJ4mJeB/22thykzH5YaNc3Rgnql4x4/zFIhvNiEx6/a1arnqvmJF0HtRO0Ehlswg0WcwTLQ== +spdlog@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.9.0.tgz#c85dd9d0b9cd385f6f3f5b92dc9d2e1691092b5c" + integrity sha512-AeLWPCYjGi4w5DfpXFKb9pCdgMe4gFBMroGfgwXiNfzwmcNYGoFQkIuD1MChZBR1Iwrx0nGhsTSHFslt/qfTAQ== dependencies: - bindings "^1.3.0" + bindings "^1.5.0" mkdirp "^0.5.1" - nan "^2.8.0" + nan "^2.14.0" spdx-correct@~1.0.0: version "1.0.2" @@ -8440,7 +8639,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^3.0.0: +string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== @@ -8501,7 +8700,7 @@ strip-ansi@^5.0.0: dependencies: ansi-regex "^4.0.0" -strip-ansi@^5.1.0: +strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== @@ -8540,10 +8739,10 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -sudo-prompt@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-8.2.0.tgz#bcd4aaacdb367b77b4bffcce1c658c2b1dd327f3" - integrity sha512-n5Nv2lIZaWfVBg10EWC8yaJCB6xV7sEsuaISAVFIS9F4fTRjy/O35A82lkweKuSqQItDlKOGQpTHK9/udQhRRw== +sudo-prompt@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.0.0.tgz#eebedeee9fcd6f661324e6bb46335e3288e8dc8a" + integrity sha512-kUn5fiOk0nhY2oKD9onIkcNCE4Zt85WTsvOfSmqCplmlEvXCcPOmp1npH5YWuf8Bmyy9wLWkIxx+D+8cThBORQ== sumchecker@^2.0.1: version "2.0.2" @@ -8623,6 +8822,18 @@ 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== +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + table@^5.0.2: version "5.2.2" resolved "https://registry.yarnpkg.com/table/-/table-5.2.2.tgz#61d474c9e4d8f4f7062c98c7504acb3c08aa738f" @@ -8633,16 +8844,6 @@ table@^5.0.2: slice-ansi "^2.0.0" string-width "^2.1.1" -table@^5.2.3: - version "5.4.1" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.1.tgz#0691ae2ebe8259858efb63e550b6d5f9300171e8" - integrity sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w== - dependencies: - ajv "^6.9.1" - lodash "^4.17.11" - slice-ansi "^2.1.0" - string-width "^3.0.0" - tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" @@ -8718,7 +8919,7 @@ temp@^0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -text-table@^0.2.0: +text-table@^0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= @@ -9011,13 +9212,6 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -typechecker@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-4.5.0.tgz#c382920097812364bbaf4595b0ab6588244117a6" - integrity sha512-bqPE/ck3bVIaXP7gMKTKSHrypT32lpYTpiqzPYeYzdSQnmaGvaGhy7TnN/M/+5R+2rs/kKcp9ZLPRp/Q9Yj+4w== - dependencies: - editions "^1.3.4" - typed-rest-client@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-0.9.0.tgz#f768cc0dc3f4e950f06e04825c36b3e7834aa1f2" @@ -9046,10 +9240,10 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@3.4.5: - version "3.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" - integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== +typescript@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" + integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== typescript@^2.6.2: version "2.6.2" @@ -9233,6 +9427,11 @@ upath@^1.0.5, upath@^1.1.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + uri-js@^4.2.1, uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -9505,46 +9704,49 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-anymatch@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-1.3.3.tgz#0613d31a949c8025473bbdad848d219f47a44f86" - integrity sha512-LQ4vF4BWb9gwAvbMtN+3HC4HKDxLd+ZyWmAjACOdD05O/ZMcgvvnjO24GseEIQ6cWn8gW+Ft08gHFihnQy1eSw== +vscode-anymatch@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-3.0.3.tgz#5a79101e6df7e659a1f070367bc42f190eb4ae76" + integrity sha512-qQgfbzJJ5nNShh4jjC3BBekY4d8emcxHFgnqcXwsB/PUKvJPCg7AZYXM7hqS7EDnKrX9tsIFwFMihZ7yut92Qg== dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" + normalize-path "^3.0.0" + picomatch "^2.0.4" -vscode-chokidar@1.6.5: - version "1.6.5" - resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-1.6.5.tgz#f38a1f909fa364a5429a4d4d70ecb40a15b54d0b" - integrity sha512-bc5dNF8tW2R+oesYT/Au//qumyd/0HTwSRqVXcg8ADQW1MsWKFwv+IxfSIuCHckaEy4I81GpSDaRWVGQqtsELw== +vscode-chokidar@2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-2.1.7.tgz#c5b31eb87402f4779bb4170915245bdcb6f7854b" + integrity sha512-uSNEQetPjAlgIAHmcF9E6M+KCw0f842rsEnJ64aamUAV6TO7gkXNCvLSzb4MuLsPU7ZQyCa++DrLQFjvciK5dg== dependencies: - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" is-binary-path "^1.0.0" - is-glob "^2.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" path-is-absolute "^1.0.0" - readdirp "^2.0.0" - vscode-anymatch "1.3.3" + readdirp "^2.2.1" + upath "^1.1.1" + vscode-anymatch "3.0.3" optionalDependencies: - vscode-fsevents "0.3.10" + vscode-fsevents "1.2.12" -vscode-debugprotocol@1.34.0: - version "1.34.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.34.0.tgz#aef63274166ccbc6d1d68e68c7d7f6d013802f08" - integrity sha512-tcMThtgk9TUtE8zzAIwPvHZfgnEYnVa7cI3YaQk/o54Q9cme+TLd/ao60a6ycj5rCrI/B5r/mAfeK5EKSItm7g== +vscode-debugprotocol@1.35.0: + version "1.35.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.35.0.tgz#565140cd42945e30c6c85cafb38c631457d4a46c" + integrity sha512-+OMm11R1bGYbpIJ5eQIkwoDGFF4GvBz3Ztl6/VM+/RNNb2Gjk2c0Ku+oMmfhlTmTlPCpgHBsH4JqVCbUYhu5bA== -vscode-fsevents@0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-0.3.10.tgz#65a586c3c6df3080bea20482146963a0f3a2c58d" - integrity sha512-iNlCKNgEB9A2JZkLf4h4sJlOS1u0lbe4QjM0Dr0PHaTmsttkJEfOaQeci2Ja1wUA7hUUAF6sNbei/Qp2DacFLw== +vscode-fsevents@1.2.12: + version "1.2.12" + resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-1.2.12.tgz#01a71a01f90ee95ca822c34427aba437a17c03a7" + integrity sha512-bH/jRdDpSesGpqiVLjp6gHLSKUOh7oNvppzZ17JIrdbRYCcDmV7dIWR5gQc27DFy0RD9JDT+t+ixMid94MkM1A== dependencies: - nan "^2.10.0" + nan "^2.14.0" -vscode-nls-dev@3.2.5: - version "3.2.5" - resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-3.2.5.tgz#bea2b6e0cae709c48144180585e1a511edc9fb8d" - integrity sha512-eiNkwDHgTjP1h23BCOmAlXbFVembGokALYIvID5LMBzYppOiJzN/rGatHBlThQl6lnHWv599UEre6/AbjioYYw== +vscode-nls-dev@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-3.3.1.tgz#15fc03e0c9ca5a150abb838690d9554ac06f77e4" + integrity sha512-fug18D7CXb8pv8JoQ0D0JmZaIYDQoKLiyZxkAy5P8Cln/FwlNsdzwQILDph62EdGY5pvsJ2Jd1T5qgHAExe/tg== dependencies: ansi-colors "^3.2.3" clone "^2.1.1" @@ -9557,17 +9759,7 @@ vscode-nls-dev@3.2.5: typescript "^2.6.2" vinyl "^2.1.0" xml2js "^0.4.19" - yargs "^10.1.1" - -vscode-nsfw@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.1.1.tgz#7c3febe153677c5850b197a0b64a197cd11e95c7" - integrity sha512-Wg3vzN1U3T6P1uE13LdVVRIhdy7XWnWkwmAXhkLsIkH2QY0E/pvNDRLrwAMMW6GC1Fvvbxm3hzdIrCmr7Hq3FA== - dependencies: - fs-extra "^7.0.0" - lodash.isinteger "^4.0.4" - lodash.isundefined "^3.0.1" - nan "^2.10.0" + yargs "^13.2.4" vscode-proxy-agent@0.4.0: version "0.4.0" @@ -9579,24 +9771,24 @@ vscode-proxy-agent@0.4.0: https-proxy-agent "2.2.1" socks-proxy-agent "4.0.1" -vscode-ripgrep@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.5.tgz#2093c8f36d52bd2dab9eb45b003dd02533c5499c" - integrity sha512-n5XBm9od5hahpljw9T8wbkuMnAY7LlAG1OyEEtcCZEX9aCHFuBKSP0IcvciGRTbtWRovNuT83A2iRjt6PL3bLg== +vscode-ripgrep@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.3.1.tgz#51fb93debcd0c18a8b90dbc37f84f94333d0c486" + integrity sha512-4WLB/n4ZeWNi5AEzPTkfYrqbKtXlv0SlgmxbRVdulwZzGx/lfWeWPu9Shy32orM27IofQAQDuirbRBOYNJVzBA== -vscode-sqlite3@4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/vscode-sqlite3/-/vscode-sqlite3-4.0.7.tgz#7adbf0fe411c87716ca3c4e467f04de3a7353125" - integrity sha512-1BqWdf6Nzs+q7JC+JFXDLX2Z8ZID7lZH69AoLh9FXos7XgLbF4dsmUZO5a6d9X3Jccu/m0PfKK1K4E6dk/xiRg== +vscode-sqlite3@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/vscode-sqlite3/-/vscode-sqlite3-4.0.8.tgz#1eba415b996d2661628d80d4a191a20767713829" + integrity sha512-FsFtYSHmy0mYjtt9ibFKsJqbRzqaltDKZ5SLdpykjvORugFMr0HfGunkh+qGaz9CvAiqjM2KVO91NE9KdyTWKQ== dependencies: - nan "~2.10.0" + nan "^2.14.0" -vscode-textmate@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.0.1.tgz#6c36f28e9059ce12bc34907f7a33ea43166b26a8" - integrity sha512-gHTXTj04TUgbjB8y7pkVwxOiuCuD6aU5gnFzIByQuqdgFpe/bJaaEIS4geGjbjWbd1XJh6zG1EthLfpNaXEqUw== +vscode-textmate@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.2.2.tgz#0b4dabc69a6fba79a065cb6b615f66eac07c8f4c" + integrity sha512-1U4ih0E/KP1zNK/EbpUqyYtI7PY+Ccd2nDGTtiMR/UalLFnmaYkwoWhN1oI7B91ptBN8NdVwWuvyUnvJAulCUw== dependencies: - oniguruma "^7.0.0" + oniguruma "^7.2.0" vscode-windows-ca-certs@0.1.0: version "0.1.0" @@ -9612,11 +9804,6 @@ vscode-windows-registry@1.0.1: dependencies: nan "^2.12.1" -vscode-xterm@3.14.0-beta3: - version "3.14.0-beta3" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.14.0-beta3.tgz#e2624e231f7b940edd0675eb569a10b3af75e29d" - integrity sha512-80Bbq6R4q0xABJl7COwE1DSNoJp3H2LyfKDHSbh1PwUY+6wofpW8/V9xSYELizeg3lVAd7mcEKW+rG+sY1hpqA== - vso-node-api@6.1.2-preview: version "6.1.2-preview" resolved "https://registry.yarnpkg.com/vso-node-api/-/vso-node-api-6.1.2-preview.tgz#aab3546df2451ecd894e071bb99b5df19c5fa78f" @@ -9766,12 +9953,12 @@ windows-mutex@0.2.1: bindings "^1.2.1" nan "^2.10.0" -windows-process-tree@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/windows-process-tree/-/windows-process-tree-0.2.3.tgz#6b781f0a320e8a0d6434c9399add4389c709cf6e" - integrity sha512-SzPJSubVVsToz1g5lr2P+4mQT70gvJ9u/nlnpfkOeQcAhOuhKz5DiO1TARgR0OnVsv21LPzxbA2m/4JQkGh1wA== +windows-process-tree@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/windows-process-tree/-/windows-process-tree-0.2.4.tgz#747af587b54cc6c996f2be0836cc8a8fd0dc038f" + integrity sha512-9gag9AHm3Iin/4YC1EwoIfZlqW/rG2eV7rJZ4Fy5NnAMGdewmnwsie5Rz+CJo2vSolqzzfw7hPeu3oOdniNejg== dependencies: - nan "^2.10.0" + nan "^2.13.2" wordwrap@0.0.2: version "0.0.2" @@ -9803,18 +9990,20 @@ wrap-ansi@^2.0.0: string-width "^1.0.1" strip-ansi "^3.0.1" +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - write@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" @@ -9893,6 +10082,21 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" +xterm-addon-search@0.2.0-beta2: + version "0.2.0-beta2" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta2.tgz#c3173f0a6f207ee9f1848849174ee5d6b6ce8262" + integrity sha512-XEcwi2TeFGk2MuIFjiI/OpVXSNO5dGQBvHH3o+9KzqG3ooVqhhDqzwxs092QGNcNCGh8hGn/PWZiczaBBnKm/g== + +xterm-addon-web-links@0.1.0-beta10: + version "0.1.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" + integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== + +xterm@3.15.0-beta71: + version "3.15.0-beta71" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta71.tgz#2728c9800ca3b08423e835e9504bd1f4b5de6253" + integrity sha512-8M/cLaxZ+iDopRxLPdPfKuDGaNNyYTdCeytdxjMSH0N7dZzbx6fbaEygQdCrV5pO9cGnT92MefSjVPGRXRiBLA== + y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" @@ -9920,6 +10124,14 @@ yargs-parser@^10.1.0: dependencies: camelcase "^4.1.0" +yargs-parser@^13.1.0: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" @@ -9927,31 +10139,6 @@ yargs-parser@^5.0.0: dependencies: camelcase "^3.0.0" -yargs-parser@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" - integrity sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ== - dependencies: - camelcase "^4.1.0" - -yargs@^10.1.1: - version "10.1.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" - integrity sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig== - dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^8.1.0" - yargs@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2" @@ -9970,6 +10157,23 @@ yargs@^12.0.1: y18n "^3.2.1 || ^4.0.0" yargs-parser "^10.1.0" +yargs@^13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.0" + yargs@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" @@ -10006,7 +10210,7 @@ yauzl@2.4.1: dependencies: fd-slicer "~1.0.1" -yauzl@^2.2.1, yauzl@^2.3.1, yauzl@^2.9.1: +yauzl@^2.2.1, yauzl@^2.3.1: version "2.9.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f" integrity sha1-qBmB6nCleUYTOIPwKcWCGok1mn8= @@ -10014,6 +10218,14 @@ yauzl@^2.2.1, yauzl@^2.3.1, yauzl@^2.9.1: buffer-crc32 "~0.2.3" fd-slicer "~1.0.1" +yauzl@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yazl@^2.2.1, yazl@^2.2.2, yazl@^2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071"